diff --git a/aide-0.16-CVE-2025-54389.patch b/aide-0.16-CVE-2025-54389.patch index e9c4348..7263d0e 100644 --- a/aide-0.16-CVE-2025-54389.patch +++ b/aide-0.16-CVE-2025-54389.patch @@ -1,19 +1,132 @@ -diff -U0 aide-0.16/ChangeLog.orig aide-0.16/ChangeLog ---- aide-0.16/ChangeLog.orig 2025-08-20 12:10:17.957899161 +0200 -+++ aide-0.16/ChangeLog 2025-08-20 12:10:21.043397557 +0200 -@@ -0,0 +1,4 @@ +diff --git a/ChangeLog b/ChangeLog +index 263c438f4a2a38edc45f91c0d5a216112a8fa38c..5d286a8e07d0b3235f97272223175a1dd85848b2 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -1,27 +1,30 @@ +2025-08-07 Hannes von Haugwitz -+ * Escape control characters in report and log output (CVE-2025-54389), -+ thanks to Rajesh Pangare for reporting this issue ++ * Escape control characters in report and log output (CVE-2025-54389) + -@@ -2 +6 @@ + 2016-07-25 Hannes von Haugwitz - * Release version 0.16 + * Release version 0.16 -diff -up aide-0.16/doc/aide.1.orig aide-0.16/doc/aide.1 -diff -up aide-0.16/include/util.h.orig aide-0.16/include/util.h ---- aide-0.16/include/util.h.orig 2025-08-20 12:10:17.961526476 +0200 -+++ aide-0.16/include/util.h 2025-08-20 12:10:21.043645131 +0200 -@@ -44,6 +44,9 @@ url_t* parse_url(char*); + + 2016-07-11 Hannes von Haugwitz + * Fix example aide.conf (xattr -> xattrs) + * aide.conf.5: update "SELECTION LINES" section + * Released version 0.16rc1 + + 2016-07-10 Hannes von Haugwitz + * Fix compilation with latest libaudit + * Use AC_PROG_CC_C99 instead of AC_PROG_CC + * Add AM_PROG_CC_C_O + * aide.conf.in: logfile -> file + * Update README + * Update manual pages (aide.1 and aide.conf.5) + + 2016-07-07 Hannes von Haugwitz + * Adapt manual to version 0.16 + + 2016-06-08 Hannes von Haugwitz + * Add missing break statements + + 2016-04-15 Hannes von Haugwitz + * Released version 0.16b1 + + 2016-04-13 Hannes von Haugwitz + * Fix spelling errors +diff --git a/doc/aide.1.in b/doc/aide.1.in +index 932810eebd2eda353c2f30ce89a042e5f9d69a51..e932b8d0273e9144b09b3e1f8cf61cba2be12155 100644 +--- a/doc/aide.1.in ++++ b/doc/aide.1.in +@@ -71,51 +71,61 @@ output. See aide.conf (5) section URLS for available values. + Prints out the standard help message. + .PP + .SH DIAGNOSTICS + Normally, the exit status is 0 if no errors occurred. Except when the + .BR --check , + .BR --compare " or" + .B --update + command was requested, in which case the exit status is defined as: + .IP "1 * (new files detected?) +" + .IP "2 * (removed files detected?) +" + .IP "4 * (changed files detected?)" + .PP + Additionally, the following exit codes are defined for generic error + conditions: + .IP "14 Error writing error" + .IP "15 Invalid argument error" + .IP "16 Unimplemented function error" + .IP "17 Invalid configureline error" + .IP "18 IO error" + .IP "19 Version mismatch error" + .PP + .SH NOTES + Please note that due to mmap issues, aide cannot be terminated with + SIGTERM. Use SIGKILL to terminate. + ++.IP "Checksum encoding" ++ + The checksums in the database and in the output are by default base64 + encoded (see also report_base16 option). + To decode them you can use the following shell command: + + echo | base64 \-d | hexdump \-v \-e '32/1 "%02x" "\\n"' + ++.IP "Control characters" ++ ++Control characters (00-31 and 127) are always escaped in log and plain report ++output. They are escaped by a literal backslash (\\) followed by exactly 3 ++digits representing the character in octal notation (e.g. a newline is output ++as "\\012"). A literal backslash is not escaped unless it is followed by 3 digits ++(0-9), in this case the literal backslash is escaped as "\\134". ++ + .PP + .SH FILES + .IP \fB@sysconfdir@/aide.conf\fR + Default aide configuration file. + .IP \fB@localstatedir@/lib/aide/aide.db\fR + Default aide database. + .IP \fB@localstatedir@/lib/aide/aide.db.new\fR + Default aide output database. + .SH SEE ALSO + .BR aide.conf (5) + .BR manual.html + .SH BUGS + There are probably bugs in this release. Please report them + at http://sourceforge.net/projects/aide . Bug fixes are more than welcome. + Unified diffs are preferred. + .SH DISCLAIMER + All trademarks are the property of their respective owners. + No animals were harmed while making this webpage or this piece of + software. Although some pizza delivery guy's feelings were hurt. + .BR +diff --git a/include/util.h b/include/util.h +index 79988536c974ca83b14696380f6006031e0fa5e4..68e6ee2a905856bc7b73f1a67633585e0c1d814d 100644 +--- a/include/util.h ++++ b/include/util.h +@@ -22,48 +22,51 @@ + #ifndef _UTIL_H_INCLUDED + #define _UTIL_H_INCLUDED + #include + #include + #include "db_config.h" + + #define HEXD2ASC(x) (((x) < 10) ? ((x) + '0') : ((x) - 10 + 'A')) + + #define ASC2HEXD(x) (((x) >= '0' && (x) <= '9') ? \ + ((x) - '0') : (toupper(x) - 'A' + 10)) + + #define ISXDIGIT(x) isxdigit ((unsigned char)(x)) + + #define CLEANDUP(x) (contains_unsafe (x) ? encode_string (x) : strdup (x)) + + #ifndef HAVE_STRICMP + # define stricmp(a,b) strcasecmp( (a), (b) ) + #endif + + int cmpurl(url_t*, url_t*); + + url_t* parse_url(char*); int contains_unsafe(const char*); @@ -23,61 +136,1527 @@ diff -up aide-0.16/include/util.h.orig aide-0.16/include/util.h void decode_string(char*); char* encode_string(const char*); -diff -up aide-0.16/src/aide.c.orig aide-0.16/src/aide.c -diff -up aide-0.16/src/gen_list.c.orig aide-0.16/src/gen_list.c -diff -up aide-0.16/src/util.c.orig aide-0.16/src/util.c ---- aide-0.16/src/util.c.orig 2025-08-20 12:10:17.954526409 +0200 -+++ aide-0.16/src/util.c 2025-08-20 12:10:21.043912548 +0200 -@@ -24,6 +24,7 @@ - #include + + char* perm_to_char(mode_t perm); + + void sig_handler(int signal); + + void init_sighandler(void); + + char *expand_tilde(char * path); + + #ifndef HAVE_STRNSTR + char* strnstr(char* haystack,char* needle,int n); + #endif + + #ifndef HAVE_STRNLEN + size_t strnlen(const char *s, size_t maxlen); + #endif + + int syslog_facility_lookup(char *); + + #endif +diff --git a/src/aide.c b/src/aide.c +index 8dd38b7c5a7130cc1136d0ee30f5addc9c2226e5..2b7eee14b6e1d4351ca5ddc9387344bd88a8951c 100644 +--- a/src/aide.c ++++ b/src/aide.c +@@ -164,54 +164,60 @@ static int read_param(int argc,char**argv) + error(0,_("-B must have a parameter\n")); + exit(INVALID_ARGUMENT_ERROR); + } + break; + } + case 'A': { + if (optarg!=NULL) { + int errorno=commandconf('A',optarg); + if (errorno!=0){ + error(0,_("Configuration error in after statement:%s\n"),optarg); + exit(INVALID_CONFIGURELINE_ERROR); + } + } else { + error(0,_("-A must have a parameter\n")); + exit(INVALID_ARGUMENT_ERROR); + } + break; + } + case 'l': { + if (optarg!=NULL) { + const char* pcre_error; + int pcre_erroffset; + conf->limit=malloc(strlen(optarg)+1); + strcpy(conf->limit,optarg); + if((conf->limit_crx=pcre_compile(conf->limit, PCRE_ANCHORED, &pcre_error, &pcre_erroffset, NULL)) == NULL) { +- error(0,_("Error in limit regexp '%s' at %i: %s\n"), conf->limit, pcre_erroffset, pcre_error); ++ char *limit_safe = stresc(conf->limit); ++ error(0,_("Error in limit regexp '%s' at %i: %s\n"), limit_safe, pcre_erroffset, pcre_error); ++ free(limit_safe); + exit(INVALID_ARGUMENT_ERROR); + } +- error(200,_("Limit set to '%s'\n"), conf->limit); ++ { ++ char *limit_safe = stresc(conf->limit); ++ error(200,_("Limit set to '%s'\n"), limit_safe); ++ free(limit_safe); ++ } + } else { + error(0,_("-l must have an argument\n")); + exit(INVALID_ARGUMENT_ERROR); + } + break; + } + case 'r': { + if(optarg!=NULL) { + do_repurldef(optarg); + }else { + error(0,_("-r must have an argument\n")); + } + break; + } + case 'i': { + if(conf->action==0){ + conf->action=DO_INIT; + }else { + error(0, + _("Cannot have multiple commands on a single commandline.\n")); + exit(INVALID_ARGUMENT_ERROR); + }; + break; + } + case 'C': { +@@ -566,53 +572,57 @@ int main(int argc,char**argv) + if(cmpurl(conf->db_in_url,conf->db_out_url)==RETOK){ + error(4,_("WARNING:Input and output database urls are the same.\n")); + if((conf->action&DO_INIT)&&(conf->action&DO_COMPARE)){ + error(0,_("Input and output database urls cannot be the same " + "when doing database update\n")); + exit(INVALID_ARGUMENT_ERROR); + } + if(conf->action&DO_DIFF){ + error(0,_("Both input databases cannot be the same " + "when doing database compare\n")); + exit(INVALID_ARGUMENT_ERROR); + } + }; + if((conf->action&DO_DIFF)&&(!(conf->db_new_url)||!(conf->db_in_url))){ + error(0,_("Must have both input databases defined for " + "database compare.\n")); + exit(INVALID_ARGUMENT_ERROR); + } + if (conf->action&(DO_INIT|DO_COMPARE) && conf->root_prefix_length > 0) { + DIR *dir; + if((dir = opendir(conf->root_prefix)) != NULL) { + closedir(dir); + } else { + char* er=strerror(errno); + if (er!=NULL) { +- error(0,"opendir() for root prefix %s failed: %s\n", conf->root_prefix,er); ++ char *rp_safe = stresc(conf->root_prefix); ++ error(0,"opendir() for root prefix %s failed: %s\n", rp_safe,er); ++ free(rp_safe); + } else { +- error(0,"opendir() for root prefix %s failed: %i\n", conf->root_prefix,errno); ++ char *rp_safe = stresc(conf->root_prefix); ++ error(0,"opendir() for root prefix %s failed: %i\n", rp_safe,errno); ++ free(rp_safe); + } + exit(INVALID_ARGUMENT_ERROR); + } + } + #ifdef WITH_MHASH + byte* dig=NULL; + char* digstr=NULL; + + if(conf->config_check&&FORCECONFIGMD){ + error(0,"Can't give config checksum when compiled with --enable-forced_configmd\n"); + exit(INVALID_ARGUMENT_ERROR); + } + + if((conf->do_configmd||conf->config_check)&& conf->confmd!=0){ + /* The patch automatically adds a newline so will also have to add it. */ + if(newlinelastinconfig==0){ + mhash(conf->confmd,"\n",1); + }; + mhash(conf->confmd, NULL,0); + dig=(byte*)malloc(sizeof(byte)*mhash_get_block_size(conf->confhmactype)); + mhash_deinit(conf->confmd,(void*)dig); + digstr=encode_base64(dig,mhash_get_block_size(conf->confhmactype)); + + if(!conf->config_check||FORCECONFIGMD){ + if(strncmp(digstr,conf->old_confmdstr,strlen(digstr))!=0){ +diff --git a/src/compare_db.c b/src/compare_db.c +index c17828d3e8b732096e00253f21623339f2168ccf..41e216527a9b05b90f1b601a61279fd408e2df71 100644 +--- a/src/compare_db.c ++++ b/src/compare_db.c +@@ -504,175 +504,225 @@ static void print_line(seltree* node) { + c = '<'; + } + u = '='; + break; + } + if (summary_attributes[i]&node->changed_attrs&(forced_attrs|(~ignored_changed_attrs))) { + summary[i]=c; + } else if (summary_attributes[i]&((node->old_data)->attr&~((node->new_data)->attr)&(forced_attrs|~(ignored_removed_attrs)))) { + summary[i]=r; + } else if (summary_attributes[i]&~((node->old_data)->attr)&(node->new_data)->attr&(forced_attrs|~(ignored_added_attrs))) { + summary[i]=a; + } else if (summary_attributes[i]& ( + (((node->old_data)->attr&~((node->new_data)->attr)&ignored_removed_attrs))| + (~((node->old_data)->attr)&(node->new_data)->attr&ignored_added_attrs)| + (((node->old_data)->attr&(node->new_data)->attr)&ignored_changed_attrs) + ) ) { + summary[i]=g; + } else if (summary_attributes[i]&((node->old_data)->attr&(node->new_data)->attr)) { + summary[i]=u; + } else { + summary[i]=s; + } + } + } + summary[length]='\0'; +- error(2,"\n%s: %s", summary, (node->checked&NODE_REMOVED?node->old_data:node->new_data)->filename); ++ { ++ char *filename_safe = stresc((node->checked&NODE_REMOVED?node->old_data:node->new_data)->filename); ++ error(2,"\n%s: %s", summary, filename_safe); ++ free(filename_safe); ++ } + free(summary); summary=NULL; + } else { + if (node->checked&NODE_ADDED) { +- error(2,"added: %s\n",(node->new_data)->filename); ++ { ++ char *filename_safe = stresc((node->new_data)->filename); ++ error(2,"added: %s\n",filename_safe); ++ free(filename_safe); ++ } + } else if (node->checked&NODE_REMOVED) { +- error(2,"removed: %s\n",(node->old_data)->filename); ++ { ++ char *filename_safe = stresc((node->old_data)->filename); ++ error(2,"removed: %s\n",filename_safe); ++ free(filename_safe); ++ } + } else if (node->checked&NODE_CHANGED) { +- error(2,"changed: %s\n",(node->new_data)->filename); ++ { ++ char *filename_safe = stresc((node->new_data)->filename); ++ error(2,"changed: %s\n",filename_safe); ++ free(filename_safe); ++ } + } + } + } + + static void print_dbline_attributes(db_line* oline, db_line* nline, DB_ATTR_TYPE + changed_attrs, DB_ATTR_TYPE force_attrs) { + char **ovalue, **nvalue; + int onumber, nnumber, olen, nlen, i, j, k, c; + int length = sizeof(details_attributes)/sizeof(DB_ATTR_TYPE); + int p = (width_details-(width_details%2?13:14))/2; + DB_ATTR_TYPE attrs; + error(2,"\n"); + char *file_type = get_file_type_string((nline==NULL?oline:nline)->perm); + if (file_type) { + error(2,"%s: ", file_type); + } +- error(2,"%s\n", (nline==NULL?oline:nline)->filename); ++ { ++ char *filename_safe = stresc((nline==NULL?oline:nline)->filename); ++ error(2,"%s\n", filename_safe); ++ free(filename_safe); ++ } + attrs=force_attrs|(~(ignored_changed_attrs)&changed_attrs); + for (j=0; j < length; ++j) { + if (details_attributes[j]&attrs) { + onumber=get_attribute_values(details_attributes[j], oline, &ovalue); + nnumber=get_attribute_values(details_attributes[j], nline, &nvalue); + i = 0; + while (i= 0 || nlen-p*k >= 0) { + c = k*(p-1); + if (!onumber) { +- error(2," %s%-9s%c %-*c %.*s\n", width_details%2?"":" ", i+k?"":details_string[j], i+k?' ':':', p, ' ', p-1, nlen-c>0?&nvalue[i][c]:""); ++ error(2," %s%-9s%c %-*c %.*s\n", width_details%2?"":" ", i+k?"":details_string[j], i+k?' ':':', p, ' ', p-1, nlen-c>0?&nv[c]:""); + } else if (!nnumber) { +- error(2," %s%-9s%c %.*s\n", width_details%2?"":" ", i+k?"":details_string[j], i+k?' ':':', p-1, olen-c>0?&ovalue[i][c]:""); ++ error(2," %s%-9s%c %.*s\n", width_details%2?"":" ", i+k?"":details_string[j], i+k?' ':':', p-1, olen-c>0?&ov[c]:""); + } else { +- error(2," %s%-9s%c %-*.*s| %.*s\n", width_details%2?"":" ", i+k?"":details_string[j], i+k?' ':':', p, p-1, olen-c>0?&ovalue[i][c]:"", p-1, nlen-c>0?&nvalue[i][c]:""); ++ error(2," %s%-9s%c %-*.*s| %.*s\n", width_details%2?"":" ", i+k?"":details_string[j], i+k?' ':':', p, p-1, olen-c>0?&ov[c]:"", p-1, nlen-c>0?&nv[c]:""); + } + k++; + } + ++i; ++ free(ov); ++ free(nv); + } + for(i=0; i < onumber; ++i) { free(ovalue[i]); ovalue[i]=NULL; } free(ovalue); ovalue=NULL; + for(i=0; i < nnumber; ++i) { free(nvalue[i]); nvalue[i]=NULL; } free(nvalue); nvalue=NULL; + } + } + } + + + static void print_dbline_attributes_syslog(db_line* oline, db_line* nline, DB_ATTR_TYPE + changed_attrs, DB_ATTR_TYPE force_attrs) { + char **ovalue, **nvalue; + int onumber, nnumber, i, j; + int length = sizeof(details_attributes)/sizeof(DB_ATTR_TYPE); + DB_ATTR_TYPE attrs; + char *file_type = get_file_type_string((nline==NULL?oline:nline)->perm); + if (file_type) { + error(0,"%s=", file_type); + } +- error(0,"%s", (nline==NULL?oline:nline)->filename); ++ { ++ char *filename_safe = stresc((nline==NULL?oline:nline)->filename); ++ error(0,"%s", filename_safe); ++ free(filename_safe); ++ } + attrs=force_attrs|(~(ignored_changed_attrs)&changed_attrs); + for (j=0; j < length; ++j) { + if (details_attributes[j]&attrs) { + onumber=get_attribute_values(details_attributes[j], oline, &ovalue); + nnumber=get_attribute_values(details_attributes[j], nline, &nvalue); + + if (details_attributes[j] == DB_ACL || details_attributes[j] == DB_XATTRS) { + + error(0, ";%s_old=|", details_string[j]); + + for (i = 0 ; i < onumber ; i++) { +- error(0, "%s|", ovalue[i]); ++ { ++ char *val_safe = stresc(ovalue[i]); ++ error(0, "%s|", val_safe); ++ free(val_safe); ++ } + } + + error(0, ";%s_new=|", details_string[j]); + + for (i = 0 ; i < nnumber ; i++) { +- error(0, "%s|", nvalue[i]); ++ { ++ char *val_safe = stresc(nvalue[i]); ++ error(0, "%s|", val_safe); ++ free(val_safe); ++ } + } + + } else { + +- error(0, ";%s_old=%s;%s_new=%s", details_string[j], *ovalue, details_string[j], *nvalue); ++ { ++ char *ov_safe = stresc(*ovalue); ++ char *nv_safe = stresc(*nvalue); ++ error(0, ";%s_old=%s;%s_new=%s", details_string[j], ov_safe, details_string[j], nv_safe); ++ free(ov_safe); ++ free(nv_safe); ++ } + + } + + for(i=0; i < onumber; ++i) { free(ovalue[i]); ovalue[i]=NULL; } free(ovalue); ovalue=NULL; + for(i=0; i < nnumber; ++i) { free(nvalue[i]); nvalue[i]=NULL; } free(nvalue); nvalue=NULL; + } + } + error(0, "\n"); + } + + static void print_attributes_added_node(db_line* line) { + print_dbline_attributes(NULL, line, 0, line->attr); + } + + static void print_attributes_removed_node(db_line* line) { + print_dbline_attributes(line, NULL, 0, line->attr); + } + + static void print_attributes_added_node_syslog(db_line* line) { + + char *file_type = get_file_type_string(line->perm); + if (file_type) { + error(0,"%s=", file_type); + } +- error(0,"%s; added\n", line->filename); ++ { ++ char *filename_safe = stresc(line->filename); ++ error(0,"%s; added\n", filename_safe); ++ free(filename_safe); ++ } + + } + + static void print_attributes_removed_node_syslog(db_line* line) { + + char *file_type = get_file_type_string(line->perm); + if (file_type) { + error(0,"%s=", file_type); + } +- error(0,"%s; removed\n", line->filename); ++ { ++ char *filename_safe = stresc(line->filename); ++ error(0,"%s; removed\n", filename_safe); ++ free(filename_safe); ++ } + + } + + static void terse_report(seltree* node) { + list* r=NULL; + if ((node->checked&(DB_OLD|DB_NEW)) != 0) { + ntotal += ((node->checked&DB_NEW) != 0); + if (!(node->checked&DB_OLD)){ + /* File is in new db but not old. (ADDED) */ + /* unless it was moved in */ + if (!((node->checked&NODE_ALLOW_NEW)||(node->checked&NODE_MOVED_IN))) { + nadd++; + node->checked|=NODE_ADDED; + } + } else if (!(node->checked&DB_NEW)){ + /* File is in old db but not new. (REMOVED) */ + /* unless it was moved out */ + if (!((node->checked&NODE_ALLOW_RM)||(node->checked&NODE_MOVED_OUT))) { + nrem++; + node->checked|=NODE_REMOVED; + } + } else if ((node->old_data!=NULL)&&(node->new_data!=NULL)){ + /* File is in both db's and the data is still there. (CHANGED) */ + if (!(node->checked&(NODE_MOVED_IN|NODE_MOVED_OUT))){ + nchg++; +@@ -739,51 +789,53 @@ static void print_syslog_format(seltree* node) { + } + + static void print_report_header() { + char *time; + int first = 1; + + time = malloc(time_string_len * sizeof (char)); + strftime(time, time_string_len, time_format, localtime(&(conf->start_time))); + error(2,_("Start timestamp: %s (AIDE " AIDEVERSION ")\n"), time); + free(time); time=NULL; + + error(0,_("AIDE")); + if(conf->action&(DO_COMPARE|DO_DIFF)) { + error(0,_(" found %sdifferences between %s%s!!\n"), (nadd||nrem||nchg)?"":"NO ", conf->action&DO_COMPARE?_("database and filesystem"):_("the two databases"), (nadd||nrem||nchg)?"":_(". Looks okay")); + if(conf->action&(DO_INIT)) { + error(0,_("New AIDE database written to %s\n"),conf->db_out_url->value); + } + } else { + error(0,_(" initialized database at %s\n"),conf->db_out_url->value); + } + + if(conf->config_version) + error(2,_("Config version used: %s\n"),conf->config_version); + + if (conf->limit != NULL) { +- error (2,_("Limit: %s"), conf->limit); ++ char *limit_safe = stresc(conf->limit); ++ error (2,_("Limit: %s"), limit_safe); ++ free(limit_safe); + first = 0; + } + if (conf->action&(DO_INIT|DO_COMPARE) && conf->root_prefix_length > 0) { + if (first) { first=0; } + else { error (2," | "); } + error (2,_("Root prefix: %s"),conf->root_prefix); + } + if (conf->verbose_level != 5) { + if (first) { first=0; } + else { error (2," | "); } + error (2,_("Verbose level: %d"), conf->verbose_level); + } + if (!first) { error (2,"\n"); } + if (ignored_added_attrs) { + error (2,_("Ignored added attributes: %s\n"),report_attrs(ignored_added_attrs)); + } + if (ignored_removed_attrs) { + error (2,_("Ignored removed attributes: %s\n"),report_attrs(ignored_removed_attrs)); + } + if (ignored_changed_attrs) { + error (2,_("Ignored changed attributes: %s\n"),report_attrs(ignored_changed_attrs)); + } + if (forced_attrs) { + error (2,_("Forced attributes: %s\n"),report_attrs(forced_attrs)); + } +diff --git a/src/db_disk.c b/src/db_disk.c +index 6161af38010633cbc2c4519a76e997c992439562..11f0d1c348a097a4175640e0f554979f7c74f7c1 100644 +--- a/src/db_disk.c ++++ b/src/db_disk.c +@@ -117,181 +117,197 @@ static char *name_construct (const char *s) + { + char *ret; + int len2 = strlen (r->path); + int len = len2 + strlen (s) + 2 + conf->root_prefix_length; + + if (r->path[len2 - 1] != '/') { + len++; + } + + ret = (char *) malloc (len); + ret[0] = (char) 0; + strcpy(ret, conf->root_prefix); + strcat (ret, r->path); + if (r->path[len2 - 1] != '/') { + strcat (ret, "/"); + } + strcat (ret, s); + return ret; + } + + void add_child (db_line * fil) + { + int i; + struct seltree *new_r; + +- error (255, "Adding child %s\n", fil->filename); ++ { ++ char *fname_safe = stresc(fil->filename); ++ error (255, "Adding child %s\n", fname_safe); ++ free(fname_safe); ++ } + + new_r = get_seltree_node (r, fil->filename); + if (new_r != NULL) { + if (S_ISDIR (fil->perm_o)) { + ; + } else { + new_r->checked |= NODE_CHECKED; + new_r->checked |= NODE_TRAVERSE; + } + return; + } + + new_r = malloc (sizeof (seltree)); + + new_r->attr = 0; + i = strlen (fil->filename); + + new_r->path = malloc (i + 1); + strncpy(new_r->path, fil->filename, i+1); + new_r->childs = NULL; + new_r->sel_rx_lst = NULL; + new_r->neg_rx_lst = NULL; + new_r->equ_rx_lst = NULL; + new_r->parent = r; + new_r->checked = 0; + new_r->new_data = NULL; + new_r->old_data = NULL; + if (S_ISDIR (fil->perm_o)) { + ; + } else { + new_r->checked |= NODE_CHECKED; + new_r->checked |= NODE_TRAVERSE; + } + r->childs = list_sorted_insert (r->childs, new_r, compare_node_by_path); + } + + static int get_file_status(char *filename, struct AIDE_STAT_TYPE *fs) { + int sres = 0; + sres = AIDE_LSTAT_FUNC(filename,fs); + if(sres == -1){ + char* er = strerror(errno); + if (er == NULL) { +- error(0,"get_file_status: lstat() failed for %s. strerror() failed for %i\n", filename, errno); ++ char *filename_safe = stresc(filename); ++ error(0,"get_file_status: lstat() failed for %s. strerror() failed for %i\n", filename_safe, errno); ++ free(filename_safe); + } else { +- error(0,"get_file_status: lstat() failed for %s: %s\n", filename, er); ++ char *filename_safe = stresc(filename); ++ error(0,"get_file_status: lstat() failed for %s: %s\n", filename_safe, er); ++ free(filename_safe); + } + } + return sres; + } + + /* + It might be a good idea to make this non recursive. + Now implemented with goto-statement. Yeah, it's ugly and easy. + */ + + db_line *db_readline_disk () + { + db_line *fil = NULL; + DB_ATTR_TYPE attr; + char *fullname; + int add = 0; + struct AIDE_STAT_TYPE fs; + + /* root needs special handling */ + if (!root_handled) { + root_handled = 1; + fullname=malloc((conf->root_prefix_length+2)*sizeof(char)); + strcpy(fullname, conf->root_prefix); + strcat (fullname, "/"); + if (!get_file_status(fullname, &fs)) { + add = check_rxtree (&fullname[conf->root_prefix_length], conf->tree, &attr, fs.st_mode); + error (240, "%s match=%d, tree=%p, attr=%llu\n", &fullname[conf->root_prefix_length], add, + conf->tree, attr); + + if (add > 0) { + fil = get_file_attrs (fullname, attr, &fs); + + error (240, "%s attr=%llu\n", &fullname[conf->root_prefix_length], attr); + + if (fil != NULL) { +- error (240, "%s attr=%llu\n", fil->filename, fil->attr); ++ { ++ char *fname_safe = stresc(fil->filename); ++ error (240, "%s attr=%llu\n", fname_safe, fil->attr); ++ free(fname_safe); ++ } + return fil; + } + } + } + free (fullname); + } + recursion: + next_in_dir (); + + if (in_this ()) { + + /* + Let's check if we have '.' or '..' entry. + If have, just skipit. + If don't do the 'normal' thing. + */ + if (strcmp (entp->d_name, ".") == 0 || strcmp (entp->d_name, "..") == 0) { + goto recursion; // return db_readline_disk(db); + } + + /* + Now we know that we actually can do something. + */ + + fullname = name_construct (entp->d_name); + + /* + Now we have a filename, which we must remember to free if it is + not used. + + Next thing is to see if we want to do something with it. + If not call, db_readline_disk again... + */ + + if (get_file_status(fullname, &fs)) { + free (fullname); + goto recursion; + } + add = check_rxtree (&fullname[conf->root_prefix_length], conf->tree, &attr, fs.st_mode); + error (240, "%s match=%d, tree=%p, attr=%llu\n", &fullname[conf->root_prefix_length], add, + conf->tree, attr); + + if (add > 0) { + fil = get_file_attrs (fullname, attr, &fs); + + error (240, "%s attr=%llu\n", &fullname[conf->root_prefix_length], attr); + + if (fil != NULL) { +- error (240, "%s attr=%llu\n", fil->filename, fil->attr); ++ { ++ char *fname_safe = stresc(fil->filename); ++ error (240, "%s attr=%llu\n", fname_safe, fil->attr); ++ free(fname_safe); ++ } + } else { + /* + Something went wrong during read process -> + Let's try next one. + */ + free (fullname); + goto recursion; // return db_readline_disk(db); + } + + if (add == 1) { + /* + add_children -> if dir, then add to children list. + */ + /* If ee are adding a file that is not a dir */ + /* add_child can make the determination and mark the tree + accordingly + */ + add_child (fil); + } else if (add == 2) { + /* + Don't add to children list. + */ + + /* + Should we do something? +diff --git a/src/db_sql.c b/src/db_sql.c +index 154579070ccacb6e9b6b8393b989eb50c843e71d..09a32504c317607150174d2bec0fcddf47992995 100644 +--- a/src/db_sql.c ++++ b/src/db_sql.c +@@ -16,50 +16,51 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + #include "aide.h" + /*for locale support*/ + #include "locale-aide.h" + /*for locale support*/ + + #ifdef WITH_PSQL + + #include + #include + #include #include + #include + #include "base64.h" + #include "db.h" + + #include "db_sql.h" + #include "db_config.h" + #include "libpq-fe.h" + #include "report.h" ++#include "util.h" + + #ifdef WITH_MHASH + #include + #endif + + char* db_get_sql(db_line*,db_config*); + + int _db_check_result(PGconn *conn, PGresult *res, char *query) + { + int status = 0; + int ret = RETOK; + + + if (!res || ( (PQresultStatus(res) != PGRES_COMMAND_OK) && + (PQresultStatus(res) != PGRES_TUPLES_OK) )){ + ret = RETFAIL; + if (res!=NULL) { + error(0,"Sql error %s while doing %s\n", PQerrorMessage(conn), query); + } else { + error(0,"Sql error while doing %s.\n",query); + } + } else { + error(255,"Sql went ok.\n"); + status = 1; + } +@@ -281,51 +282,55 @@ db_line* db_readline_sql(int db, db_config* conf) { + db_readline_sql_byte((void*)&(rline->haval),db,db_haval, conf); + db_readline_sql_byte((void*)&(rline->gost),db,db_gost, conf); + #endif + db_readline_sql_char((void*)&(rline->fullpath),db,db_filename, conf); + rline->filename=rline->fullpath; + db_readline_sql_char((void*)&(rline->linkname),db,db_linkname, conf); + + db_readline_sql_int((void*)&(rline->perm),db,db_perm, conf); + db_readline_sql_int((void*)&(rline->uid),db,db_uid, conf); + db_readline_sql_int((void*)&(rline->gid),db,db_gid, conf); + db_readline_sql_int((void*)&(rline->inode),db,db_inode, conf); + db_readline_sql_int((void*)&(rline->nlink),db,db_lnkcount, conf); + + db_readline_sql_int((void*)&(rline->size),db,*db_osize, conf); + db_readline_sql_int((void*)&(rline->bcount),db,db_bcount, conf); + db_readline_sql_int((void*)&(rline->attr),db,db_attr, conf); + + db_readline_sql_time((void*)&(rline->atime),db,db_atime, conf); + db_readline_sql_time((void*)&(rline->ctime),db,db_ctime, conf); + db_readline_sql_time((void*)&(rline->mtime),db,db_mtime, conf); + #ifdef WITH_ACL + rline->acl=NULL; + #endif + ((psql_data*)(*db_filep))->curread++; + +- error(255,"filename %s\n",rline->filename); ++ { ++ char *filename_safe = stresc(rline->filename); ++ error(255,"filename %s\n",filename_safe); ++ free(filename_safe); ++ } + + return rline; + } + + + void sql_writeint(int data,char *s,int i){ + char t[10]; + t[0]=0; + if (i!=0) { + s = strcat(s,","); + } + sprintf(t,"%i",data); + + strcat(s,t); + + } + + void sql_writeoct(int data,char *s,int i){ + char t[10]; + t[0]=0; + if (i!=0) { + s = strcat(s,","); + } + sprintf(t,"%lo",data); + +diff --git a/src/do_md.c b/src/do_md.c +index 77d2e15f5f9cdba5168a92feaf2f97128e705f36..4a648b6f5ff14edd553a3f8d94b1171708ccadaf 100644 +--- a/src/do_md.c ++++ b/src/do_md.c +@@ -16,50 +16,51 @@ + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + #include "aide.h" + + #ifndef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 200112L + #endif + + #include + #include + #include + #include + #include + #include #include -+#include - #include - #include - #include -@@ -63,6 +64,44 @@ int cmpurl(url_t* u1,url_t* u2) - return RETOK; - }; + #include + #include + #include + + #include "md.h" ++#include "util.h" + + #include "db_config.h" + #include "do_md.h" + #include "report.h" + #include "list.h" + /*for locale support*/ + #include "locale-aide.h" + /*for locale support*/ + + + /* This define should be somewhere else */ + #define READ_BLOCK_SIZE 16777216 + + #ifdef WITH_MHASH + #include + #endif /* WITH_MHASH */ + + /* Redhat 5.0 needs this */ + #ifdef HAVE_MMAP + #ifndef MAP_FAILED + #define MAP_FAILED (-1) + #endif /* MAP_FAILED */ + #define MMAP_BLOCK_SIZE 16777216 + #endif /* HAVE_MMAP */ + +@@ -206,55 +207,59 @@ void calc_md(struct AIDE_STAT_TYPE* old_fs,db_line* line) { + from we are about to calculate the hash is the correct one, + and we don't read from a pipe :) + */ + struct AIDE_STAT_TYPE fs; + int stat_diff,filedes; + #ifdef WITH_PRELINK + pid_t pid; + #endif + + error(255,"calc_md called\n"); + #ifdef _PARAMETER_CHECK_ + if (line==NULL) { + abort(); + } + #endif + + #ifdef HAVE_O_NOATIME + filedes=open(line->fullpath,O_RDONLY|O_NOATIME); + if(filedes<0) + #endif + filedes=open(line->fullpath,O_RDONLY); + + if (filedes==-1) { + char* er=strerror(errno); + if (er!=NULL) { ++ char *fp_safe = stresc(line->fullpath); + error(3,"do_md(): open() for %s failed: %s\n", +- line->fullpath,er); ++ fp_safe,er); ++ free(fp_safe); + } else { ++ char *fp_safe = stresc(line->fullpath); + error(3,"do_md(): open() for %s failed: %i\n", +- line->fullpath,errno); ++ fp_safe,errno); ++ free(fp_safe); + } + /* + Nop. Cannot cal hashes. Mark it. + */ + no_hash(line); + return; + } + + AIDE_FSTAT_FUNC(filedes,&fs); + if(!(line->attr&DB_RDEV)) + fs.st_rdev=0; + + #ifdef HAVE_POSIX_FADVISE + if (posix_fadvise(filedes,0,fs.st_size,POSIX_FADV_NOREUSE)!=0) { + error(255,"posix_fadvise error %s\n",strerror(errno)); + } else { + error(255,"posix_fadvise(%i,0,%li,POSIX_FADV_NOREUSE) ok\n",filedes,fs.st_size); + } + #endif + if ((stat_diff=stat_cmp(&fs,old_fs))==RETOK) { + /* + Now we have a 'valid' filehandle to read from a file. + */ + + #ifdef WITH_PRELINK +@@ -288,51 +293,55 @@ void calc_md(struct AIDE_STAT_TYPE* old_fs,db_line* line) { + off_t curpos=0; + + r_size=fs.st_size; + /* in mmap branch r_size is used as size remaining */ + while(r_size>0){ + if(r_sizefullpath,strerror(errno)); ++ { ++ char *fp_safe = stresc(line->fullpath); ++ error(0,"error mmap'ing %s: %s\n", fp_safe,strerror(errno)); ++ free(fp_safe); ++ } + close(filedes); + close_md(&mdc); + return; + } + conf->catch_mmap=1; + if (update_md(&mdc,buf,size)!=RETOK) { + error(0,"Message digest failed during update\n"); + close(filedes); + close_md(&mdc); + munmap(buf,size); + return; + } + munmap(buf,size); + conf->catch_mmap=0; + } + /* we have used MMAP, let's return */ + close_md(&mdc); + md2line(&mdc,line); + close(filedes); + return; + #ifdef WITH_PRELINK + } + #endif + #endif /* not HAVE_MMAP */ + // buf=malloc(READ_BLOCK_SIZE); +@@ -491,53 +500,61 @@ void acl2line(db_line* line) { + ret->acl_d = NULL; + else + { + tmp = acl_to_text(acl_d, NULL); + if (!tmp || !*tmp) + ret->acl_d = NULL; + else + ret->acl_d = strdup(tmp); + acl_free(tmp); + } + + acl_free(acl_a); + acl_free(acl_d); + } + line->acl = ret; + #endif + #ifdef WITH_SUN_ACL + if(DB_ACL&line->attr) { /* There might be a bug here. */ + int res; + line->acl=malloc(sizeof(acl_type)); + line->acl->entries=acl(line->fullpath,GETACLCNT,0,NULL); + if (line->acl->entries==-1) { + char* er=strerror(errno); + line->acl->entries=0; + if (er==NULL) { +- error(0,"ACL query failed for %s. strerror failed for %i\n",line->fullpath,errno); ++ { ++ char *fp_safe = stresc(line->fullpath); ++ error(0,"ACL query failed for %s. strerror failed for %i\n",fp_safe,errno); ++ free(fp_safe); ++ } + } else { +- error(0,"ACL query failed for %s:%s\n",line->fullpath,er); ++ { ++ char *fp_safe = stresc(line->fullpath); ++ error(0,"ACL query failed for %s:%s\n",fp_safe,er); ++ free(fp_safe); ++ } + } + } else { + line->acl->acl=malloc(sizeof(aclent_t)*line->acl->entries); + res=acl(line->fullpath,GETACL,line->acl->entries,line->acl->acl); + if (res==-1) { + error(0,"ACL error %s\n",strerror(errno)); + } else { + if (res!=line->acl->entries) { + error(0,"Tried to read %i acl but got %i\n",line->acl->entries,res); + } + } + } + }else{ + line->acl=NULL; + } + #endif + } + #endif + + #ifdef WITH_XATTR + static xattrs_type *xattr_new(void) { + xattrs_type *ret = NULL; + + ret = malloc(sizeof(xattrs_type)); + ret->num = 0; +@@ -571,100 +588,112 @@ static void xattr_add(xattrs_type *xattrs, const char *key, const char + + xattrs->num += 1; + } + + void xattrs2line(db_line *line) { + /* get all generic user xattrs. */ + xattrs_type *xattrs = NULL; + static ssize_t xsz = 1024; + static char *xatrs = NULL; + ssize_t xret = -1; + + if (!(DB_XATTRS&line->attr)) + return; + + /* assume memory allocs work, like rest of AIDE code... */ + if (!xatrs) xatrs = malloc(xsz); + + while (((xret = llistxattr(line->fullpath, xatrs, xsz)) == -1) && (errno == ERANGE)) { + xsz <<= 1; + xatrs = realloc(xatrs, xsz); + } + + if ((xret == -1) && ((errno == ENOSYS) || (errno == ENOTSUP))) { + line->attr&=(~DB_XATTRS); + } else if (xret == -1) { +- error(0, "listxattrs failed for %s:%s\n", line->fullpath, strerror(errno)); ++ { ++ char *fp_safe = stresc(line->fullpath); ++ error(0, "listxattrs failed for %s:%s\n", fp_safe, strerror(errno)); ++ free(fp_safe); ++ } + } else if (xret) { + const char *attr = xatrs; + static ssize_t asz = 1024; + static char *val = NULL; + + if (!val) val = malloc(asz); + + xattrs = xattr_new(); + + while (xret > 0) { + size_t len = strlen(attr); + ssize_t aret = 0; + + if (strncmp(attr, "user.", strlen("user.")) && + strncmp(attr, "root.", strlen("root."))) + goto next_attr; /* only store normal xattrs, and SELinux */ + + while (((aret = getxattr(line->fullpath, attr, val, asz)) == + -1) && (errno == ERANGE)) { + asz <<= 1; + val = realloc (val, asz); + } + + if (aret != -1) + xattr_add(xattrs, attr, val, aret); + else if (errno != ENOATTR) +- error(0, "getxattr failed for %s:%s\n", line->fullpath, strerror(errno)); ++ { ++ char *fp_safe = stresc(line->fullpath); ++ error(0, "getxattr failed for %s:%s\n", fp_safe, strerror(errno)); ++ free(fp_safe); ++ } + + next_attr: + attr += len + 1; + xret -= len + 1; + } + } + + line->xattrs = xattrs; + } + #endif + + #ifdef WITH_SELINUX + void selinux2line(db_line *line) { + char *cntx = NULL; + + if (!(DB_SELINUX&line->attr)) + return; + + if (lgetfilecon_raw(line->fullpath, &cntx) == -1) { + line->attr&=(~DB_SELINUX); + if ((errno != ENOATTR) && (errno != EOPNOTSUPP)) +- error(0, "lgetfilecon_raw failed for %s:%s\n", line->fullpath, strerror(errno)); ++ { ++ char *fp_safe = stresc(line->fullpath); ++ error(0, "lgetfilecon_raw failed for %s:%s\n", fp_safe, strerror(errno)); ++ free(fp_safe); ++ } + return; + } + + line->cntx = strdup(cntx); + + freecon(cntx); + } + #endif + + #ifdef WITH_E2FSATTRS + void e2fsattrs2line(db_line* line) { + unsigned long flags; + if (DB_E2FSATTRS&line->attr) { + if (fgetflags(line->fullpath, &flags) == 0) { + line->e2fsattrs=flags; + } else { + line->attr&=(~DB_E2FSATTRS); + line->e2fsattrs=0; + } + } else { + line->e2fsattrs=0; + } + } + #endif + +diff --git a/src/gen_list.c b/src/gen_list.c +index ab257811485831b1db1b027a94b5c0447a92f923..5b4a93eef28e1930a52e8156df661ca999035f54 100644 +--- a/src/gen_list.c ++++ b/src/gen_list.c +@@ -16,50 +16,51 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + #include "aide.h" + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include "report.h" + #include "list.h" + #include "gen_list.h" + #include "seltree.h" + #include "db.h" ++#include "util.h" + #include "db_config.h" + #include "commandconf.h" + #include "report.h" + /*for locale support*/ + #include "locale-aide.h" + /*for locale support*/ + + #define CLOCK_SKEW 5 + + #ifdef WITH_MHASH + #include + #endif + #include "md.h" + #include "do_md.h" + + void hsymlnk(db_line* line); + void fs2db_line(struct AIDE_STAT_TYPE* fs,db_line* line); + void calc_md(struct AIDE_STAT_TYPE* old_fs,db_line* line); + void no_hash(db_line* line); + + static DB_ATTR_TYPE get_special_report_group(char* group) { + DB_ATTR_TYPE attr = get_groupval(group); + return attr==DB_ATTR_UNDEF?0:attr; + } + +@@ -971,103 +972,127 @@ static void add_file_to_tree(seltree* tree,db_line* file,int db, + } + } + + int check_rxtree(char* filename,seltree* tree,DB_ATTR_TYPE* attr, mode_t perm) + { + int retval=0; + char * tmp=NULL; + char * parentname=NULL; + seltree* pnode=NULL; + + parentname=strdup(filename); + tmp=strrchr(parentname,'/'); + if(tmp!=parentname){ + *tmp='\0'; + }else { + + if(parentname[1]!='\0'){ + /* we are in the root dir */ + parentname[1]='\0'; + } + } + + if(conf->limit!=NULL) { + retval=pcre_exec(conf->limit_crx, NULL, filename, strlen(filename), 0, PCRE_PARTIAL_SOFT, NULL, 0); + if (retval >= 0) { +- error(220, "check_rxtree: %s does match limit: %s\n", filename, conf->limit); ++ char *fname_safe = stresc(filename); ++ char *limit_safe = conf->limit?stresc(conf->limit):NULL; ++ error(220, "check_rxtree: %s does match limit: %s\n", fname_safe, limit_safe?limit_safe:""); ++ free(fname_safe); ++ free(limit_safe); + } else if (retval == PCRE_ERROR_PARTIAL) { +- error(220, "check_rxtree: %s does PARTIAL match limit: %s\n", filename, conf->limit); ++ char *fname_safe = stresc(filename); ++ char *limit_safe = conf->limit?stresc(conf->limit):NULL; ++ error(220, "check_rxtree: %s does PARTIAL match limit: %s\n", fname_safe, limit_safe?limit_safe:""); + if(S_ISDIR(perm) && get_seltree_node(tree,filename)==NULL){ +- error(220, "check_rxtree: creating new seltree node for '%s'\n", filename); ++ error(220, "check_rxtree: creating new seltree node for '%s'\n", fname_safe); + new_seltree_node(tree,filename,0,NULL); + } ++ free(fname_safe); ++ free(limit_safe); + return -1; + } else { +- error(220, "check_rxtree: %s does NOT match limit: %s\n", filename, conf->limit); ++ char *fname_safe = stresc(filename); ++ char *limit_safe = conf->limit?stresc(conf->limit):NULL; ++ error(220, "check_rxtree: %s does NOT match limit: %s\n", fname_safe, limit_safe?limit_safe:""); ++ free(fname_safe); ++ free(limit_safe); + return -2; + } + } + + pnode=get_seltree_node(tree,parentname); + + *attr=0; + retval=check_node_for_match(pnode,filename, perm, 0,attr); + + free(parentname); + + return retval; + } + + db_line* get_file_attrs(char* filename,DB_ATTR_TYPE attr, struct AIDE_STAT_TYPE *fs) + { + db_line* line=NULL; + time_t cur_time; + + if(!(attr&DB_RDEV)) + fs->st_rdev=0; + /* + Get current time for future time notification. + */ + cur_time=time(NULL); + + if (cur_time==(time_t)-1) { + char* er=strerror(errno); + if (er==NULL) { + error(0,_("Can not get current time. strerror failed for %i\n"),errno); + } else { + error(0,_("Can not get current time with reason %s\n"),er); + } + } else { + + if(fs->st_atime>cur_time){ +- error(CLOCK_SKEW,_("%s atime in future\n"),filename); ++ { ++ char *fname_safe = stresc(filename); ++ error(CLOCK_SKEW,_("%s atime in future\n"),fname_safe); ++ free(fname_safe); ++ } + } + if(fs->st_mtime>cur_time){ +- error(CLOCK_SKEW,_("%s mtime in future\n"),filename); ++ { ++ char *fname_safe = stresc(filename); ++ error(CLOCK_SKEW,_("%s mtime in future\n"),fname_safe); ++ free(fname_safe); ++ } + } + if(fs->st_ctime>cur_time){ +- error(CLOCK_SKEW,_("%s ctime in future\n"),filename); ++ { ++ char *fname_safe = stresc(filename); ++ error(CLOCK_SKEW,_("%s ctime in future\n"),fname_safe); ++ free(fname_safe); ++ } + } + } + + /* + Malloc if we have something to store.. + */ + + line=(db_line*)malloc(sizeof(db_line)); + + memset(line,0,sizeof(db_line)); + + /* + We want filename + */ + + line->attr=attr|DB_FILENAME; + + /* + Just copy some needed fields. + */ + + line->fullpath=filename; + line->filename=&filename[conf->root_prefix_length]; + line->perm_o=fs->st_mode; + line->size_o=fs->st_size; +@@ -1198,51 +1223,55 @@ void populate_tree(seltree* tree) + initdbwarningprinted=1; + } + } + } + } + if(conf->action&DO_INIT) { + write_tree(tree); + } + } + + void hsymlnk(db_line* line) { + + if((S_ISLNK(line->perm_o))){ + int len=0; + #ifdef WITH_ACL + if(conf->no_acl_on_symlinks!=1) { + line->attr&=(~DB_ACL); + } + #endif + + if(conf->warn_dead_symlinks==1) { + struct AIDE_STAT_TYPE fs; + int sres; + sres=AIDE_STAT_FUNC(line->fullpath,&fs); + if (sres!=0 && sres!=EACCES) { +- error(4,"Dead symlink detected at %s\n",line->fullpath); ++ { ++ char *fp_safe = stresc(line->fullpath); ++ error(4,"Dead symlink detected at %s\n",fp_safe); ++ free(fp_safe); ++ } + } + if(!(line->attr&DB_RDEV)) + fs.st_rdev=0; + } + /* + Is this valid?? + No, We should do this elsewhere. + */ + line->linkname=(char*)malloc(_POSIX_PATH_MAX+1); + if(line->linkname==NULL){ + error(0,_("malloc failed in hsymlnk()\n")); + abort(); + } + + /* + Remember to nullify the buffer, because man page says + + readlink places the contents of the symbolic link path in + the buffer buf, which has size bufsiz. readlink does not + append a NUL character to buf. It will truncate the con- + tents (to a length of bufsiz characters), in case the + buffer is too small to hold all of the contents. + + */ + memset(line->linkname,0,_POSIX_PATH_MAX+1); +diff --git a/src/util.c b/src/util.c +index 21c75a2f176270f47f480c8143984e8a00ce8780..7e3da74a6acbf6e656b833591972be06fb1ae0f1 100644 +--- a/src/util.c ++++ b/src/util.c +@@ -82,101 +82,142 @@ url_t* parse_url(char* val) + + if(r[0]!='\0'){ + r[0]='\0'; + r++; + } + u->type=url_unknown; + for(i=0;itype=url_value[i]; + break; + } + } + + switch (u->type) { + case url_file : { + if(r[0]=='/'&&(r+1)[0]=='/'&&(r+2)[0]=='/'){ + u->value=strdup(r+2); + break; + } + if(r[0]=='/'&&(r+1)[0]=='/'&&(r+2)[0]!='/'){ + char*hostname=(char*)malloc(sizeof(char)*MAXHOSTNAMELEN); + char* t=r+2; + r+=2; + for(i=0;r[0]!='/'&&r[0]!='\0';r++,i++); + if(r[0]=='\0'){ +- error(0,"Invalid file-URL,no path after hostname: file:%s\n",t); ++ char *t_safe = stresc(t); ++ error(0,"Invalid file-URL,no path after hostname: file:%s\n",t_safe); ++ free(t_safe); + free(u); + free(val_copy); + free(hostname); +- return NULL; ++ return NULL; + } + u->value=strdup(r); + r[0]='\0'; + if(gethostname(hostname,MAXHOSTNAMELEN)==-1){ + strncpy(hostname,"localhost",MAXHOSTNAMELEN); + } + + if( (strcmp(t,"localhost")==0)||(strcmp(t,hostname)==0)){ + free(hostname); + break; + } else { +- error(0,"Invalid file-URL, cannot use hostname other than localhost or %s: file:%s\n",hostname,u->value); ++ char *value_safe = stresc(u->value); ++ error(0,"Invalid file-URL, cannot use hostname other than localhost or %s: file:%s\n",hostname,value_safe); ++ free(value_safe); + free(u->value); + free(u); + free(val_copy); +- free(hostname); +- return NULL; ++ free(hostname); ++ return NULL; + } + + break; + } + u->value=strdup(r); + + break; + } + case url_https : + case url_http : + case url_ftp : { + u->value=strdup(val); + break; + } + case url_unknown : { + error(0,"Unknown URL-type:%s\n",val_copy); + break; + } + default : { + u->value=strdup(r); + break; + } + } + + free(val_copy); + + return u; + } +static size_t escape_str(const char *unescaped_str, char *str, size_t s) { -+ size_t n = 0; -+ size_t i = 0; -+ char c; -+ while (i < s && (c = unescaped_str[i])) { -+ if ((c >= 0 && (c < 0x1f || c == 0x7f)) || -+ (c == '\\' && isdigit(unescaped_str[i+1]) -+ && isdigit(unescaped_str[i+2]) -+ && isdigit(unescaped_str[i+3]) -+ ) ) { -+ if (str) { snprintf(&str[n], 5, "\\%03o", c); } -+ n += 4; -+ } else { -+ if (str) { str[n] = c; } -+ n++; ++ size_t n = 0; ++ size_t i = 0; ++ char c; ++ while (i < s && (c = unescaped_str[i])) { ++ if ((c >= 0 && (c < 0x1f || c == 0x7f)) || ++ (c == '\\' && isdigit(unescaped_str[i+1]) ++ && isdigit(unescaped_str[i+2]) ++ && isdigit(unescaped_str[i+3]))) { ++ if (str) { snprintf(&str[n], 5, "\\%03o", c); } ++ n += 4; ++ } else { ++ if (str) { str[n] = c; } ++ n++; ++ } ++ i++; + } -+ i++; -+ } -+ if (str) { str[n] = '\0'; } -+ n++; -+ return n; ++ if (str) { str[n] = '\0'; } ++ n++; ++ return n; +} + +char *strnesc(const char *unescaped_str, size_t s) { -+ int n = escape_str(unescaped_str, NULL, s); -+ char *str = malloc(n); -+ if (str == NULL) { -+ error(0, "malloc: failed to allocate %d bytes of memory\n", n); -+ exit(EXIT_FAILURE); -+ } -+ escape_str(unescaped_str, str, s); -+ return str; ++ int n = escape_str(unescaped_str, NULL, s); ++ char *str = malloc(n); ++ if (str == NULL) { ++ error(0, "malloc: failed to allocate %d bytes of memory\n", n); ++ exit(1); ++ } ++ escape_str(unescaped_str, str, s); ++ return str; +} + +char *stresc(const char *unescaped_str) { -+ return strnesc(unescaped_str, strlen(unescaped_str)); ++ return strnesc(unescaped_str, strlen(unescaped_str)); +} + - url_t* parse_url(char* val) + /* Returns 1 if the string contains unsafe characters, 0 otherwise. */ + int contains_unsafe (const char *s) { - url_t* u=NULL; + for (; *s; s++) + if (strchr (URL_UNSAFE,(int) *s)||!ISPRINT((int)*s)) + return 1; + return 0; + } + + /* Decodes the forms %xy in a URL to the character the hexadecimal + code of which is xy. xy are hexadecimal digits from + [0123456789ABCDEF] (case-insensitive). If x or y are not + hex-digits or `%' precedes `\0', the sequence is inserted + literally. */ + + void decode_string (char* s) + { + char *p = s; + + for (; *s; s++, p++) + { + if (*s != '%') + *p = *s; + else + { + diff --git a/aide.spec b/aide.spec index 5ecf983..532444d 100644 --- a/aide.spec +++ b/aide.spec @@ -1,7 +1,7 @@ Summary: Intrusion detection environment Name: aide Version: 0.16 -Release: 104%{?dist} +Release: 105%{?dist} URL: http://sourceforge.net/projects/aide License: GPLv2+ @@ -41,8 +41,7 @@ Patch9: aide-static-analysis.patch Patch10: aide-0.16-CVE-2021-45417.patch Patch11: aide-db-problem.patch Patch12: rootPrefix.patch -Patch13: aide-0.16-CVE-2025-54389-part1.patch -Patch14: aide-0.16-CVE-2025-54389-part2.patch +Patch13: aide-0.16-CVE-2025-54389.patch %description AIDE (Advanced Intrusion Detection Environment) is a file integrity @@ -89,7 +88,7 @@ mkdir -p -m0700 %{buildroot}%{_localstatedir}/lib/aide %dir %attr(0700,root,root) %{_localstatedir}/log/aide %changelog -* Wed Aug 20 2025 Attila Lakatos - 0.16-104 +* Wed Aug 20 2025 Attila Lakatos - 0.16-105 RHEL 9.7 ERRATUM - CVE-2025-54389 aide: improper output neutralization enables bypassing Resolves: RHEL-109912