From ea4fc9e55d43ad921f76df263c16f738ca56358c Mon Sep 17 00:00:00 2001 From: Cropi Date: Thu, 21 Aug 2025 10:11:33 +0200 Subject: [PATCH] RHEL 8.10.Z ERRATUM CVE-2025-54389 aide: improper output neutralization enables bypassing resolves: RHEL-109907 --- aide-0.16-CVE-2025-54389-part2.patch | 220 ++++++++++ aide-0.16-CVE-2025-54389.patch | 609 --------------------------- aide.spec | 5 +- 3 files changed, 223 insertions(+), 611 deletions(-) create mode 100644 aide-0.16-CVE-2025-54389-part2.patch diff --git a/aide-0.16-CVE-2025-54389-part2.patch b/aide-0.16-CVE-2025-54389-part2.patch new file mode 100644 index 0000000..3fed87e --- /dev/null +++ b/aide-0.16-CVE-2025-54389-part2.patch @@ -0,0 +1,220 @@ +diff -up aide-0.16/src/db_disk.c.orig aide-0.16/src/db_disk.c +--- aide-0.16/src/db_disk.c.orig 2025-08-21 09:58:21.271581589 +0200 ++++ aide-0.16/src/db_disk.c 2025-08-21 10:01:26.310573141 +0200 +@@ -139,7 +139,11 @@ 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) { +@@ -182,9 +186,13 @@ static int get_file_status(char *filenam + 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; +@@ -220,7 +228,11 @@ db_line *db_readline_disk () + 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; + } + } +@@ -269,7 +281,11 @@ recursion: + 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 -> +diff -up aide-0.16/src/gen_list.c.orig aide-0.16/src/gen_list.c +--- aide-0.16/src/gen_list.c.orig 2025-08-21 09:58:21.273581610 +0200 ++++ aide-0.16/src/gen_list.c 2025-08-21 10:04:29.190502666 +0200 +@@ -37,6 +37,7 @@ + #include "list.h" + #include "gen_list.h" + #include "seltree.h" ++#include "util.h" + #include "db.h" + #include "db_config.h" + #include "commandconf.h" +@@ -993,16 +994,28 @@ int check_rxtree(char* filename,seltree* + 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; + } + } +@@ -1039,13 +1052,25 @@ db_line* get_file_attrs(char* filename,D + } 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); ++ } + } + } + +@@ -1220,7 +1245,11 @@ void hsymlnk(db_line* line) { + 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; +diff -up aide-0.16/src/util.c.orig aide-0.16/src/util.c +--- aide-0.16/src/util.c.orig 2025-08-21 09:58:21.272581600 +0200 ++++ aide-0.16/src/util.c 2025-08-21 10:07:50.157894133 +0200 +@@ -104,9 +104,11 @@ url_t* parse_url(char* val) + 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(hostname); +- return NULL; ++ return NULL; + } + u->value=strdup(r); + r[0]='\0'; +@@ -118,9 +120,11 @@ url_t* parse_url(char* val) + free(hostname); + break; + } else { +- error(0,"Invalid file-URL, cannot use hostname other than localhost or %s: file:%s\n",hostname,u->value); +- free(hostname); +- return NULL; ++ 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(hostname); ++ return NULL; + } + + break; +@@ -150,6 +154,43 @@ url_t* parse_url(char* val) + 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++; ++ } ++ i++; ++ } ++ 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(1); ++ } ++ escape_str(unescaped_str, str, s); ++ return str; ++} ++ ++char *stresc(const char *unescaped_str) { ++ return strnesc(unescaped_str, strlen(unescaped_str)); ++} ++ + /* Returns 1 if the string contains unsafe characters, 0 otherwise. */ + int contains_unsafe (const char *s) + { diff --git a/aide-0.16-CVE-2025-54389.patch b/aide-0.16-CVE-2025-54389.patch index 7263d0e..706c219 100644 --- a/aide-0.16-CVE-2025-54389.patch +++ b/aide-0.16-CVE-2025-54389.patch @@ -584,213 +584,6 @@ index c17828d3e8b732096e00253f21623339f2168ccf..41e216527a9b05b90f1b601a61279fd4 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 @@ -1258,405 +1051,3 @@ index 77d2e15f5f9cdba5168a92feaf2f97128e705f36..4a648b6f5ff14edd553a3f8d94b11717 } } #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++; -+ } -+ i++; -+ } -+ 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(1); -+ } -+ escape_str(unescaped_str, str, s); -+ return str; -+} -+ -+char *stresc(const char *unescaped_str) { -+ return strnesc(unescaped_str, strlen(unescaped_str)); -+} -+ - /* Returns 1 if the string contains unsafe characters, 0 otherwise. */ - int contains_unsafe (const char *s) - { - 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 49e5c8a..8756006 100644 --- a/aide.spec +++ b/aide.spec @@ -1,7 +1,7 @@ Summary: Intrusion detection environment Name: aide Version: 0.16 -Release: 15%{?dist}.1 +Release: 15%{?dist}.2 URL: http://sourceforge.net/projects/aide License: GPLv2+ Source0: %{url}/files/aide/%{version}/%{name}-%{version}.tar.gz @@ -42,6 +42,7 @@ Patch8: aide-0.16-CVE-2021-45417.patch # CVE-2025-54389 aide: improper output neutralization enables bypassing Patch9: aide-0.16-CVE-2025-54389.patch +Patch10: aide-0.16-CVE-2025-54389-part2.patch %description AIDE (Advanced Intrusion Detection Environment) is a file integrity @@ -87,7 +88,7 @@ mkdir -p -m0700 %{buildroot}%{_localstatedir}/lib/aide %dir %attr(0700,root,root) %{_localstatedir}/log/aide %changelog -* Thu Aug 21 2025 Attila Lakatos - 0.16.15.1 +* Thu Aug 21 2025 Attila Lakatos - 0.16.15.2 - CVE-2025-54389 aide: improper output neutralization enables bypassing resolves: RHEL-109907