1 /*
2 * AIDE (Advanced Intrusion Detection Environment)
3 *
4 * Copyright (C) 1999-2006, 2010-2011, 2013, 2015-2016, 2019-2021 Rami Lehti,
5 * Pablo Virolainen, Richard van den Berg, Hannes von Haugwitz
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include "aide.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <sys/param.h>
29
30 #include "commandconf.h"
31 #include "conf_lex.h"
32 #include "log.h"
33 #include "conf_yacc.h"
34 #include "db.h"
35 #include "db_config.h"
36 #include "report.h"
37 #include "gen_list.h"
38 #include "symboltable.h"
39 #include "md.h"
40 #include "util.h"
41 #include "base64.h"
42 #include "conf_eval.h"
43 /*for locale support*/
44 #include "locale-aide.h"
45 /*for locale support*/
46 #ifdef WITH_CURL
47 #include "fopen.h"
48 #endif
49
50 #define BUFSIZE 4096
51 #define ZBUFSIZE 16384
52
parse_url(char * val,int linenumber,char * filename,char * linebuf)53 url_t* parse_url(char* val, int linenumber, char* filename, char* linebuf)
54 {
55 url_t* u=NULL;
56 char* r=NULL;
57 char* val_copy=NULL;
58 int i=0;
59
60 u=checked_malloc(sizeof(url_t));
61
62 /* We don't want to modify the original hence strdup(val) */
63 val_copy=checked_strdup(val);
64 for(r=val_copy;r[0]!=':'&&r[0]!='\0';r++);
65
66 if(r[0]!='\0'){
67 r[0]='\0';
68 r++;
69 }
70
71 u->type = get_url_type(val_copy);
72 if (u->type) {
73 switch (u->type) {
74 case url_file : {
75 if(r[0]=='/'&&(r+1)[0]=='/'&&(r+2)[0]=='/'){
76 u->value=checked_strdup(r+2);
77 break;
78 }
79 if(r[0]=='/'&&(r+1)[0]=='/'&&(r+2)[0]!='/'){
80 char* t=r+2;
81 r+=2;
82 for(i=0;r[0]!='/'&&r[0]!='\0';r++,i++);
83 if(r[0]=='\0'){
84 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, invalid file-URL '%s': no path after hostname, val)
85 free(val_copy);
86 free(u);
87 return NULL;
88 }
89 if( (strcmp(t,"localhost") != 0) && !( conf->hostname && strcmp(t,conf->hostname)==0)){
90 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, invalid file-URL '%s': cannot use hostname other than 'localhost' or '%s', val, conf->hostname);
91 free(u);
92 free(val_copy);
93 return NULL;
94 }
95 u->value=checked_strdup(r);
96 r[0]='\0';
97
98 break;
99 }
100 u->value=checked_strdup(r);
101 break;
102 }
103 case url_ftp :
104 case url_https :
105 case url_http : {
106 #ifdef WITH_CURL
107 u->value=checked_strdup(val);
108 #else
109 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, %s, "http, https and ftp URL support not compiled in, recompile AIDE with '--with-curl'")
110 free(val_copy);
111 free(u);
112 return NULL;
113 #endif /* WITH CURL */
114 break;
115 }
116 case url_fd:
117 case url_stdin:
118 case url_stdout:
119 case url_stderr: {
120 u->value=checked_strdup(r);
121 break;
122 }
123 case url_syslog : {
124 #ifdef HAVE_SYSLOG
125 u->value=checked_strdup(r);
126 #else
127 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, %s, "syslog url support not compiled in, recompile AIDE with syslog support")
128 free(val_copy);
129 free(u);
130 return NULL;
131 #endif
132 break;
133 }
134 }
135 } else {
136 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, unknown URL-type: '%s', val_copy);
137 free(u);
138 return NULL;
139 }
140
141 free(val_copy);
142
143 return u;
144 }
145
parse_config(char * before,char * config,char * after)146 int parse_config(char *before, char *config, char* after) {
147 if(before==NULL && after==NULL && (config==NULL||strcmp(config,"")==0)){
148 log_msg(LOG_LEVEL_ERROR,_("missing configuration (use '--config' '--before' or '--after' command line parameter)"));
149 return RETFAIL;
150 }
151
152 ast* config_ast = NULL;
153 if (before) {
154 conf_lex_string("(--before)", before);
155 if(confparse(&config_ast)){
156 return RETFAIL;
157 }
158 conf_lex_delete_buffer();
159 eval_config(config_ast, 0);
160 deep_free(config_ast);
161 config_ast = NULL;
162 }
163 if (config) {
164 conf_lex_file(config);
165 if(confparse(&config_ast)){
166 return RETFAIL;
167 }
168 conf_lex_delete_buffer();
169 eval_config(config_ast, 0);
170 deep_free(config_ast);
171 config_ast = NULL;
172 }
173 if (after) {
174 conf_lex_string("(--after)", after);
175 if(confparse(&config_ast)){
176 return RETFAIL;
177 }
178 conf_lex_delete_buffer();
179 eval_config(config_ast, 0);
180 deep_free(config_ast);
181 config_ast = NULL;
182 }
183 return RETOK;
184 }
185
conf_input_wrapper(char * buf,int max_size,FILE * in)186 int conf_input_wrapper(char* buf, int max_size, FILE* in)
187 {
188 int retval=0;
189
190 /* FIXME Add support for gzipped config. :) */
191 retval=fread(buf,1,max_size,in);
192
193 return retval;
194 }
195
db_input_wrapper(char * buf,int max_size,database * db)196 int db_input_wrapper(char* buf, int max_size, database* db)
197 {
198 log_msg(LOG_LEVEL_TRACE,"db_input_wrapper(): parameters: buf=%p, max_size=%d, db=%p)", buf, max_size, db);
199 int retval=0;
200 #ifdef WITH_ZLIB
201 int c=0;
202 #endif
203
204 #ifdef WITH_CURL
205 switch ((db->url)->type) {
206 case url_http:
207 case url_https:
208 case url_ftp: {
209 retval=url_fread(buf,1,max_size,(URL_FILE *)db->fp);
210 if (db->mdc) {
211 update_md(db->mdc, buf, retval);
212 }
213 break;
214 }
215 default:
216 #endif /* WITH CURL */
217
218 #ifdef WITH_ZLIB
219 if (db->gzp!=NULL) {
220 c=gzgetc(db->gzp);
221 retval= (c==EOF) ? 0 : (buf[0] = c,1);
222 }
223 if (db->gzp==NULL) {
224 c=fgetc(db->fp);
225 if(c==(unsigned char)'\037'){
226 c=fgetc(db->fp);
227 if(c==(unsigned char)'\213'){
228 log_msg(LOG_LEVEL_DEBUG,"db_input_wrapper(): handle gzip header");
229 lseek(fileno((FILE *)(db->fp)),0L,SEEK_SET);
230 db->gzp=gzdopen(fileno((FILE *)(db->fp)),"rb");
231 c=gzgetc(db->gzp);
232 log_msg(LOG_LEVEL_DEBUG, "db_input_wrapper(): first character after gzip header is: %c(%#X)\n",c,c);
233 if(c==-1) {
234 int xx;
235 log_msg(LOG_LEVEL_ERROR,"reading gzipped file failed: %s", gzerror(db->gzp,&xx));
236 exit(EXIT_FAILURE);
237 }
238 }else {
239 /* False alarm */
240 ungetc(c,db->fp);
241 }
242 }
243 retval= (c==EOF) ? 0 : (buf[0] = c,1);
244 }
245
246 #else /* WITH_ZLIB */
247 retval=fread(buf,1,max_size,db->fp);
248 #endif /* WITH_ZLIB */
249
250 if (db->mdc) {
251 update_md(db->mdc, buf, retval);
252 }
253
254
255 #ifdef WITH_CURL
256 }
257 #endif /* WITH CURL */
258 log_msg(LOG_LEVEL_TRACE,"db_input_wrapper(): return value: %d", retval);
259 return retval;
260 }
261
do_define(char * name,char * value,int linenumber,char * filename,char * linebuf)262 void do_define(char* name, char* value, int linenumber, char* filename, char* linebuf)
263 {
264 symba* s=NULL;
265 list* l=NULL;
266
267 if(!(l=list_find(name,conf->defsyms))){
268 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, define '%s' with value '%s', name, value)
269 s=(symba*)checked_malloc(sizeof(symba));
270 s->name=checked_strdup(name);
271 s->value=value;
272 conf->defsyms=list_append(conf->defsyms,(void*)s);
273 }
274 else {
275 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_NOTICE, redefine '%s' with value '%s' (previous value: '%s'), name, value, ((symba*)l->data)->value)
276 free(((symba*)l->data)->value);
277 ((symba*)l->data)->value=NULL;
278 ((symba*)l->data)->value=value;
279 }
280 }
281
do_undefine(char * name,int linenumber,char * filename,char * linebuf)282 void do_undefine(char* name, int linenumber, char* filename, char* linebuf)
283 {
284 list*r=NULL;
285
286 if((r=list_find(name,conf->defsyms))){
287 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, undefine '%s' (value: '%s'), name, ((symba*)r->data)->value)
288 free(((symba*)r->data)->name);
289 free(((symba*)r->data)->value);
290 free((symba*)r->data);
291 r->data=NULL;
292 conf->defsyms=list_delete_item(r);
293 } else {
294 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_NOTICE, variable '%s' to be undefined not found, name);
295 }
296 }
297
add_rx_rule_to_tree(char * rx,RESTRICTION_TYPE restriction,DB_ATTR_TYPE attr,int type,seltree * tree,int linenumber,char * filename,char * linebuf)298 bool add_rx_rule_to_tree(char* rx, RESTRICTION_TYPE restriction, DB_ATTR_TYPE attr, int type, seltree *tree, int linenumber, char* filename, char* linebuf) {
299
300 rx_rule* r=NULL;
301
302 bool retval = false;
303
304 char *attr_str = NULL;
305 char *rs_str = NULL;
306
307 const char* rule_error;
308 int rule_erroffset;
309
310 if ((r = add_rx_to_tree(rx, restriction, type, tree, &rule_error, &rule_erroffset)) == NULL) {
311 log_msg(LOG_LEVEL_ERROR, "%s:%d:%i: error in rule '%s': %s (line: '%s')", filename, linenumber, rule_erroffset, rx, rule_error, linebuf);
312 retval = false;
313 }else {
314 r->config_linenumber = linenumber;
315 r->config_filename = filename;
316 r->config_line = checked_strdup(linebuf);
317
318 DB_ATTR_TYPE unsupported_hashes = attr&(get_hashes(true)&~get_hashes(false));
319 if (unsupported_hashes) {
320 char *str;
321 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_WARNING, ignoring unsupported hash algorithm(s): %s, str = diff_attributes(0, unsupported_hashes));
322 free(str);
323 attr &= ~unsupported_hashes;
324 }
325
326 r->attr=attr;
327 conf->db_out_attrs |= attr;
328
329 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, add %s '%s%s %s %s' to node '%s', get_rule_type_long_string(type), get_rule_type_char(type), r->rx, rs_str = get_restriction_string(r->restriction), attr_str = diff_attributes(0, r->attr), (r->node)->path)
330 free(rs_str);
331 free(attr_str);
332
333 retval = true;
334 }
335 return retval;
336 }
337
do_groupdef(char * group,DB_ATTR_TYPE value)338 DB_ATTR_TYPE do_groupdef(char* group,DB_ATTR_TYPE value)
339 {
340 log_msg(LOG_LEVEL_DEBUG, "define attribute group '%s' with value %llu", group, value);
341 list* r=NULL;
342 symba* s=NULL;
343
344 if((r=list_find(group,conf->groupsyms))){
345 DB_ATTR_TYPE prev_value = ((symba*)r->data)->ival;
346 ((symba*)r->data)->ival=value;
347 return prev_value;
348 }
349 /* This is a new group */
350 s=checked_malloc(sizeof(symba));
351 s->name=checked_strdup(group);
352 s->ival=value;
353 conf->groupsyms=list_append(conf->groupsyms,(void*)s);
354 return 0;
355 }
356
get_groupval(char * group)357 DB_ATTR_TYPE get_groupval(char* group)
358 {
359 list* r=NULL;
360
361 if((r=list_find(group,conf->groupsyms))){
362 return (((symba*)r->data)->ival);
363 }
364 return DB_ATTR_UNDEF;
365 }
366
do_dbdef(DB_TYPE dbtype,char * val,int linenumber,char * filename,char * linebuf)367 bool do_dbdef(DB_TYPE dbtype ,char* val, int linenumber, char* filename, char* linebuf)
368 {
369 url_t* u=NULL;
370 database *db = NULL;
371 char *db_option_name = NULL;
372
373 switch(dbtype) {
374 case DB_TYPE_IN: {
375 db_option_name = "database_in";
376 db = &(conf->database_in);
377 break;
378 }
379 case DB_TYPE_OUT: {
380 db_option_name = "database_out";
381 db = &(conf->database_out);
382 break;
383 }
384 case DB_TYPE_NEW: {
385 db_option_name = "database_new";
386 db = &(conf->database_new);
387 break;
388 }
389 }
390
391 if(db->url == NULL){
392 if ((u=parse_url(val, linenumber, filename, linebuf)) != NULL) {
393 /* FIXME Check the URL if you add support for databases that cannot be
394 * both input and output urls */
395 switch (dbtype) {
396 case DB_TYPE_IN:
397 case DB_TYPE_NEW:{
398 switch (u->type) {
399 case url_stdout:
400 case url_stderr:
401 case url_syslog: {
402 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, '%s': unsupported URL-type: '%s', db_option_name, get_url_type_string(u->type))
403 return false;
404 }
405 case url_stdin:
406 case url_ftp:
407 case url_http:
408 case url_https:
409 case url_fd:
410 case url_file:
411 break;
412 }
413 break;
414 }
415 case DB_TYPE_OUT: {
416 switch (u->type) {
417 case url_stdin:
418 case url_stderr:
419 case url_syslog: {
420 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, '%s': unsupported URL-type: '%s', db_option_name, get_url_type_string(u->type))
421 return false;
422 }
423 case url_stdout:
424 case url_ftp:
425 case url_http:
426 case url_https:
427 case url_fd:
428 case url_file:
429 break;
430 }
431 break;
432 }
433 }
434 db->url = u;
435 db->linenumber = linenumber;
436 db->filename = filename;
437 db->linebuf = linebuf?checked_strdup(linebuf):NULL;
438 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, set '%s' option to '%s:%s', db_option_name, get_url_type_string(u->type), u->value)
439 } else {
440 return false;
441 }
442 } else {
443 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_NOTICE, '%s' option already set to '%s:%s' (ignore new value '%s'), db_option_name, get_url_type_string((db->url)->type), (db->url)->value, val);
444 }
445 return true;
446 }
447
do_repurldef(char * val,int linenumber,char * filename,char * linebuf)448 bool do_repurldef(char* val, int linenumber, char* filename, char* linebuf) {
449 url_t* u = parse_url(val, linenumber, filename, linebuf);
450 if (add_report_url(u, linenumber, filename, linebuf)) {
451 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, set 'report_url' to '%s%s%s', get_url_type_string(u->type), u->value?":":"", u->value?u->value:"")
452 return true;
453 }
454 return false;
455 }
456
do_reportlevel(char * val,int linenumber,char * filename,char * linebuf)457 bool do_reportlevel(char* val, int linenumber, char* filename, char* linebuf) {
458 REPORT_LEVEL report_level=0;
459
460 report_level = get_report_level(val);
461 if (report_level) {
462 conf->report_level = report_level;
463 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, set 'report_level' option to '%s' (raw: %d), val, report_level)
464 return true;
465 } else {
466 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, invalid report level: '%s', val);
467 return false;
468 }
469 }
470
do_rootprefix(char * val,int linenumber,char * filename,char * linebuf)471 void do_rootprefix(char* val, int linenumber, char* filename, char* linebuf) {
472 if (conf->root_prefix == NULL) {
473 conf->root_prefix=val;
474 conf->root_prefix_length=strlen(conf->root_prefix);
475 if (conf->root_prefix_length && conf->root_prefix[conf->root_prefix_length-1] == '/') {
476 conf->root_prefix[--conf->root_prefix_length] = '\0';
477 log_msg(LOG_LEVEL_NOTICE, "%s:%d: removed trailing '/' from root prefix", filename, linenumber);
478 }
479 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, set 'root_prefix' option to '%s', conf->root_prefix)
480 } else {
481 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_NOTICE, 'root_prefix' option already set to '%s' (ignore new value '%s'), conf->root_prefix, val);
482 free(val);
483 }
484 }
485
486 #ifdef WITH_E2FSATTRS
487 #define easy_e2fsattrs_case(c,f) \
488 case c: { \
489 conf->report_ignore_e2fsattrs|=f; \
490 break; \
491 }
492
do_report_ignore_e2fsattrs(char * val,int linenumber,char * filename,char * linebuf)493 void do_report_ignore_e2fsattrs(char* val, int linenumber, char* filename, char* linebuf) {
494 conf->report_ignore_e2fsattrs = 0UL;
495 while (*val) {
496 switch(*val){
497 /* source for mappings see report.c */
498 easy_e2fsattrs_case('s',EXT2_SECRM_FL)
499 easy_e2fsattrs_case('u',EXT2_UNRM_FL)
500 easy_e2fsattrs_case('S',EXT2_SYNC_FL)
501 easy_e2fsattrs_case('D',EXT2_DIRSYNC_FL)
502 easy_e2fsattrs_case('i',EXT2_IMMUTABLE_FL)
503 easy_e2fsattrs_case('a',EXT2_APPEND_FL)
504 easy_e2fsattrs_case('d',EXT2_NODUMP_FL)
505 easy_e2fsattrs_case('A',EXT2_NOATIME_FL)
506 easy_e2fsattrs_case('c',EXT2_COMPR_FL)
507 easy_e2fsattrs_case('B',EXT2_COMPRBLK_FL)
508 easy_e2fsattrs_case('Z',EXT2_DIRTY_FL)
509 easy_e2fsattrs_case('X',EXT2_NOCOMPR_FL)
510 #ifdef EXT2_ECOMPR_FL
511 easy_e2fsattrs_case('E',EXT2_ECOMPR_FL)
512 #else
513 easy_e2fsattrs_case('E',EXT4_ENCRYPT_FL)
514 #endif
515 easy_e2fsattrs_case('j',EXT3_JOURNAL_DATA_FL)
516 easy_e2fsattrs_case('I',EXT2_INDEX_FL)
517 easy_e2fsattrs_case('t',EXT2_NOTAIL_FL)
518 easy_e2fsattrs_case('T',EXT2_TOPDIR_FL)
519 #ifdef EXT4_EXTENTS_FL
520 easy_e2fsattrs_case('e',EXT4_EXTENTS_FL)
521 #endif
522 #ifdef EXT4_HUGE_FILE_FL
523 easy_e2fsattrs_case('h',EXT4_HUGE_FILE_FL)
524 #endif
525 #ifdef FS_NOCOW_FL
526 easy_e2fsattrs_case('C',FS_NOCOW_FL)
527 #endif
528 #ifdef EXT4_INLINE_DATA_FL
529 easy_e2fsattrs_case('N',EXT4_INLINE_DATA_FL)
530 #endif
531 case '0': {
532 break;
533 }
534 default: {
535 LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_NOTICE, ignore invalid ext2 file attribute: '%c', *val)
536 break;
537 }
538 }
539 val++;
540 }
541 }
542 #endif
543