1 /*
2 * AIDE (Advanced Intrusion Detection Environment)
3 *
4 * Copyright (C) 1999-2007, 2010-2013, 2016, 2018-2021 Rami Lehti,
5 * Pablo Virolainen, Mike Markley, Richard van den Berg,
6 * Hannes von Haugwitz
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include "aide.h"
24 #include <stdio.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <time.h>
30
31 #include "attributes.h"
32
33 #include <errno.h>
34
35 #include "types.h"
36 #include "base64.h"
37 #include "db.h"
38 #include "db_lex.h"
39 #include "db_file.h"
40 #include "gen_list.h"
41 #include "util.h"
42 #include "commandconf.h"
43 /*for locale support*/
44 #include "locale-aide.h"
45 /*for locale support*/
46
47 #ifdef WITH_MHASH
48 #include <mhash.h>
49 #endif
50
51 #ifdef WITH_ZLIB
52 #include <zlib.h>
53 #endif
54
55 #define BUFSIZE 16384
56
57 #include "md.h"
58
59
dofflush(void)60 int dofflush(void)
61 {
62
63 int retval;
64 #ifdef WITH_ZLIB
65 if(conf->gzip_dbout){
66 /* Should not flush using gzip, it degrades compression */
67 retval=Z_OK;
68 }else {
69 #endif
70 retval=fflush(conf->database_out.fp);
71 #ifdef WITH_ZLIB
72 }
73 #endif
74
75 return retval;
76 }
77
78 int dofprintf(const char*, ...)
79 #ifdef __GNUC__
80 __attribute__ ((format (printf, 1, 2)))
81 #endif
82 ;
dofprintf(const char * s,...)83 int dofprintf( const char* s,...)
84 {
85 char buf[3];
86 int retval;
87 char* temp=NULL;
88 va_list ap;
89
90 va_start(ap,s);
91 retval=vsnprintf(buf,3,s,ap);
92 va_end(ap);
93
94 temp=(char*)checked_malloc(retval+2);
95
96 va_start(ap,s);
97 retval=vsnprintf(temp,retval+1,s,ap);
98 va_end(ap);
99
100 if ((conf->database_out).mdc) {
101 update_md((conf->database_out).mdc,temp ,retval);
102 }
103
104 #ifdef WITH_ZLIB
105 if(conf->gzip_dbout){
106 retval=gzwrite((conf->database_out).gzp,temp,retval);
107 }else{
108 #endif
109 /* writing is ok with fwrite with curl.. */
110 retval=fwrite(temp,1,retval,conf->database_out.fp);
111 #ifdef WITH_ZLIB
112 }
113 #endif
114 free(temp);
115
116 return retval;
117 }
118
119
120
db_file_read_spec(database * db)121 static int db_file_read_spec(database* db){
122 int i=0;
123
124 DB_ATTR_TYPE seen_attrs = 0LLU;
125
126 db->fields = checked_malloc(1*sizeof(ATTRIBUTE));
127
128 while ((i=db_scan())!=TNEWLINE){
129 LOG_DB_FORMAT_LINE(LOG_LEVEL_TRACE, db_file_read_spec(): db_scan() returned token=%d, i);
130
131 switch (i) {
132
133 case TSTRING : {
134 ATTRIBUTE l;
135 db->fields = checked_realloc(db->fields, (db->num_fields+1)*sizeof(ATTRIBUTE));
136 db->fields[db->num_fields]=attr_unknown;
137 for (l=0;l<num_attrs;l++){
138 if (attributes[l].db_name && strcmp(attributes[l].db_name,dbtext)==0) {
139 if (ATTR(l)&seen_attrs) {
140 LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, @@dbspec: skip redefined field '%s' at position %i, dbtext, db->num_fields)
141 db->fields[db->num_fields]=attr_unknown;
142 } else {
143 db->fields[db->num_fields]=l;
144 seen_attrs |= ATTR(l);
145 LOG_DB_FORMAT_LINE(LOG_LEVEL_DEBUG, @@dpspec: define field '%s' at position %i, dbtext, db->num_fields)
146 }
147 db->num_fields++;
148 break;
149 }
150 }
151
152 if(l==attr_unknown){
153 LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, @@dbspec: skip unknown field '%s' at position %i, dbtext, db->num_fields);
154 db->fields[db->num_fields]=attr_unknown;
155 db->num_fields++;
156 }
157 break;
158 }
159
160 default : {
161 LOG_DB_FORMAT_LINE(LOG_LEVEL_ERROR, unexpected token while reading dbspec: '%s', dbtext);
162 return RETFAIL;
163 }
164 }
165 }
166
167 /* Lets generate attr from db_order if database does not have attr */
168 conf->attr=DB_ATTR_UNDEF;
169
170 for (i=0;i<db->num_fields;i++) {
171 if (db->fields[i] == attr_attr) {
172 conf->attr=1;
173 }
174 }
175 if (conf->attr==DB_ATTR_UNDEF) {
176 conf->attr=0;
177 for(i=0;i<db->num_fields;i++) {
178 conf->attr|=1LL<<db->fields[i];
179 }
180 char *str;
181 LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, missing attr field%c generated attr field from dbspec: %s (comparison may be incorrect), ',', str = diff_database_attributes(0, conf->attr))
182 free(str);
183 }
184 return RETOK;
185 }
186
skip_line(database * db)187 DB_TOKEN skip_line(database* db) {
188 DB_TOKEN token;
189 do {
190 token = db_scan();
191 LOG_DB_FORMAT_LINE(LOG_LEVEL_TRACE, db_readline_file(): db_scan() returned a=%d, token);
192 LOG_DB_FORMAT_LINE(LOG_LEVEL_DEBUG, skip_line(): skip '%s', token==TNEWLINE?"\n":dbtext)
193 } while(token != TNEWLINE && token != TEOF);
194 return token;
195 }
196
db_readline_file(database * db)197 char** db_readline_file(database* db) {
198 log_msg(LOG_LEVEL_TRACE, "db_readline_file(): arguments db=%p", db);
199 char** s=NULL;
200
201 int i=0;
202 int a=0;
203 DB_TOKEN token;
204 bool found_enddb = false;;
205
206 do {
207 token = db_scan();
208 LOG_DB_FORMAT_LINE(LOG_LEVEL_TRACE, db_readline_file(): db_scan() returned token=%d, token);
209 if (db->fields) {
210 switch (token) {
211 case TUNKNOWN: {
212 LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, unknown token '%s' found inside database (skip line), dbtext)
213 skip_line(db);
214 break;
215 }
216 case TDBSPEC:
217 case TBEGIN_DB: {
218 LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, additional '%s' found inside database (skip line), dbtext)
219 skip_line(db);
220 break;
221 }
222 case TEND_DB: {
223 LOG_DB_FORMAT_LINE(LOG_LEVEL_DEBUG, %s, "'@@end_db' found")
224 found_enddb = true;
225 break;
226 }
227 case TEOF:
228 case TNEWLINE: {
229 if (found_enddb) {
230 LOG_DB_FORMAT_LINE(LOG_LEVEL_DEBUG, %s, "stop reading database")
231 return s;
232 } else if (token == TEOF) {
233 LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, %s, "missing '@@end_db' in database")
234 return s;
235 }
236 if (s) {
237 if (i<db->num_fields-1) {
238 LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, cutoff database line '%s' found (field '%s' (position: %d) is missing) (skip line), s[0], attributes[db->fields[i+1]].db_name, i+1);
239 for(a=0;a<i;a++){
240 free(s[db->fields[a]]);
241 s[db->fields[a]] = NULL;
242 }
243 free(s);
244 s = NULL;
245 } else {
246 return s;
247 }
248 }
249 break;
250 }
251 case TPATH: {
252 i = 0;
253 s = checked_malloc(sizeof(char*)*num_attrs);
254 for(ATTRIBUTE j=0; j<num_attrs; j++){
255 s[j]=NULL;
256 }
257 s[i] = checked_strdup(dbtext);
258 LOG_DB_FORMAT_LINE(LOG_LEVEL_DEBUG, '%s' set field '%s' (position %d): '%s', s[0], attributes[db->fields[i]].db_name, i, dbtext);
259 break;
260 }
261 case TSTRING: {
262 if (s) {
263 if (++i<db->num_fields) {
264 if (db->fields[i] != attr_unknown) {
265 LOG_DB_FORMAT_LINE(LOG_LEVEL_DEBUG, '%s' set field '%s' (position %d): '%s', s[0], attributes[db->fields[i]].db_name, i, dbtext);
266 s[db->fields[i]] = checked_strdup(dbtext);
267 } else {
268 LOG_DB_FORMAT_LINE(LOG_LEVEL_DEBUG, skip unknown/redefined field at position: %d: '%s', i, dbtext);
269 }
270 } else {
271 LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, expected newline or end of file (skip found string '%s'), dbtext);
272 }
273 } else {
274 LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, expected newline or end of file (skip found string '%s'), dbtext);
275 }
276 break;
277 }
278 }
279 } else {
280 if (token == TEOF) {
281 /* allow empty database */
282 LOG_DB_FORMAT_LINE(LOG_LEVEL_INFO, db_readline_file(): empty database file, NULL);
283 return s;
284 }
285 while (token != TBEGIN_DB) {
286 if (token == TEOF) {
287 LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, db_readline_file(): '@@begin_db' NOT found (stop reading database), NULL);
288 return s;
289 }
290 LOG_DB_FORMAT_LINE(LOG_LEVEL_DEBUG, db_readline_file(): skip '%s', dbtext);
291 token = db_scan();
292 LOG_DB_FORMAT_LINE(LOG_LEVEL_TRACE, db_readline_file(): db_scan() returned token=%d, token);
293 }
294 LOG_DB_FORMAT_LINE(LOG_LEVEL_DEBUG, '@@begin_db' found, NULL)
295 token = db_scan();
296 LOG_DB_FORMAT_LINE(LOG_LEVEL_TRACE, db_readline_file(): db_scan() returned token=%d, token);
297 if (token != TNEWLINE) {
298 LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, db_readline_file(): missing newline after '@@begin_db' (stop reading database), NULL);
299 return s;
300
301 } else {
302 token = db_scan();
303 LOG_DB_FORMAT_LINE(LOG_LEVEL_TRACE, db_readline_file(): db_scan() returned token=%d, token);
304 if (token != TDBSPEC) {
305 LOG_DB_FORMAT_LINE(LOG_LEVEL_WARNING, db_readline_file(): unexpected token '%s'%c expected '@@db_spec' (stop reading database), dbtext, 'c');
306 return s;
307 } else {
308 LOG_DB_FORMAT_LINE(LOG_LEVEL_DEBUG, '@@dbspec' found, NULL)
309 if (db_file_read_spec(db)!=0) {
310 /* something went wrong */
311 return s;
312 }
313 }
314 }
315 }
316 } while (token != TEOF);
317
318 return s;
319
320 }
321
db_writechar(char * s,FILE * file,int i)322 int db_writechar(char* s,FILE* file,int i)
323 {
324 char* r=NULL;
325 int retval=0;
326
327 (void)file;
328
329 if(i) {
330 dofprintf(" ");
331 }
332
333 if(s==NULL){
334 retval=dofprintf("0");
335 return retval;
336 }
337 if(s[0]=='\0'){
338 retval=dofprintf("0-");
339 return retval;
340 }
341 if(s[0]=='0'){
342 retval=dofprintf("00");
343 if(retval<0){
344 return retval;
345 }
346 s++;
347 }
348
349 if (!i && s[0]=='#') {
350 dofprintf("# ");
351 r=CLEANDUP(s+1);
352 } else {
353 r=CLEANDUP(s);
354 }
355
356 retval=dofprintf("%s",r);
357 free(r);
358 return retval;
359 }
360
db_writelong(long i,FILE * file,int a)361 static int db_writelong(long i,FILE* file,int a)
362 {
363 (void)file;
364
365 if(a) {
366 dofprintf(" ");
367 }
368
369 return dofprintf("%li",i);
370
371 }
372
db_writelonglong(long long i,FILE * file,int a)373 static int db_writelonglong(long long i,FILE* file,int a)
374 {
375 (void)file;
376
377 if(a) {
378 dofprintf(" ");
379 }
380
381 return dofprintf("%lli",i);
382
383 }
384
385
db_write_attr(DB_ATTR_TYPE i,FILE * file,int a)386 int db_write_attr(DB_ATTR_TYPE i,FILE* file,int a)
387 {
388 (void)file;
389 if(a) {
390 dofprintf(" ");
391 }
392 return dofprintf("%llu", i);
393 }
394
db_write_byte_base64(byte * data,size_t len,FILE * file,int i,DB_ATTR_TYPE th,DB_ATTR_TYPE attr)395 int db_write_byte_base64(byte*data,size_t len,FILE* file,int i,
396 DB_ATTR_TYPE th, DB_ATTR_TYPE attr )
397 {
398 char* tmpstr=NULL;
399 int retval=0;
400
401 (void)file;
402 if (data && !len)
403 len = strlen((const char *)data);
404
405 if (data!=NULL&&th&attr) {
406 tmpstr=encode_base64(data,len);
407 } else {
408 tmpstr=NULL;
409 }
410 if(i){
411 dofprintf(" ");
412 }
413
414 if(tmpstr){
415 retval=dofprintf("%s", tmpstr);
416 free(tmpstr);
417 return retval;
418 }else {
419 return dofprintf("0");
420 }
421 return 0;
422
423 }
424
db_write_time_base64(time_t i,FILE * file,int a)425 int db_write_time_base64(time_t i,FILE* file,int a)
426 {
427 static char* ptr=NULL;
428 char* tmpstr=NULL;
429 int retval=0;
430
431 (void)file;
432
433 if(a){
434 dofprintf(" ");
435 }
436
437 if(i==0){
438 retval=dofprintf("0");
439 return retval;
440 }
441
442
443 ptr=(char*)checked_malloc(sizeof(char)*TIMEBUFSIZE);
444
445 memset((void*)ptr,0,sizeof(char)*TIMEBUFSIZE);
446
447 sprintf(ptr,"%li",i);
448
449
450 tmpstr=encode_base64((byte *)ptr,strlen(ptr));
451 retval=dofprintf("%s", tmpstr);
452 free(tmpstr);
453 free(ptr);
454
455 return retval;
456
457 }
458
db_writeoct(long i,FILE * file,int a)459 int db_writeoct(long i, FILE* file,int a)
460 {
461 (void)file;
462
463 if(a) {
464 dofprintf(" ");
465 }
466
467 return dofprintf("%lo",i);
468
469 }
470
db_writespec_file(db_config * dbconf)471 int db_writespec_file(db_config* dbconf)
472 {
473 int retval=1;
474 struct tm* st;
475 time_t tim=time(&tim);
476 st=localtime(&tim);
477
478 retval=dofprintf("@@begin_db\n");
479 if(retval==0){
480 return RETFAIL;
481 }
482
483 if(dbconf->database_add_metadata) {
484 retval=dofprintf(
485 "# This file was generated by Aide, version %s\n"
486 "# Time of generation was %.4u-%.2u-%.2u %.2u:%.2u:%.2u\n",
487 AIDEVERSION,
488 st->tm_year+1900, st->tm_mon+1, st->tm_mday,
489 st->tm_hour, st->tm_min, st->tm_sec
490 );
491 if(retval==0){
492 return RETFAIL;
493 }
494 }
495 if(dbconf->config_version){
496 retval=dofprintf(
497 "# The config version used to generate this file was:\n"
498 "# %s\n", dbconf->config_version);
499 if(retval==0){
500 return RETFAIL;
501 }
502 }
503 retval=dofprintf("@@db_spec ");
504 if(retval==0){
505 return RETFAIL;
506 }
507 for (ATTRIBUTE i = 0 ; i < num_attrs ; ++i) {
508 if (attributes[i].db_name && attributes[i].attr&conf->db_out_attrs) {
509 retval=dofprintf("%s ", attributes[i].db_name);
510 if(retval==0){
511 return RETFAIL;
512 }
513 }
514 }
515 retval=dofprintf("\n");
516 if(retval==0){
517 return RETFAIL;
518 }
519 return RETOK;
520 }
521
522 #ifdef WITH_ACL
db_writeacl(acl_type * acl,FILE * file,int a)523 int db_writeacl(acl_type* acl,FILE* file,int a)
524 {
525 #ifdef WITH_POSIX_ACL
526 if(a) {
527 dofprintf(" ");
528 }
529
530 if (acl==NULL) {
531 dofprintf("0");
532 } else {
533 dofprintf("POSIX"); /* This is _very_ incompatible */
534
535 dofprintf(",");
536 if (acl->acl_a)
537 db_write_byte_base64((byte*)acl->acl_a, 0, file,0,1,1);
538 else
539 dofprintf("0");
540 dofprintf(",");
541 if (acl->acl_d)
542 db_write_byte_base64((byte*)acl->acl_d, 0, file,0,1,1);
543 else
544 dofprintf("0");
545 }
546 #endif
547 return RETOK;
548 }
549 #endif
550
551
552 #define WRITE_HASHSUM(x) \
553 case attr_ ##x : { \
554 db_write_byte_base64(line->hashsums[hash_ ##x], \
555 hashsums[hash_ ##x].length, \
556 dbconf->database_out.fp, i, \
557 ATTR(attr_ ##x), line->attr); \
558 break; \
559 }
560
db_writeline_file(db_line * line,db_config * dbconf,url_t * url)561 int db_writeline_file(db_line* line,db_config* dbconf, url_t* url){
562
563 (void)url;
564
565 for (ATTRIBUTE i = 0 ; i < num_attrs ; ++i) {
566 if (attributes[i].db_name && ATTR(i)&conf->db_out_attrs) {
567 switch (i) {
568 case attr_filename : {
569 db_writechar(line->filename,dbconf->database_out.fp,i);
570 break;
571 }
572 case attr_linkname : {
573 db_writechar(line->linkname,dbconf->database_out.fp,i);
574 break;
575 }
576 case attr_bcount : {
577 db_writelonglong(line->bcount,dbconf->database_out.fp,i);
578 break;
579 }
580
581 case attr_mtime : {
582 db_write_time_base64(line->mtime,dbconf->database_out.fp,i);
583 break;
584 }
585 case attr_atime : {
586 db_write_time_base64(line->atime,dbconf->database_out.fp,i);
587 break;
588 }
589 case attr_ctime : {
590 db_write_time_base64(line->ctime,dbconf->database_out.fp,i);
591 break;
592 }
593 case attr_inode : {
594 db_writelong(line->inode,dbconf->database_out.fp,i);
595 break;
596 }
597 case attr_linkcount : {
598 db_writelong(line->nlink,dbconf->database_out.fp,i);
599 break;
600 }
601 case attr_uid : {
602 db_writelong(line->uid,dbconf->database_out.fp,i);
603 break;
604 }
605 case attr_gid : {
606 db_writelong(line->gid,dbconf->database_out.fp,i);
607 break;
608 }
609 case attr_size : {
610 db_writelonglong(line->size,dbconf->database_out.fp,i);
611 break;
612 }
613 case attr_perm : {
614 db_writeoct(line->perm,dbconf->database_out.fp,i);
615 break;
616 }
617 WRITE_HASHSUM(md5)
618 WRITE_HASHSUM(sha1)
619 WRITE_HASHSUM(rmd160)
620 WRITE_HASHSUM(tiger)
621 WRITE_HASHSUM(crc32)
622 WRITE_HASHSUM(crc32b)
623 WRITE_HASHSUM(haval)
624 WRITE_HASHSUM(gostr3411_94)
625 WRITE_HASHSUM(stribog256)
626 WRITE_HASHSUM(stribog512)
627 WRITE_HASHSUM(sha256)
628 WRITE_HASHSUM(sha512)
629 WRITE_HASHSUM(whirlpool)
630 case attr_attr : {
631 db_write_attr(line->attr, dbconf->database_out.fp,i);
632 break;
633 }
634 #ifdef WITH_ACL
635 case attr_acl : {
636 db_writeacl(line->acl,dbconf->database_out.fp,i);
637 break;
638 }
639 #endif
640 case attr_xattrs : {
641 xattr_node *xattr = NULL;
642 size_t num = 0;
643
644 if (!line->xattrs)
645 {
646 db_writelong(0, dbconf->database_out.fp, i);
647 break;
648 }
649
650 db_writelong(line->xattrs->num, dbconf->database_out.fp, i);
651
652 xattr = line->xattrs->ents;
653 while (num < line->xattrs->num)
654 {
655 dofprintf(",");
656 db_writechar(xattr->key, dbconf->database_out.fp, 0);
657 dofprintf(",");
658 db_write_byte_base64(xattr->val, xattr->vsz, dbconf->database_out.fp, 0, 1, 1);
659
660 ++xattr;
661 ++num;
662 }
663 break;
664 }
665 case attr_selinux : {
666 db_write_byte_base64((byte*)line->cntx, 0, dbconf->database_out.fp, i, 1, 1);
667 break;
668 }
669 #ifdef WITH_E2FSATTRS
670 case attr_e2fsattrs : {
671 db_writelong(line->e2fsattrs,dbconf->database_out.fp,i);
672 break;
673 }
674 #endif
675 #ifdef WITH_CAPABILITIES
676 case attr_capabilities : {
677 db_write_byte_base64((byte*)line->capabilities, 0, dbconf->database_out.fp, i, 1, 1);
678 break;
679 }
680 #endif
681 default : {
682 log_msg(LOG_LEVEL_ERROR,"not implemented in db_writeline_file %i", i);
683 return RETFAIL;
684 }
685
686 }
687
688 }
689
690 }
691
692 dofprintf("\n");
693 /* Can't use fflush because of zlib.*/
694 dofflush();
695
696 return RETOK;
697 }
698
db_close_file(db_config * dbconf)699 int db_close_file(db_config* dbconf){
700
701 if(dbconf->database_out.fp
702 #ifdef WITH_ZLIB
703 || dbconf->database_out.gzp
704 #endif
705 ){
706 dofprintf("@@end_db\n");
707 }
708
709 #ifdef WITH_ZLIB
710 if(dbconf->gzip_dbout){
711 if(gzclose(dbconf->database_out.gzp)){
712 log_msg(LOG_LEVEL_ERROR,"unable to gzclose database '%s:%s': %s", get_url_type_string((dbconf->database_out.url)->type), (dbconf->database_out.url)->value, strerror(errno));
713 return RETFAIL;
714 }
715 }else {
716 #endif
717 if(fclose(dbconf->database_out.fp)){
718 log_msg(LOG_LEVEL_ERROR,"unable to close database '%s:%s': %s", get_url_type_string((dbconf->database_out.url)->type), (dbconf->database_out.url)->value, strerror(errno));
719 return RETFAIL;
720 }
721 #ifdef WITH_ZLIB
722 }
723 #endif
724
725 return RETOK;
726 }
727 // vi: ts=8 sw=8
728