/* aide, Advanced Intrusion Detection Environment * * Copyright (C) 1999-2006,2009-2012,2015,2016 Rami Lehti,Pablo Virolainen, * Mike Markley, Richard van den Berg, Hannes von Haugwitz * $Header$ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "aide.h" #include #include #include #include #include #include #include #include #include #include #include #include "report.h" #include "list.h" #include "gen_list.h" #include "seltree.h" #include "db.h" #include "db_config.h" #include "commandconf.h" #include "report.h" /*for locale support*/ #include "locale-aide.h" /*for locale support*/ #define CLOCK_SKEW 5 #ifdef WITH_MHASH #include #endif #include "md.h" #include "do_md.h" void hsymlnk(db_line* line); void fs2db_line(struct AIDE_STAT_TYPE* fs,db_line* line); void calc_md(struct AIDE_STAT_TYPE* old_fs,db_line* line); void no_hash(db_line* line); static DB_ATTR_TYPE get_special_report_group(char* group) { DB_ATTR_TYPE attr = get_groupval(group); return attr==DB_ATTR_UNDEF?0:attr; } static int bytecmp(byte *b1, byte *b2, size_t len) { return strncmp((char *)b1, (char *)b2, len); } static int has_str_changed(char* old,char* new) { return (((old!=NULL && new!=NULL) && strcmp(old,new)!=0 ) || ((old!=NULL && new==NULL) || (old==NULL && new!=NULL))); } static int has_md_changed(byte* old,byte* new,int len) { error(255,"Debug, has_md_changed %p %p\n",old,new); return (((old!=NULL && new!=NULL) && (bytecmp(old,new,len)!=0)) || ((old!=NULL && new==NULL) || (old==NULL && new!=NULL))); } #ifdef WITH_ACL #ifdef WITH_SUN_ACL static int compare_single_acl(aclent_t* a1,aclent_t* a2) { if (a1->a_type!=a2->a_type || a1->a_id!=a2->a_id || a1->a_perm!=a2->a_perm) { return RETFAIL; } return RETOK; } #endif static int has_acl_changed(acl_type* old, acl_type* new) { #ifdef WITH_SUN_ACL int i; #endif if (old==NULL && new==NULL) { return RETOK; } if (old==NULL || new==NULL) { return RETFAIL; } #ifdef WITH_POSIX_ACL if ((!old->acl_a != !new->acl_a) || (!old->acl_d != !new->acl_d) || (old->acl_a && strcmp(old->acl_a, new->acl_a)) || (old->acl_d && strcmp(old->acl_d, new->acl_d))){ return RETFAIL; } #endif #ifdef WITH_SUN_ACL if (old->entries!=new->entries) { return RETFAIL; } /* Sort em up. */ aclsort(old->entries,0,old->acl); aclsort(new->entries,0,new->acl); for(i=0;ientries;i++){ if (compare_single_acl(old->acl+i,new->acl+i)==RETFAIL) { return RETFAIL; } } #endif return RETOK; } #endif #ifdef WITH_XATTR static int cmp_xattr_node(const void *c1, const void *c2) { const xattr_node *x1 = c1; const xattr_node *x2 = c2; return (strcmp(x1->key, x2->key)); } static int have_xattrs_changed(xattrs_type* x1,xattrs_type* x2) { size_t num = 0; if (x1 && (x1->num == 0)) x1 = NULL; if (x2 && (x2->num == 0)) x2 = NULL; if (x1==NULL && x2==NULL) { return RETOK; } if (x1==NULL || x2==NULL) { return RETFAIL; } if (x1->num != x2->num) { return RETFAIL; } qsort(x1->ents, x1->num, sizeof(xattr_node), cmp_xattr_node); qsort(x2->ents, x2->num, sizeof(xattr_node), cmp_xattr_node); while (num++ < x1->num) { const char *x1key = NULL; const byte *x1val = NULL; size_t x1vsz = 0; const char *x2key = NULL; const byte *x2val = NULL; size_t x2vsz = 0; x1key = x1->ents[num - 1].key; x1val = x1->ents[num - 1].val; x1vsz = x1->ents[num - 1].vsz; x2key = x2->ents[num - 1].key; x2val = x2->ents[num - 1].val; x2vsz = x2->ents[num - 1].vsz; if (strcmp(x1key, x2key) || x1vsz != x2vsz || memcmp(x1val, x2val, x1vsz)) return RETFAIL; } return RETOK; } #endif #ifdef WITH_E2FSATTRS static int has_e2fsattrs_changed(unsigned long old, unsigned long new) { return (~(conf->report_ignore_e2fsattrs)&(old^new)); } #endif /* * Returns the changed attributes for two database lines. * * Attributes are only compared if they exist in both database lines. */ static DB_ATTR_TYPE get_changed_attributes(db_line* l1,db_line* l2) { #define easy_compare(a,b) \ if((a&l1->attr && (a&l2->attr)) && l1->b!=l2->b){\ ret|=a;\ } #define easy_md_compare(a,b,c) \ if((a&l1->attr && (a&l2->attr)) && has_md_changed(l1->b,l2->b, c)){ \ ret|=a; \ } #define easy_function_compare(a,b,c) \ if((a&l1->attr && (a&l2->attr)) && c(l1->b,l2->b)){ \ ret|=a; \ } DB_ATTR_TYPE ret=0; if ((DB_FTYPE&l1->attr && DB_FTYPE&l2->attr) && (l1->perm&S_IFMT)!=(l2->perm&S_IFMT)) { ret|=DB_FTYPE; } easy_function_compare(DB_LINKNAME,linkname,has_str_changed); if ((DB_SIZEG&l1->attr && DB_SIZEG&l2->attr) && l1->size>l2->size){ ret|=DB_SIZEG; } easy_compare(DB_SIZE,size); easy_compare(DB_BCOUNT,bcount); easy_compare(DB_PERM,perm); easy_compare(DB_UID,uid); easy_compare(DB_GID,gid); easy_compare(DB_ATIME,atime); easy_compare(DB_MTIME,mtime); easy_compare(DB_CTIME,ctime); easy_compare(DB_INODE,inode); easy_compare(DB_LNKCOUNT,nlink); easy_md_compare(DB_MD5,md5,HASH_MD5_LEN); easy_md_compare(DB_SHA1,sha1,HASH_SHA1_LEN); easy_md_compare(DB_RMD160,rmd160,HASH_RMD160_LEN); easy_md_compare(DB_TIGER,tiger,HASH_TIGER_LEN); easy_md_compare(DB_SHA256,sha256,HASH_SHA256_LEN); easy_md_compare(DB_SHA512,sha512,HASH_SHA512_LEN); #ifdef WITH_MHASH easy_md_compare(DB_CRC32,crc32,HASH_CRC32_LEN); easy_md_compare(DB_HAVAL,haval,HASH_HAVAL256_LEN); easy_md_compare(DB_GOST,gost,HASH_GOST_LEN); easy_md_compare(DB_CRC32B,crc32b,HASH_CRC32B_LEN); easy_md_compare(DB_WHIRLPOOL,whirlpool,HASH_WHIRLPOOL_LEN); #endif #ifdef WITH_ACL easy_function_compare(DB_ACL,acl,has_acl_changed); #endif #ifdef WITH_XATTR easy_function_compare(DB_XATTRS,xattrs,have_xattrs_changed); #endif #ifdef WITH_SELINUX easy_function_compare(DB_SELINUX,cntx,has_str_changed); #endif #ifdef WITH_E2FSATTRS easy_function_compare(DB_E2FSATTRS,e2fsattrs,has_e2fsattrs_changed); #endif error(255,"Debug, changed attributes for entry %s [%llx %llx]: %llx\n", l1->filename,l1->attr,l2->attr,ret); return ret; } int compare_node_by_path(const void *n1, const void *n2) { const seltree *x1 = n1; const seltree *x2 = n2; return strcmp(x1->path, x2->path); } char* strrxtok(char* rx) { char*p=NULL; char*t=NULL; size_t i=0; /* The following code assumes that the first character is a slash */ size_t lastslash=1; p=strdup(rx); p[0]='/'; for(i=1;iparent) depth++; return depth; } /* This function returns a node with the same inode value as the 'file' */ /* The only place it is used is in add_file_to_tree() function */ static seltree* get_seltree_inode(seltree* tree, db_line* file, int db) { seltree* node=NULL; list* r=NULL; char* tmp=NULL; if(tree==NULL){ return NULL; } /* found the match */ if((db == DB_NEW && tree->new_data != NULL && file->inode == tree->new_data->inode) || (db == DB_OLD && tree->old_data != NULL && file->inode == tree->old_data->inode)) { return tree; } /* tmp is the directory of the file->filename */ tmp=strgetndirname(file->filename,treedepth(tree)+1); for(r=tree->childs;r;r=r->next){ /* We are interested only in files with the same regexp specification */ if(strlen(tmp) == strlen(file->filename) || strncmp(((seltree*)r->data)->path,tmp,strlen(tmp)+1)==0){ node=get_seltree_inode((seltree*)r->data,file,db); if(node!=NULL){ break; } } } free(tmp); return node; } seltree* get_seltree_node(seltree* tree,char* path) { seltree* node=NULL; list* r=NULL; char* tmp=NULL; if(tree==NULL){ return NULL; } if(strncmp(path,tree->path,strlen(path)+1)==0){ return tree; } else{ tmp=strgetndirname(path,treedepth(tree)+1); for(r=tree->childs;r;r=r->next){ if(strncmp(((seltree*)r->data)->path,tmp,strlen(tmp)+1)==0){ node=get_seltree_node((seltree*)r->data,path); if(node!=NULL){ /* Don't leak memory */ free(tmp); return node; } } } free(tmp); } return NULL; } void copy_rule_ref(seltree* node, rx_rule* r) { if( r!=NULL ){ node->conf_lineno = r->conf_lineno; node->rx=strdup(r->rx); } else { node->conf_lineno = -1; node->rx=NULL; } } seltree* new_seltree_node( seltree* tree, char*path, int isrx, rx_rule* r) { seltree* node=NULL; seltree* parent=NULL; char* tmprxtok = NULL; node=(seltree*)malloc(sizeof(seltree)); node->childs=NULL; node->path=strdup(path); node->sel_rx_lst=NULL; node->neg_rx_lst=NULL; node->equ_rx_lst=NULL; node->checked=0; node->attr=0; node->new_data=NULL; node->old_data=NULL; copy_rule_ref(node,r); if(tree!=NULL){ tmprxtok = strrxtok(path); if(isrx){ parent=get_seltree_node(tree,tmprxtok); }else { char* dirn=strlastslash(path); parent=get_seltree_node(tree,dirn); free(dirn); } if(parent==NULL){ if(isrx){ parent=new_seltree_node(tree,tmprxtok,isrx,r); }else { char* dirn=strlastslash(path); parent=new_seltree_node(tree,dirn,isrx,r); free(dirn); } } free(tmprxtok); parent->childs=list_sorted_insert(parent->childs,(void*)node, compare_node_by_path); node->parent=parent; }else { node->parent=NULL; } return node; } void gen_seltree(list* rxlist,seltree* tree,char type) { pcre* rxtmp = NULL; const char* pcre_error; int pcre_erroffset; seltree* curnode = NULL; list* r = NULL; char* rxtok = NULL; rx_rule* rxc = NULL; for(r=rxlist;r;r=r->next){ rx_rule* curr_rule = (rx_rule*)r->data; rxtok=strrxtok(curr_rule->rx); curnode=get_seltree_node(tree,rxtok); if(curnode==NULL){ curnode=new_seltree_node(tree,rxtok,1,curr_rule); } error(240,"Handling %s with %c \"%s\" with node \"%s\"\n",rxtok,type,curr_rule->rx,curnode->path); if((rxtmp=pcre_compile(curr_rule->rx, PCRE_ANCHORED, &pcre_error, &pcre_erroffset, NULL)) == NULL) { error(0,_("Error in regexp '%s' at %i: %s\n"),curr_rule->rx, pcre_erroffset, pcre_error); }else{ /* replace regexp text with regexp compiled */ rxc=(rx_rule*)malloc(sizeof(rx_rule)); /* and copy the rest */ rxc->rx=curr_rule->rx; rxc->crx=rxtmp; rxc->attr=curr_rule->attr; rxc->conf_lineno=curr_rule->conf_lineno; rxc->restriction=curr_rule->restriction; switch (type){ case 's':{ curnode->sel_rx_lst=list_append(curnode->sel_rx_lst,(void*)rxc); break; } case 'n':{ curnode->neg_rx_lst=list_append(curnode->neg_rx_lst,(void*)rxc); break; } case 'e':{ curnode->equ_rx_lst=list_append(curnode->equ_rx_lst,(void*)rxc); break; } } } /* Data should not be free'ed because it's in rxc struct * and freeing is done if error occour. */ free(rxtok); } } static RESTRICTION_TYPE get_file_type(mode_t mode) { switch (mode & S_IFMT) { case S_IFREG: return RESTRICTION_FT_REG; case S_IFDIR: return RESTRICTION_FT_DIR; #ifdef S_IFIFO case S_IFIFO: return RESTRICTION_FT_FIFO; #endif case S_IFLNK: return RESTRICTION_FT_LNK; case S_IFBLK: return RESTRICTION_FT_BLK; case S_IFCHR: return RESTRICTION_FT_CHR; #ifdef S_IFSOCK case S_IFSOCK: return RESTRICTION_FT_SOCK; #endif #ifdef S_IFDOOR case S_IFDOOR: return RESTRICTION_FT_DOOR; #endif #ifdef S_IFDOOR case S_IFPORT: return RESTRICTION_FT_PORT; #endif default: return RESTRICTION_NULL; } } static int check_list_for_match(list* rxrlist,char* text,DB_ATTR_TYPE* attr, RESTRICTION_TYPE file_type) { list* r=NULL; int retval=1; int pcre_retval; pcre_extra *pcre_extra = NULL; for(r=rxrlist;r;r=r->next){ pcre_retval=pcre_exec((pcre*)((rx_rule*)r->data)->crx, pcre_extra, text, strlen(text), 0, PCRE_PARTIAL_SOFT, NULL, 0); if (pcre_retval >= 0) { error(231,"\"%s\" matches (pcre_exec return value: %i) rule from line #%ld: %s\n",text, pcre_retval, ((rx_rule*)r->data)->conf_lineno,((rx_rule*)r->data)->rx); if (!((rx_rule*)r->data)->restriction || file_type&((rx_rule*)r->data)->restriction) { *attr=((rx_rule*)r->data)->attr; error(231,"\"%s\" matches restriction (%u) for rule from line #%ld: %s\n",text, ((rx_rule*)r->data)->restriction, ((rx_rule*)r->data)->conf_lineno,((rx_rule*)r->data)->rx); return 0; } else { error(232,"\"%s\" doesn't match restriction (%u) for rule from line #%ld: %s\n",text, ((rx_rule*)r->data)->restriction, ((rx_rule*)r->data)->conf_lineno,((rx_rule*)r->data)->rx); retval=-1; } } else if (pcre_retval == PCRE_ERROR_PARTIAL) { error(232,"\"%s\" PARTIAL matches (pcre_exec return value: %i) rule from line #%ld: %s\n",text, pcre_retval, ((rx_rule*)r->data)->conf_lineno,((rx_rule*)r->data)->rx); retval=-1; } else { error(232,"\"%s\" doesn't match (pcre_exec return value: %i) rule from line #%ld: %s\n",text, pcre_retval,((rx_rule*)r->data)->conf_lineno,((rx_rule*)r->data)->rx); } } return retval; } /* * Function check_node_for_match() * calls itself recursively to go to the top and then back down. * uses check_list_for_match() * returns: * 0, if a negative rule was matched * 1, if a selective rule was matched * 2, if a equals rule was matched * retval if no rule was matched. * retval&3 if no rule was matched and first in the recursion * to keep state revat is orred with: * 4, matched deeper on equ rule * 8, matched deeper on sel rule *16, this is a recursed call */ static int check_node_for_match(seltree*node,char*text, mode_t perm, int retval,DB_ATTR_TYPE* attr) { int top=0; RESTRICTION_TYPE file_type; if(node==NULL){ return retval; } file_type = get_file_type(perm); /* if this call is not recursive we check the equals list and we set top * * and retval so we know following calls are recursive */ if(!(retval&16)){ top=1; retval|=16; switch (check_list_for_match(node->equ_rx_lst, text, attr, file_type)) { case 0: { error(220, "check_node_for_match: equal match for '%s'\n", text); retval|=2|4; break; } case -1: { if(S_ISDIR(perm) && get_seltree_node(node,text)==NULL) { error(220, "check_node_for_match: creating new seltree node for '%s'\n", text); new_seltree_node(node,text,0,NULL); } break; } } } /* We'll use retval to pass information on whether to recurse * the dir or not */ /* If 4 and 8 are not set, we will check for matches */ if(!(retval&(4|8))){ switch (check_list_for_match(node->sel_rx_lst, text, attr, file_type)) { case 0: { error(220, "check_node_for_match: selective match for '%s'\n", text); retval|=1|8; break; } case -1: { if(S_ISDIR(perm) && get_seltree_node(node,text)==NULL) { error(220, "check_node_for_match: creating new seltree node for '%s'\n", text); new_seltree_node(node,text,0,NULL); } break; } } } /* Now let's check the ancestors */ retval=check_node_for_match(node->parent,text, perm, retval,attr); /* Negative regexps are the strongest so they are checked last */ /* If this file is to be added */ if(retval){ if(!check_list_for_match(node->neg_rx_lst, text, attr, file_type)){ error(220, "check_node_for_match: negative match for '%s'\n", text); retval=0; } } /* Now we discard the info whether a match was made or not * * and just return 0,1 or 2 */ if(top){ retval&=3; } return retval; } void print_tree(seltree* tree) { list* r; rx_rule* rxc; error(220,"tree: \"%s\"\n",tree->path); for(r=tree->sel_rx_lst;r!=NULL;r=r->next) { rxc=r->data; error(220,"%li\t%s\n",rxc->conf_lineno,rxc->rx); } for(r=tree->equ_rx_lst;r!=NULL;r=r->next) { rxc=r->data; error(220,"%li=\t%s\n",rxc->conf_lineno,rxc->rx); } for(r=tree->neg_rx_lst;r!=NULL;r=r->next) { rxc=r->data; error(220,"%li!\t%s\n",rxc->conf_lineno,rxc->rx); } for(r=tree->childs;r!=NULL;r=r->next) { print_tree(r->data); } } seltree* gen_tree(list* prxlist,list* nrxlist,list* erxlist) { seltree* tree=NULL; tree=new_seltree_node(NULL,"/",0,NULL); gen_seltree(prxlist,tree,'s'); gen_seltree(nrxlist,tree,'n'); gen_seltree(erxlist,tree,'e'); print_tree(tree); return tree; } /* * strip_dbline() * strips given dbline */ void strip_dbline(db_line* line) { #define checked_free(x) do { free(x); x=NULL; } while (0) DB_ATTR_TYPE attr = line->attr; /* filename is always needed, hence it is never stripped */ if(!(attr&DB_LINKNAME)){ checked_free(line->linkname); } /* permissions are always needed for file type detection, hence they are * never stripped */ if(!(attr&DB_UID)){ line->uid=0; } if(!(attr&DB_GID)){ line->gid=0; } if(!(attr&DB_ATIME)){ line->atime=0; } if(!(attr&DB_CTIME)){ line->ctime=0; } if(!(attr&DB_MTIME)){ line->mtime=0; } /* inode is always needed for ignoring changed filename, hence it is * never stripped */ if(!(attr&DB_LNKCOUNT)){ line->nlink=0; } if(!(attr&DB_SIZE)&&!(attr&DB_SIZEG)){ line->size=0; } if(!(attr&DB_BCOUNT)){ line->bcount=0; } if(!(attr&DB_MD5)){ checked_free(line->md5); } if(!(attr&DB_SHA1)){ checked_free(line->sha1); } if(!(attr&DB_RMD160)){ checked_free(line->rmd160); } if(!(attr&DB_TIGER)){ checked_free(line->tiger); } if(!(attr&DB_HAVAL)){ checked_free(line->haval); } if(!(attr&DB_CRC32)){ checked_free(line->crc32); } #ifdef WITH_MHASH if(!(attr&DB_CRC32B)){ checked_free(line->crc32b); } if(!(attr&DB_GOST)){ checked_free(line->gost); } if(!(attr&DB_WHIRLPOOL)){ checked_free(line->whirlpool); } #endif if(!(attr&DB_SHA256)){ checked_free(line->sha256); } if(!(attr&DB_SHA512)){ checked_free(line->sha512); } #ifdef WITH_ACL if(!(attr&DB_ACL)){ if (line->acl) { free(line->acl->acl_a); free(line->acl->acl_d); } checked_free(line->acl); } #endif #ifdef WITH_XATTR if(!(attr&DB_XATTRS)){ if (line->xattrs) free(line->xattrs->ents); checked_free(line->xattrs); } #endif #ifdef WITH_SELINUX if(!(attr&DB_SELINUX)){ checked_free(line->cntx); } #endif /* e2fsattrs is stripped within e2fsattrs2line in do_md */ } /* * add_file_to_tree * db = which db this file belongs to * attr attributes to add */ static void add_file_to_tree(seltree* tree,db_line* file,int db, DB_ATTR_TYPE attr) { seltree* node=NULL; DB_ATTR_TYPE localignorelist=0; DB_ATTR_TYPE ignored_added_attrs, ignored_removed_attrs, ignored_changed_attrs; if(file==NULL){ error(0, "add_file_to_tree was called with NULL db_line\n"); } node=get_seltree_node(tree,file->filename); if(!node){ node=new_seltree_node(tree,file->filename,0,NULL); } /* add note to this node which db has modified it */ node->checked|=db; node->attr=attr; strip_dbline(file); switch (db) { case DB_OLD: { node->old_data=file; break; } case DB_NEW: { node->new_data=file; break; } case DB_OLD|DB_NEW: { node->new_data=file; if(conf->action&DO_INIT) { node->checked|=NODE_FREE; } else { free_db_line(node->new_data); free(node->new_data); node->new_data=NULL; } return; } } /* We have a way to ignore some changes... */ ignored_added_attrs = get_special_report_group("report_ignore_added_attrs"); ignored_removed_attrs = get_special_report_group("report_ignore_removed_attrs"); ignored_changed_attrs = get_special_report_group("report_ignore_changed_attrs"); if((node->checked&DB_OLD)&&(node->checked&DB_NEW)){ if (((node->old_data)->attr&~((node->new_data)->attr)&~(ignored_removed_attrs))|(~((node->old_data)->attr)&(node->new_data)->attr&~(ignored_added_attrs))) { error(2,"Entry %s in databases has different attributes: %llx %llx\n", node->old_data->filename,node->old_data->attr,node->new_data->attr); } node->changed_attrs=get_changed_attributes(node->old_data,node->new_data); /* Free the data if same else leave as is for report_tree */ if((~(ignored_changed_attrs)&node->changed_attrs)==RETOK){ /* FIXME this messes up the tree on SunOS. Don't know why. Fix needed badly otherwise we leak memory like hell. */ node->changed_attrs=0; free_db_line(node->old_data); free(node->old_data); node->old_data=NULL; /* Free new data if not needed for write_tree */ if(conf->action&DO_INIT) { node->checked|=NODE_FREE; } else { free_db_line(node->new_data); free(node->new_data); node->new_data=NULL; } return; } } /* Do verification if file was moved only if we are asked for it. * old and new data are NULL only if file present in both DBs * and has not been changed. */ if( (node->old_data!=NULL || node->new_data!=NULL) && (file->attr & DB_CHECKINODE)) { /* Check if file was moved (same inode, different name in the other DB)*/ db_line *oldData; db_line *newData; seltree* moved_node; moved_node=get_seltree_inode(tree,file,db==DB_OLD?DB_NEW:DB_OLD); if(!(moved_node == NULL || moved_node == node)) { /* There's mo match for inode or it matches the node with the same name. * In first case we don't have a match to compare with. * In the second - we already compared those files. */ if(db == DB_NEW) { newData = node->new_data; oldData = moved_node->old_data; } else { newData = moved_node->new_data; oldData = node->old_data; } localignorelist=(oldData->attr^newData->attr)&(~(DB_NEWFILE|DB_RMFILE|DB_CHECKINODE)); if (localignorelist!=0) { error(220,"Ignoring moved entry (\"%s\" [%llx] => \"%s\" [%llx]) due to different attributes: %llx\n", oldData->filename, oldData->attr, newData->filename, newData->attr, localignorelist); } else { /* Free the data if same else leave as is for report_tree */ if ((get_changed_attributes(oldData, newData)&~(ignored_changed_attrs|DB_CTIME)) == RETOK) { node->checked |= db==DB_NEW ? NODE_MOVED_IN : NODE_MOVED_OUT; moved_node->checked |= db==DB_NEW ? NODE_MOVED_OUT : NODE_MOVED_IN; error(220,_("Entry was moved: %s [%llx] => %s [%llx]\n"), oldData->filename , oldData->attr, newData->filename, newData->attr); } else { error(220,"Ignoring moved entry (\"%s\" => \"%s\") because the entries mismatch\n", oldData->filename, newData->filename); } } } } if( (db == DB_NEW) && (node->new_data!=NULL) && (file->attr & DB_NEWFILE) ){ node->checked|=NODE_ALLOW_NEW; } if( (db == DB_OLD) && (node->old_data!=NULL) && (file->attr & DB_RMFILE) ){ node->checked|=NODE_ALLOW_RM; } } int check_rxtree(char* filename,seltree* tree,DB_ATTR_TYPE* attr, mode_t perm) { int retval=0; char * tmp=NULL; char * parentname=NULL; seltree* pnode=NULL; parentname=strdup(filename); tmp=strrchr(parentname,'/'); if(tmp!=parentname){ *tmp='\0'; }else { if(parentname[1]!='\0'){ /* we are in the root dir */ parentname[1]='\0'; } } if(conf->limit!=NULL) { retval=pcre_exec(conf->limit_crx, NULL, filename, strlen(filename), 0, PCRE_PARTIAL_SOFT, NULL, 0); if (retval >= 0) { error(220, "check_rxtree: %s does match limit: %s\n", filename, conf->limit); } else if (retval == PCRE_ERROR_PARTIAL) { error(220, "check_rxtree: %s does PARTIAL match limit: %s\n", filename, conf->limit); if(S_ISDIR(perm) && get_seltree_node(tree,filename)==NULL){ error(220, "check_rxtree: creating new seltree node for '%s'\n", filename); new_seltree_node(tree,filename,0,NULL); } return -1; } else { error(220, "check_rxtree: %s does NOT match limit: %s\n", filename, conf->limit); return -2; } } pnode=get_seltree_node(tree,parentname); *attr=0; retval=check_node_for_match(pnode,filename, perm, 0,attr); free(parentname); return retval; } db_line* get_file_attrs(char* filename,DB_ATTR_TYPE attr, struct AIDE_STAT_TYPE *fs) { db_line* line=NULL; time_t cur_time; if(!(attr&DB_RDEV)) fs->st_rdev=0; /* Get current time for future time notification. */ cur_time=time(NULL); if (cur_time==(time_t)-1) { char* er=strerror(errno); if (er==NULL) { error(0,_("Can not get current time. strerror failed for %i\n"),errno); } else { error(0,_("Can not get current time with reason %s\n"),er); } } else { if(fs->st_atime>cur_time){ error(CLOCK_SKEW,_("%s atime in future\n"),filename); } if(fs->st_mtime>cur_time){ error(CLOCK_SKEW,_("%s mtime in future\n"),filename); } if(fs->st_ctime>cur_time){ error(CLOCK_SKEW,_("%s ctime in future\n"),filename); } } /* Malloc if we have something to store.. */ line=(db_line*)malloc(sizeof(db_line)); memset(line,0,sizeof(db_line)); /* We want filename */ line->attr=attr|DB_FILENAME; /* Just copy some needed fields. */ line->fullpath=filename; line->filename=&filename[conf->root_prefix_length]; line->perm_o=fs->st_mode; line->size_o=fs->st_size; line->linkname=NULL; /* Handle symbolic link */ hsymlnk(line); /* Set normal part */ fs2db_line(fs,line); /* ACL stuff */ #ifdef WITH_ACL acl2line(line); #endif #ifdef WITH_XATTR xattrs2line(line); #endif #ifdef WITH_SELINUX selinux2line(line); #endif #ifdef WITH_E2FSATTRS e2fsattrs2line(line); #endif if (attr&DB_HASHES && S_ISREG(fs->st_mode)) { calc_md(fs,line); } else { /* We cannot calculate hash for nonfile. Mark it to attr. */ no_hash(line); } return line; } static void write_tree(seltree* node) { list* r=NULL; if (node->checked&DB_NEW) { db_writeline(node->new_data,conf); if (node->checked&NODE_FREE) { free_db_line(node->new_data); free(node->new_data); node->new_data=NULL; } } for (r=node->childs;r;r=r->next) { write_tree((seltree*)r->data); } } void populate_tree(seltree* tree) { /* FIXME this function could really use threads */ int add=0; db_line* old=NULL; db_line* new=NULL; int initdbwarningprinted=0; DB_ATTR_TYPE attr=0; seltree* node=NULL; /* With this we avoid unnecessary checking of removed files. */ if(conf->action&DO_INIT){ initdbwarningprinted=1; } if(conf->action&DO_DIFF){ while((new=db_readline(DB_NEW)) != NULL){ /* FIXME add support config checking at this stage config check = add only those files that match config rxs make this configurable Only configurability is not implemented. */ /* This is needed because check_rxtree assumes there is a parent for the node for old->filename */ if((node=get_seltree_node(tree,new->filename))==NULL){ node=new_seltree_node(tree,new->filename,0,NULL); } if((add=check_rxtree(new->filename,tree,&attr, new->perm))>0){ add_file_to_tree(tree,new,DB_NEW,attr); } else { free_db_line(new); free(new); new=NULL; } } } if((conf->action&DO_INIT)||(conf->action&DO_COMPARE)){ /* FIXME */ new=NULL; while((new=db_readline(DB_DISK)) != NULL) { add_file_to_tree(tree,new,DB_NEW,attr); } } if((conf->action&DO_COMPARE)||(conf->action&DO_DIFF)){ while((old=db_readline(DB_OLD)) != NULL) { /* This is needed because check_rxtree assumes there is a parent for the node for old->filename */ if((node=get_seltree_node(tree,old->filename))==NULL){ node=new_seltree_node(tree,old->filename,0,NULL); } add=check_rxtree(old->filename,tree,&attr, old->perm); if(add > 0) { add_file_to_tree(tree,old,DB_OLD,attr); } else if (conf->limit!=NULL && add < 0) { add_file_to_tree(tree,old,DB_OLD|DB_NEW,attr); }else{ free_db_line(old); free(old); old=NULL; if(!initdbwarningprinted){ error(3,_("WARNING: Old db contains a entry that shouldn\'t be there, run --init or --update\n")); initdbwarningprinted=1; } } } } if(conf->action&DO_INIT) { write_tree(tree); } } void hsymlnk(db_line* line) { if((S_ISLNK(line->perm_o))){ int len=0; #ifdef WITH_ACL if(conf->no_acl_on_symlinks!=1) { line->attr&=(~DB_ACL); } #endif if(conf->warn_dead_symlinks==1) { struct AIDE_STAT_TYPE fs; int sres; sres=AIDE_STAT_FUNC(line->fullpath,&fs); if (sres!=0 && sres!=EACCES) { error(4,"Dead symlink detected at %s\n",line->fullpath); } if(!(line->attr&DB_RDEV)) fs.st_rdev=0; } /* Is this valid?? No, We should do this elsewhere. */ line->linkname=(char*)malloc(_POSIX_PATH_MAX+1); if(line->linkname==NULL){ error(0,_("malloc failed in hsymlnk()\n")); abort(); } /* Remember to nullify the buffer, because man page says readlink places the contents of the symbolic link path in the buffer buf, which has size bufsiz. readlink does not append a NUL character to buf. It will truncate the con- tents (to a length of bufsiz characters), in case the buffer is too small to hold all of the contents. */ memset(line->linkname,0,_POSIX_PATH_MAX+1); len=readlink(line->fullpath,line->linkname,_POSIX_PATH_MAX+1); /* * We use realloc :) */ line->linkname=realloc(line->linkname,len+1); } else { line->attr&=(~DB_LINKNAME); } } // vi: ts=8 sw=2