1 /*
2  * AIDE (Advanced Intrusion Detection Environment)
3  *
4  * Copyright (C) 1999-2006, 2010-2013, 2015-2017, 2019-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 
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <sys/types.h>
32 #include <dirent.h>
33 #include <time.h>
34 
35 #if HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 
39 #include "md.h"
40 #include "commandconf.h"
41 #include "report.h"
42 #include "db_config.h"
43 #include "db_disk.h"
44 #include "db.h"
45 #include "do_md.h"
46 #include "log.h"
47 #include "errorcodes.h"
48 #include "gen_list.h"
49 #include "getopt.h"
50 #include "list.h"
51 #include "util.h"
52 #include "base64.h"
53 /*for locale support*/
54 #include "locale-aide.h"
55 /*for locale support*/
56 db_config* conf;
57 char* before = NULL;
58 char* after = NULL;
59 
60 #ifndef MAXHOSTNAMELEN
61 #define MAXHOSTNAMELEN 256
62 #endif
63 
usage(int exitvalue)64 static void usage(int exitvalue)
65 {
66   fprintf(stdout,
67 	  _("Aide " AIDEVERSION" \n\n"
68 	    "Usage: aide [options] command\n\n"
69 	    "Commands:\n"
70 	    "  -i, --init\t\tInitialize the database\n"
71 	    "  -n, --dry-init\tTraverse the file system and match each file against rule tree\n"
72 	    "  -C, --check\t\tCheck the database\n"
73 	    "  -u, --update\t\tCheck and update the database non-interactively\n"
74 	    "  -E, --compare\t\tCompare two databases\n\n"
75 	    "Miscellaneous:\n"
76 	    "  -D,\t\t\t--config-check\t\t\tTest the configuration file\n"
77 	    "  -p file_type:path\t--path-check=file_type:path\tMatch file type and path against rule tree\n"
78 	    "  -v,\t\t\t--version\t\t\tShow version of AIDE and compilation options\n"
79 	    "  -h,\t\t\t--help\t\t\t\tShow this help message\n\n"
80 	    "Options:\n"
81 	    "  -c [cfgfile]\t--config=[cfgfile]\tGet config options from [cfgfile]\n"
82 	    "  -l [REGEX]\t--limit=[REGEX]\t\tLimit command to entries matching [REGEX]\n"
83 	    "  -B \"OPTION\"\t--before=\"OPTION\"\tBefore configuration file is read define OPTION\n"
84 	    "  -A \"OPTION\"\t--after=\"OPTION\"\tAfter configuration file is read define OPTION\n"
85 	    "  -L [level]\t--log-level=[level]\tSet log message level to [level]\n"
86 	    "\n")
87 	  );
88 
89   exit(exitvalue);
90 }
91 
92 static void sig_handler(int);
93 
init_sighandler()94 static void init_sighandler()
95 {
96   signal(SIGBUS,sig_handler);
97   signal(SIGTERM,sig_handler);
98   signal(SIGUSR1,sig_handler);
99   signal(SIGHUP,sig_handler);
100 
101   return;
102 }
103 
sig_handler(int signum)104 static void sig_handler(int signum)
105 {
106   switch(signum){
107   case SIGBUS  : {
108     if(conf->catch_mmap==1){
109       log_msg(LOG_LEVEL_NOTICE, "Caught SIGBUS while mmapping. File was truncated while aide was running?");
110       conf->catch_mmap=0;
111     } else {
112       log_msg(LOG_LEVEL_ERROR, "Caught SIGBUS. Exiting");
113       exit(EXIT_FAILURE);
114     }
115     break;
116   }
117   case SIGHUP : {
118     log_msg(LOG_LEVEL_INFO, "Caught SIGHUP");
119     break;
120   }
121   case SIGTERM : {
122     log_msg(LOG_LEVEL_INFO, "Caught SIGTERM. Use SIGKILL to terminate");
123     break;
124   }
125   case SIGUSR1 : {
126     log_msg(LOG_LEVEL_INFO, "Caught SIGUSR1, toggle debug level: set log level to %s", get_log_level_name(toogle_log_level(LOG_LEVEL_DEBUG)));
127     break;
128   }
129   }
130   init_sighandler();
131 
132   return;
133 }
134 
print_version(void)135 static void print_version(void)
136 {
137   fprintf(stdout,
138 	  "Aide " AIDEVERSION "\n\n"
139 	  "Compiled with the following options:\n\n" AIDECOMPILEOPTIONS "\n");
140 
141   fprintf(stdout, "Default config values:\n");
142   fprintf(stdout, "config file: %s\n", conf->config_file?conf->config_file:"<none>");
143   fprintf(stdout, "database_in: %s\n",
144 #ifdef DEFAULT_DB
145           DEFAULT_DB
146 #else
147           "<none>"
148 #endif
149           ),
150   fprintf(stdout, "database_out: %s\n",
151 #ifdef DEFAULT_DB_OUT
152           DEFAULT_DB_OUT
153 #else
154           "<none>"
155 #endif
156           ),
157 
158   fprintf(stdout, "\nAvailable hashsum groups:\n");
159   DB_ATTR_TYPE available_hashsums = get_hashes(false);
160   for (int i = 0; i < num_hashes; ++i) {
161       fprintf(stdout, "%s: %s\n", attributes[hashsums[i].attribute].config_name, ATTR(hashsums[i].attribute)&available_hashsums?"yes":"no");
162   }
163 
164   fprintf(stdout, "\nDefault compound groups:\n");
165   char* predefined_groups[] = { "R", "L", ">", "H", "X" };
166   for (unsigned long i = 0 ; i < sizeof(predefined_groups)/sizeof(char*); ++i) {
167       char* str;
168       fprintf(stdout, "%s: %s\n", predefined_groups[i], str = diff_attributes(0, get_groupval(predefined_groups[i])));
169       free(str);
170   }
171 
172   exit(0);
173 }
174 
append_line_to_config(char * config,char * line)175 static char *append_line_to_config(char *config, char *line) {
176     size_t line_length = strlen(line);
177     if (config == NULL) {
178         config = checked_malloc((line_length+2)*sizeof(char));
179         strcpy(config,line);
180         strcat(config,"\n");
181     } else {
182         char *tmp = checked_malloc(sizeof(char) *(strlen(config)+line_length+2));
183         strcpy(tmp,config);
184         strcat(tmp,line);
185         strcat(tmp,"\n");
186         free(config);
187         config=tmp;
188     }
189     return config;
190 }
191 
192 #define INVALID_ARGUMENT(option, format, ...) \
193         fprintf(stderr, "%s: (%s): " #format "\n", argv[0], option, __VA_ARGS__); \
194         exit(INVALID_ARGUMENT_ERROR);
195 
196 #define ACTION_CASE(longopt, option, _action, desc) \
197       case option: { \
198             if(conf->action==0){ \
199                 conf->action=_action; \
200                 log_msg(LOG_LEVEL_INFO,"(%s): %s command", longopt, desc); \
201             } else { \
202                 INVALID_ARGUMENT(longopt, %s, "cannot have multiple commands on a single commandline") \
203                 exit(INVALID_ARGUMENT_ERROR); \
204             } \
205             break; \
206         }
207 
read_param(int argc,char ** argv)208 static void read_param(int argc,char**argv)
209 {
210   int option = -1;
211   int i=0;
212 
213 
214   static struct option options[] =
215   {
216     { "help", no_argument, NULL, 'h' },
217     { "verbose", optional_argument, NULL, 'V'},
218     { "version", no_argument, NULL, 'v'},
219     { "config", required_argument, NULL, 'c'},
220     { "before", required_argument, NULL, 'B'},
221     { "after", required_argument, NULL, 'A'},
222     { "report", no_argument, NULL, 'r'},
223     { "init", no_argument, NULL, 'i'},
224     { "dry-init", no_argument, NULL, 'n'},
225     { "check", no_argument, NULL, 'C'},
226     { "update", no_argument, NULL, 'u'},
227     { "config-check", no_argument, NULL, 'D'},
228     { "path-check", required_argument, NULL, 'p'},
229     { "limit", required_argument, NULL, 'l'},
230     { "log-level", required_argument, NULL, 'L'},
231     { "compare", no_argument, NULL, 'E'},
232     { NULL,0,NULL,0 }
233   };
234 
235   while(1){
236     option = getopt_long(argc, argv, "hL:V::vc:l:p:B:A:riCuDEn", options, &i);
237     if(option==-1)
238       break;
239     switch(option)
240       {
241       case 'h':{
242 	usage(0);
243 	break;
244       }
245       case 'v':{
246 	print_version();
247 	break;
248       }
249       case 'V':{
250         INVALID_ARGUMENT("--verbose", %s, "option no longer supported, use 'log_level' and 'report_level' options instead (see man aide.conf for details)")
251 	break;
252       }
253       case 'c':{
254 	  conf->config_file=optarg;
255       log_msg(LOG_LEVEL_INFO,_("(--config): set config file to '%s'"), conf->config_file);
256 	break;
257       }
258       case 'B': {
259         before = append_line_to_config(before, optarg);
260         log_msg(LOG_LEVEL_INFO,_("(--before): append '%s' to before config"), optarg);
261 	break;
262       }
263       case 'A': {
264         after = append_line_to_config(after, optarg);
265         log_msg(LOG_LEVEL_INFO,_("(--after): append '%s' to after config"), optarg);
266 	break;
267       }
268       case 'l': {
269                 const char* pcre_error;
270                 int pcre_erroffset;
271                 conf->limit=checked_malloc(strlen(optarg)+1);
272                 strcpy(conf->limit,optarg);
273                 if((conf->limit_crx=pcre_compile(conf->limit, PCRE_ANCHORED, &pcre_error, &pcre_erroffset, NULL)) == NULL) {
274                     INVALID_ARGUMENT("--limit", error in regular expression '%s' at %i: %s, conf->limit, pcre_erroffset, pcre_error)
275                 }
276                 log_msg(LOG_LEVEL_INFO,_("(--limit): set limit to '%s'"), conf->limit);
277             break;
278       }
279       case 'L':{
280             LOG_LEVEL level = get_log_level_from_string(optarg);
281             if (level == LOG_LEVEL_UNSET) {
282                 INVALID_ARGUMENT("--log-level", invalid log level '%s' (see man aide.conf for details), optarg)
283             } else {
284                 set_log_level(level);
285                 log_msg(LOG_LEVEL_INFO,"(--log-level): set log level to '%s'", optarg);
286             }
287            break;
288                }
289       case 'p':{
290             if(conf->action==0){
291                 conf->action=DO_DRY_RUN;
292                 log_msg(LOG_LEVEL_INFO,"(--path-check): path check command");
293 
294                 if (strlen(optarg) >= 3 && optarg[1] == ':') {
295                     RESTRICTION_TYPE file_type = get_restriction_from_char(*optarg);
296                     if (file_type == FT_NULL) {
297                         INVALID_ARGUMENT("--path-check", invalid file type '%c' (see man aide for details), *optarg)
298                     } else {
299                         conf->check_file_type = file_type;
300                         if (optarg[2] != '/') {
301                             INVALID_ARGUMENT("--path-check", '%s' needs to be an absolute path, optarg+2)
302                         } else {
303                             conf->check_path = checked_strdup(optarg+2);
304                             log_msg(LOG_LEVEL_INFO,"(--path-check): set path to '%s' (filetype: %c)", optarg+2, get_restriction_char(conf->check_file_type));
305                         }
306                     }
307                 } else {
308                     INVALID_ARGUMENT("--path-check", %s, "missing file type or path (see man aide for details)")
309                 }
310 
311             } else {
312                 INVALID_ARGUMENT("--path-check", %s, "cannot have multiple commands on a single commandline")
313                 exit(INVALID_ARGUMENT_ERROR);
314             }
315             break;
316       }
317       case 'r': {
318        INVALID_ARGUMENT("--report", %s, "option no longer supported, use 'report_url' config option instead (see man aide.conf for detail)")
319        break;
320       }
321       ACTION_CASE("--init", 'i', DO_INIT, "database init")
322       ACTION_CASE("--dry-init", 'n', DO_INIT|DO_DRY_RUN, "dry init")
323       ACTION_CASE("--check", 'C', DO_COMPARE, "database check")
324       ACTION_CASE("--update", 'u', DO_INIT|DO_COMPARE, "database update")
325       ACTION_CASE("--compare", 'E', DO_DIFF, "database compare")
326       ACTION_CASE("--config-check", 'D', DO_DRY_RUN, "config check")
327       default: /* '?' */
328 	  exit(INVALID_ARGUMENT_ERROR);
329       }
330   }
331 
332   if(optind<argc){
333     fprintf(stderr, "%s: extra parameter: '%s'\n", argv[0], argv[optind]);
334     exit(INVALID_ARGUMENT_ERROR);
335   }
336 }
337 
setdefaults_before_config()338 static void setdefaults_before_config()
339 {
340   DB_ATTR_TYPE X;
341 
342   conf=(db_config*)checked_malloc(sizeof(db_config));
343   conf->defsyms=NULL;
344 
345   /* Setting some defaults */
346 
347   log_msg(LOG_LEVEL_INFO, "initialise rule tree");
348   conf->tree=init_tree();
349   conf->database_add_metadata=1;
350   conf->report_detailed_init=0;
351   conf->report_base16=0;
352   conf->report_quiet=0;
353   conf->report_append=false;
354   conf->report_ignore_added_attrs = 0;
355   conf->report_ignore_removed_attrs = 0;
356   conf->report_ignore_changed_attrs = 0;
357   conf->report_force_attrs = 0;
358 #ifdef WITH_E2FSATTRS
359   conf->report_ignore_e2fsattrs = 0UL;
360 #endif
361 
362   conf->check_path=NULL;
363   conf->check_file_type = FT_REG;
364 
365   conf->report_urls=NULL;
366   conf->report_level=REPORT_LEVEL_CHANGED_ATTRIBUTES;
367 
368   conf->config_file=
369 #ifdef CONFIG_FILE
370           CONFIG_FILE
371 #else
372       NULL
373 #endif
374       ;
375   conf->config_version=NULL;
376 
377 #ifdef WITH_ACL
378   conf->no_acl_on_symlinks=0; /* zero means don't do ACLs on symlinks */
379 #endif
380   conf->db_out_attrs = ATTR(attr_filename)|ATTR(attr_attr)|ATTR(attr_perm)|ATTR(attr_inode);
381 
382   conf->symlinks_found=0;
383 
384   conf->database_in.url = NULL;
385   conf->database_in.filename=NULL;
386   conf->database_in.linenumber=0;
387   conf->database_in.linebuf=NULL;
388   conf->database_in.fp=NULL;
389 #ifdef WITH_ZLIB
390   conf->database_in.gzp = NULL;
391 #endif
392   conf->database_in.lineno = 0;
393   conf->database_in.fields = NULL;
394   conf->database_in.num_fields = 0;
395   conf->database_in.buffer_state = NULL;
396   conf->database_in.mdc = NULL;
397   conf->database_in.db_line = NULL;
398 
399   conf->database_out.url = NULL;
400   conf->database_out.filename=NULL;
401   conf->database_out.linenumber=0;
402   conf->database_out.linebuf=NULL;
403   conf->database_out.fp=NULL;
404 #ifdef WITH_ZLIB
405   conf->database_out.gzp = NULL;
406 #endif
407   conf->database_out.lineno = 0;
408   conf->database_out.fields = NULL;
409   conf->database_out.num_fields = 0;
410   conf->database_out.buffer_state = NULL;
411   conf->database_out.mdc = NULL;
412   conf->database_out.db_line = NULL;
413 
414   conf->database_new.url = NULL;
415   conf->database_new.filename=NULL;
416   conf->database_new.linenumber=0;
417   conf->database_new.linebuf=NULL;
418   conf->database_new.fp=NULL;
419 #ifdef WITH_ZLIB
420   conf->database_new.gzp = NULL;
421 #endif
422   conf->database_new.lineno = 0;
423   conf->database_new.fields = NULL;
424   conf->database_new.num_fields = 0;
425   conf->database_new.buffer_state = NULL;
426   conf->database_new.mdc = NULL;
427   conf->database_new.db_line = NULL;
428 
429   conf->db_attrs = get_hashes(false);
430 
431 #ifdef WITH_ZLIB
432   conf->gzip_dbout=0;
433 #endif
434 
435   conf->action=0;
436   conf->catch_mmap=0;
437 
438   conf->warn_dead_symlinks=0;
439 
440   conf->report_grouped=1;
441 
442   conf->report_summarize_changes=1;
443 
444   conf->root_prefix=NULL;
445   conf->root_prefix_length=0;
446 
447   conf->limit=NULL;
448   conf->limit_crx=NULL;
449 
450   conf->groupsyms=NULL;
451 
452   conf->start_time=time(&(conf->start_time));
453 
454   log_msg(LOG_LEVEL_INFO, "define default group definitions");
455 
456   for (ATTRIBUTE i = 0 ; i < num_attrs ; ++i) {
457       if (attributes[i].config_name) {
458           do_groupdef(attributes[i].config_name, attributes[i].attr);
459       }
460   }
461 
462   X=0LLU;
463 #ifdef WITH_ACL
464   X|=ATTR(attr_acl);
465 #endif
466 #ifdef WITH_SELINUX
467   X|=ATTR(attr_selinux);
468 #endif
469 #ifdef WITH_XATTR
470   X|=ATTR(attr_xattrs);
471 #endif
472 #ifdef WITH_E2FSATTRS
473   X|=ATTR(attr_e2fsattrs);
474 #endif
475 #ifdef WITH_CAPABILITIES
476   X|=ATTR(attr_capabilities);
477 #endif
478 
479   DB_ATTR_TYPE common_attrs = ATTR(attr_perm)|ATTR(attr_ftype)|ATTR(attr_inode)|ATTR(attr_linkcount)|ATTR(attr_uid)|ATTR(attr_gid);
480 
481   do_groupdef("R",common_attrs|ATTR(attr_size)|ATTR(attr_linkname)|ATTR(attr_mtime)|ATTR(attr_ctime)
482 #if defined(WITH_MHASH) || defined(WITH_GCRYPT)
483           |ATTR(attr_md5)
484 #endif
485           |X);
486   do_groupdef("L",common_attrs|ATTR(attr_linkname)|X);
487   do_groupdef(">",common_attrs|ATTR(attr_sizeg)|ATTR(attr_linkname)|X);
488   do_groupdef("H",get_hashes(false));
489   do_groupdef("X",X);
490   do_groupdef("E",0);
491 
492 }
493 
setdefaults_after_config()494 static void setdefaults_after_config()
495 {
496   int linenumber=1;
497 
498 #ifdef DEFAULT_DB
499   if(conf->database_in.url==NULL){
500     do_dbdef(DB_TYPE_IN, DEFAULT_DB, linenumber++, "(default)",  NULL);
501   }
502 #endif
503 #ifdef DEFAULT_DB_OUT
504   if(conf->database_out.url==NULL){
505     do_dbdef(DB_TYPE_OUT, DEFAULT_DB_OUT, linenumber++, "(default)",  NULL);
506   }
507 #endif
508 
509   if(conf->root_prefix==NULL){
510     do_rootprefix("" , linenumber++, "(default)",  NULL);
511   }
512 
513   if(conf->report_urls==NULL){
514     do_repurldef("stdout" , linenumber++, "(default)",  NULL);
515   }
516 
517   if(conf->action==0){
518     conf->action=DO_COMPARE;
519   }
520 
521   if (is_log_level_unset()) {
522           set_log_level(LOG_LEVEL_WARNING);
523   };
524 }
525 
526 
527 
main(int argc,char ** argv)528 int main(int argc,char**argv)
529 {
530   int errorno=0;
531 
532 #ifdef USE_LOCALE
533   setlocale(LC_ALL,"");
534   bindtextdomain(PACKAGE,LOCALEDIR);
535   textdomain(PACKAGE);
536 #endif
537   umask(0177);
538   init_sighandler();
539 
540   setdefaults_before_config();
541 
542   log_msg(LOG_LEVEL_INFO, "read command line parameters");
543   read_param(argc,argv);
544 
545   /* get hostname */
546   conf->hostname = checked_malloc(sizeof(char) * MAXHOSTNAMELEN + 1);
547   if (gethostname(conf->hostname,MAXHOSTNAMELEN) == -1) {
548       log_msg(LOG_LEVEL_WARNING,"gethostname failed: %s", strerror(errno));
549       free(conf->hostname);
550       conf->hostname = NULL;
551   } else {
552       log_msg(LOG_LEVEL_DEBUG, "hostname: '%s'", conf->hostname);
553   }
554 
555   log_msg(LOG_LEVEL_INFO, "parse configuration");
556   errorno=parse_config(before, conf->config_file, after);
557   if (errorno==RETFAIL){
558     exit(INVALID_CONFIGURELINE_ERROR);
559   }
560   free (before);
561   free (after);
562 
563   setdefaults_after_config();
564 
565   log_msg(LOG_LEVEL_CONFIG, "report_urls:");
566   log_report_urls(LOG_LEVEL_CONFIG);
567 
568   log_msg(LOG_LEVEL_RULE, "rule tree:");
569   log_tree(LOG_LEVEL_RULE, conf->tree, 0);
570 
571   if (conf->check_path) {
572       rx_rule* rule = NULL;
573       int match = check_rxtree(conf->check_path, conf->tree, &rule, conf->check_file_type, true);
574       if (match < 0) {
575         fprintf(stdout, "[ ] %c '%s': outside of limit '%s'\n", get_restriction_char(conf->check_file_type), conf->check_path, conf->limit);
576         exit(2);
577       } else {
578         exit(match?0:1);
579       }
580   }
581 
582   /* Let's do some sanity checks for the config */
583   if (conf->action&(DO_DIFF|DO_COMPARE) && !(conf->database_in.url)) {
584     log_msg(LOG_LEVEL_ERROR,_("missing 'database_in', config option is required"));
585     exit(INVALID_ARGUMENT_ERROR);
586   }
587   if (conf->action&DO_INIT && !(conf->database_out.url)) {
588     log_msg(LOG_LEVEL_ERROR,_("missing 'database_out', config option is required"));
589     exit(INVALID_ARGUMENT_ERROR);
590   }
591   if(conf->database_in.url && conf->database_out.url && cmpurl(conf->database_in.url,conf->database_out.url)==RETOK){
592       log_msg(LOG_LEVEL_NOTICE, "input and output database URLs are the same: '%s'", (conf->database_in.url)->value);
593     if((conf->action&DO_INIT)&&(conf->action&DO_COMPARE)){
594       log_msg(LOG_LEVEL_ERROR,_("input and output database urls cannot be the same "
595 	    "when doing database update"));
596       exit(INVALID_ARGUMENT_ERROR);
597     }
598     if(conf->action&DO_DIFF){
599       log_msg(LOG_LEVEL_ERROR,_("both input databases cannot be the same "
600 		"when doing database compare"));
601       exit(INVALID_ARGUMENT_ERROR);
602     }
603   };
604   if((conf->action&DO_DIFF)&&(!(conf->database_new.url)||!(conf->database_in.url))){
605     log_msg(LOG_LEVEL_ERROR,_("must have both input databases defined for "
606 	      "database compare"));
607     exit(INVALID_ARGUMENT_ERROR);
608   }
609 
610   if (conf->action&DO_INIT && conf->action&DO_DRY_RUN) {
611       if(db_disk_init()==RETFAIL) {
612           exit(IO_ERROR);
613       }
614       log_msg(LOG_LEVEL_INFO, "populate tree (dry-run)");
615       populate_tree(conf->tree, true);
616       exit (0);
617   }
618 
619   if (!(conf->action&DO_DRY_RUN)) {
620 
621   if (!init_report_urls()) {
622       exit(INVALID_CONFIGURELINE_ERROR);
623   }
624 
625   if (conf->action&(DO_INIT|DO_COMPARE) && conf->root_prefix_length > 0) {
626       DIR *dir;
627       if((dir = opendir(conf->root_prefix)) != NULL) {
628           closedir(dir);
629       } else {
630           log_msg(LOG_LEVEL_ERROR,"opendir() for root_prefix %s failed: %s", conf->root_prefix, strerror(errno));
631           exit(INVALID_CONFIGURELINE_ERROR);
632       }
633   }
634     if(conf->action&DO_INIT){
635       if(db_init(&(conf->database_out), false,
636 #ifdef WITH_ZLIB
637         conf->gzip_dbout
638 #else
639         false
640 #endif
641        ) == RETFAIL) {
642 	exit(IO_ERROR);
643       }
644       if(db_writespec(conf)==RETFAIL){
645 	log_msg(LOG_LEVEL_ERROR,_("Error while writing database. Exiting.."));
646 	exit(IO_ERROR);
647       }
648     }
649     if((conf->action&DO_INIT)||(conf->action&DO_COMPARE)){
650       if(db_disk_init()==RETFAIL)
651 	exit(IO_ERROR);
652     }
653     if((conf->action&DO_COMPARE)||(conf->action&DO_DIFF)){
654       if(db_init(&(conf->database_in), true, false)==RETFAIL)
655 	exit(IO_ERROR);
656     }
657     if(conf->action&DO_DIFF){
658       if(db_init(&(conf->database_new), true, false)==RETFAIL)
659 	exit(IO_ERROR);
660     }
661 
662     log_msg(LOG_LEVEL_INFO, "populate tree");
663     populate_tree(conf->tree, false);
664 
665     if(conf->action&DO_INIT) {
666         log_msg(LOG_LEVEL_INFO, "write new entries to database: %s:%s", get_url_type_string((conf->database_out.url)->type), (conf->database_out.url)->value);
667         write_tree(conf->tree);
668     }
669 
670     db_close();
671 
672     log_msg(LOG_LEVEL_INFO, "generate reports");
673 
674     int exitcode = gen_report(conf->tree);
675 
676     log_msg(LOG_LEVEL_INFO, "exit AIDE with exit code '%d'", exitcode);
677 
678     exit(exitcode);
679   }
680   return RETOK;
681 }
682 // vi: ts=8 sw=8
683