diff options
Diffstat (limited to 'lib/util')
-rw-r--r-- | lib/util/Makefile.in | 2 | ||||
-rw-r--r-- | lib/util/secure_path.c | 58 | ||||
-rw-r--r-- | lib/util/sudo_conf.c | 94 | ||||
-rw-r--r-- | lib/util/util.exp.in | 2 |
4 files changed, 112 insertions, 44 deletions
diff --git a/lib/util/Makefile.in b/lib/util/Makefile.in index 1b7eb11c9..71aeb5f06 100644 --- a/lib/util/Makefile.in +++ b/lib/util/Makefile.in @@ -65,7 +65,7 @@ INSTALL_OWNER = -o $(install_uid) -g $(install_gid) INSTALL_BACKUP = @INSTALL_BACKUP@ # C preprocessor defines -CPPDEFS = -D_PATH_SUDO_CONF=\"$(sysconfdir)/sudo.conf\" +CPPDEFS = -D_PATH_SUDO_CONF=\"@sudo_conf@\" # C preprocessor flags CPPFLAGS = -I$(incdir) -I$(top_builddir) -I. -I$(srcdir) $(CPPDEFS) \ diff --git a/lib/util/secure_path.c b/lib/util/secure_path.c index 89d21deee..31df507dc 100644 --- a/lib/util/secure_path.c +++ b/lib/util/secure_path.c @@ -24,6 +24,7 @@ #include <config.h> #include <sys/stat.h> +#include <errno.h> #include <fcntl.h> #include <string.h> #include <unistd.h> @@ -95,6 +96,21 @@ sudo_secure_dir_v1(const char *path, uid_t uid, gid_t gid, struct stat *sb) } /* + * Verify that fd matches type and not writable by other users. + */ +int +sudo_secure_fd_v1(int fd, unsigned int type, uid_t uid, gid_t gid, + struct stat *sb) +{ + int ret = SUDO_PATH_MISSING; + debug_decl(sudo_secure_fd, SUDO_DEBUG_UTIL); + + if (fd != -1 && fstat(fd, sb) == 0) + ret = sudo_check_secure(sb, type, uid, gid); + debug_return_int(ret); +} + +/* * Open path read-only as long as it is not writable by other users. * Returns an open file descriptor on success, else -1. * Sets error to SUDO_PATH_SECURE on success, and a value < 0 on failure. @@ -143,3 +159,45 @@ sudo_secure_open_dir_v1(const char *path, uid_t uid, gid_t gid, { return sudo_secure_open(path, S_IFDIR, uid, gid, sb, error); } + +/* + * Open the first file found in a colon-separated list of paths. + * Subsequent files in the path are only attempted if the + * previous file does not exist. Errors other than ENOENT are + * considered fatal and will stop processing the path. + * Sets name based on the last file it tried to open, even on error. + */ +int +sudo_open_conf_path_v1(const char *path, char *name, size_t namesize, + int (*fn)(const char *, int)) +{ + const char *cp, *ep, *path_end; + int fd = -1; + debug_decl(sudo_open_conf_path, SUDO_DEBUG_UTIL); + + path_end = path + strlen(path); + for (cp = sudo_strsplit(path, path_end, ":", &ep); + cp != NULL; cp = sudo_strsplit(NULL, path_end, ":", &ep)) { + + const size_t len = ep - cp; + if (len >= namesize) { + /* We always set name, even on error. */ + memcpy(name, cp, namesize - 1); + name[namesize - 1] = '\0'; + errno = ENAMETOOLONG; + break; + } + memcpy(name, cp, len); + name[len] = '\0'; + + fd = fn ? + fn(name, O_RDONLY|O_NONBLOCK) : open(name, O_RDONLY|O_NONBLOCK); + if (fd != -1) { + (void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK); + break; + } + if (errno != ENOENT) + break; + } + debug_return_int(fd); +} diff --git a/lib/util/sudo_conf.c b/lib/util/sudo_conf.c index 9c5612098..f1026f85e 100644 --- a/lib/util/sudo_conf.c +++ b/lib/util/sudo_conf.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2009-2021 Todd C. Miller <Todd.Miller@sudo.ws> + * Copyright (c) 2009-2023 Todd C. Miller <Todd.Miller@sudo.ws> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -629,12 +629,13 @@ sudo_conf_init(int conf_types) * Read in /etc/sudo.conf and populates sudo_conf_data. */ int -sudo_conf_read_v1(const char *conf_file, int conf_types) +sudo_conf_read_v1(const char *path, int conf_types) { FILE *fp = NULL; - int fd, ret = false; + int fd = -1, ret = false; char *prev_locale, *line = NULL; unsigned int conf_lineno = 0; + char conf_file[PATH_MAX]; size_t linesize = 0; debug_decl(sudo_conf_read, SUDO_DEBUG_UTIL); @@ -651,57 +652,62 @@ sudo_conf_read_v1(const char *conf_file, int conf_types) if (prev_locale[0] != 'C' || prev_locale[1] != '\0') setlocale(LC_ALL, "C"); + if (path != NULL) { + /* Caller specified a single file, which must exist. */ + if (strlcpy(conf_file, path, sizeof(conf_file)) >= sizeof(conf_file)) { + errno = ENAMETOOLONG; + sudo_warn("%s", path); + goto done; + } + fd = open(conf_file, O_RDONLY); + if (fd == -1) { + sudo_warn(U_("unable to open %s"), conf_file); + goto done; + } + } else { #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (conf_file == NULL) { struct stat sb; int error; - conf_file = _PATH_SUDO_CONF; - fd = sudo_secure_open_file(conf_file, ROOT_UID, -1, &sb, &error); - if (fd == -1) { - switch (error) { - case SUDO_PATH_MISSING: - /* Root should always be able to read sudo.conf. */ - if (errno != ENOENT && geteuid() == ROOT_UID) - sudo_warn(U_("unable to open %s"), conf_file); - break; - case SUDO_PATH_BAD_TYPE: - sudo_warnx(U_("%s is not a regular file"), conf_file); - break; - case SUDO_PATH_WRONG_OWNER: - sudo_warnx(U_("%s is owned by uid %u, should be %u"), - conf_file, (unsigned int) sb.st_uid, ROOT_UID); - break; - case SUDO_PATH_WORLD_WRITABLE: - sudo_warnx(U_("%s is world writable"), conf_file); - break; - case SUDO_PATH_GROUP_WRITABLE: - sudo_warnx(U_("%s is group writable"), conf_file); - break; - default: - sudo_warnx("%s: internal error, unexpected error %d", - __func__, error); - break; - } + /* _PATH_SUDO_CONF is a colon-separated list of path. */ + fd = sudo_open_conf_path(_PATH_SUDO_CONF, conf_file, + sizeof(conf_file), NULL); + error = sudo_secure_fd(fd, S_IFREG, ROOT_UID, -1, &sb); + switch (error) { + case SUDO_PATH_SECURE: + /* OK! */ + break; + case SUDO_PATH_MISSING: + /* Root should always be able to read sudo.conf. */ + if (errno != ENOENT && geteuid() == ROOT_UID) + sudo_warn(U_("unable to open %s"), conf_file); + goto done; + case SUDO_PATH_BAD_TYPE: + sudo_warnx(U_("%s is not a regular file"), conf_file); + goto done; + case SUDO_PATH_WRONG_OWNER: + sudo_warnx(U_("%s is owned by uid %u, should be %u"), + conf_file, (unsigned int) sb.st_uid, ROOT_UID); + goto done; + case SUDO_PATH_WORLD_WRITABLE: + sudo_warnx(U_("%s is world writable"), conf_file); + goto done; + case SUDO_PATH_GROUP_WRITABLE: + sudo_warnx(U_("%s is group writable"), conf_file); + goto done; + default: + sudo_warnx("%s: internal error, unexpected error %d", + __func__, error); goto done; } - } else #else - if (conf_file == NULL) - conf_file = _PATH_SUDO_CONF; + /* No default sudo.conf when fuzzing. */ + goto done; #endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ - { - fd = open(conf_file, O_RDONLY); - if (fd == -1) { - sudo_warn(U_("unable to open %s"), conf_file); - goto done; - } } if ((fp = fdopen(fd, "r")) == NULL) { - if (errno != ENOENT && geteuid() == ROOT_UID) - sudo_warn(U_("unable to open %s"), conf_file); - close(fd); + sudo_warn(U_("unable to open %s"), conf_file); goto done; } @@ -749,6 +755,8 @@ sudo_conf_read_v1(const char *conf_file, int conf_types) done: if (fp != NULL) fclose(fp); + else if (fd != -1) + close(fd); free(line); /* Restore locale if needed. */ diff --git a/lib/util/util.exp.in b/lib/util/util.exp.in index 47e776c0d..ced75fb33 100644 --- a/lib/util/util.exp.in +++ b/lib/util/util.exp.in @@ -120,6 +120,7 @@ sudo_mmap_free_v1 sudo_mmap_protect_v1 sudo_mmap_strdup_v1 sudo_new_key_val_v1 +sudo_open_conf_path_v1 sudo_open_parent_dir_v1 sudo_parse_gids_v1 sudo_parseln_v1 @@ -131,6 +132,7 @@ sudo_rcstr_delref sudo_rcstr_dup sudo_regex_compile_v1 sudo_secure_dir_v1 +sudo_secure_fd_v1 sudo_secure_file_v1 sudo_secure_open_dir_v1 sudo_secure_open_file_v1 |