summaryrefslogtreecommitdiff
path: root/lib/util
diff options
context:
space:
mode:
Diffstat (limited to 'lib/util')
-rw-r--r--lib/util/Makefile.in2
-rw-r--r--lib/util/secure_path.c58
-rw-r--r--lib/util/sudo_conf.c94
-rw-r--r--lib/util/util.exp.in2
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