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