/* SAMHAIN file system integrity testing */ /* Copyright (C) 1999, 2000, 2001, 2002 Rainer Wichmann */ /* */ /* 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config_xor.h" /* define this if you want version 1.3 style database file */ /* #define OLD_BUG */ /* make sure state changes of a file are always reported, even * with reportonlyonce=true */ /* #define REPLACE_OLD *//* moved to samhain.h */ #include #include #include #include #include #include #include #ifdef MAJOR_IN_MKDEV #include #else #ifdef MAJOR_IN_SYSMACROS #include #endif #endif #ifdef HAVE_MEMORY_H #include #endif #if defined(SH_WITH_CLIENT) || defined(SH_STANDALONE) #include "sh_hash.h" #include "sh_utils.h" #include "sh_error.h" #include "sh_tiger.h" #include "sh_gpg.h" #include "sh_unix.h" #include "sh_files.h" #include "sh_ignore.h" #include "sh_pthread.h" #if defined(SH_WITH_CLIENT) #include "sh_forward.h" #endif #define SH_KEY_NULL _("000000000000000000000000000000000000000000000000") #undef FIL__ #define FIL__ _("sh_hash.c") SH_MUTEX_STATIC(mutex_hash,PTHREAD_MUTEX_INITIALIZER); const char notalink[2] = { '-', '\0' }; static char * all_items (file_type * theFile, char * fileHash, int is_new); #define QUOTE_CHAR '=' char * unquote_string (const char * str, size_t len) { int i = 0, t1, t2; char * tmp = NULL; size_t l2, j, k = 0; SL_ENTER(_("unquote_string")); if (str != NULL) { l2 = len - 2; tmp = SH_ALLOC(len + 1); for (j = 0; j <= len; ++j) { if (str[j] != QUOTE_CHAR) { tmp[k] = str[j]; } else if (str[j] == QUOTE_CHAR && j < l2) { t1 = sh_util_hexchar(str[j+1]); t2 = sh_util_hexchar(str[j+2]); if ((t1|t2) >= 0) { i = 16 * t1 + t2; tmp[k] = i; j += 2; } else { tmp[k] = str[j]; } } else tmp[k] = str[j]; ++k; } } SL_RETURN(tmp, _("unquote_string")); } static char * int2hex (unsigned char i, char * i2h) { static char hexchars[] = "0123456789ABCDEF"; i2h[0] = hexchars[(((i) & 0xF0) >> 4)]; /* high */ i2h[1] = hexchars[((i) & 0x0F)]; /* low */ return i2h; } char * quote_string (const char * str, size_t len) { char * tmp; char * tmp2; size_t l2, j, i = 0, k = 0; char i2h[2]; SL_ENTER(_("quote_string")); if (str == NULL) { SL_RETURN(NULL, _("quote_string")); } for (j = 0; j < len; ++j) if (str[j] == '\n' || str[j] == QUOTE_CHAR) ++i; l2 = len + 1; if (sl_ok_muls(3, i) && sl_ok_adds(l2, (3*i))) { tmp = SH_ALLOC(len + 1 + 3*i); } else { sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN, _("integer overflow"), _("quote_string")); SL_RETURN(NULL, _("quote_string")); } for (j = 0; j <= len; ++j) { if (str[j] == '\n') { tmp2 = int2hex((unsigned char) '\n', i2h); /* was 'n', fixed in 1.5.4 */ tmp[k] = QUOTE_CHAR; ++k; tmp[k] = tmp2[0]; ++k; tmp[k] = tmp2[1]; } else if (str[j] == QUOTE_CHAR) { tmp2 = int2hex((unsigned char) QUOTE_CHAR, i2h); tmp[k] = QUOTE_CHAR; ++k; tmp[k] = tmp2[0]; ++k; tmp[k] = tmp2[1]; } else { tmp[k] = str[j]; } ++k; } SL_RETURN(tmp, _("quote_string")); } static UINT32 * swap_32 (UINT32 * iptr) { #ifdef WORDS_BIGENDIAN unsigned char swap; unsigned char * ii = (unsigned char *) iptr; swap = ii[0]; ii[0] = ii[3]; ii[3] = swap; swap = ii[1]; ii[1] = ii[2]; ii[2] = swap; return iptr; #else return iptr; #endif } static UINT64 * swap_64 (UINT64 * iptr) { #ifdef WORDS_BIGENDIAN #ifdef UINT64_IS_32 swap_32 ((UINT32*) iptr); #else unsigned char swap; unsigned char * ii = (unsigned char *) iptr; swap = ii[0]; ii[0] = ii[7]; ii[7] = swap; swap = ii[1]; ii[1] = ii[6]; ii[6] = swap; swap = ii[2]; ii[2] = ii[5]; ii[5] = swap; swap = ii[3]; ii[3] = ii[4]; ii[4] = swap; #endif return iptr; #else return iptr; #endif } static unsigned short * swap_short (unsigned short * iptr) { #ifdef WORDS_BIGENDIAN if (sizeof(short) == 4) swap_32 ((UINT32*) iptr); else { /* alignment problem */ unsigned char swap; static unsigned short ooop; unsigned char * ii; ooop = *iptr; ii = (unsigned char *) &ooop; /* printf("SWAP0: %hd %d\n", *iptr, sizeof(unsigned short)); */ swap = ii[0]; ii[0] = ii[1]; ii[1] = swap; /* printf("SWAP1: %hd\n", (unsigned short) ooop); */ #ifndef OLD_BUG return &ooop; #endif } return iptr; #else return iptr; #endif } typedef struct store_info { UINT32 mode; UINT32 linkmode; UINT64 dev; UINT64 rdev; UINT32 hardlinks; UINT32 ino; UINT64 size; UINT64 atime; UINT64 mtime; UINT64 ctime; UINT32 owner; UINT32 group; #ifdef OLD_BUG #if defined(__linux__) UINT32 attributes; char c_attributes[ATTRBUF_SIZE]; #endif #else /* #if defined(__linux__) */ UINT32 attributes; char c_attributes[ATTRBUF_SIZE]; /* endif */ #endif unsigned short mark; char c_owner[USER_MAX+2]; char c_group[GROUP_MAX+2]; char c_mode[CMODE_SIZE]; char checksum[KEY_LEN+1]; } sh_filestore_t; typedef struct file_info { sh_filestore_t theFile; char * fullpath; char * linkpath; char * attr_string; int fflags; unsigned long modi_mask; struct file_info * next; } sh_file_t; static const char *policy[] = { N_("[]"), N_("[ReadOnly]"), N_("[LogFiles]"), N_("[GrowingLogs]"), N_("[IgnoreNone]"), N_("[IgnoreAll]"), N_("[Attributes]"), N_("[User0]"), N_("[User1]"), N_("[User2]"), N_("[User3]"), N_("[User4]"), N_("[Prelink]"), NULL }; const char * sh_hash_getpolicy(int class) { if (class > 0 && class < SH_ERR_T_DIR) return _(policy[class]); return _("[indef]"); } /********************************** * * hash table functions * ********************************** */ #include "sh_hash.h" /* must fit an int */ /* #define TABSIZE 2048 */ #define TABSIZE 65536 /* must fit an unsigned short */ /* changed for V0.8, as the */ /* database format has changed */ /* changed again for V0.9 */ /* #define REC_MAGIC 19 */ /* changed again for V1.3 */ #ifdef OLD_BUG #define REC_MAGIC 20 #else /* changed again for V1.4 */ #define REC_MAGIC 21 #endif #define REC_FLAGS_ATTR (1<<8) #define REC_FLAGS_MASK 0xFF00 /************************************************************** * * create a file_type from a sh_file_t * **************************************************************/ static file_type * sh_hash_create_ft (const sh_file_t * p, char * fileHash) { file_type * theFile; SL_ENTER(_("sh_hash_create_ft")); theFile = SH_ALLOC(sizeof(file_type)); sl_strlcpy(theFile->c_mode, p->theFile.c_mode, 11); theFile->mode = p->theFile.mode; #if defined(__linux__) || defined(HAVE_STAT_FLAGS) sl_strlcpy(theFile->c_attributes, p->theFile.c_attributes, ATTRBUF_SIZE); theFile->attributes = p->theFile.attributes; #endif sl_strlcpy(theFile->fullpath, p->fullpath, PATH_MAX); if (p->linkpath != NULL /* && theFile->c_mode[0] == 'l' */) { theFile->link_path = sh_util_strdup(p->linkpath); } else { theFile->link_path = NULL; } sl_strlcpy(fileHash, p->theFile.checksum, KEY_LEN+1); theFile->mtime = p->theFile.mtime; theFile->ctime = p->theFile.ctime; theFile->atime = p->theFile.atime; theFile->size = p->theFile.size; sl_strlcpy(theFile->c_group, p->theFile.c_group, GROUP_MAX+2); theFile->group = p->theFile.group; sl_strlcpy(theFile->c_owner, p->theFile.c_owner, USER_MAX+2); theFile->owner = p->theFile.owner; theFile->ino = p->theFile.ino; theFile->rdev = p->theFile.rdev; theFile->dev = p->theFile.dev; theFile->hardlinks = p->theFile.hardlinks; if (p->attr_string) theFile->attr_string = sh_util_strdup(p->attr_string); else theFile->attr_string = NULL; SL_RETURN((theFile), _("sh_hash_create_ft")); } struct two_sh_file_t { sh_file_t * prev; sh_file_t * this; }; static sh_file_t * hashsearch (const char * s); static int hashsearch_prev (const char * s, struct two_sh_file_t * a, int * index); static sh_file_t * tab[TABSIZE]; /************************************************************** * * compute hash function * **************************************************************/ static int hashfunc(const char *s) { unsigned int n = 0; for ( ; *s; s++) n = 31 * n + *s; return n & (TABSIZE - 1); /* % TABSIZE */; } int hashreport_missing( char *fullpath, int level) { sh_file_t * p; char * tmp; char fileHash[KEY_LEN + 1]; file_type * theFile; char * str; char hashbuf[KEYBUF_SIZE]; volatile int retval; /* -------- find the entry for the file ---------------- */ SH_MUTEX_LOCK(mutex_hash); retval = 0; if (sl_strlen(fullpath) <= MAX_PATH_STORE) p = hashsearch(fullpath); else p = hashsearch( sh_tiger_hash(fullpath, TIGER_DATA, sl_strlen(fullpath), hashbuf, sizeof(hashbuf)) ); if (p == NULL) { retval = -1; goto unlock_and_return; } theFile = sh_hash_create_ft (p, fileHash); str = all_items(theFile, fileHash, 0); tmp = sh_util_safe_name(fullpath); SH_MUTEX_LOCK(mutex_thread_nolog); sh_error_handle (level, FIL__, __LINE__, 0, MSG_FI_MISS2, tmp, str); SH_MUTEX_UNLOCK(mutex_thread_nolog); ++sh.statistics.files_report; SH_FREE(tmp); SH_FREE(str); if (theFile->attr_string) SH_FREE(theFile->attr_string); if (theFile->link_path) SH_FREE(theFile->link_path); SH_FREE(theFile); unlock_and_return: ; /* 'label at end of compound statement */ SH_MUTEX_UNLOCK(mutex_hash); /* remove here to avoid second message from hash_unvisited */ if (retval == 0) sh_hash_remove (fullpath); return retval; } /************************************************************** * * search for files not visited, and check whether they exist * **************************************************************/ static sh_file_t * delete_db_entry(sh_file_t *p) { if (p->fullpath) { SH_FREE(p->fullpath); p->fullpath = NULL; } if (p->linkpath) { if (p->linkpath != notalink) SH_FREE(p->linkpath); p->linkpath = NULL; } if (p->attr_string) { SH_FREE(p->attr_string); p->attr_string = NULL; } SH_FREE(p); return NULL; } static void hash_unvisited (int j, sh_file_t *prev, sh_file_t *p, ShErrLevel level) { struct stat buf; int i; char * tmp; char * ptr; char fileHash[KEY_LEN + 1]; file_type * theFile; char * str; SL_ENTER(_("hash_unvisited")); if (p->next != NULL) hash_unvisited (j, p, p->next, level); if (p->fullpath == NULL) { SL_RET0(_("hash_unvisited")); } /* Not a fully qualified path, i.e. some info stored by some module */ if (p->fullpath[0] != '/') { SL_RET0(_("hash_unvisited")); } /* visited flag not set: not seen; * checked flag set: not seen (i.e. missing), and already checked * reported flag not set: not reported yet * allignore flag not set: not under IgnoreAll * * Files/directories under IgnoreAll are noticed as missing already * during the file check. */ if (((!SH_FFLAG_VISITED_SET(p->fflags)) || SH_FFLAG_CHECKED_SET(p->fflags)) && (!SH_FFLAG_REPORTED_SET(p->fflags)) && (!SH_FFLAG_ALLIGNORE_SET(p->fflags))) { i = retry_lstat(FIL__, __LINE__, p->fullpath, &buf); /* if file does not exist */ if (0 != i) { ptr = sh_util_dirname (p->fullpath); if (ptr) { /* If any of the parent directories is under IgnoreAll */ if (0 != sh_files_is_allignore(ptr)) level = ShDFLevel[SH_LEVEL_ALLIGNORE]; SH_FREE(ptr); } /* Only report if !SH_FFLAG_CHECKED_SET */ if (!SH_FFLAG_CHECKED_SET(p->fflags)) { if (S_FALSE == sh_ignore_chk_del(p->fullpath)) { tmp = sh_util_safe_name(p->fullpath); theFile = sh_hash_create_ft (p, fileHash); str = all_items(theFile, fileHash, 0); sh_error_handle (level, FIL__, __LINE__, 0, MSG_FI_MISS2, tmp, str); ++sh.statistics.files_report; SH_FREE(str); if (theFile->attr_string) SH_FREE(theFile->attr_string); if (theFile->link_path) SH_FREE(theFile->link_path); SH_FREE(theFile); SH_FREE(tmp); } } /* We rewrite the db on update, thus we need to keep this * if the user does not want to purge it from the db. */ if ((sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) || (S_TRUE == sh.flag.update && S_TRUE == sh_util_ask_update(p->fullpath))) { #ifdef REPLACE_OLD /* Remove the old entry */ if (prev == p) tab[j] = p->next; else prev->next = p->next; delete_db_entry(p); SL_RET0(_("hash_unvisited")); #else SET_SH_FFLAG_REPORTED(p->fflags); #endif } } } else if (SH_FFLAG_VISITED_SET(p->fflags) && SH_FFLAG_REPORTED_SET(p->fflags) && (!SH_FFLAG_ALLIGNORE_SET(p->fflags))) { if (S_FALSE == sh_ignore_chk_new(p->fullpath)) { tmp = sh_util_safe_name(p->fullpath); theFile = sh_hash_create_ft (p, fileHash); str = all_items(theFile, fileHash, 0); sh_error_handle (level, FIL__, __LINE__, 0, MSG_FI_MISS2, tmp, str); ++sh.statistics.files_report; SH_FREE(str); if (theFile->attr_string) SH_FREE(theFile->attr_string); SH_FREE(theFile); SH_FREE(tmp); } CLEAR_SH_FFLAG_REPORTED(p->fflags); } if (sh.flag.reportonce == S_FALSE) CLEAR_SH_FFLAG_REPORTED(p->fflags); CLEAR_SH_FFLAG_VISITED(p->fflags); CLEAR_SH_FFLAG_CHECKED(p->fflags); SET_SH_FFLAG_ENOENT(p->fflags); SL_RET0(_("hash_unvisited")); } /********************************************************************* * * Search for files in the database that have been deleted from disk. * *********************************************************************/ void sh_hash_unvisited (ShErrLevel level) { int i; SL_ENTER(_("sh_hash_unvisited")); SH_MUTEX_LOCK(mutex_hash); for (i = 0; i < TABSIZE; ++i) { if (tab[i] != NULL) hash_unvisited (i, tab[i], tab[i], level); } SH_MUTEX_UNLOCK(mutex_hash); SL_RET0(_("hash_unvisited")); } /********************************************************************* * * Remove a single file from the database. * *********************************************************************/ void sh_hash_remove (const char * path) { struct two_sh_file_t entries; int index; SL_ENTER(_("sh_hash_remove")); SH_MUTEX_LOCK(mutex_hash); if ((sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) || (S_TRUE == sh.flag.update && S_TRUE == sh_util_ask_update(path))) { if (0 == hashsearch_prev (path, &entries, &index)) { sh_file_t * p = entries.this; #ifdef REPLACE_OLD /* Remove the old entry */ if (entries.prev == p) tab[index] = p->next; else entries.prev->next = p->next; delete_db_entry(p); goto end; #else SET_SH_FFLAG_REPORTED(p->fflags); #endif } } end: ; /* 'label at end of compound statement' */ SH_MUTEX_UNLOCK(mutex_hash); SL_RET0(_("sh_hash_remove")); } /********************************************************************* * * Search for unvisited entries in the database, custom error handler. * *********************************************************************/ void sh_hash_unvisited_custom (char prefix, void(*handler)(const char * key)) { int i; sh_file_t *p = NULL; sh_file_t *prev = NULL; sh_file_t *next = NULL; SL_ENTER(_("sh_hash_unvisited_custom")); SH_MUTEX_LOCK(mutex_hash); for (i = 0; i < TABSIZE; ++i) { if (tab[i] != NULL) { p = tab[i]; prev = p; do { next = p->next; if (p->fullpath && prefix == p->fullpath[0]) { if ((!SH_FFLAG_VISITED_SET(p->fflags)) && (!SH_FFLAG_REPORTED_SET(p->fflags))) { handler(p->fullpath); if (!SH_FFLAG_CHECKED_SET(p->fflags)) { /* delete */ if (tab[i] == p) { tab[i] = p->next; prev = tab[i]; next = prev; } else { prev->next = p->next; next = prev->next; } p = delete_db_entry(p); } } if (p) { CLEAR_SH_FFLAG_VISITED(p->fflags); CLEAR_SH_FFLAG_CHECKED(p->fflags); } } if (p) prev = p; p = next; } while (p); } } SH_MUTEX_UNLOCK(mutex_hash); SL_RET0(_("hash_unvisited_custom")); } /********************************************************************** * * delete hash array * **********************************************************************/ static void hash_kill (sh_file_t *p) { SL_ENTER(_("hash_kill")); if (p == NULL) SL_RET0(_("hash_kill")); if (p->next != NULL) hash_kill (p->next); if (p->fullpath) { SH_FREE(p->fullpath); p->fullpath = NULL; } if (p->linkpath) { if (p->linkpath != notalink) SH_FREE(p->linkpath); p->linkpath = NULL; } if (p->attr_string) { SH_FREE(p->attr_string); p->attr_string = NULL; } SH_FREE(p); p = NULL; SL_RET0(_("hash_kill")); } /*********************************************************************** * * get info out of hash array * ***********************************************************************/ static sh_file_t * hashsearch (const char * s) { sh_file_t * p; SL_ENTER(_("hashsearch")); if (s) { for (p = tab[hashfunc(s)]; p; p = p->next) if ((p->fullpath != NULL) && (0 == strcmp(s, p->fullpath))) SL_RETURN( p, _("hashsearch")); } SL_RETURN( NULL, _("hashsearch")); } static int hashsearch_prev (const char * s, struct two_sh_file_t * a, int * index) { sh_file_t * this; sh_file_t * prev = NULL; SL_ENTER(_("hashsearch_prev")); if (s) { *index = hashfunc(s); this = tab[*index]; prev = this; if (this) { do { if ((this->fullpath != NULL) && (0 == strcmp(s, this->fullpath))) { a->prev = prev; a->this = this; SL_RETURN( 0, _("hashsearch_prev")); } prev = this; this = this->next; } while(this); } } SL_RETURN( -1, _("hashsearch")); } /*********************************************************************** * * insert into hash array * ***********************************************************************/ static void hashinsert (sh_file_t * s) { sh_file_t * p; sh_file_t * q; int key; SL_ENTER(_("hashinsert")); key = hashfunc(s->fullpath); if (tab[key] == NULL) { tab[key] = s; tab[key]->next = NULL; SL_RET0(_("hashinsert")); } else { p = tab[key]; while (1) { if (p && p->fullpath && 0 == strcmp(s->fullpath, p->fullpath)) { q = p->next; SH_FREE(p->fullpath); if(p->linkpath && p->linkpath != notalink) SH_FREE(p->linkpath); if(p->attr_string) SH_FREE(p->attr_string); memcpy(p, s, sizeof(sh_file_t)); p->next = q; SH_FREE(s); s = NULL; SL_RET0(_("hashinsert")); } else if (p && p->next == NULL) { p->next = s; p->next->next = NULL; SL_RET0(_("hashinsert")); } if (p) p = p->next; else /* cannot really happen, but llvm/clang does not know */ break; } } /* notreached */ } /****************************************************************** * * Get a single line * ******************************************************************/ static FILE * sh_fin_fd = NULL; static int sh_hash_getline (FILE * fd, char * line, int sizeofline) { register int n = 0; char * res; if (sizeofline < 2) { if (sizeofline > 0) line[0] = '\0'; return 0; } res = fgets(line, sizeofline, fd); if (res == NULL) { line[0] = '\0'; return -1; } n = strlen(line); if (n > 0) { --n; line[n] = '\0'; /* remove terminating '\n' */ } return n; } static void sh_hash_getline_end (void) { sl_fclose (FIL__, __LINE__, sh_fin_fd); sh_fin_fd = NULL; return; } /****************************************************************** * * ------- Check functions ------- * ******************************************************************/ static int IsInit = 0; /****************************************************************** * * Fast forward to start of data * ******************************************************************/ int sh_hash_setdataent (SL_TICKET fd, char * line, int size, const char * file) { long i; extern int get_the_fd (SL_TICKET ticket); SL_ENTER(_("sh_hash_setdataent")); sl_rewind (fd); if (sh_fin_fd != NULL) { sl_fclose (FIL__, __LINE__, sh_fin_fd); sh_fin_fd = NULL; } sh_fin_fd = fdopen(dup(get_the_fd(fd)), "rb"); if (!sh_fin_fd) { dlog(1, FIL__, __LINE__, _("The file signature database: %s is not readable.\n"), (NULL == file) ? _("(null)") : file); sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_P_NODATA, ( (NULL == file) ? _("(null)") : file) ); aud_exit (FIL__, __LINE__, EXIT_FAILURE); } while (1) { i = sh_hash_getline (sh_fin_fd, line, size); if (i < 0 ) { SH_FREE(line); dlog(1, FIL__, __LINE__, _("The file signature database: %s does not\ncontain any data, or the start-of-file marker is missing (unlikely,\nunless modified by hand).\n"), (NULL == file) ? _("(null)") : file); sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_P_NODATA, ( (NULL == file) ? _("(null)") : file) ); aud_exit (FIL__, __LINE__, EXIT_FAILURE); } #if defined(SH_STEALTH) if (0 == sl_strncmp (line, N_("[SOF]"), 5)) #else if (0 == sl_strncmp (line, _("[SOF]"), 5)) #endif break; } SL_RETURN( 1, _("sh_hash_setdataent")); } static int sh_hash_setdataent_old (SL_TICKET fd, char * line, int size, char * file) { long i; SL_ENTER(_("sh_hash_setdataent_old")); sl_rewind (fd); while (1) { i = sh_unix_getline (fd, line, size-1); if (i < 0 ) { SH_FREE(line); dlog(1, FIL__, __LINE__, _("The file signature database: %s does not\ncontain any data, or the start-of-file marker is missing (unlikely,\nunless modified by hand).\n"), (NULL == file) ? _("(null)") : file); sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_P_NODATA, ( (NULL == file) ? _("(null)") : file) ); aud_exit (FIL__, __LINE__, EXIT_FAILURE); } #if defined(SH_STEALTH) if (0 == sl_strncmp (line, N_("[SOF]"), 5)) #else if (0 == sl_strncmp (line, _("[SOF]"), 5)) #endif break; } SL_RETURN( 1, _("sh_hash_setdataent_old")); } /****************************************************************** * * Read next record * ******************************************************************/ sh_file_t * sh_hash_getdataent (SL_TICKET fd, char * line, int size) { sh_file_t * p; sh_filestore_t ft; long i; size_t len; char * fullpath; char * linkpath; char * attr_string = NULL; char * tmp; SL_ENTER(_("sh_hash_getdataent")); (void) fd; /* Read next record -- Part One */ p = SH_ALLOC(sizeof(sh_file_t)); i = fread (&ft, sizeof(sh_filestore_t), 1, sh_fin_fd); /* i = sl_read(fd, &ft, sizeof(sh_filestore_t)); */ /* if ( SL_ISERROR(i) || i == 0) */ if (i < 1) { SH_FREE(p); SL_RETURN( NULL, _("sh_hash_getdataent")); } swap_32(&(ft.mode)); swap_32(&(ft.linkmode)); swap_64(&(ft.dev)); swap_64(&(ft.rdev)); swap_32(&(ft.hardlinks)); swap_32(&(ft.ino)); swap_64(&(ft.size)); swap_64(&(ft.atime)); swap_64(&(ft.mtime)); swap_64(&(ft.ctime)); swap_32(&(ft.owner)); swap_32(&(ft.group)); #if defined(__linux__) || defined(HAVE_STAT_FLAGS) swap_32(&(ft.attributes)); #endif #ifdef OLD_BUG swap_short(&(ft.mark)); #else ft.mark = *(swap_short(&(ft.mark))); #endif if ((ft.mark & ~REC_FLAGS_MASK) != REC_MAGIC) { SH_FREE(p); SL_RETURN( NULL, _("sh_hash_getdataent")); } /* Read next record -- Part Two -- Fullpath */ i = sh_hash_getline (sh_fin_fd, line, size); if (i <= 0 ) { SH_FREE(line); SH_FREE(p); dlog(1, FIL__, __LINE__, _("There is a corrupt record in the file signature database: %s\nThe file path is missing.\n"), (NULL == file_path('D', 'R'))? _("(null)"):file_path('D', 'R')); sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_P_NODATA, ( (NULL == file_path('D', 'R')) ? _("(null)") : file_path('D', 'R')) ); aud_exit (FIL__, __LINE__,EXIT_FAILURE); } tmp = unquote_string (line, i); len = sl_strlen(tmp)+1; fullpath = SH_ALLOC(len); (void) sl_strlcpy (fullpath, tmp, len); if (tmp) SH_FREE(tmp); if (fullpath[len-2] == '\n') fullpath[len-2] = '\0'; /* Read next record -- Part Three -- Linkpath */ i = sh_hash_getline (sh_fin_fd, line, size); if (i <= 0 ) { SH_FREE(line); SH_FREE(fullpath); SH_FREE(p); dlog(1, FIL__, __LINE__, _("There is a corrupt record in the file signature database: %s\nThe link path (or its placeholder) is missing.\n"), (NULL == file_path('D', 'R'))? _("(null)"):file_path('D', 'R')); sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_P_NODATA, ( (NULL == file_path('D', 'R')) ? _("(null)") : file_path('D', 'R')) ); aud_exit (FIL__, __LINE__,EXIT_FAILURE); } tmp = unquote_string (line, i); if ( tmp && tmp[0] == '-' && (tmp[1] == '\0' || (tmp[1] == '\n' && tmp[2] == '\0'))) { linkpath = (char *)notalink; } else { len = sl_strlen(tmp); linkpath = sh_util_strdup_l(tmp, len); if (len > 0 && linkpath[len-1] == '\n') linkpath[len-1] = '\0'; } if (tmp) SH_FREE(tmp); /* Read next record -- Part Four -- attr_string */ if ((ft.mark & REC_FLAGS_ATTR) != 0) { i = sh_hash_getline (sh_fin_fd, line, size); if (i <= 0 ) { SH_FREE(line); SH_FREE(fullpath); if (linkpath != notalink) SH_FREE(linkpath); SH_FREE(p); dlog(1, FIL__, __LINE__, _("There is a corrupt record in the file signature database: %s\nThe attribute string is missing.\n"), (NULL == file_path('D', 'R'))? _("(null)"):file_path('D', 'R')); sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_P_NODATA, ( (NULL == file_path('D', 'R')) ? _("(null)") : file_path('D', 'R')) ); aud_exit (FIL__, __LINE__,EXIT_FAILURE); } tmp = unquote_string (line, i); len = sl_strlen(tmp)+1; attr_string = SH_ALLOC(len); (void) sl_strlcpy (attr_string, tmp, len); if (tmp) SH_FREE(tmp); if (attr_string[len-2] == '\n') attr_string[len-2] = '\0'; } /* Read next record -- Part Four -- Decode */ #if defined(SH_STEALTH) sh_do_decode(fullpath, sl_strlen(fullpath)); #if defined(__linux__) || defined(HAVE_STAT_FLAGS) sh_do_decode(ft.c_attributes, sl_strlen(ft.c_attributes)); #endif sh_do_decode(ft.c_mode, sl_strlen(ft.c_mode)); sh_do_decode(ft.c_owner, sl_strlen(ft.c_owner)); sh_do_decode(ft.c_group, sl_strlen(ft.c_group)); sh_do_decode(ft.checksum, sl_strlen(ft.checksum)); /* TXT entries are c_mode[0] != 'l' and do not get decoded */ if (ft.c_mode[0] == 'l' && linkpath != notalink) { sh_do_decode(linkpath, sl_strlen(linkpath)); } if ((ft.mark & REC_FLAGS_ATTR) != 0) { sh_do_decode(attr_string, sl_strlen(attr_string)); } #endif memcpy( &(*p).theFile, &ft, sizeof(sh_filestore_t) ); /* init fflags, such that suid files in * database are recognized as such */ { mode_t mode = (mode_t) ft.mode; if (S_ISREG(mode) && (0 !=(S_ISUID & mode) || #if defined(HOST_IS_LINUX) (0 !=(S_ISGID & mode) && 0 !=(S_IXGRP & mode)) #else 0 !=(S_ISGID & mode) #endif ) ) p->fflags = SH_FFLAG_SUIDCHK; else p->fflags = 0; } p->modi_mask = 0L; p->fullpath = fullpath; p->linkpath = linkpath; p->attr_string = attr_string; /* set to an invalid value */ ft.mark = (REC_MAGIC + 5); SL_RETURN( p, _("sh_hash_getdataent")); } /****************************************************************** * * Initialize * ******************************************************************/ void sh_hash_init () { #define FGETS_BUF 16384 sh_file_t * p; SL_TICKET fd; long i; int count = 0; char * line = NULL; #if defined(WITH_GPG) || defined(WITH_PGP) extern int get_the_fd (SL_TICKET ticket); SL_TICKET fdTmp = (-1); #endif char hashbuf[KEYBUF_SIZE]; volatile int retval = 0; volatile int exitval = EXIT_SUCCESS; SL_ENTER(_("sh_hash_init")); if ( sh.flag.checkSum == SH_CHECK_INIT ) { dlog(1, FIL__, __LINE__, _("Attempt to load the baseline database during initialisation. This is an internal error, please report it to the developer.\n")); SH_ABORT; aud_exit (FIL__, __LINE__, EXIT_FAILURE); } SH_MUTEX_LOCK(mutex_hash); if (IsInit == 1) { goto unlock_and_return; } fd = (-1); #if defined(SH_WITH_CLIENT) /* Data file from Server */ if (fd == (-1) && 0 == sl_strcmp(file_path('D', 'R'), _("REQ_FROM_SERVER"))) { sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_D_DSTART); fd = sh_forward_req_file(_("DATA")); if (SL_ISERROR(fd)) { dlog(1, FIL__, __LINE__, _("Could not retrieve the file signature database from the server(errnum = %ld).\nPossible reasons include:\n - the server is not running,\n - session key negotiation failed (see the manual for proper setup), or\n - the server cannot access the file.\n"), fd); sh_error_handle ((-1), FIL__, __LINE__, fd, MSG_EXIT_ABORT1, sh.prg_name); retval = 1; exitval = EXIT_FAILURE; goto unlock_and_return; } sl_rewind (fd); sl_strlcpy (sh.data.hash, sh_tiger_hash (file_path('D', 'R'), fd, TIGER_NOLIM, hashbuf, sizeof(hashbuf)), KEY_LEN+1); sl_rewind (fd); } else #endif /* Local data file */ if (fd == (-1)) { if ( SL_ISERROR(fd = sl_open_read(FIL__, __LINE__, file_path('D', 'R'), SL_YESPRIV))) { TPT(( 0, FIL__, __LINE__, _("msg=\n"), file_path('D', 'R'))); dlog(1, FIL__, __LINE__, _("Could not open the local file signature database for reading because\nof the following error: %s (errnum = %ld)\nIf this is a permission problem, you need to change file permissions\nto make the file readable for the effective UID: %d\n"), sl_get_errmsg(), fd, (int) sl_ret_euid()); sh_error_handle ((-1), FIL__, __LINE__, fd, MSG_EXIT_ABORT1, sh.prg_name); retval = 1; exitval = EXIT_FAILURE; goto unlock_and_return; } TPT(( 0, FIL__, __LINE__, _("msg=\n"), file_path('D', 'R'))); if (0 != sl_strncmp(sh.data.hash, sh_tiger_hash (file_path('D', 'R'), fd, TIGER_NOLIM, hashbuf, sizeof(hashbuf)), KEY_LEN) && sh.flag.checkSum != SH_CHECK_INIT) { dlog(1, FIL__, __LINE__, _("The checksum of the file signature database has changed since startup: %s -> %s\n"), sh.data.hash, sh_tiger_hash (file_path('D', 'R'), fd, TIGER_NOLIM, hashbuf, sizeof(hashbuf))); sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_AUTH, ( (NULL == file_path('D', 'R')) ? _("(null)") : file_path('D', 'R') ) ); retval = 1; exitval = EXIT_FAILURE; goto unlock_and_return; } sl_rewind (fd); } /* new 1.4.8 */ if (sig_termfast == 1) /* SIGTERM */ { TPT((0, FIL__, __LINE__, _("msg=\n"))); --sig_raised; --sig_urgent; retval = 1; exitval = EXIT_SUCCESS; goto unlock_and_return; } #if defined(WITH_GPG) || defined(WITH_PGP) /* extract the data and copy to temporary file */ fdTmp = sh_gpg_extract_signed(fd); if (sig_termfast == 1) /* SIGTERM */ { TPT((0, FIL__, __LINE__, _("msg=\n"))); --sig_raised; --sig_urgent; retval = 1; exitval = EXIT_SUCCESS; goto unlock_and_return; } sl_close(fd); fd = fdTmp; /* Validate signature of open file. */ if (0 != sh_gpg_check_sign (0, fd, 2)) { retval = 1; exitval = EXIT_FAILURE; goto unlock_and_return; } sl_rewind (fd); #endif line = SH_ALLOC(MAX_PATH_STORE+2); /* fast forward to start of data */ sh_hash_setdataent(fd, line, MAX_PATH_STORE+1, file_path('D', 'R')); for (i = 0; i < TABSIZE; ++i) tab[i] = NULL; while (1) { if (sig_termfast == 1) /* SIGTERM */ { TPT((0, FIL__, __LINE__, _("msg=\n"))); --sig_raised; --sig_urgent; retval = 1; exitval = EXIT_SUCCESS; SH_FREE(line); line = NULL; goto unlock_and_return; } p = sh_hash_getdataent (fd, line, MAX_PATH_STORE+1); if (p != NULL) { hashinsert (p); ++count; } else break; } /* Initialization completed. */ IsInit = 1; if (line != NULL) SH_FREE(line); /* Always keep db in memory, so we have no open file */ sl_close (fd); sh_hash_getline_end(); /* fd = -1; */ unlock_and_return: ; /* 'label at end of compound statement */ SH_MUTEX_UNLOCK(mutex_hash); if (retval == 0) { SL_RET0(_("sh_hash_init")); } aud_exit (FIL__, __LINE__, exitval); } /***************************************************************** * * delete hash array * *****************************************************************/ void sh_hash_hashdelete () { int i; SL_ENTER(_("sh_hash_hashdelete")); /* need deadlock detection here if called from exit handler */ SH_MUTEX_TRYLOCK(mutex_hash); if (IsInit == 0) goto unlock_and_exit; for (i = 0; i < TABSIZE; ++i) if (tab[i] != NULL) { hash_kill (tab[i]); tab[i] = NULL; } IsInit = 0; unlock_and_exit: ; /* 'label at end of compound statement */ SH_MUTEX_TRYLOCK_UNLOCK(mutex_hash); SL_RET0(_("sh_hash_hashdelete")); } /****************************************************************** * * Insert a file into the database. * ******************************************************************/ static int pushdata_isfirst = 1; static SL_TICKET pushdata_fd = -1; static int pushdata_stdout = S_FALSE; static char * sh_db_version_string = NULL; int sh_hash_pushdata_stdout (const char * str) { if (!str) { pushdata_stdout = S_TRUE; return 0; } return -1; } int sh_hash_version_string(const char * str) { if (str) { if (sh_db_version_string != NULL) { SH_FREE(sh_db_version_string); } if (0 == sl_strncmp(str, _("NULL"), 4)) { sh_db_version_string = NULL; return 0; } sh_db_version_string = sh_util_strdup(str); return 0; } return -1; } static int sh_loosedircheck = S_FALSE; int sh_hash_loosedircheck(const char * str) { return sh_util_flagval(str, &sh_loosedircheck); } static void sh_hash_pushdata_int (file_type * buf, char * fileHash) { static long p_count = 0; int status = 0; char * tmp; size_t tmp_len = 0; size_t old_len = 0; size_t path_len = 0; sh_filestore_t p; struct stat sbuf; char * fullpath = NULL; char * linkpath = NULL; char * attr_string = NULL; char * line = NULL; char timestring[81]; #if !defined(__linux__) && !defined(HAVE_STAT_FLAGS) int i; #endif SL_ENTER(_("sh_hash_pushdata_int")); fullpath = SH_ALLOC(MAX_PATH_STORE+1); linkpath = SH_ALLOC(MAX_PATH_STORE+1); linkpath[0] = '-'; linkpath[1] = '\0'; fullpath[0] = '-'; fullpath[1] = '\0'; if (!buf) { memset(&p, '\0', sizeof(sh_filestore_t)); } if ((pushdata_stdout == S_TRUE) && (sh.flag.update == S_TRUE)) { dlog(1, FIL__, __LINE__, _("You cannot write the database to stdout when you use update rather than init.\n")); sh_error_handle((-1), FIL__, __LINE__, 0, MSG_EXIT_ABORTS, _("Writing database to stdout with update"), sh.prg_name, _("sh_hash_pushdata_int")); aud_exit(FIL__, __LINE__, EXIT_FAILURE); } if ((pushdata_stdout == S_TRUE) && (sl_is_suid())) { dlog(1, FIL__, __LINE__, _("You cannot write the database to stdout when running with suid privileges.\n")); sh_error_handle((-1), FIL__, __LINE__, 0, MSG_EXIT_ABORTS, _("Writing database to stdout when suid"), sh.prg_name, _("sh_hash_pushdata_int")); aud_exit(FIL__, __LINE__, EXIT_FAILURE); } if ((pushdata_isfirst == 1) && (pushdata_stdout == S_FALSE) && ( (NULL == file_path('D', 'W')) || (0 == sl_strcmp(file_path('D', 'W'), _("REQ_FROM_SERVER"))) )) { dlog(1, FIL__, __LINE__, _("You need to configure a local path for initializing the database\nlike ./configure --with-data-file=REQ_FROM_SERVER/some/local/path\n")); sh_error_handle((-1), FIL__, __LINE__, 0, MSG_EXIT_ABORTS, _("No local path for database specified"), sh.prg_name, _("sh_hash_pushdata_int")); aud_exit(FIL__, __LINE__, EXIT_FAILURE); } if ((pushdata_isfirst == 1) && (pushdata_stdout == S_FALSE)) { /* Warn that file already exists; file_path != NULL here because * checked above */ if (0 == retry_lstat(FIL__, __LINE__, file_path('D', 'W'), &sbuf)) { if (sh.flag.update == S_FALSE) { sh_error_handle((-1), FIL__, __LINE__, 0, MSG_FI_DBEX, file_path('D', 'W')); } } } if (sh.flag.update == S_FALSE) { if (pushdata_stdout == S_FALSE && pushdata_fd == -1) { if ( SL_ISERROR(pushdata_fd = sl_open_write(FIL__, __LINE__, file_path('D', 'W'), SL_YESPRIV))) { SH_FREE(fullpath); SH_FREE(linkpath); sh_error_handle((-1), FIL__, __LINE__, pushdata_fd, MSG_E_ACCESS, geteuid(), file_path('D', 'W')); aud_exit(FIL__, __LINE__, EXIT_FAILURE); } if (SL_ISERROR(status = sl_lock (pushdata_fd))) { SH_FREE(fullpath); SH_FREE(linkpath); sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_SUBGPATH, _("Failed to lock baseline database"), _("sh_hash_pushdata_int"), file_path('D', 'W')); aud_exit(FIL__, __LINE__, EXIT_FAILURE); } if ( SL_ISERROR(status = sl_forward(pushdata_fd))) { SH_FREE(fullpath); SH_FREE(linkpath); sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_SUBGPATH, _("Failed to seek to end of baseline database"), _("sh_hash_pushdata_int"), file_path('D', 'W')); aud_exit(FIL__, __LINE__, EXIT_FAILURE); } } } else /* update == TRUE */ { if (pushdata_isfirst == 1) { TPT((0, FIL__, __LINE__, _("msg=\n"))) if ( SL_ISERROR(pushdata_fd = sl_open_rdwr(FIL__, __LINE__, file_path('D', 'W'), SL_YESPRIV))){ SH_FREE(fullpath); SH_FREE(linkpath); sh_error_handle((-1), FIL__, __LINE__, pushdata_fd, MSG_E_ACCESS, geteuid(), file_path('D', 'W')); aud_exit(FIL__, __LINE__, EXIT_FAILURE); } if (SL_ISERROR(status = sl_lock (pushdata_fd))) { SH_FREE(fullpath); SH_FREE(linkpath); sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_SUBGPATH, _("Failed to lock baseline database"), _("sh_hash_pushdata_int"), file_path('D', 'W')); aud_exit(FIL__, __LINE__, EXIT_FAILURE); } line = SH_ALLOC(MAX_PATH_STORE+1); if (SL_ISERROR(sh_hash_setdataent_old (pushdata_fd, line, MAX_PATH_STORE, file_path('D', 'W')))) { SH_FREE(fullpath); SH_FREE(linkpath); SH_FREE(line); aud_exit(FIL__, __LINE__, EXIT_FAILURE); } SH_FREE(line); } } if (buf != NULL) { old_len = sl_strlen(buf->fullpath); #if defined(SH_STEALTH) sh_do_encode(buf->fullpath, old_len); #endif tmp = quote_string(buf->fullpath, old_len); tmp_len = sl_strlen(tmp); #if defined(SH_STEALTH) sh_do_decode(buf->fullpath, old_len); #endif if (tmp && tmp_len <= MAX_PATH_STORE) { sl_strlcpy(fullpath, buf->fullpath, MAX_PATH_STORE+1); } else { char hashbuf[KEYBUF_SIZE]; sl_strlcpy(fullpath, sh_tiger_hash (buf->fullpath, TIGER_DATA, old_len, hashbuf, sizeof(hashbuf)), KEY_LEN+1); } if (tmp) SH_FREE(tmp); } path_len = sl_strlen(fullpath); #if defined(SH_STEALTH) sh_do_encode(fullpath, path_len); #endif tmp = quote_string(fullpath, path_len); if (tmp) { sl_strlcpy(fullpath, tmp, MAX_PATH_STORE+1); SH_FREE(tmp); } /* NOTE: TXT entries are c_mode[0] != 'l' and do not get decoded */ if (buf != NULL /* && buf->c_mode[0] == 'l' */ && buf->link_path != NULL) { old_len = sl_strlen(buf->link_path); #if defined(SH_STEALTH) if (buf->c_mode[0] == 'l') sh_do_encode(buf->link_path, old_len); #endif tmp = quote_string(buf->link_path, old_len); tmp_len = sl_strlen(tmp); #if defined(SH_STEALTH) if (buf->c_mode[0] == 'l') sh_do_decode(buf->link_path, old_len); #endif if (tmp && tmp_len <= MAX_PATH_STORE) { sl_strlcpy(linkpath, buf->link_path, MAX_PATH_STORE+1); } else { char hashbuf[KEYBUF_SIZE]; sl_strlcpy(linkpath, sh_tiger_hash (buf->link_path, TIGER_DATA, old_len, hashbuf, sizeof(hashbuf)), KEY_LEN+1); } if (tmp) SH_FREE(tmp); path_len = sl_strlen(linkpath); #if defined(SH_STEALTH) if (buf->c_mode[0] == 'l') sh_do_encode(linkpath, path_len); #endif tmp = quote_string(linkpath, path_len); if (tmp) { sl_strlcpy(linkpath, tmp, MAX_PATH_STORE+1); SH_FREE(tmp); } } if (buf != NULL && buf->attr_string != NULL) { old_len = sl_strlen(buf->attr_string); #if defined(SH_STEALTH) sh_do_encode(buf->attr_string, old_len); #endif tmp = quote_string(buf->attr_string, old_len); if (tmp) { attr_string = tmp; tmp = NULL; } #if defined(SH_STEALTH) sh_do_decode(buf->attr_string, old_len); #endif } if (buf != NULL) { p.mark = REC_MAGIC; if (attr_string) p.mark |= REC_FLAGS_ATTR; sl_strlcpy(p.c_mode, buf->c_mode, CMODE_SIZE); sl_strlcpy(p.c_group, buf->c_group, GROUP_MAX+1); sl_strlcpy(p.c_owner, buf->c_owner, USER_MAX+1); if (fileHash) { sl_strlcpy(p.checksum, fileHash, KEY_LEN+1); } #if defined(__linux__) || defined(HAVE_STAT_FLAGS) sl_strlcpy(p.c_attributes, buf->c_attributes, ATTRBUF_SIZE); #else for (i = 0; i < ATTRBUF_USED; ++i) p.c_attributes[i] = '-'; p.c_attributes[ATTRBUF_USED] = '\0'; #endif #if defined(SH_STEALTH) sh_do_encode(p.c_mode, sl_strlen(p.c_mode)); sh_do_encode(p.c_owner, sl_strlen(p.c_owner)); sh_do_encode(p.c_group, sl_strlen(p.c_group)); sh_do_encode(p.checksum, sl_strlen(p.checksum)); sh_do_encode(p.c_attributes, sl_strlen(p.c_attributes)); #endif #if defined(__linux__) || defined(HAVE_STAT_FLAGS) p.attributes = (UINT32) buf->attributes; #else p.attributes = 0; #endif p.linkmode = (UINT32) buf->linkmode; p.hardlinks = (UINT32) buf->hardlinks; p.dev = (UINT64) buf->dev; p.rdev = (UINT64) buf->rdev; p.mode = (UINT32) buf->mode; p.ino = (UINT32) buf->ino; p.size = (UINT64) buf->size; p.mtime = (UINT64) buf->mtime; p.atime = (UINT64) buf->atime; p.ctime = (UINT64) buf->ctime; p.owner = (UINT32) buf->owner; p.group = (UINT32) buf->group; swap_32(&(p.mode)); swap_32(&(p.linkmode)); swap_64(&(p.dev)); swap_64(&(p.rdev)); swap_32(&(p.hardlinks)); swap_32(&(p.ino)); swap_64(&(p.size)); swap_64(&(p.atime)); swap_64(&(p.mtime)); swap_64(&(p.ctime)); swap_32(&(p.owner)); swap_32(&(p.group)); swap_32(&(p.attributes)); #ifdef OLD_BUG swap_short(&(p.mark)); #else p.mark = *(swap_short(&(p.mark))); #endif } /* write the start marker */ if (pushdata_isfirst == 1) { if (sh.flag.update == S_FALSE) { if (sh_db_version_string != NULL) { if (pushdata_stdout == S_FALSE) { sl_write (pushdata_fd, _("\n#Host "), 7); sl_write (pushdata_fd, sh.host.name, sl_strlen(sh.host.name)); sl_write (pushdata_fd, _(" Version "), 9); sl_write (pushdata_fd, sh_db_version_string, sl_strlen(sh_db_version_string)); sl_write (pushdata_fd, _(" Date "), 6); (void) sh_unix_time(0, timestring, sizeof(timestring)); sl_write (pushdata_fd, timestring, strlen(timestring)); sl_write (pushdata_fd, "\n", 1); } else { printf ("%s",_("\n#Host ")); printf ("%s", sh.host.name); printf ("%s",_(" Version ")); printf ("%s", sh_db_version_string); printf ("%s",_(" Date ")); (void) sh_unix_time(0, timestring, sizeof(timestring)); printf ("%s\n", timestring); } } if (pushdata_stdout == S_FALSE) { #if defined(SH_STEALTH) sl_write (pushdata_fd, "\n", 1); sl_write_line (pushdata_fd, N_("[SOF]"), 5); #else sl_write_line (pushdata_fd, _("\n[SOF]"), 6); #endif } else { #if defined(SH_STEALTH) printf ("\n%s\n", N_("[SOF]")); #else printf ("%s\n", _("\n[SOF]")); #endif } } pushdata_isfirst = 0; } if (pushdata_stdout == S_FALSE) { sl_write (pushdata_fd, &p, sizeof(sh_filestore_t)); sl_write_line_fast (pushdata_fd, fullpath, sl_strlen(fullpath)); sl_write_line_fast (pushdata_fd, linkpath, sl_strlen(linkpath)); if (attr_string) sl_write_line_fast (pushdata_fd, attr_string, sl_strlen(attr_string)); } else { if (fwrite (&p, sizeof(sh_filestore_t), 1, stdout)) { printf ("%s\n", fullpath); printf ("%s\n", linkpath); if (attr_string) printf ("%s\n", attr_string); } else { perror(_("Error writing database")); aud_exit (FIL__, __LINE__, EXIT_FAILURE); } } ++p_count; if ((sh.flag.update != S_TRUE) && (pushdata_stdout == S_FALSE)) { if (sh.flag.checkSum != SH_CHECK_INIT || (buf == NULL && fileHash == NULL)) { sl_close (pushdata_fd); pushdata_fd = -1; } } SH_FREE(fullpath); SH_FREE(linkpath); if (attr_string) SH_FREE(attr_string); SL_RET0(_("sh_hash_pushdata_int")); } SH_MUTEX_STATIC(mutex_writeout,PTHREAD_MUTEX_INITIALIZER); void sh_hash_pushdata (file_type * buf, char * fileHash) { SH_MUTEX_LOCK(mutex_writeout); sh_hash_pushdata_int (buf, fileHash); SH_MUTEX_UNLOCK(mutex_writeout); return; } int sh_hash_writeout() { sh_file_t * p; int i; file_type * f; char fileHash[KEY_LEN + 1]; SL_ENTER(_("sh_hash_writeout")); if (S_TRUE == file_is_remote()) { sh_error_handle((-1), FIL__, __LINE__, S_FALSE, MSG_E_SUBGEN, _("Baseline database is remote"), _("sh_hash_writeout")); SL_RETURN (1, _("sh_hash_writeout")); } SH_MUTEX_LOCK(mutex_writeout); if (!SL_ISERROR(pushdata_fd)) { sl_close(pushdata_fd); pushdata_fd = -1; } pushdata_isfirst = 1; SH_MUTEX_LOCK(mutex_hash); for (i = 0; i < TABSIZE; ++i) { for (p = tab[i]; p; p = p->next) { f = sh_hash_create_ft (p, fileHash); sh_hash_pushdata_int (f, fileHash); if (f->attr_string) SH_FREE(f->attr_string); if (f->link_path) SH_FREE(f->link_path); SH_FREE(f); } } SH_MUTEX_UNLOCK(mutex_hash); if (!SL_ISERROR(pushdata_fd)) { sl_close(pushdata_fd); pushdata_fd = -1; } pushdata_isfirst = 1; SH_MUTEX_UNLOCK(mutex_writeout); SL_RETURN (0, _("sh_hash_writeout")); } /********************************************************************* * * Check whether a file is present in the database. * *********************************************************************/ static sh_file_t * sh_hash_have_it_int (const char * newname) { sh_file_t * p; char hashbuf[KEYBUF_SIZE]; SL_ENTER(_("sh_hash_have_it_int")); if (newname == NULL) SL_RETURN( (NULL), _("sh_hash_have_it_int")); if (sl_strlen(newname) <= MAX_PATH_STORE) p = hashsearch(newname); else p = hashsearch ( sh_tiger_hash(newname, TIGER_DATA, sl_strlen(newname), hashbuf, sizeof(hashbuf)) ); if (p == NULL) SL_RETURN( (NULL), _("sh_hash_have_it_int")); SL_RETURN( (p), _("sh_hash_have_it_int")); } int sh_hash_have_it (const char * newname) { sh_file_t * p; int retval; if (IsInit != 1) sh_hash_init(); SH_MUTEX_LOCK(mutex_hash); retval = 0; p = sh_hash_have_it_int (newname); if (!p) retval = (-1); else if ((!SH_FFLAG_ALLIGNORE_SET(p->fflags)) && (p->modi_mask & MODI_CHK) != 0 && (p->modi_mask & MODI_MOD) != 0) retval = 1; SH_MUTEX_UNLOCK(mutex_hash); return retval; } int sh_hash_get_it (const char * newname, file_type * tmpFile, char * fileHash) { sh_file_t * p; int retval; if (IsInit != 1) sh_hash_init(); tmpFile->link_path = NULL; tmpFile->attr_string = NULL; SH_MUTEX_LOCK(mutex_hash); retval = (-1); p = sh_hash_have_it_int (newname); if (p) { sl_strlcpy(tmpFile->fullpath, p->fullpath, PATH_MAX); if (p->linkpath) tmpFile->link_path = sh_util_strdup (p->linkpath); tmpFile->size = p->theFile.size; tmpFile->mtime = p->theFile.mtime; tmpFile->ctime = p->theFile.ctime; tmpFile->atime = p->theFile.atime; if (NULL != fileHash) sl_strlcpy(fileHash, p->theFile.checksum, KEY_LEN+1); tmpFile->attr_string = NULL; retval = 0; } SH_MUTEX_UNLOCK(mutex_hash); return retval; } int sh_hash_getflags (char * filename) { sh_file_t * p; int retval = 0; if ( sh.flag.checkSum != SH_CHECK_INIT ) { if (IsInit != 1) sh_hash_init(); SH_MUTEX_LOCK(mutex_hash); p = sh_hash_have_it_int (filename); if (p) retval = p->fflags; else retval = -1; SH_MUTEX_UNLOCK(mutex_hash); } return retval; } int sh_hash_setflags (char * filename, int flags) { sh_file_t * p; int retval = 0; if ( sh.flag.checkSum != SH_CHECK_INIT ) { if (IsInit != 1) sh_hash_init(); SH_MUTEX_LOCK(mutex_hash); p = sh_hash_have_it_int (filename); if (p) { p->fflags = flags; retval = 0; } else retval = -1; SH_MUTEX_UNLOCK(mutex_hash); } return retval; } /* needs lock to be threadsafe */ void sh_hash_set_flag (char * filename, int flag_to_set) { sh_file_t * p; if ( sh.flag.checkSum != SH_CHECK_INIT ) { if (IsInit != 1) sh_hash_init(); SH_MUTEX_LOCK(mutex_hash); p = sh_hash_have_it_int (filename); if (p) { p->fflags |= flag_to_set; } SH_MUTEX_UNLOCK(mutex_hash); } return; } /* needs lock to be threadsafe */ void sh_hash_clear_flag (char * filename, int flag_to_clear) { sh_file_t * p; if ( sh.flag.checkSum != SH_CHECK_INIT ) { if (IsInit != 1) sh_hash_init(); SH_MUTEX_LOCK(mutex_hash); p = sh_hash_have_it_int (filename); if (p) { p->fflags &= ~flag_to_clear; } SH_MUTEX_UNLOCK(mutex_hash); } return; } /***************************************************************** * * Set a file's status to 'visited'. This is required for * files that should be ignored, and may be present in the * database, but not on disk. * *****************************************************************/ static int sh_hash_set_visited_int (char * newname, int flag) { sh_file_t * p; char hashbuf[KEYBUF_SIZE]; int retval; SL_ENTER(_("sh_hash_set_visited_int")); if (newname == NULL) SL_RETURN((-1), _("sh_hash_set_visited_int")); if (IsInit != 1) sh_hash_init(); SH_MUTEX_LOCK(mutex_hash); if (sl_strlen(newname) <= MAX_PATH_STORE) p = hashsearch(newname); else p = hashsearch (sh_tiger_hash(newname, TIGER_DATA, sl_strlen(newname), hashbuf, sizeof(hashbuf))); if (p) { if (flag == SH_FFLAG_CHECKED) { CLEAR_SH_FFLAG_REPORTED(p->fflags); CLEAR_SH_FFLAG_VISITED(p->fflags); SET_SH_FFLAG_CHECKED(p->fflags); } else { SET_SH_FFLAG_VISITED(p->fflags); CLEAR_SH_FFLAG_CHECKED(p->fflags); if (flag == SH_FFLAG_REPORTED) SET_SH_FFLAG_REPORTED(p->fflags); else CLEAR_SH_FFLAG_REPORTED(p->fflags); } retval = 0; } else retval = -1; SH_MUTEX_UNLOCK(mutex_hash); SL_RETURN((retval), _("sh_hash_set_visited_int")); } /* cause the record to be deleted without a 'missing' message */ int sh_hash_set_missing (char * newname) { int i; SL_ENTER(_("sh_hash_set_missing")); i = sh_hash_set_visited_int(newname, SH_FFLAG_CHECKED); if (sh.flag.checkSum != SH_CHECK_INIT) { sh_hash_remove(newname); } SL_RETURN(i, _("sh_hash_set_missing")); } /* mark the file as visited and reported */ int sh_hash_set_visited (char * newname) { int i; SL_ENTER(_("sh_hash_set_visited")); i = sh_hash_set_visited_int(newname, SH_FFLAG_REPORTED); SL_RETURN(i, _("sh_hash_set_visited")); } /* mark the file as visited and NOT reported * used to avoid deletion of file from internal database */ int sh_hash_set_visited_true (char * newname) { int i; SL_ENTER(_("sh_hash_set_visited_true")); i = sh_hash_set_visited_int(newname, 0); SL_RETURN(i, _("sh_hash_set_visited_true")); } /****************************************************************** * * Data entry for arbitrary data into database * ******************************************************************/ void sh_hash_push2db (const char * key, struct store2db * save) { int i = 0; char * p; char i2h[2]; file_type * tmpFile = SH_ALLOC(sizeof(file_type)); int size = save->size; unsigned char * str = save->str; tmpFile->attr_string = NULL; tmpFile->link_path = NULL; sl_strlcpy(tmpFile->fullpath, key, PATH_MAX); tmpFile->size = save->val0; tmpFile->mtime = save->val1; tmpFile->ctime = save->val2; tmpFile->atime = save->val3; tmpFile->mode = 0; tmpFile->owner = 0; tmpFile->group = 0; sl_strlcpy(tmpFile->c_owner, _("root"), 5); sl_strlcpy(tmpFile->c_group, _("root"), 5); if ((str != NULL) && (size < (PATH_MAX/2)-1)) { tmpFile->c_mode[0] = 'l'; tmpFile->c_mode[1] = 'r'; tmpFile->c_mode[2] = 'w'; tmpFile->c_mode[3] = 'x'; tmpFile->c_mode[4] = 'r'; tmpFile->c_mode[5] = 'w'; tmpFile->c_mode[6] = 'x'; tmpFile->c_mode[7] = 'r'; tmpFile->c_mode[8] = 'w'; tmpFile->c_mode[9] = 'x'; tmpFile->c_mode[10] = '\0'; tmpFile->link_path = SH_ALLOC((size * 2) + 2); for (i = 0; i < size; ++i) { p = sh_util_charhex (str[i],i2h); tmpFile->link_path[2*i] = p[0]; tmpFile->link_path[2*i+1] = p[1]; tmpFile->link_path[2*i+2] = '\0'; } } else { for (i = 0; i < 10; ++i) tmpFile->c_mode[i] = '-'; tmpFile->c_mode[10] = '\0'; tmpFile->link_path = sh_util_strdup("-"); } if (sh.flag.checkSum == SH_CHECK_INIT) sh_hash_pushdata (tmpFile, (save->checksum[0] == '\0') ? SH_KEY_NULL : save->checksum); else sh_hash_pushdata_memory (tmpFile, (save->checksum[0] == '\0') ? SH_KEY_NULL : save->checksum); if (tmpFile->link_path) SH_FREE(tmpFile->link_path); SH_FREE(tmpFile); return; } extern int sh_util_hextobinary (char * binary, char * hex, int bytes); char * sh_hash_db2pop (const char * key, struct store2db * save) { size_t len; char * p; int i; char * retval = NULL; char fileHash[KEY_LEN+1]; file_type * tmpFile = SH_ALLOC(sizeof(file_type)); save->size = 0; if (0 == sh_hash_get_it (key, tmpFile, fileHash)) { save->val0 = tmpFile->size; save->val1 = tmpFile->mtime; save->val2 = tmpFile->ctime; save->val3 = tmpFile->atime; sl_strlcpy(save->checksum, fileHash, KEY_LEN+1); if (tmpFile->link_path && tmpFile->link_path[0] != '-') { len = strlen(tmpFile->link_path); p = SH_ALLOC((len/2)+1); i = sh_util_hextobinary (p, tmpFile->link_path, len); if (i == 0) { save->size = (len/2); p[save->size] = '\0'; retval = p; } else { SH_FREE(p); save->size = 0; } } else { save->size = 0; } } else { save->size = -1; save->val0 = 0; save->val1 = 0; save->val2 = 0; save->val3 = 0; } if (tmpFile->link_path) SH_FREE(tmpFile->link_path); SH_FREE(tmpFile); return retval; } /****************************************************************** * * Data entry in hash table * ******************************************************************/ sh_file_t * sh_hash_push_int (file_type * buf, char * fileHash) { sh_file_t * fp; sh_filestore_t p; size_t len; char * fullpath; char * linkpath; char * attr_string = NULL; char hashbuf[KEYBUF_SIZE]; SL_ENTER(_("sh_hash_push_int")); fp = SH_ALLOC(sizeof(sh_file_t)); p.mark = REC_MAGIC; if (buf->attr_string) p.mark |= REC_FLAGS_ATTR; sl_strlcpy(p.c_mode, buf->c_mode, 11); sl_strlcpy(p.c_group, buf->c_group, GROUP_MAX+1); sl_strlcpy(p.c_owner, buf->c_owner, USER_MAX+1); sl_strlcpy(p.checksum, fileHash, KEY_LEN+1); #if defined(__linux__) || defined(HAVE_STAT_FLAGS) sl_strlcpy(p.c_attributes, buf->c_attributes, 13); #endif #if defined(__linux__) || defined(HAVE_STAT_FLAGS) p.attributes = (UINT32) buf->attributes; #endif p.linkmode = (UINT32) buf->linkmode; p.hardlinks = (UINT32) buf->hardlinks; p.dev = (UINT64) buf->dev; p.rdev = (UINT64) buf->rdev; p.mode = (UINT32) buf->mode; p.ino = (UINT32) buf->ino; p.size = (UINT64) buf->size; p.mtime = (UINT64) buf->mtime; p.atime = (UINT64) buf->atime; p.ctime = (UINT64) buf->ctime; p.owner = (UINT32) buf->owner; p.group = (UINT32) buf->group; memcpy( &(*fp).theFile, &p, sizeof(sh_filestore_t) ); fp->fflags = 0; /* init fflags */ fp->modi_mask = 0L; if (buf->attr_string) attr_string = sh_util_strdup(buf->attr_string); fp->attr_string = attr_string; len = sl_strlen(buf->fullpath); if (len <= MAX_PATH_STORE) { fullpath = SH_ALLOC(len+1); sl_strlcpy(fullpath, buf->fullpath, len+1); } else { fullpath = SH_ALLOC(KEY_LEN + 1); sl_strlcpy(fullpath, sh_tiger_hash (buf->fullpath, TIGER_DATA, len, hashbuf, sizeof(hashbuf)), KEY_LEN+1); } fp->fullpath = fullpath; if (buf->link_path) { len = sl_strlen(buf->link_path); if (len <= MAX_PATH_STORE) { linkpath = SH_ALLOC(len+1); sl_strlcpy(linkpath, buf->link_path, len+1); } else { linkpath = SH_ALLOC(KEY_LEN + 1); sl_strlcpy(linkpath, sh_tiger_hash (buf->link_path, TIGER_DATA, len, hashbuf, sizeof(hashbuf)), KEY_LEN+1); } fp->linkpath = linkpath; } else fp->linkpath = NULL; SL_RETURN( fp, _("sh_hash_push_int")); } #ifdef HAVE_INTTYPES_H #include #else #ifdef HAVE_STDINT_H #include #endif #endif #ifndef PRIu64 #ifdef HAVE_LONG_32 #define PRIu64 "llu" #else #define PRIu64 "lu" #endif #endif char * sh_hash_size_format() { static char form_rval[81]; SL_ENTER(_("sh_hash_size_format")); #ifdef SH_USE_XML sl_snprintf(form_rval, 80, _("%s%s%s%s%s"), _("size_old=\"%"), PRIu64, _("\" size_new=\"%"), PRIu64, "\" "); #else sl_snprintf(form_rval, 80, _("%s%s%s%s%s"), _("size_old=<%"), PRIu64, _(">, size_new=<%"), PRIu64, ">, "); #endif SL_RETURN( form_rval, _("sh_hash_size_format")); } #ifdef SH_USE_XML static char * all_items (file_type * theFile, char * fileHash, int is_new) { char timstr1c[32]; char timstr1a[32]; char timstr1m[32]; char * tmp_lnk; char * format; char * tmp = SH_ALLOC(SH_MSG_BUF); char * msg = SH_ALLOC(SH_MSG_BUF); tmp[0] = '\0'; msg[0] = '\0'; #if defined(__linux__) || defined(HAVE_STAT_FLAGS) if (is_new) format = _("mode_new=\"%s\" attr_new=\"%s\" imode_new=\"%ld\" iattr_new=\"%ld\" "); else format = _("mode_old=\"%s\" attr_old=\"%s\" imode_old=\"%ld\" iattr_old=\"%ld\" "); sl_snprintf(tmp, SH_MSG_BUF, format, theFile->c_mode, theFile->c_attributes, (long) theFile->mode, (long) theFile->attributes ); #else if (is_new) format = _("mode_new=\"%s\" imode_new=\"%ld\" "); else format = _("mode_old=\"%s\" imode_old=\"%ld\" "); sl_snprintf(tmp, SH_MSG_BUF, format, theFile->c_mode, (long) theFile->mode ); #endif sl_strlcat(msg, tmp, SH_MSG_BUF); if (is_new) format = _("hardlinks_new=\"%lu\" "); else format = _("hardlinks_old=\"%lu\" "); sl_snprintf(tmp, SH_MSG_BUF, format, (unsigned long) theFile->hardlinks); sl_strlcat(msg, tmp, SH_MSG_BUF); if (is_new) format = _("idevice_new=\"%lu\" "); else format = _("idevice_old=\"%lu\" "); sl_snprintf(tmp, SH_MSG_BUF, format, (unsigned long) theFile->rdev); sl_strlcat(msg, tmp, SH_MSG_BUF); if (is_new) format = _("inode_new=\"%lu\" "); else format = _("inode_old=\"%lu\" "); sl_snprintf(tmp, SH_MSG_BUF, format, (unsigned long) theFile->ino); sl_strlcat(msg, tmp, SH_MSG_BUF); /* * also report device for prelude */ #if defined(HAVE_LIBPRELUDE) if (is_new) format = _("dev_new=\"%lu,%lu\" "); else format = _("dev_old=\"%lu,%lu\" "); sl_snprintf(tmp, SH_MSG_BUF, format, (unsigned long) major(theFile->dev), (unsigned long) minor(theFile->dev)); sl_strlcat(msg, tmp, SH_MSG_BUF); #endif if (is_new) format = _("owner_new=\"%s\" iowner_new=\"%ld\" "); else format = _("owner_old=\"%s\" iowner_old=\"%ld\" "); sl_snprintf(tmp, SH_MSG_BUF, format, theFile->c_owner, (long) theFile->owner); sl_strlcat(msg, tmp, SH_MSG_BUF); if (is_new) format = _("group_new=\"%s\" igroup_new=\"%ld\" "); else format = _("group_old=\"%s\" igroup_old=\"%ld\" "); sl_snprintf(tmp, SH_MSG_BUF, format, theFile->c_group, (long) theFile->group); sl_strlcat(msg, tmp, SH_MSG_BUF); if (is_new) sl_snprintf(tmp, SH_MSG_BUF, sh_hash_size_format(), (UINT64) 0, (UINT64) theFile->size); else sl_snprintf(tmp, SH_MSG_BUF, sh_hash_size_format(), (UINT64) theFile->size, (UINT64) 0); sl_strlcat(msg, tmp, SH_MSG_BUF); (void) sh_unix_gmttime (theFile->ctime, timstr1c, sizeof(timstr1c)); if (is_new) sl_snprintf(tmp, SH_MSG_BUF, _("ctime_new=\"%s\" "), timstr1c); else sl_snprintf(tmp, SH_MSG_BUF, _("ctime_old=\"%s\" "), timstr1c); sl_strlcat(msg, tmp, SH_MSG_BUF); (void) sh_unix_gmttime (theFile->atime, timstr1a, sizeof(timstr1a)); if (is_new) sl_snprintf(tmp, SH_MSG_BUF, _("atime_new=\"%s\" "), timstr1a); else sl_snprintf(tmp, SH_MSG_BUF, _("atime_old=\"%s\" "), timstr1a); sl_strlcat(msg, tmp, SH_MSG_BUF); (void) sh_unix_gmttime (theFile->mtime, timstr1m, sizeof(timstr1m)); if (is_new) sl_snprintf(tmp, SH_MSG_BUF, _("mtime_new=\"%s\" "), timstr1m); else sl_snprintf(tmp, SH_MSG_BUF, _("mtime_old=\"%s\" "), timstr1m); sl_strlcat(msg, tmp, SH_MSG_BUF); if (is_new) sl_snprintf(tmp, SH_MSG_BUF, _("chksum_new=\"%s\" "), fileHash); else sl_snprintf(tmp, SH_MSG_BUF, _("chksum_old=\"%s\" "), fileHash); sl_strlcat(msg, tmp, SH_MSG_BUF); if (theFile->c_mode[0] == 'l' || (theFile->link_path != NULL && theFile->link_path[0] != '-')) { tmp_lnk = sh_util_safe_name(theFile->link_path); if (tmp_lnk) { if (is_new) sl_snprintf(tmp, SH_MSG_BUF, _("link_new=\"%s\" "), tmp_lnk); else sl_snprintf(tmp, SH_MSG_BUF, _("link_old=\"%s\" "), tmp_lnk); SH_FREE(tmp_lnk); sl_strlcat(msg, tmp, SH_MSG_BUF); } } if (theFile->attr_string) { tmp_lnk = sh_util_safe_name(theFile->attr_string); if (tmp_lnk) { if (is_new) sl_snprintf(tmp, SH_MSG_BUF, _("acl_new=\"%s\" "), tmp_lnk); else sl_snprintf(tmp, SH_MSG_BUF, _("acl_old=\"%s\" "), tmp_lnk); SH_FREE(tmp_lnk); sl_strlcat(msg, tmp, SH_MSG_BUF); } } SH_FREE(tmp); return (msg); } #else static char * all_items (file_type * theFile, char * fileHash, int is_new) { char timstr1c[32]; char timstr1a[32]; char timstr1m[32]; char * tmp_lnk; char * format; char * tmp = SH_ALLOC(SH_MSG_BUF); char * msg = SH_ALLOC(SH_MSG_BUF); tmp[0] = '\0'; msg[0] = '\0'; #if defined(__linux__) || defined(HAVE_STAT_FLAGS) if (is_new) format = _("mode_new=<%s>, attr_new=<%s>, imode_new=<%ld>, iattr_new=<%ld>, "); else format = _("mode_old=<%s>, attr_old=<%s>, imode_old=<%ld>, iattr_old=<%ld>, "); sl_snprintf(tmp, SH_MSG_BUF, format, theFile->c_mode, theFile->c_attributes, (long) theFile->mode, (long) theFile->attributes ); #else if (is_new) format = _("mode_new=<%s>, imode_new=<%ld>, "); else format = _("mode_old=<%s>, imode_old=<%ld>, "); sl_snprintf(tmp, SH_MSG_BUF, format, theFile->c_mode, (long) theFile->mode ); #endif sl_strlcat(msg, tmp, SH_MSG_BUF); if (is_new) format = _("hardlinks_new=<%lu>, "); else format = _("hardlinks_old=<%lu>, "); sl_snprintf(tmp, SH_MSG_BUF, format, (unsigned long) theFile->hardlinks); sl_strlcat(msg, tmp, SH_MSG_BUF); if (is_new) format = _("idevice_new=<%lu>, "); else format = _("idevice_old=<%lu>, "); sl_snprintf(tmp, SH_MSG_BUF, format, (unsigned long) theFile->rdev); sl_strlcat(msg, tmp, SH_MSG_BUF); if (is_new) format = _("inode_new=<%lu>, "); else format = _("inode_old=<%lu>, "); sl_snprintf(tmp, SH_MSG_BUF, format, (unsigned long) theFile->ino); sl_strlcat(msg, tmp, SH_MSG_BUF); /* * also report device for prelude */ #if defined(HAVE_LIBPRELUDE) if (is_new) format = _("dev_new=<%lu,%lu>, "); else format = _("dev_old=<%lu,%lu>, "); sl_snprintf(tmp, SH_MSG_BUF, format, (unsigned long) major(theFile->dev), (unsigned long) minor(theFile->dev)); sl_strlcat(msg, tmp, SH_MSG_BUF); #endif if (is_new) format = _("owner_new=<%s>, iowner_new=<%ld>, "); else format = _("owner_old=<%s>, iowner_old=<%ld>, "); sl_snprintf(tmp, SH_MSG_BUF, format, theFile->c_owner, (long) theFile->owner); sl_strlcat(msg, tmp, SH_MSG_BUF); if (is_new) format = _("group_new=<%s>, igroup_new=<%ld>, "); else format = _("group_old=<%s>, igroup_old=<%ld>, "); sl_snprintf(tmp, SH_MSG_BUF, format, theFile->c_group, (long) theFile->group); sl_strlcat(msg, tmp, SH_MSG_BUF); if (is_new) sl_snprintf(tmp, SH_MSG_BUF, sh_hash_size_format(), (UINT64) 0, (UINT64) theFile->size); else sl_snprintf(tmp, SH_MSG_BUF, sh_hash_size_format(), (UINT64) theFile->size, (UINT64) 0); sl_strlcat(msg, tmp, SH_MSG_BUF); (void) sh_unix_gmttime (theFile->ctime, timstr1c, sizeof(timstr1c)); if (is_new) sl_snprintf(tmp, SH_MSG_BUF, _("ctime_new=<%s>, "), timstr1c); else sl_snprintf(tmp, SH_MSG_BUF, _("ctime_old=<%s>, "), timstr1c); sl_strlcat(msg, tmp, SH_MSG_BUF); (void) sh_unix_gmttime (theFile->atime, timstr1a, sizeof(timstr1a)); if (is_new) sl_snprintf(tmp, SH_MSG_BUF, _("atime_new=<%s>, "), timstr1a); else sl_snprintf(tmp, SH_MSG_BUF, _("atime_old=<%s>, "), timstr1a); sl_strlcat(msg, tmp, SH_MSG_BUF); (void) sh_unix_gmttime (theFile->mtime, timstr1m, sizeof(timstr1m)); if (is_new) sl_snprintf(tmp, SH_MSG_BUF, _("mtime_new=<%s>, "), timstr1m); else sl_snprintf(tmp, SH_MSG_BUF, _("mtime_old=<%s>, "), timstr1m); sl_strlcat(msg, tmp, SH_MSG_BUF); if (is_new) sl_snprintf(tmp, SH_MSG_BUF, _("chksum_new=<%s>"), fileHash); else sl_snprintf(tmp, SH_MSG_BUF, _("chksum_old=<%s>"), fileHash); sl_strlcat(msg, tmp, SH_MSG_BUF); if (theFile->c_mode[0] == 'l' || (theFile->link_path != NULL && theFile->link_path[0] != '-')) { tmp_lnk = sh_util_safe_name(theFile->link_path); if (tmp_lnk) { if (is_new) sl_snprintf(tmp, SH_MSG_BUF, _(", link_new=<%s> "), tmp_lnk); else sl_snprintf(tmp, SH_MSG_BUF, _(", link_old=<%s> "), tmp_lnk); SH_FREE(tmp_lnk); sl_strlcat(msg, tmp, SH_MSG_BUF); } } if (theFile->attr_string) { tmp_lnk = sh_util_safe_name(theFile->attr_string); if (tmp_lnk) { if (is_new) sl_snprintf(tmp, SH_MSG_BUF, _(", acl_new=<%s> "), tmp_lnk); else sl_snprintf(tmp, SH_MSG_BUF, _(", acl_old=<%s> "), tmp_lnk); SH_FREE(tmp_lnk); sl_strlcat(msg, tmp, SH_MSG_BUF); } } SH_FREE(tmp); return (msg); } #endif void sh_hash_pushdata_memory (file_type * theFile, char * fileHash) { sh_file_t * p; SL_ENTER(_("sh_hash_pushdata_memory")); p = sh_hash_push_int(theFile, fileHash); if (p) { SH_MUTEX_LOCK(mutex_hash); hashinsert (p); p->modi_mask = theFile->check_mask; SH_MUTEX_UNLOCK(mutex_hash); } SL_RET0(_("sh_hash_pushdata_memory")); } /***************************************************************** * * Compare a file with the database status. * *****************************************************************/ int sh_hash_compdata (int class, file_type * theFile, char * fileHash, char * policy_override, int severity_override) { char * msg; sh_file_t * p; char * tmp; char * tmp_path; char * tmp_lnk; char * tmp_lnk_old; char * str; char timstr1c[32]; char timstr2c[32]; char timstr1a[32]; char timstr2a[32]; char timstr1m[32]; char timstr2m[32]; char linkHash[KEY_LEN+1]; char * linkComp; int maxcomp; char change_code[16]; int i; unsigned long modi_mask; char log_policy[32]; volatile int log_severity; char hashbuf[KEYBUF_SIZE]; int retval; SL_ENTER(_("sh_hash_compdata")); if (IsInit != 1) sh_hash_init(); if (severity_override < 0) log_severity = ShDFLevel[class]; else log_severity = severity_override; if (policy_override != NULL) sl_strlcpy (log_policy, policy_override, 32); /* -------- find the entry for the file ---------------- */ SH_MUTEX_LOCK(mutex_hash); modi_mask = 0; retval = 0; if (sl_strlen(theFile->fullpath) <= MAX_PATH_STORE) p = hashsearch(theFile->fullpath); else p = hashsearch( sh_tiger_hash(theFile->fullpath, TIGER_DATA, sl_strlen(theFile->fullpath), hashbuf, sizeof(hashbuf)) ); /* --------- Not found in database. ------------ */ if (p == NULL) { if (S_FALSE == sh_ignore_chk_new(theFile->fullpath)) { tmp = sh_util_safe_name(theFile->fullpath); str = all_items (theFile, fileHash, 1); sh_error_handle (log_severity, FIL__, __LINE__, 0, MSG_FI_ADD2, tmp, str); ++sh.statistics.files_report; SH_FREE(str); SH_FREE(tmp); } if (sh.flag.reportonce == S_TRUE) SET_SH_FFLAG_REPORTED(theFile->file_reported); if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) { p = sh_hash_push_int(theFile, fileHash); if (p) { hashinsert (p); p->modi_mask = theFile->check_mask; } } else if (S_TRUE == sh.flag.update) { if (S_TRUE == sh_util_ask_update (theFile->fullpath)) { p = sh_hash_push_int(theFile, fileHash); if (p) { hashinsert (p); p->modi_mask = theFile->check_mask; } } else { retval = 1; goto unlock_and_return; } } goto unlock_and_return; } /* --------- Skip if we don't want to report changes. ------------ */ if (S_TRUE == sh_ignore_chk_mod(theFile->fullpath)) { goto unlock_and_return; } p->modi_mask = theFile->check_mask; /* initialize change_code */ for (i = 0; i < 15; ++i) change_code[i] = '-'; change_code[15] = '\0'; TPT ((0, FIL__, __LINE__, _("file=<%s>, cs_old=<%s>, cs_new=<%s>\n"), theFile->fullpath, fileHash, p->theFile.checksum)); if ( (fileHash != NULL) && (strncmp (fileHash, p->theFile.checksum, KEY_LEN) != 0) && (theFile->check_mask & MODI_CHK) != 0) { if ((theFile->check_mask & MODI_SGROW) == 0) { modi_mask |= MODI_CHK; change_code[0] = 'C'; TPT ((0, FIL__, __LINE__, _("mod="))); } else { if (0 != strncmp (&fileHash[KEY_LEN + 1], p->theFile.checksum, KEY_LEN)) { if (S_FALSE == sh_check_rotated_log (theFile->fullpath, (UINT64) p->theFile.size, (UINT64) p->theFile.ino, p->theFile.checksum)) { modi_mask |= MODI_CHK; change_code[0] = 'C'; TPT ((0, FIL__, __LINE__, _("mod="))); } else { /* logfile has been rotated */ p->theFile.size = theFile->size; p->theFile.ino = theFile->ino; sl_strlcpy(p->theFile.checksum, fileHash, KEY_LEN+1); } } else { p->theFile.size = theFile->size; sl_strlcpy(p->theFile.checksum, fileHash, KEY_LEN+1); } } } if (p->theFile.c_mode[0] == 'l') { if (!(theFile->link_path) && (theFile->check_mask & MODI_LNK) != 0) { linkComp = NULL; modi_mask |= MODI_LNK; change_code[1] = 'L'; TPT ((0, FIL__, __LINE__, _("mod="))); } else { if (sl_strlen(theFile->link_path) >= MAX_PATH_STORE) { sl_strlcpy(linkHash, sh_tiger_hash(theFile->link_path, TIGER_DATA, sl_strlen(theFile->link_path), hashbuf, sizeof(hashbuf)), MAX_PATH_STORE+1); linkComp = linkHash; maxcomp = KEY_LEN; } else { linkComp = theFile->link_path; maxcomp = MAX_PATH_STORE; } if ( sl_strncmp (linkComp, p->linkpath, maxcomp) != 0 && (theFile->check_mask & MODI_LNK) != 0) { modi_mask |= MODI_LNK; change_code[1] = 'L'; TPT ((0, FIL__, __LINE__, _("mod="))); } } } if (p->theFile.c_mode[0] == 'c' || p->theFile.c_mode[0] == 'b') { if ( ( major(theFile->rdev) != major((dev_t)p->theFile.rdev) || minor(theFile->rdev) != minor((dev_t)p->theFile.rdev) ) && (theFile->check_mask & MODI_RDEV) != 0) { modi_mask |= MODI_RDEV; change_code[2] = 'D'; TPT ((0, FIL__, __LINE__, _("mod="))); } } /* cast to UINT32 in case ino_t is not 32bit */ if ( (UINT32) theFile->ino != (UINT32) p->theFile.ino && (theFile->check_mask & MODI_INO) != 0) { modi_mask |= MODI_INO; change_code[3] = 'I'; TPT ((0, FIL__, __LINE__, _("mod="))); } if ( theFile->hardlinks != (nlink_t) p->theFile.hardlinks && (theFile->check_mask & MODI_HLN) != 0) { modi_mask |= MODI_HLN; change_code[4] = 'H'; TPT ((0, FIL__, __LINE__, _("mod="))); } if ( ( (theFile->mode != p->theFile.mode) #if defined(USE_ACL) || defined(USE_XATTR) || ( (sh_unix_check_selinux|sh_unix_check_acl) && ( (theFile->attr_string == NULL && p->attr_string != NULL) || (theFile->attr_string != NULL && p->attr_string == NULL) || (theFile->attr_string != NULL && 0 != strcmp(theFile->attr_string, p->attr_string)) ) ) #endif #if defined(__linux__) || defined(HAVE_STAT_FLAGS) || (theFile->attributes != p->theFile.attributes) #endif ) && (theFile->check_mask & MODI_MOD) != 0) { modi_mask |= MODI_MOD; change_code[5] = 'M'; TPT ((0, FIL__, __LINE__, _("mod="))); /* * report link path if switch link/no link */ if ((theFile->check_mask & MODI_LNK) != 0 && (theFile->c_mode[0] != p->theFile.c_mode[0]) && (theFile->c_mode[0] == 'l' || p->theFile.c_mode[0] == 'l')) { modi_mask |= MODI_LNK; change_code[1] = 'L'; TPT ((0, FIL__, __LINE__, _("mod="))); } } if ( theFile->owner != (uid_t) p->theFile.owner && (theFile->check_mask & MODI_USR) != 0) { modi_mask |= MODI_USR; change_code[6] = 'U'; TPT ((0, FIL__, __LINE__, _("mod="))); } if ( theFile->group != (gid_t) p->theFile.group && (theFile->check_mask & MODI_GRP) != 0) { modi_mask |= MODI_GRP; change_code[7] = 'G'; TPT ((0, FIL__, __LINE__, _("mod="))); } if ( theFile->mtime != (time_t) p->theFile.mtime && (theFile->check_mask & MODI_MTM) != 0) { modi_mask |= MODI_MTM; change_code[8] = 'T'; TPT ((0, FIL__, __LINE__, _("mod="))); } if ( (theFile->check_mask & MODI_ATM) != 0 && theFile->atime != (time_t) p->theFile.atime) { modi_mask |= MODI_ATM; change_code[8] = 'T'; TPT ((0, FIL__, __LINE__, _("mod="))); } /* Resetting the access time will set a new ctime. Thus, either we ignore * the access time or the ctime for NOIGNORE */ if ( theFile->ctime != (time_t) p->theFile.ctime && (theFile->check_mask & MODI_CTM) != 0) { modi_mask |= MODI_CTM; change_code[8] = 'T'; TPT ((0, FIL__, __LINE__, _("mod="))); } if ( theFile->size != (off_t) p->theFile.size && (theFile->check_mask & MODI_SIZ) != 0) { if ((theFile->check_mask & MODI_SGROW) == 0 || theFile->size < (off_t) p->theFile.size) { modi_mask |= MODI_SIZ; change_code[9] = 'S'; TPT ((0, FIL__, __LINE__, _("mod="))); } } change_code[10] = '\0'; /* --- Directories special case --- */ if (p->theFile.c_mode[0] == 'd' && 0 == (modi_mask & ~(MODI_SIZ|MODI_ATM|MODI_CTM|MODI_MTM)) && sh_loosedircheck == S_TRUE) { modi_mask = 0; } /* --- Report full details. --- */ if (modi_mask != 0 && sh.flag.fulldetail == S_TRUE) { if ((theFile->check_mask & MODI_ATM) == 0) modi_mask = MASK_READONLY_; else modi_mask = MASK_NOIGNORE_; } /* --- Report on modified files. --- */ if (modi_mask != 0 && (!SH_FFLAG_REPORTED_SET(p->fflags))) { tmp = SH_ALLOC(SH_MSG_BUF); msg = SH_ALLOC(SH_MSG_BUF); msg[0] = '\0'; if ( ((modi_mask & MODI_MOD) != 0) #if defined(HAVE_LIBPRELUDE) || ((modi_mask & MODI_USR) != 0) || ((modi_mask & MODI_GRP) != 0) #endif ) { #if defined(__linux__) || defined(HAVE_STAT_FLAGS) sl_snprintf(tmp, SH_MSG_BUF, #ifdef SH_USE_XML _("mode_old=\"%s\" mode_new=\"%s\" attr_old=\"%s\" attr_new=\"%s\" imode_old=\"%ld\" imode_new=\"%ld\" iattr_old=\"%ld\" iattr_new=\"%ld\" "), #else _("mode_old=<%s>, mode_new=<%s>, attr_old=<%s>, attr_new=<%s>, "), #endif p->theFile.c_mode, theFile->c_mode, p->theFile.c_attributes, theFile->c_attributes #ifdef SH_USE_XML , (long) p->theFile.mode, (long) theFile->mode, (long) p->theFile.attributes, (long) theFile->attributes #endif ); #else #ifdef SH_USE_XML sl_snprintf(tmp, SH_MSG_BUF, _("mode_old=\"%s\" mode_new=\"%s\" imode_old=\"%ld\" imode_new=\"%ld\" "), p->theFile.c_mode, theFile->c_mode, (long) p->theFile.mode, (long) theFile->mode); #else sl_snprintf(tmp, SH_MSG_BUF, _("mode_old=<%s>, mode_new=<%s>, "), p->theFile.c_mode, theFile->c_mode); #endif #endif sl_strlcat(msg, tmp, SH_MSG_BUF); #if defined(USE_ACL) || defined(USE_XATTR) if (theFile->attr_string != NULL || p->attr_string != NULL) { sl_snprintf(tmp, SH_MSG_BUF, #ifdef SH_USE_XML _("acl_old=\"%s\" acl_new=\"%s\" "), #else _("acl_old=<%s>, acl_new=<%s>, "), #endif (p->attr_string) ? p->attr_string : _("none"), (theFile->attr_string) ? theFile->attr_string : _("none")); sl_strlcat(msg, tmp, SH_MSG_BUF); } #endif #ifdef REPLACE_OLD if ((modi_mask & MODI_MOD) != 0) { /* * We postpone update if sh.flag.update == S_TRUE because * in interactive mode the user may not accept the change. */ if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) { sl_strlcpy(p->theFile.c_mode, theFile->c_mode, 11); p->theFile.mode = theFile->mode; #if defined(__linux__) || defined(HAVE_STAT_FLAGS) sl_strlcpy(p->theFile.c_attributes,theFile->c_attributes,16); p->theFile.attributes = theFile->attributes; #endif #if defined(USE_ACL) || defined(USE_XATTR) if (p->attr_string == NULL && theFile->attr_string != NULL) { p->attr_string = sh_util_strdup (theFile->attr_string); } else if (p->attr_string != NULL && theFile->attr_string == NULL) { SH_FREE(p->attr_string); p->attr_string = NULL; } else if (theFile->attr_string != NULL && p->attr_string != NULL) { if (0 != strcmp(theFile->attr_string, p->attr_string)) { SH_FREE(p->attr_string); p->attr_string = sh_util_strdup (theFile->attr_string); } } #endif } } #endif } if ((modi_mask & MODI_HLN) != 0) { sl_snprintf(tmp, SH_MSG_BUF, #ifdef SH_USE_XML _("hardlinks_old=\"%lu\" hardlinks_new=\"%lu\" "), #else _("hardlinks_old=<%lu>, hardlinks_new=<%lu>, "), #endif (unsigned long) p->theFile.hardlinks, (unsigned long) theFile->hardlinks); sl_strlcat(msg, tmp, SH_MSG_BUF); #ifdef REPLACE_OLD if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) p->theFile.hardlinks = theFile->hardlinks; #endif } if ((modi_mask & MODI_RDEV) != 0) { sl_snprintf(tmp, SH_MSG_BUF, #ifdef SH_USE_XML _("device_old=\"%lu,%lu\" device_new=\"%lu,%lu\" idevice_old=\"%lu\" idevice_new=\"%lu\" "), #else _("device_old=<%lu,%lu>, device_new=<%lu,%lu>, "), #endif (unsigned long) major(p->theFile.rdev), (unsigned long) minor(p->theFile.rdev), (unsigned long) major(theFile->rdev), (unsigned long) minor(theFile->rdev) #ifdef SH_USE_XML , (unsigned long) p->theFile.rdev, (unsigned long) theFile->rdev #endif ); sl_strlcat(msg, tmp, SH_MSG_BUF); #ifdef REPLACE_OLD if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) p->theFile.rdev = theFile->rdev; #endif } if ((modi_mask & MODI_INO) != 0) { sl_snprintf(tmp, SH_MSG_BUF, #ifdef SH_USE_XML _("inode_old=\"%lu\" inode_new=\"%lu\" "), #else _("inode_old=<%lu>, inode_new=<%lu>, "), #endif (unsigned long) p->theFile.ino, (unsigned long) theFile->ino); sl_strlcat(msg, tmp, SH_MSG_BUF); #ifdef REPLACE_OLD if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) { p->theFile.ino = theFile->ino; p->theFile.dev = theFile->dev; } #endif } /* * also report device for prelude */ #if defined(HAVE_LIBPRELUDE) if ((modi_mask & MODI_INO) != 0) { sl_snprintf(tmp, SH_MSG_BUF, #ifdef SH_USE_XML _("dev_old=\"%lu,%lu\" dev_new=\"%lu,%lu\" "), #else _("dev_old=<%lu,%lu>, dev_new=<%lu,%lu>, "), #endif (unsigned long) major(p->theFile.dev), (unsigned long) minor(p->theFile.dev), (unsigned long) major(theFile->dev), (unsigned long) minor(theFile->dev) ); sl_strlcat(msg, tmp, SH_MSG_BUF); #ifdef REPLACE_OLD if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) p->theFile.dev = theFile->dev; #endif } #endif if ( ((modi_mask & MODI_USR) != 0) #if defined(HAVE_LIBPRELUDE) || ((modi_mask & MODI_MOD) != 0) #endif ) { #ifdef SH_USE_XML sl_snprintf(tmp, SH_MSG_BUF, _("owner_old=\"%s\" owner_new=\"%s\" iowner_old=\"%ld\" iowner_new=\"%ld\" "), #else sl_snprintf(tmp, SH_MSG_BUF, _("owner_old=<%s>, owner_new=<%s>, iowner_old=<%ld>, iowner_new=<%ld>, "), #endif p->theFile.c_owner, theFile->c_owner, (long) p->theFile.owner, (long) theFile->owner ); sl_strlcat(msg, tmp, SH_MSG_BUF); #ifdef REPLACE_OLD if ((modi_mask & MODI_USR) != 0) { if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) { sl_strlcpy(p->theFile.c_owner, theFile->c_owner, USER_MAX+2); p->theFile.owner = theFile->owner; } } #endif } if ( ((modi_mask & MODI_GRP) != 0) #if defined(HAVE_LIBPRELUDE) || ((modi_mask & MODI_MOD) != 0) #endif ) { #ifdef SH_USE_XML sl_snprintf(tmp, SH_MSG_BUF, _("group_old=\"%s\" group_new=\"%s\" igroup_old=\"%ld\" igroup_new=\"%ld\" "), p->theFile.c_group, theFile->c_group, (long) p->theFile.group, (long) theFile->group); #else sl_snprintf(tmp, SH_MSG_BUF, _("group_old=<%s>, group_new=<%s>, igroup_old=<%ld>, igroup_new=<%ld>, "), p->theFile.c_group, theFile->c_group, (long) p->theFile.group, (long) theFile->group); #endif sl_strlcat(msg, tmp, SH_MSG_BUF); #ifdef REPLACE_OLD if ((modi_mask & MODI_GRP) != 0) { if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) { sl_strlcpy(p->theFile.c_group, theFile->c_group, GROUP_MAX+2); p->theFile.group = theFile->group; } } #endif } if ((modi_mask & MODI_SIZ) != 0) { sl_snprintf(tmp, SH_MSG_BUF, sh_hash_size_format(), (UINT64) p->theFile.size, (UINT64) theFile->size); sl_strlcat(msg, tmp, SH_MSG_BUF); #ifdef REPLACE_OLD if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) p->theFile.size = theFile->size; #endif } if ((modi_mask & MODI_CTM) != 0) { (void) sh_unix_gmttime (p->theFile.ctime, timstr1c, sizeof(timstr1c)); (void) sh_unix_gmttime (theFile->ctime, timstr2c, sizeof(timstr2c)); #ifdef SH_USE_XML sl_snprintf(tmp, SH_MSG_BUF, _("ctime_old=\"%s\" ctime_new=\"%s\" "), timstr1c, timstr2c); #else sl_snprintf(tmp, SH_MSG_BUF, _("ctime_old=<%s>, ctime_new=<%s>, "), timstr1c, timstr2c); #endif sl_strlcat(msg, tmp, SH_MSG_BUF); #ifdef REPLACE_OLD if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) p->theFile.ctime = theFile->ctime; #endif } if ((modi_mask & MODI_ATM) != 0) { (void) sh_unix_gmttime (p->theFile.atime, timstr1a, sizeof(timstr1a)); (void) sh_unix_gmttime (theFile->atime, timstr2a, sizeof(timstr2a)); #ifdef SH_USE_XML sl_snprintf(tmp, SH_MSG_BUF, _("atime_old=\"%s\" atime_new=\"%s\" "), timstr1a, timstr2a); #else sl_snprintf(tmp, SH_MSG_BUF, _("atime_old=<%s>, atime_new=<%s>, "), timstr1a, timstr2a); #endif sl_strlcat(msg, tmp, SH_MSG_BUF); #ifdef REPLACE_OLD if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) p->theFile.atime = theFile->atime; #endif } if ((modi_mask & MODI_MTM) != 0) { (void) sh_unix_gmttime (p->theFile.mtime, timstr1m, sizeof(timstr1m)); (void) sh_unix_gmttime (theFile->mtime, timstr2m, sizeof(timstr2m)); #ifdef SH_USE_XML sl_snprintf(tmp, SH_MSG_BUF, _("mtime_old=\"%s\" mtime_new=\"%s\" "), timstr1m, timstr2m); #else sl_snprintf(tmp, SH_MSG_BUF, _("mtime_old=<%s>, mtime_new=<%s>, "), timstr1m, timstr2m); #endif sl_strlcat(msg, tmp, SH_MSG_BUF); #ifdef REPLACE_OLD if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) p->theFile.mtime = theFile->mtime; #endif } if ((modi_mask & MODI_CHK) != 0) { sl_snprintf(tmp, SH_MSG_BUF, #ifdef SH_USE_XML _("chksum_old=\"%s\" chksum_new=\"%s\" "), #else _("chksum_old=<%s>, chksum_new=<%s>, "), #endif p->theFile.checksum, fileHash); sl_strlcat(msg, tmp, SH_MSG_BUF); #ifdef REPLACE_OLD if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) { sl_strlcpy(p->theFile.checksum, fileHash, KEY_LEN+1); if ((theFile->check_mask & MODI_SGROW) != 0) p->theFile.size = theFile->size; } #endif /* FIXME is this correct? */ if (theFile->c_mode[0] != 'l' && theFile->link_path && strlen(theFile->link_path) > 2) modi_mask |= MODI_LNK; } if ((modi_mask & MODI_LNK) != 0 /* && theFile->c_mode[0] == 'l' */) { if (theFile->link_path) tmp_lnk = sh_util_safe_name(theFile->link_path); else tmp_lnk = sh_util_strdup("-"); if (p->linkpath) tmp_lnk_old = sh_util_safe_name(p->linkpath); else tmp_lnk_old = sh_util_strdup("-"); #ifdef SH_USE_XML sl_snprintf(tmp, SH_MSG_BUF, _("link_old=\"%s\" link_new=\"%s\" "), tmp_lnk_old, tmp_lnk); #else sl_snprintf(tmp, SH_MSG_BUF, _("link_old=<%s>, link_new=<%s>, "), tmp_lnk_old, tmp_lnk); #endif SH_FREE(tmp_lnk); SH_FREE(tmp_lnk_old); sl_strlcat(msg, tmp, SH_MSG_BUF); #ifdef REPLACE_OLD if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) { if (p->linkpath != NULL && p->linkpath != notalink) SH_FREE(p->linkpath); if (!(theFile->link_path) || (theFile->link_path[0] == '-' && theFile->link_path[1] == '\0')) p->linkpath = (char *)notalink; else p->linkpath = sh_util_strdup(theFile->link_path); } #endif } if (MODI_AUDIT_ENABLED(theFile->check_mask)) { char result[256]; if (NULL != sh_audit_fetch (theFile->fullpath, theFile->mtime, result, sizeof(result))) { #ifdef SH_USE_XML sl_strlcat(msg, _("obj=\""), SH_MSG_BUF); #else sl_strlcat(msg, _("obj=<"), SH_MSG_BUF); #endif sl_strlcat(msg, result, SH_MSG_BUF); #ifdef SH_USE_XML sl_strlcat(msg, _("\" "), SH_MSG_BUF); #else sl_strlcat(msg, _(">"), SH_MSG_BUF); #endif } } tmp_path = sh_util_safe_name(theFile->fullpath); sh_error_handle(log_severity, FIL__, __LINE__, (long) modi_mask, MSG_FI_CHAN, (policy_override == NULL) ? _(policy[class]):log_policy, change_code, tmp_path, msg); ++sh.statistics.files_report; SH_FREE(tmp_path); SH_FREE(tmp); SH_FREE(msg); #ifndef REPLACE_OLD SET_SH_FFLAG_REPORTED(p->fflags); #endif if (S_TRUE == sh.flag.update) { if (S_FALSE == sh_util_ask_update(theFile->fullpath)) { /* user does not want to update, thus we replace * with data from the baseline database */ sl_strlcpy(theFile->c_mode, p->theFile.c_mode, 11); theFile->mode = p->theFile.mode; #if defined(__linux__) || defined(HAVE_STAT_FLAGS) sl_strlcpy(theFile->c_attributes, p->theFile.c_attributes, 16); theFile->attributes = p->theFile.attributes; #endif #if defined(USE_ACL) || defined(USE_XATTR) if (theFile->attr_string == NULL && p->attr_string != NULL) { theFile->attr_string = sh_util_strdup (p->attr_string); } else if (theFile->attr_string != NULL && p->attr_string == NULL) { SH_FREE(theFile->attr_string); theFile->attr_string = NULL; } else if (theFile->attr_string != NULL && p->attr_string != NULL) { if (0 != strcmp(theFile->attr_string, p->attr_string)) { SH_FREE(theFile->attr_string); theFile->attr_string = sh_util_strdup (p->attr_string); } } #endif if (theFile->c_mode[0] == 'l') /* c_mode is already copied */ { if (theFile->link_path) SH_FREE(theFile->link_path); if (p->linkpath) theFile->link_path = sh_util_strdup(p->linkpath); else theFile->link_path = sh_util_strdup("-"); } else { if (theFile->link_path) SH_FREE(theFile->link_path); if (p->linkpath && p->linkpath != notalink) theFile->link_path = sh_util_strdup(p->linkpath); else theFile->link_path = NULL; } sl_strlcpy(fileHash, p->theFile.checksum, KEY_LEN+1); theFile->mtime = p->theFile.mtime; theFile->ctime = p->theFile.ctime; theFile->atime = p->theFile.atime; theFile->size = p->theFile.size; sl_strlcpy(theFile->c_group, p->theFile.c_group, GROUP_MAX+2); theFile->group = p->theFile.group; sl_strlcpy(theFile->c_owner, p->theFile.c_owner, USER_MAX+2); theFile->owner = p->theFile.owner; theFile->ino = p->theFile.ino; theFile->rdev = p->theFile.rdev; theFile->dev = p->theFile.dev; theFile->hardlinks = p->theFile.hardlinks; SET_SH_FFLAG_VISITED(p->fflags); CLEAR_SH_FFLAG_CHECKED(p->fflags); retval = 1; goto unlock_and_return; } else /* if (sh.flag.reportonce == S_TRUE) */ { /* we replace the data in the in-memory copy of the * baseline database, because otherwise we would get * another warning if the suidcheck runs */ sl_strlcpy(p->theFile.c_mode, theFile->c_mode, 11); p->theFile.mode = theFile->mode; #if defined(__linux__) || defined(HAVE_STAT_FLAGS) sl_strlcpy(p->theFile.c_attributes, theFile->c_attributes, 16); p->theFile.attributes = theFile->attributes; #endif #if defined(USE_ACL) || defined(USE_XATTR) if (p->attr_string == NULL && theFile->attr_string != NULL) { p->attr_string = sh_util_strdup (theFile->attr_string); } else if (p->attr_string != NULL && theFile->attr_string == NULL) { SH_FREE(p->attr_string); p->attr_string = NULL; } else if (theFile->attr_string != NULL && p->attr_string != NULL) { if (0 != strcmp(theFile->attr_string, p->attr_string)) { SH_FREE(p->attr_string); p->attr_string = sh_util_strdup (theFile->attr_string); } } #endif if (theFile->c_mode[0] == 'l' || theFile->link_path) { if (p->linkpath != NULL && p->linkpath != notalink) SH_FREE(p->linkpath); p->linkpath = sh_util_strdup(theFile->link_path); } else { if (p->linkpath != NULL && p->linkpath != notalink) { SH_FREE(p->linkpath); } p->linkpath = (char *)notalink; } sl_strlcpy(p->theFile.checksum, fileHash, KEY_LEN+1); p->theFile.mtime = theFile->mtime; p->theFile.ctime = theFile->ctime; p->theFile.atime = theFile->atime; p->theFile.size = theFile->size; sl_strlcpy(p->theFile.c_group, theFile->c_group, GROUP_MAX+2); p->theFile.group = theFile->group; sl_strlcpy(p->theFile.c_owner, theFile->c_owner, USER_MAX+2); p->theFile.owner = theFile->owner; p->theFile.ino = theFile->ino; p->theFile.rdev = theFile->rdev; p->theFile.dev = theFile->dev; p->theFile.hardlinks = theFile->hardlinks; } } } SET_SH_FFLAG_VISITED(p->fflags); CLEAR_SH_FFLAG_CHECKED(p->fflags); unlock_and_return: ; /* 'label at end of compound statement */ SH_MUTEX_UNLOCK(mutex_hash); SL_RETURN(retval, _("sh_hash_compdata")); } int hash_full_tree () { sh_file_t * p; int i; SL_ENTER(_("hash_full_tree")); if (IsInit != 1) SL_RETURN(0, _("hash_full_tree")); SH_MUTEX_LOCK_UNSAFE(mutex_hash); for (i = 0; i < TABSIZE; ++i) { for (p = tab[i]; p; p = p->next) CLEAR_SH_FFLAG_ALLIGNORE(p->fflags); } SH_MUTEX_UNLOCK_UNSAFE(mutex_hash); SL_RETURN (0, _("hash_full_tree")); } #if !defined(SH_CUTEST) static #endif int hash_remove_tree_test(char * s, char * fullpath, size_t len_s) { size_t len_p; char * test; len_p = strlen(fullpath); if (len_p >= len_s) { if (0 == strncmp(s, fullpath, len_s)) { if (len_p > len_s) { /* continue if not inside directory; * len_s > 1 because everything is inside '/' */ if ((len_s > 1) && (fullpath[len_s] != '/')) return S_FALSE; test = sh_files_find_mostspecific_dir(fullpath); if (test && 0 != strcmp(test, s)) { /* There is a more specific directory, continue */ return S_FALSE; } if (NULL == sh_files_findfile(fullpath)) { /* SET_SH_FFLAG_ALLIGNORE(p->fflags); */ return S_TRUE; } } else /* len_p == len */ { /* it is 's' itself, mark and continue * unless there is a policy for the inode itself */ if (NULL == sh_files_findfile(fullpath)) { /* SET_SH_FFLAG_ALLIGNORE(p->fflags); */ return S_TRUE; } else { return S_FALSE; } } } /* if path is in tree */ } /* if path is possibly in tree */ return S_FALSE; } int hash_remove_tree (char * s) { sh_file_t * p; size_t len_s; unsigned int i; SL_ENTER(_("hash_remove_tree")); if (!s || *s == '\0') SL_RETURN ((-1), _("hash_remove_tree")); len_s = sl_strlen(s); if (IsInit != 1) sh_hash_init(); SH_MUTEX_LOCK_UNSAFE(mutex_hash); for (i = 0; i < TABSIZE; ++i) { for (p = tab[i]; p; p = p->next) { if (p->fullpath) { /* if (0 == strncmp(s, p->fullpath, len_s)) *//* old */ if (S_TRUE == hash_remove_tree_test(s, p->fullpath, len_s)) SET_SH_FFLAG_ALLIGNORE(p->fflags); } /* if path is not null */ } } SH_MUTEX_UNLOCK_UNSAFE(mutex_hash); SL_RETURN ((0), _("hash_remove_tree")); } #if TIME_WITH_SYS_TIME #include #include #else #if HAVE_SYS_TIME_H #include #else #include #endif #endif static int ListFullDetail = S_FALSE; static int ListWithDelimiter = S_FALSE; static char * ListFile = NULL; int set_list_file (const char * c) { ListFile = sh_util_strdup(c); return 0; } int set_full_detail (const char * c) { (void) c; ListFullDetail = S_TRUE; return 0; } int set_list_delimited (const char * c) { (void) c; ListFullDetail = S_TRUE; ListWithDelimiter = S_TRUE; return 0; } /* Always quote the string, except if it is empty. Quote quotes by * doubling them. */ char * csv_escape(const char * str) { const char * p = str; const char * q; size_t size = 0; size_t flag_quote = 0; char * new; char * pnew; if (p) { while (*p) { if (*p == '"') ++flag_quote; ++size; ++p; } if (sl_ok_adds(size, flag_quote)) size += flag_quote; /* double each quote */ else return NULL; if (sl_ok_adds(size, 3)) size += 3; /* two quotes and terminating null */ else return NULL; new = SH_ALLOC(size); if (flag_quote != 0) { new[0] = '"'; pnew = &new[1]; q = str; while (*q) { *pnew = *q; if (*pnew == '"') { ++pnew; *pnew = '"'; } ++pnew; ++q; } *pnew = '"'; ++pnew; *pnew = '\0'; } else { if (size > 3) { new[0] = '"'; sl_strlcpy (&new[1], str, size-1); new[size-2] = '"'; new[size-1] = '\0'; } else { new[0] = '\0'; } } return new; } return NULL; } int isHexKey(char * s) { int i; for (i = 0; i < KEY_LEN; ++i) { if (*s) { if ((*s >= '0' && *s <= '9') || (*s >= 'A' && *s <= 'F') || (*s >= 'a' && *s <= 'f')) { ++s; continue; } } return S_FALSE; } return S_TRUE; } #include "sh_checksum.h" static char * KEYBUFtolower (char * s, char * result) { char * r = result; if (s) { for (; *s; ++s) { *r = tolower((unsigned char) *s); ++r; } *r = '\0'; } return result; } void sh_hash_list_db_entry_full_detail (sh_file_t * p) { char * tmp; char * esc; char str[81]; char hexdigest[SHA256_DIGEST_STRING_LENGTH]; char keybuffer[KEYBUF_SIZE]; if (ListWithDelimiter == S_TRUE) { printf(_("%7ld, %7ld, %10s, %5d, %12s, %5d, %3d, %-8s, %5d, %-8s, %5d, "), (unsigned long) p->theFile.ino, (unsigned long) p->theFile.dev, p->theFile.c_mode, (int) p->theFile.mode, p->theFile.c_attributes, (int) p->theFile.attributes, (int) p->theFile.hardlinks, p->theFile.c_owner, (int) p->theFile.owner, p->theFile.c_group, (int) p->theFile.group); } else { printf(_("%7ld %7ld %10s %5d %12s %5d %3d %-8s %5d %-8s %5d "), (unsigned long) p->theFile.ino, (unsigned long) p->theFile.dev, p->theFile.c_mode, (int) p->theFile.mode, p->theFile.c_attributes, (int) p->theFile.attributes, (int) p->theFile.hardlinks, p->theFile.c_owner, (int) p->theFile.owner, p->theFile.c_group, (int) p->theFile.group); } if ('c' == p->theFile.c_mode[0] || 'b' == p->theFile.c_mode[0]) sl_snprintf(str, sizeof(str), "%"PRIu64, p->theFile.rdev); else sl_snprintf(str, sizeof(str), "%"PRIu64, p->theFile.size); printf( _(" %8s"), str); if (ListWithDelimiter == S_TRUE) putchar(','); printf( _(" %s"), sh_unix_gmttime (p->theFile.ctime, str, sizeof(str))); if (ListWithDelimiter == S_TRUE) putchar(','); printf( _(" %s"), sh_unix_gmttime (p->theFile.mtime, str, sizeof(str))); if (ListWithDelimiter == S_TRUE) putchar(','); printf( _(" %s"), sh_unix_gmttime (p->theFile.atime, str, sizeof(str))); if (ListWithDelimiter == S_TRUE) putchar(','); if (isHexKey(p->theFile.checksum)) printf( _(" %s"), KEYBUFtolower(p->theFile.checksum, keybuffer)); else printf( _(" %s"), SHA256_Base2Hex(p->theFile.checksum, hexdigest)); if (ListWithDelimiter == S_TRUE) putchar(','); tmp = sh_util_safe_name(p->fullpath); if (ListWithDelimiter != S_TRUE) { printf( _(" %s"), tmp); } else { esc = csv_escape(tmp); printf( _(" %s,"), (esc != NULL) ? esc : _("(null)")); if (esc) SH_FREE(esc); } SH_FREE(tmp); if ('l' == p->theFile.c_mode[0]) { tmp = sh_util_safe_name(p->linkpath); if (ListWithDelimiter != S_TRUE) { printf(_(" -> %s"), tmp); } else { esc = csv_escape(tmp); printf( _(" %s,"), (esc != NULL) ? esc : _("(null)")); if (esc) SH_FREE(esc); } SH_FREE(tmp); } if (p->attr_string) { tmp = sh_util_safe_name(p->attr_string); if (ListWithDelimiter != S_TRUE) { printf(_(" %s"), tmp); } else { esc = csv_escape(tmp); printf( _(" %s"), (esc != NULL) ? esc : _("(null)")); if (esc) SH_FREE(esc); } SH_FREE(tmp); } else { if (ListWithDelimiter == S_TRUE) printf("%s",_(" no_attr")); } putchar('\n'); return; } void sh_hash_list_db_entry (sh_file_t * p) { char nowtime[128]; char thetime[128]; char * tmp; time_t now = time(NULL); time_t then = (time_t) p->theFile.mtime; #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GMTIME_R) struct tm * time_ptr; struct tm time_tm; time_ptr = gmtime_r(&then, &time_tm); strftime(thetime, 127, _("%b %d %Y"), time_ptr); time_ptr = gmtime_r(&now, &time_tm); strftime(nowtime, 127, _("%b %d %Y"), time_ptr); if (0 == strncmp(&nowtime[7], &thetime[7], 4)) { time_ptr = gmtime_r(&then, &time_tm); strftime(thetime, 127, _("%b %d %H:%M"), time_ptr); } #else strftime(thetime, 127, _("%b %d %Y"), gmtime(&then)); strftime(nowtime, 127, _("%b %d %Y"), gmtime(&now)); if (0 == strncmp(&nowtime[7], &thetime[7], 4)) strftime(thetime, 127, _("%b %d %H:%M"), gmtime(&then)); #endif tmp = sh_util_safe_name(p->fullpath); if ('c' == p->theFile.c_mode[0] || 'b' == p->theFile.c_mode[0]) printf(_("%10s %3d %-8s %-8s %3d,%4d %s %s"), p->theFile.c_mode, (int) p->theFile.hardlinks, p->theFile.c_owner, p->theFile.c_group, (int) major((dev_t)p->theFile.rdev), (int) minor((dev_t)p->theFile.rdev), thetime, tmp); else printf(_("%10s %3d %-8s %-8s %8ld %s %s"), p->theFile.c_mode, (int) p->theFile.hardlinks, p->theFile.c_owner, p->theFile.c_group, (long) p->theFile.size, thetime, tmp); SH_FREE(tmp); if ('l' == p->theFile.c_mode[0]) { tmp = sh_util_safe_name(p->linkpath); printf(_(" -> %s\n"), tmp); SH_FREE(tmp); } else printf("\n"); return; } #ifdef HAVE_LIBZ #include #endif int sh_hash_printcontent(char * linkpath) { #ifdef HAVE_LIBZ unsigned char * decoded; unsigned char * decompressed = NULL; size_t dlen; unsigned long clen; unsigned long clen_o; int res; if (linkpath && *linkpath != '-') { dlen = sh_util_base64_dec_alloc (&decoded, (unsigned char *)linkpath, strlen(linkpath)); clen = dlen * 2 + 1; do { if (decompressed) SH_FREE(decompressed); clen += dlen; clen_o = clen; decompressed = SH_ALLOC(clen); res = uncompress(decompressed, &clen, decoded, dlen); if (res == Z_MEM_ERROR) { fprintf(stderr, "%s",_("Error: Not enough memory\n")); return -1; } if (res == Z_DATA_ERROR) { fprintf(stderr, "%s",_("Error: Data corrupt or incomplete\n")); return -1; } } while (res == Z_BUF_ERROR || clen == clen_o); decompressed[clen] = '\0'; fputs( (char*) decompressed, stdout); SH_FREE(decompressed); return 0; } #else (void) linkpath; #endif fprintf(stderr, "%s",_("Error: No data available\n")); return -1; } int sh_hash_list_db (const char * db_file) { sh_file_t * p; SL_TICKET fd; char * line; int flag = 0; if (!db_file) { _exit(EXIT_FAILURE); return -1; } if (sl_is_suid()) { fprintf(stderr, "%s",_("ERROR: insufficient privilege\n")); _exit (EXIT_FAILURE); return -1; /* for Mac OSX compiler */ } if (0 == strcmp(db_file, _("default"))) db_file = file_path('D', 'W'); if (!db_file) { _exit(EXIT_FAILURE); return -1; } line = SH_ALLOC(MAX_PATH_STORE+2); if ( SL_ISERROR(fd = sl_open_read(FIL__, __LINE__, db_file, SL_YESPRIV))) { fprintf(stderr, _("ERROR: can't open %s for read (errnum = %ld)\n"), db_file, fd); _exit(EXIT_FAILURE); return -1; } /* fast forward to start of data */ sh_hash_setdataent(fd, line, MAX_PATH_STORE+1, db_file); while (1) { p = sh_hash_getdataent (fd, line, MAX_PATH_STORE+1); if ((p != NULL) && (p->fullpath[0] == '/')) { if (!ListFile) { flag = 1; if (ListFullDetail == S_FALSE) sh_hash_list_db_entry (p); else sh_hash_list_db_entry_full_detail (p); } else { if (0 != sl_strcmp(ListFile, p->fullpath)) { continue; } flag = 1; if ('l' != p->theFile.c_mode[0]) { if (sh_hash_printcontent(p->linkpath) < 0) { _exit(EXIT_FAILURE); return -1; } } else { fprintf(stderr, "%s",_("File is a link\n")); _exit(EXIT_FAILURE); return -1; } break; } } else if (p == NULL) { break; } } if (line != NULL) SH_FREE(line); sl_close (fd); fflush(NULL); if (flag == 0) { fprintf(stderr, "%s",_("File not found\n")); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); return 0; } /* if defined(SH_WITH_CLIENT) || defined(SH_STANDALONE) */ #endif