From: Alan T. DeKok Date: Sun, 28 Sep 2025 16:00:48 +0000 (-0400) Subject: allow UTF-8 characters while reading directories X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cdcbe903e841e63ef5ba2dd69ccd58d88bc41f43;p=thirdparty%2Ffreeradius-server.git allow UTF-8 characters while reading directories --- diff --git a/src/lib/util/file.c b/src/lib/util/file.c index 5f60d93405..84d4f14add 100644 --- a/src/lib/util/file.c +++ b/src/lib/util/file.c @@ -429,6 +429,91 @@ int fr_dirfd(int *dirfd, char const **filename, char const *pathname) } } +static bool fr_globdir_file_ok(char const *try, fr_globdir_iter_t *iter) +{ + size_t len, room; + char const *p; + struct stat stat_buf; + + /* + * Filter the filenames. + */ + if (try[0] == '.') return false; + + /* + * Check for valid characters + */ + p = try; + while (*p) { + /* + * Control characters are invalid UTF-8, too. + */ + len = fr_utf8_char((uint8_t const *) p, -1); + if (!len) return false; + + /* + * The string is valid UTF-8, but is NOT an ASCII + * character. We allow it. + */ + if (len > 1) { + p += len; + continue; + } + + /* + * Limit the ASCII characters we allow. + */ + if (isalpha((uint8_t)*p) || + isdigit((uint8_t)*p) || + (*p == '-') || + (*p == '_') || + (*p == '.')) { + p++; + continue; + } + + /* + * Invalid character in filename, it's not a match. + */ + return false; + } + + len = p - try; + if (!len) return false; + + /* + * Ignore files generated by deb / rpm packaging updates. + */ + if ((len > 10) && (strncmp(&try[len - 10], ".dpkg-dist", 10) == 0)) return false; + if ((len > 9) && (strncmp(&try[len - 9], ".dpkg-old", 9) == 0)) return false; + if ((len > 7) && (strncmp(&try[len - 7], ".rpmnew", 9) == 0)) return false; + if ((len > 8) && (strncmp(&try[len - 8], ".rpmsave", 10) == 0)) return false; + + /* + * strlcpy() returns the length of the input, which can be larger than the available space. + */ + room = (iter->path + PATH_MAX) - iter->filename; + + if (strlcpy(iter->filename, try, room) >= room) return false; + + /* + * We only read normal files which are NOT executable, and symlinks. + */ + if (stat(iter->path, &stat_buf) != 0) { + return false; + } + + if (S_ISREG(stat_buf.st_mode)) { + if ((stat_buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0) { + return false; + } + } else if (!S_ISLNK(stat_buf.st_mode)) { /* soft links are executable */ + return false; + } + + return true; +} + #ifdef HAVE_DIRENT_H /* * Filter filenames when reading a directory. @@ -436,8 +521,6 @@ int fr_dirfd(int *dirfd, char const **filename, char const *pathname) static int fr_globdir_dir_next(char const **filename, fr_globdir_iter_t *iter) { struct dirent *dp; - char const *p; - size_t len; fr_assert(iter->dir != NULL); @@ -455,36 +538,7 @@ static int fr_globdir_dir_next(char const **filename, fr_globdir_iter_t *iter) return 0; } - /* - * Filter the filenames. - */ - if (dp->d_name[0] == '.') continue; - - /* - * Check for valid characters - */ - for (p = dp->d_name; *p != '\0'; p++) { - if (isalpha((uint8_t)*p) || - isdigit((uint8_t)*p) || - (*p == '-') || - (*p == '_') || - (*p == '.')) continue; - break; - } - - /* - * Invalid character in filename. Skip it. - */ - if (*p != '\0') continue; - - /* - * Ignore files generated by deb / rpm packaging updates. - */ - len = strlen(dp->d_name); - if ((len > 10) && (strncmp(&dp->d_name[len - 10], ".dpkg-dist", 10) == 0)) continue; - if ((len > 9) && (strncmp(&dp->d_name[len - 9], ".dpkg-old", 9) == 0)) continue; - if ((len > 7) && (strncmp(&dp->d_name[len - 7], ".rpmnew", 9) == 0)) continue; - if ((len > 8) && (strncmp(&dp->d_name[len - 8], ".rpmsave", 10) == 0)) continue; + if (!fr_globdir_file_ok(dp->d_name, iter)) continue; /* * It's mostly ASCII, and not a leftover file name, stop. @@ -492,7 +546,7 @@ static int fr_globdir_dir_next(char const **filename, fr_globdir_iter_t *iter) break; } - *filename = dp->d_name; + *filename = iter->filename; return 0; } #endif @@ -505,12 +559,11 @@ static int fr_globdir_get_path(char const *dir, char const *pattern, fr_globdir_ char const *p; char *q; bool slash; - size_t size = strlen(dir) + 1 + strlen(pattern) + 1; /* * Bootstrap the full path. */ - iter->path = malloc(size); + iter->path = malloc(PATH_MAX); if (!iter->path) { errno = ENOMEM; return -1; @@ -536,7 +589,7 @@ static int fr_globdir_get_path(char const *dir, char const *pattern, fr_globdir_ continue; } - if ((size_t) (q - iter->path) >= size) { + if ((size_t) (q - iter->path) >= PATH_MAX) { free(iter->path); errno = ENOMEM; return -1; @@ -557,7 +610,7 @@ static int fr_globdir_get_path(char const *dir, char const *pattern, fr_globdir_ */ p = pattern; while (*p) { - if ((size_t) (q - iter->path) >= size) { + if ((size_t) (q - iter->path) >= PATH_MAX) { free(iter->path); errno = ENOMEM; return -1; @@ -674,6 +727,9 @@ int fr_globdir_iter_init(char const **filename, char const *dir, char const *pat */ if ((*p == '*') || (*p == '?') || (*p == '[')) { #ifdef HAVE_GLOB_H + /* + * @todo - call realpath() to get the canonical filename? + */ if ((pattern[0] != '/') && (dir[0] != '/')) { errno = ENOENT; return -1; @@ -760,6 +816,10 @@ int fr_globdir_iter_init(char const **filename, char const *dir, char const *pat *filename = NULL; } else { iter->gl_current = 0; + + /* + * @todo - check the filenames using fr_globdir_file_ok() + */ *filename = iter->glob.gl_pathv[iter->gl_current]; } break;