}
}
+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.
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);
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.
break;
}
- *filename = dp->d_name;
+ *filename = iter->filename;
return 0;
}
#endif
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;
continue;
}
- if ((size_t) (q - iter->path) >= size) {
+ if ((size_t) (q - iter->path) >= PATH_MAX) {
free(iter->path);
errno = ENOMEM;
return -1;
*/
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;
*/
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;
*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;