/* * AIDE (Advanced Intrusion Detection Environment) * * Copyright (C) 1999-2006, 2010-2011, 2013, 2015-2016, 2019-2021 Rami Lehti, * Pablo Virolainen, Richard van den Berg, Hannes von Haugwitz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "aide.h" #include #include #include #include #include #include #include "commandconf.h" #include "conf_lex.h" #include "log.h" #include "conf_yacc.h" #include "db.h" #include "db_config.h" #include "report.h" #include "gen_list.h" #include "symboltable.h" #include "md.h" #include "util.h" #include "base64.h" #include "conf_eval.h" /*for locale support*/ #include "locale-aide.h" /*for locale support*/ #ifdef WITH_CURL #include "fopen.h" #endif #define BUFSIZE 4096 #define ZBUFSIZE 16384 url_t* parse_url(char* val, int linenumber, char* filename, char* linebuf) { url_t* u=NULL; char* r=NULL; char* val_copy=NULL; int i=0; u=checked_malloc(sizeof(url_t)); /* We don't want to modify the original hence strdup(val) */ val_copy=checked_strdup(val); for(r=val_copy;r[0]!=':'&&r[0]!='\0';r++); if(r[0]!='\0'){ r[0]='\0'; r++; } u->type = get_url_type(val_copy); if (u->type) { switch (u->type) { case url_file : { if(r[0]=='/'&&(r+1)[0]=='/'&&(r+2)[0]=='/'){ u->value=checked_strdup(r+2); break; } if(r[0]=='/'&&(r+1)[0]=='/'&&(r+2)[0]!='/'){ char* t=r+2; r+=2; for(i=0;r[0]!='/'&&r[0]!='\0';r++,i++); if(r[0]=='\0'){ LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, invalid file-URL '%s': no path after hostname, val) free(val_copy); free(u); return NULL; } if( (strcmp(t,"localhost") != 0) && !( conf->hostname && strcmp(t,conf->hostname)==0)){ LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, invalid file-URL '%s': cannot use hostname other than 'localhost' or '%s', val, conf->hostname); free(u); free(val_copy); return NULL; } u->value=checked_strdup(r); r[0]='\0'; break; } u->value=checked_strdup(r); break; } case url_ftp : case url_https : case url_http : { #ifdef WITH_CURL u->value=checked_strdup(val); #else LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, %s, "http, https and ftp URL support not compiled in, recompile AIDE with '--with-curl'") free(val_copy); free(u); return NULL; #endif /* WITH CURL */ break; } case url_fd: case url_stdin: case url_stdout: case url_stderr: { u->value=checked_strdup(r); break; } case url_syslog : { #ifdef HAVE_SYSLOG u->value=checked_strdup(r); #else LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, %s, "syslog url support not compiled in, recompile AIDE with syslog support") free(val_copy); free(u); return NULL; #endif break; } } } else { LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, unknown URL-type: '%s', val_copy); free(u); return NULL; } free(val_copy); return u; } int parse_config(char *before, char *config, char* after) { if(before==NULL && after==NULL && (config==NULL||strcmp(config,"")==0)){ log_msg(LOG_LEVEL_ERROR,_("missing configuration (use '--config' '--before' or '--after' command line parameter)")); return RETFAIL; } ast* config_ast = NULL; if (before) { conf_lex_string("(--before)", before); if(confparse(&config_ast)){ return RETFAIL; } conf_lex_delete_buffer(); eval_config(config_ast, 0); deep_free(config_ast); config_ast = NULL; } if (config) { conf_lex_file(config); if(confparse(&config_ast)){ return RETFAIL; } conf_lex_delete_buffer(); eval_config(config_ast, 0); deep_free(config_ast); config_ast = NULL; } if (after) { conf_lex_string("(--after)", after); if(confparse(&config_ast)){ return RETFAIL; } conf_lex_delete_buffer(); eval_config(config_ast, 0); deep_free(config_ast); config_ast = NULL; } return RETOK; } int conf_input_wrapper(char* buf, int max_size, FILE* in) { int retval=0; /* FIXME Add support for gzipped config. :) */ retval=fread(buf,1,max_size,in); return retval; } int db_input_wrapper(char* buf, int max_size, database* db) { log_msg(LOG_LEVEL_TRACE,"db_input_wrapper(): parameters: buf=%p, max_size=%d, db=%p)", buf, max_size, db); int retval=0; #ifdef WITH_ZLIB int c=0; #endif #ifdef WITH_CURL switch ((db->url)->type) { case url_http: case url_https: case url_ftp: { retval=url_fread(buf,1,max_size,(URL_FILE *)db->fp); if (db->mdc) { update_md(db->mdc, buf, retval); } break; } default: #endif /* WITH CURL */ #ifdef WITH_ZLIB if (db->gzp!=NULL) { c=gzgetc(db->gzp); retval= (c==EOF) ? 0 : (buf[0] = c,1); } if (db->gzp==NULL) { c=fgetc(db->fp); if(c==(unsigned char)'\037'){ c=fgetc(db->fp); if(c==(unsigned char)'\213'){ log_msg(LOG_LEVEL_DEBUG,"db_input_wrapper(): handle gzip header"); lseek(fileno((FILE *)(db->fp)),0L,SEEK_SET); db->gzp=gzdopen(fileno((FILE *)(db->fp)),"rb"); c=gzgetc(db->gzp); log_msg(LOG_LEVEL_DEBUG, "db_input_wrapper(): first character after gzip header is: %c(%#X)\n",c,c); if(c==-1) { int xx; log_msg(LOG_LEVEL_ERROR,"reading gzipped file failed: %s", gzerror(db->gzp,&xx)); exit(EXIT_FAILURE); } }else { /* False alarm */ ungetc(c,db->fp); } } retval= (c==EOF) ? 0 : (buf[0] = c,1); } #else /* WITH_ZLIB */ retval=fread(buf,1,max_size,db->fp); #endif /* WITH_ZLIB */ if (db->mdc) { update_md(db->mdc, buf, retval); } #ifdef WITH_CURL } #endif /* WITH CURL */ log_msg(LOG_LEVEL_TRACE,"db_input_wrapper(): return value: %d", retval); return retval; } void do_define(char* name, char* value, int linenumber, char* filename, char* linebuf) { symba* s=NULL; list* l=NULL; if(!(l=list_find(name,conf->defsyms))){ LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, define '%s' with value '%s', name, value) s=(symba*)checked_malloc(sizeof(symba)); s->name=checked_strdup(name); s->value=value; conf->defsyms=list_append(conf->defsyms,(void*)s); } else { LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_NOTICE, redefine '%s' with value '%s' (previous value: '%s'), name, value, ((symba*)l->data)->value) free(((symba*)l->data)->value); ((symba*)l->data)->value=NULL; ((symba*)l->data)->value=value; } } void do_undefine(char* name, int linenumber, char* filename, char* linebuf) { list*r=NULL; if((r=list_find(name,conf->defsyms))){ LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, undefine '%s' (value: '%s'), name, ((symba*)r->data)->value) free(((symba*)r->data)->name); free(((symba*)r->data)->value); free((symba*)r->data); r->data=NULL; conf->defsyms=list_delete_item(r); } else { LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_NOTICE, variable '%s' to be undefined not found, name); } } 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) { rx_rule* r=NULL; bool retval = false; char *attr_str = NULL; char *rs_str = NULL; const char* rule_error; int rule_erroffset; if ((r = add_rx_to_tree(rx, restriction, type, tree, &rule_error, &rule_erroffset)) == NULL) { log_msg(LOG_LEVEL_ERROR, "%s:%d:%i: error in rule '%s': %s (line: '%s')", filename, linenumber, rule_erroffset, rx, rule_error, linebuf); retval = false; }else { r->config_linenumber = linenumber; r->config_filename = filename; r->config_line = checked_strdup(linebuf); DB_ATTR_TYPE unsupported_hashes = attr&(get_hashes(true)&~get_hashes(false)); if (unsupported_hashes) { char *str; LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_WARNING, ignoring unsupported hash algorithm(s): %s, str = diff_attributes(0, unsupported_hashes)); free(str); attr &= ~unsupported_hashes; } r->attr=attr; conf->db_out_attrs |= attr; 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) free(rs_str); free(attr_str); retval = true; } return retval; } DB_ATTR_TYPE do_groupdef(char* group,DB_ATTR_TYPE value) { log_msg(LOG_LEVEL_DEBUG, "define attribute group '%s' with value %llu", group, value); list* r=NULL; symba* s=NULL; if((r=list_find(group,conf->groupsyms))){ DB_ATTR_TYPE prev_value = ((symba*)r->data)->ival; ((symba*)r->data)->ival=value; return prev_value; } /* This is a new group */ s=checked_malloc(sizeof(symba)); s->name=checked_strdup(group); s->ival=value; conf->groupsyms=list_append(conf->groupsyms,(void*)s); return 0; } DB_ATTR_TYPE get_groupval(char* group) { list* r=NULL; if((r=list_find(group,conf->groupsyms))){ return (((symba*)r->data)->ival); } return DB_ATTR_UNDEF; } bool do_dbdef(DB_TYPE dbtype ,char* val, int linenumber, char* filename, char* linebuf) { url_t* u=NULL; database *db = NULL; char *db_option_name = NULL; switch(dbtype) { case DB_TYPE_IN: { db_option_name = "database_in"; db = &(conf->database_in); break; } case DB_TYPE_OUT: { db_option_name = "database_out"; db = &(conf->database_out); break; } case DB_TYPE_NEW: { db_option_name = "database_new"; db = &(conf->database_new); break; } } if(db->url == NULL){ if ((u=parse_url(val, linenumber, filename, linebuf)) != NULL) { /* FIXME Check the URL if you add support for databases that cannot be * both input and output urls */ switch (dbtype) { case DB_TYPE_IN: case DB_TYPE_NEW:{ switch (u->type) { case url_stdout: case url_stderr: case url_syslog: { LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, '%s': unsupported URL-type: '%s', db_option_name, get_url_type_string(u->type)) return false; } case url_stdin: case url_ftp: case url_http: case url_https: case url_fd: case url_file: break; } break; } case DB_TYPE_OUT: { switch (u->type) { case url_stdin: case url_stderr: case url_syslog: { LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, '%s': unsupported URL-type: '%s', db_option_name, get_url_type_string(u->type)) return false; } case url_stdout: case url_ftp: case url_http: case url_https: case url_fd: case url_file: break; } break; } } db->url = u; db->linenumber = linenumber; db->filename = filename; db->linebuf = linebuf?checked_strdup(linebuf):NULL; LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, set '%s' option to '%s:%s', db_option_name, get_url_type_string(u->type), u->value) } else { return false; } } else { 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); } return true; } bool do_repurldef(char* val, int linenumber, char* filename, char* linebuf) { url_t* u = parse_url(val, linenumber, filename, linebuf); if (add_report_url(u, linenumber, filename, linebuf)) { 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:"") return true; } return false; } bool do_reportlevel(char* val, int linenumber, char* filename, char* linebuf) { REPORT_LEVEL report_level=0; report_level = get_report_level(val); if (report_level) { conf->report_level = report_level; LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, set 'report_level' option to '%s' (raw: %d), val, report_level) return true; } else { LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, invalid report level: '%s', val); return false; } } void do_rootprefix(char* val, int linenumber, char* filename, char* linebuf) { if (conf->root_prefix == NULL) { conf->root_prefix=val; conf->root_prefix_length=strlen(conf->root_prefix); if (conf->root_prefix_length && conf->root_prefix[conf->root_prefix_length-1] == '/') { conf->root_prefix[--conf->root_prefix_length] = '\0'; log_msg(LOG_LEVEL_NOTICE, "%s:%d: removed trailing '/' from root prefix", filename, linenumber); } LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, set 'root_prefix' option to '%s', conf->root_prefix) } else { LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_NOTICE, 'root_prefix' option already set to '%s' (ignore new value '%s'), conf->root_prefix, val); free(val); } } #ifdef WITH_E2FSATTRS #define easy_e2fsattrs_case(c,f) \ case c: { \ conf->report_ignore_e2fsattrs|=f; \ break; \ } void do_report_ignore_e2fsattrs(char* val, int linenumber, char* filename, char* linebuf) { conf->report_ignore_e2fsattrs = 0UL; while (*val) { switch(*val){ /* source for mappings see report.c */ easy_e2fsattrs_case('s',EXT2_SECRM_FL) easy_e2fsattrs_case('u',EXT2_UNRM_FL) easy_e2fsattrs_case('S',EXT2_SYNC_FL) easy_e2fsattrs_case('D',EXT2_DIRSYNC_FL) easy_e2fsattrs_case('i',EXT2_IMMUTABLE_FL) easy_e2fsattrs_case('a',EXT2_APPEND_FL) easy_e2fsattrs_case('d',EXT2_NODUMP_FL) easy_e2fsattrs_case('A',EXT2_NOATIME_FL) easy_e2fsattrs_case('c',EXT2_COMPR_FL) easy_e2fsattrs_case('B',EXT2_COMPRBLK_FL) easy_e2fsattrs_case('Z',EXT2_DIRTY_FL) easy_e2fsattrs_case('X',EXT2_NOCOMPR_FL) #ifdef EXT2_ECOMPR_FL easy_e2fsattrs_case('E',EXT2_ECOMPR_FL) #else easy_e2fsattrs_case('E',EXT4_ENCRYPT_FL) #endif easy_e2fsattrs_case('j',EXT3_JOURNAL_DATA_FL) easy_e2fsattrs_case('I',EXT2_INDEX_FL) easy_e2fsattrs_case('t',EXT2_NOTAIL_FL) easy_e2fsattrs_case('T',EXT2_TOPDIR_FL) #ifdef EXT4_EXTENTS_FL easy_e2fsattrs_case('e',EXT4_EXTENTS_FL) #endif #ifdef EXT4_HUGE_FILE_FL easy_e2fsattrs_case('h',EXT4_HUGE_FILE_FL) #endif #ifdef FS_NOCOW_FL easy_e2fsattrs_case('C',FS_NOCOW_FL) #endif #ifdef EXT4_INLINE_DATA_FL easy_e2fsattrs_case('N',EXT4_INLINE_DATA_FL) #endif case '0': { break; } default: { LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_NOTICE, ignore invalid ext2 file attribute: '%c', *val) break; } } val++; } } #endif