diff --git a/plugins/sudoers/defaults.c b/plugins/sudoers/defaults.c index 5a3d18485..637bf9947 100644 --- a/plugins/sudoers/defaults.c +++ b/plugins/sudoers/defaults.c @@ -688,7 +688,7 @@ default_binding_matches(struct sudoers_parse_tree *parse_tree, debug_return_bool(true); break; case DEFAULTS_CMND: - if (cmndlist_matches(parse_tree, d->binding) == ALLOW) + if (cmndlist_matches(parse_tree, d->binding, NULL) == ALLOW) debug_return_bool(true); break; } diff --git a/plugins/sudoers/editor.c b/plugins/sudoers/editor.c index 3ff08c1ee..99b0b93c8 100644 --- a/plugins/sudoers/editor.c +++ b/plugins/sudoers/editor.c @@ -68,7 +68,8 @@ resolve_editor(const char *ed, size_t edlen, int nfiles, char **files, } /* If we can't find the editor in the user's PATH, give up. */ - if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"), 0, whitelist) != FOUND) { + if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"), NULL, + 0, whitelist) != FOUND) { free(editor); errno = ENOENT; debug_return_str(NULL); diff --git a/plugins/sudoers/find_path.c b/plugins/sudoers/find_path.c index fb04998a8..82943a685 100644 --- a/plugins/sudoers/find_path.c +++ b/plugins/sudoers/find_path.c @@ -43,14 +43,14 @@ * On failure, returns false. */ static bool -cmnd_allowed(char *cmnd, size_t cmnd_size, struct stat *cmnd_sbp, - char * const *whitelist) +cmnd_allowed(char *cmnd, size_t cmnd_size, const char *runchroot, + struct stat *cmnd_sbp, char * const *whitelist) { const char *cmnd_base; char * const *wl; debug_decl(cmnd_allowed, SUDOERS_DEBUG_UTIL); - if (!sudo_goodpath(cmnd, cmnd_sbp)) + if (!sudo_goodpath(cmnd, runchroot, cmnd_sbp)) debug_return_bool(false); if (whitelist == NULL) @@ -62,21 +62,20 @@ cmnd_allowed(char *cmnd, size_t cmnd_size, struct stat *cmnd_sbp, cmnd_base++; for (wl = whitelist; *wl != NULL; wl++) { + const char *base, *path = *wl; struct stat sb; - const char *base; - if ((base = strrchr(*wl, '/')) == NULL) + if ((base = strrchr(path, '/')) == NULL) continue; /* XXX - warn? */ base++; if (strcmp(cmnd_base, base) != 0) continue; - if (sudo_goodpath(*wl, &sb) && + if (sudo_goodpath(path, runchroot, &sb) && sb.st_dev == cmnd_sbp->st_dev && sb.st_ino == cmnd_sbp->st_ino) { /* Overwrite cmnd with safe version from whitelist. */ - if (strlcpy(cmnd, *wl, cmnd_size) < cmnd_size) - return true; + if (strlcpy(cmnd, path, cmnd_size) < cmnd_size) debug_return_bool(true); } } @@ -93,7 +92,8 @@ cmnd_allowed(char *cmnd, size_t cmnd_size, struct stat *cmnd_sbp, */ int find_path(const char *infile, char **outfile, struct stat *sbp, - const char *path, int ignore_dot, char * const *whitelist) + const char *path, const char *runchroot, int ignore_dot, + char * const *whitelist) { char command[PATH_MAX]; const char *cp, *ep, *pathend; @@ -111,7 +111,8 @@ find_path(const char *infile, char **outfile, struct stat *sbp, errno = ENAMETOOLONG; debug_return_int(NOT_FOUND_ERROR); } - found = cmnd_allowed(command, sizeof(command), sbp, whitelist); + found = cmnd_allowed(command, sizeof(command), runchroot, sbp, + whitelist); goto done; } @@ -140,7 +141,8 @@ find_path(const char *infile, char **outfile, struct stat *sbp, errno = ENAMETOOLONG; debug_return_int(NOT_FOUND_ERROR); } - found = cmnd_allowed(command, sizeof(command), sbp, whitelist); + found = cmnd_allowed(command, sizeof(command), runchroot, + sbp, whitelist); if (found) break; } @@ -154,7 +156,8 @@ find_path(const char *infile, char **outfile, struct stat *sbp, errno = ENAMETOOLONG; debug_return_int(NOT_FOUND_ERROR); } - found = cmnd_allowed(command, sizeof(command), sbp, whitelist); + found = cmnd_allowed(command, sizeof(command), runchroot, + sbp, whitelist); if (found && ignore_dot) debug_return_int(NOT_FOUND_DOT); } diff --git a/plugins/sudoers/goodpath.c b/plugins/sudoers/goodpath.c index 4382b8b7f..9f12c40f6 100644 --- a/plugins/sudoers/goodpath.c +++ b/plugins/sudoers/goodpath.c @@ -39,14 +39,24 @@ * Verify that path is a normal file and executable by root. */ bool -sudo_goodpath(const char *path, struct stat *sbp) +sudo_goodpath(const char *path, const char *runchroot, struct stat *sbp) { bool ret = false; debug_decl(sudo_goodpath, SUDOERS_DEBUG_UTIL); if (path != NULL) { + char pathbuf[PATH_MAX]; struct stat sb; + if (runchroot != NULL) { + const int len = + snprintf(pathbuf, sizeof(pathbuf), "%s%s", runchroot, path); + if (len >= ssizeof(pathbuf)) { + errno = ENAMETOOLONG; + goto done; + } + path = pathbuf; + } if (sbp == NULL) sbp = &sb; @@ -58,6 +68,6 @@ sudo_goodpath(const char *path, struct stat *sbp) errno = EACCES; } } - +done: debug_return_bool(ret); } diff --git a/plugins/sudoers/match.c b/plugins/sudoers/match.c index bc6f69605..66bfd2911 100644 --- a/plugins/sudoers/match.c +++ b/plugins/sudoers/match.c @@ -366,14 +366,14 @@ host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, */ int cmndlist_matches(struct sudoers_parse_tree *parse_tree, - const struct member_list *list) + const struct member_list *list, const char *runchroot) { struct member *m; int matched = UNSPEC; debug_decl(cmndlist_matches, SUDOERS_DEBUG_MATCH); TAILQ_FOREACH_REVERSE(m, list, member_list, entries) { - matched = cmnd_matches(parse_tree, m); + matched = cmnd_matches(parse_tree, m, runchroot); if (matched != UNSPEC) break; } @@ -385,7 +385,8 @@ cmndlist_matches(struct sudoers_parse_tree *parse_tree, * Returns ALLOW, DENY or UNSPEC. */ int -cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m) +cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m, + const char *runchroot) { struct alias *a; struct sudo_command *c; @@ -401,13 +402,13 @@ cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m) FALLTHROUGH; case COMMAND: c = (struct sudo_command *)m->name; - if (command_matches(c->cmnd, c->args, &c->digests)) + if (command_matches(c->cmnd, c->args, runchroot, &c->digests)) matched = !m->negated; break; case ALIAS: a = alias_get(parse_tree, m->name, CMNDALIAS); if (a != NULL) { - rc = cmndlist_matches(parse_tree, &a->members); + rc = cmndlist_matches(parse_tree, &a->members, runchroot); if (rc != UNSPEC) matched = m->negated ? !rc : rc; alias_put(a); diff --git a/plugins/sudoers/match_command.c b/plugins/sudoers/match_command.c index c7094b1c4..07769320f 100644 --- a/plugins/sudoers/match_command.c +++ b/plugins/sudoers/match_command.c @@ -84,12 +84,24 @@ command_args_match(const char *sudoers_cmnd, const char *sudoers_args) * Returns true on success, else false. */ static bool -do_stat(int fd, const char *path, struct stat *sb) +do_stat(int fd, const char *path, const char *runchroot, struct stat *sb) { + char pathbuf[PATH_MAX]; debug_decl(do_stat, SUDOERS_DEBUG_MATCH); if (fd != -1) debug_return_bool(fstat(fd, sb) == 0); + + /* Make path relative to the new root, if any. */ + if (runchroot != NULL) { + const int len = + snprintf(pathbuf, sizeof(pathbuf), "%s%s", runchroot, path); + if (len >= ssizeof(pathbuf)) { + errno = ENAMETOOLONG; + debug_return_bool(false); + } + path = pathbuf; + } debug_return_bool(stat(path, sb) == 0); } @@ -119,15 +131,28 @@ is_script(int fd) * Returns false on error, else true. */ static bool -open_cmnd(const char *path, const struct command_digest_list *digests, int *fdp) +open_cmnd(const char *path, const char *runchroot, + const struct command_digest_list *digests, int *fdp) { int fd = -1; + char pathbuf[PATH_MAX]; debug_decl(open_cmnd, SUDOERS_DEBUG_MATCH); /* Only open the file for fdexec or for digest matching. */ if (def_fdexec != always && TAILQ_EMPTY(digests)) debug_return_bool(true); + /* Make path relative to the new root, if any. */ + if (runchroot != NULL) { + const int len = + snprintf(pathbuf, sizeof(pathbuf), "%s%s", runchroot, path); + if (len >= ssizeof(pathbuf)) { + errno = ENAMETOOLONG; + debug_return_bool(false); + } + path = pathbuf; + } + fd = open(path, O_RDONLY|O_NONBLOCK); # ifdef O_EXEC if (fd == -1 && errno == EACCES && TAILQ_EMPTY(digests)) { @@ -189,16 +214,29 @@ set_cmnd_fd(int fd) * Return true if user_cmnd names one of the inodes in dir, else false. */ static bool -command_matches_dir(const char *sudoers_dir, size_t dlen, +command_matches_dir(const char *sudoers_dir, size_t dlen, const char *runchroot, const struct command_digest_list *digests) { + char buf[PATH_MAX], sdbuf[PATH_MAX]; struct stat sudoers_stat; struct dirent *dent; - char buf[PATH_MAX]; + size_t chrootlen = 0; int fd = -1; DIR *dirp; debug_decl(command_matches_dir, SUDOERS_DEBUG_MATCH); + /* Make sudoers_dir relative to the new root, if any. */ + if (runchroot != NULL) { + const int len = + snprintf(sdbuf, sizeof(sdbuf), "%s%s", runchroot, sudoers_dir); + if (len >= ssizeof(sdbuf)) { + errno = ENAMETOOLONG; + debug_return_bool(false); + } + sudoers_dir = sdbuf; + chrootlen = strlen(runchroot); + } + /* * Grot through directory entries, looking for user_base. */ @@ -226,9 +264,9 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, continue; /* Open the file for fdexec or for digest matching. */ - if (!open_cmnd(buf, digests, &fd)) + if (!open_cmnd(buf, NULL, digests, &fd)) continue; - if (!do_stat(fd, buf, &sudoers_stat)) + if (!do_stat(fd, buf, NULL, &sudoers_stat)) continue; if (user_stat == NULL || @@ -237,7 +275,7 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, if (!digest_matches(fd, buf, digests)) continue; free(safe_cmnd); - if ((safe_cmnd = strdup(buf)) == NULL) { + if ((safe_cmnd = strdup(buf + chrootlen)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); dent = NULL; @@ -257,7 +295,8 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, } static bool -command_matches_all(const struct command_digest_list *digests) +command_matches_all(const char *runchroot, + const struct command_digest_list *digests) { struct stat sb; /* XXX - unused */ int fd = -1; @@ -265,9 +304,9 @@ command_matches_all(const struct command_digest_list *digests) if (user_cmnd[0] == '/') { /* Open the file for fdexec or for digest matching. */ - if (!open_cmnd(user_cmnd, digests, &fd)) + if (!open_cmnd(user_cmnd, runchroot, digests, &fd)) goto bad; - if (!do_stat(fd, user_cmnd, &sb)) + if (!do_stat(fd, user_cmnd, runchroot, &sb)) goto bad; } @@ -288,7 +327,7 @@ bad: static bool command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args, - const struct command_digest_list *digests) + const char *runchroot, const struct command_digest_list *digests) { struct stat sb; /* XXX - unused */ int fd = -1; @@ -299,15 +338,17 @@ command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args, * a) there are no args in sudoers OR * b) there are no args on command line and none required by sudoers OR * c) there are args in sudoers and on command line and they match - * else return false. + * else return false. + * + * Neither sudoers_cmnd nor user_cmnd are relative to runchroot. */ if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0) debug_return_bool(false); if (command_args_match(sudoers_cmnd, sudoers_args)) { /* Open the file for fdexec or for digest matching. */ - if (!open_cmnd(user_cmnd, digests, &fd)) + if (!open_cmnd(user_cmnd, runchroot, digests, &fd)) goto bad; - if (!do_stat(fd, user_cmnd, &sb)) + if (!do_stat(fd, user_cmnd, runchroot, &sb)) goto bad; /* Check digest of user_cmnd since sudoers_cmnd is a pattern. */ if (!digest_matches(fd, user_cmnd, digests)) @@ -328,13 +369,14 @@ bad: static bool command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, - const struct command_digest_list *digests) + const char *runchroot, const struct command_digest_list *digests) { struct stat sudoers_stat; bool bad_digest = false; char **ap, *base, *cp; + char pathbuf[PATH_MAX]; int fd = -1; - size_t dlen; + size_t dlen, chrootlen = 0; glob_t gl; debug_decl(command_matches_glob, SUDOERS_DEBUG_MATCH); @@ -351,6 +393,19 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, debug_return_bool(false); } } + + /* Make sudoers_cmnd relative to the new root, if any. */ + if (runchroot != NULL) { + const int len = + snprintf(pathbuf, sizeof(pathbuf), "%s%s", runchroot, sudoers_cmnd); + if (len >= ssizeof(pathbuf)) { + errno = ENAMETOOLONG; + debug_return_bool(false); + } + sudoers_cmnd = pathbuf; + chrootlen = strlen(runchroot); + } + /* * Return true if we find a match in the glob(3) results AND * a) there are no args in sudoers OR @@ -369,12 +424,15 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, close(fd); fd = -1; } + /* Remove the runchroot, if any. */ + cp += chrootlen; + if (strcmp(cp, user_cmnd) != 0) continue; /* Open the file for fdexec or for digest matching. */ - if (!open_cmnd(cp, digests, &fd)) + if (!open_cmnd(cp, runchroot, digests, &fd)) continue; - if (!do_stat(fd, cp, &sudoers_stat)) + if (!do_stat(fd, cp, runchroot, &sudoers_stat)) continue; if (user_stat == NULL || (user_stat->st_dev == sudoers_stat.st_dev && @@ -405,10 +463,13 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, fd = -1; } + /* Remove the runchroot, if any. */ + cp += chrootlen; + /* If it ends in '/' it is a directory spec. */ dlen = strlen(cp); if (cp[dlen - 1] == '/') { - if (command_matches_dir(cp, dlen, digests)) + if (command_matches_dir(cp, dlen, runchroot, digests)) debug_return_bool(true); continue; } @@ -422,9 +483,9 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, continue; /* Open the file for fdexec or for digest matching. */ - if (!open_cmnd(cp, digests, &fd)) + if (!open_cmnd(cp, runchroot, digests, &fd)) continue; - if (!do_stat(fd, cp, &sudoers_stat)) + if (!do_stat(fd, cp, runchroot, &sudoers_stat)) continue; if (user_stat == NULL || (user_stat->st_dev == sudoers_stat.st_dev && @@ -456,7 +517,8 @@ done: } static bool -command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests) +command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, + const char *runchroot, const struct command_digest_list *digests) { struct stat sudoers_stat; const char *base; @@ -467,7 +529,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const /* If it ends in '/' it is a directory spec. */ dlen = strlen(sudoers_cmnd); if (sudoers_cmnd[dlen - 1] == '/') - debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, digests)); + debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, runchroot, digests)); /* Only proceed if user_base and basename(sudoers_cmnd) match */ if ((base = strrchr(sudoers_cmnd, '/')) == NULL) @@ -478,7 +540,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const debug_return_bool(false); /* Open the file for fdexec or for digest matching. */ - if (!open_cmnd(sudoers_cmnd, digests, &fd)) + if (!open_cmnd(sudoers_cmnd, runchroot, digests, &fd)) goto bad; /* @@ -488,7 +550,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const * c) there are args in sudoers and on command line and they match * d) there is a digest and it matches */ - if (user_stat != NULL && do_stat(fd, sudoers_cmnd, &sudoers_stat)) { + if (user_stat != NULL && do_stat(fd, sudoers_cmnd, runchroot, &sudoers_stat)) { if (user_stat->st_dev != sudoers_stat.st_dev || user_stat->st_ino != sudoers_stat.st_ino) goto bad; @@ -521,13 +583,37 @@ bad: * otherwise, return true if user_cmnd names one of the inodes in path. */ bool -command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests) +command_matches(const char *sudoers_cmnd, const char *sudoers_args, + const char *runchroot, const struct command_digest_list *digests) { + char *saved_user_cmnd = NULL; + struct stat saved_user_stat; bool rc = false; debug_decl(command_matches, SUDOERS_DEBUG_MATCH); + if (user_runchroot != NULL) { + if (runchroot != NULL && strcmp(runchroot, "*") != 0 && + strcmp(runchroot, user_runchroot) != 0) { + /* CHROOT mismatch */ + goto done; + } + /* User-specified runchroot (user_stat already set appropriately). */ + runchroot = user_runchroot; + } else if (runchroot == NULL) { + /* No rule-specific runchroot, use global (user_stat already set). */ + if (def_runchroot != NULL && strcmp(def_runchroot, "*") != '\0') + runchroot = def_runchroot; + } else { + /* Rule-specific runchroot, reset user_cmnd and user_stat. */ + saved_user_cmnd = user_cmnd; + if (user_stat != NULL) + saved_user_stat = *user_stat; + if (set_cmnd_path(runchroot) != FOUND) + saved_user_cmnd = NULL; + } + if (sudoers_cmnd == NULL) { - rc = command_matches_all(digests); + rc = command_matches_all(runchroot, digests); goto done; } @@ -554,17 +640,24 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct * use glob(3) and/or fnmatch(3) to do the matching. */ if (def_fast_glob) - rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, digests); + rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, runchroot, digests); else - rc = command_matches_glob(sudoers_cmnd, sudoers_args, digests); + rc = command_matches_glob(sudoers_cmnd, sudoers_args, runchroot, digests); } else { - rc = command_matches_normal(sudoers_cmnd, sudoers_args, digests); + rc = command_matches_normal(sudoers_cmnd, sudoers_args, runchroot, digests); } done: + if (saved_user_cmnd != NULL) { + free(user_cmnd); + user_cmnd = saved_user_cmnd; + if (user_stat != NULL) + *user_stat = saved_user_stat; + } sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, - "user command \"%s%s%s\" matches sudoers command \"%s%s%s\": %s", + "user command \"%s%s%s\" matches sudoers command \"%s%s%s\"%s%s: %s", user_cmnd, user_args ? " " : "", user_args ? user_args : "", sudoers_cmnd, sudoers_args ? " " : "", sudoers_args ? sudoers_args : "", + runchroot ? ", chroot " : "", runchroot ? runchroot : "", rc ? "true" : "false"); debug_return_bool(rc); } diff --git a/plugins/sudoers/parse.c b/plugins/sudoers/parse.c index 3307677a2..b469b959d 100644 --- a/plugins/sudoers/parse.c +++ b/plugins/sudoers/parse.c @@ -92,7 +92,7 @@ sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw, /* Only check the command when listing another user. */ if (user_uid == 0 || list_pw == NULL || user_uid == list_pw->pw_uid || - cmnd_matches(nss->parse_tree, cs->cmnd) == ALLOW) + cmnd_matches(nss->parse_tree, cs->cmnd, cs->runchroot) == ALLOW) match = ALLOW; } } @@ -146,7 +146,8 @@ sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw, cs->runasuserlist, cs->runasgrouplist, &matching_user, NULL); if (runas_match == ALLOW) { - cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd); + cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd, + cs->runchroot); if (cmnd_match != UNSPEC) { /* * If user is running command as himself, @@ -196,6 +197,8 @@ apply_cmndspec(struct cmndspec *cs) } else { user_role = def_role; } + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "user_role -> %s", user_role); } if (user_type == NULL) { if (cs->type != NULL) { @@ -208,6 +211,8 @@ apply_cmndspec(struct cmndspec *cs) } else { user_type = def_type; } + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "user_type -> %s", user_type); } #endif /* HAVE_SELINUX */ #ifdef HAVE_PRIV_SET @@ -223,6 +228,8 @@ apply_cmndspec(struct cmndspec *cs) } else { runas_privs = def_privs; } + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "runas_privs -> %s", runas_privs); } if (runas_limitprivs == NULL) { if (cs->limitprivs != NULL) { @@ -235,10 +242,15 @@ apply_cmndspec(struct cmndspec *cs) } else { runas_limitprivs = def_limitprivs; } + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "runas_limitprivs -> %s", runas_limitprivs); } #endif /* HAVE_PRIV_SET */ - if (cs->timeout > 0) + if (cs->timeout > 0) { def_command_timeout = cs->timeout; + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_command_timeout -> %d", def_command_timeout); + } if (cs->runcwd != NULL) { free(def_runcwd); def_runcwd = strdup(cs->runcwd); @@ -247,6 +259,8 @@ apply_cmndspec(struct cmndspec *cs) U_("unable to allocate memory")); debug_return_bool(false); } + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_runcwd -> %s", def_runcwd); } if (cs->runchroot != NULL) { free(def_runchroot); @@ -256,28 +270,53 @@ apply_cmndspec(struct cmndspec *cs) U_("unable to allocate memory")); debug_return_bool(false); } + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_runchroot -> %s", def_runchroot); } - if (cs->tags.nopasswd != UNSPEC) + if (cs->tags.nopasswd != UNSPEC) { def_authenticate = !cs->tags.nopasswd; - if (cs->tags.noexec != UNSPEC) + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_authenticate -> %s", def_authenticate ? "true" : "false"); + } + if (cs->tags.noexec != UNSPEC) { def_noexec = cs->tags.noexec; - if (cs->tags.setenv != UNSPEC) + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_noexec -> %s", def_noexec ? "true" : "false"); + } + if (cs->tags.setenv != UNSPEC) { def_setenv = cs->tags.setenv; - if (cs->tags.log_input != UNSPEC) + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_setenv -> %s", def_setenv ? "true" : "false"); + } + if (cs->tags.log_input != UNSPEC) { def_log_input = cs->tags.log_input; - if (cs->tags.log_output != UNSPEC) + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_log_input -> %s", def_log_input ? "true" : "false"); + } + if (cs->tags.log_output != UNSPEC) { def_log_output = cs->tags.log_output; + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_log_output -> %s", def_log_output ? "true" : "false"); + } if (cs->tags.send_mail != UNSPEC) { if (cs->tags.send_mail) { def_mail_all_cmnds = true; + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_mail_all_cmnds -> true"); } else { def_mail_all_cmnds = false; def_mail_always = false; def_mail_no_perms = false; + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_mail_all_cmnds -> false, def_mail_always -> false, " + "def_mail_no_perms -> false"); } } - if (cs->tags.follow != UNSPEC) + if (cs->tags.follow != UNSPEC) { def_sudoedit_follow = cs->tags.follow; + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_sudoedit_follow -> %s", def_sudoedit_follow ? "true" : "false"); + } } debug_return_bool(true); @@ -836,7 +875,8 @@ display_cmnd_check(struct sudoers_parse_tree *parse_tree, struct passwd *pw, runas_match = runaslist_matches(parse_tree, cs->runasuserlist, cs->runasgrouplist, NULL, NULL); if (runas_match == ALLOW) { - cmnd_match = cmnd_matches(parse_tree, cs->cmnd); + cmnd_match = cmnd_matches(parse_tree, cs->cmnd, + cs->runchroot); if (cmnd_match != UNSPEC) debug_return_int(cmnd_match); } diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h index 7c4fa9350..6fc23d7c4 100644 --- a/plugins/sudoers/parse.h +++ b/plugins/sudoers/parse.h @@ -312,7 +312,7 @@ void reparent_parse_tree(struct sudoers_parse_tree *new_tree); bool addr_matches(char *n); /* match_command.c */ -bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests); +bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const char *runchroot, const struct command_digest_list *digests); /* match_digest.c */ bool digest_matches(int fd, const char *file, const struct command_digest_list *digests); @@ -325,8 +325,8 @@ bool hostname_matches(const char *shost, const char *lhost, const char *pattern) bool netgr_matches(const char *netgr, const char *lhost, const char *shost, const char *user); bool usergr_matches(const char *group, const char *user, const struct passwd *pw); bool userpw_matches(const char *sudoers_user, const char *user, const struct passwd *pw); -int cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m); -int cmndlist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *list); +int cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m, const char *runchroot); +int cmndlist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *list, const char *runchroot); int host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const char *host, const char *shost, const struct member *m); int hostlist_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const struct member_list *list); int runaslist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *user_list, const struct member_list *group_list, struct member **matching_user, struct member **matching_group); diff --git a/plugins/sudoers/stubs.c b/plugins/sudoers/stubs.c index 7a7b1436c..e2c671e36 100644 --- a/plugins/sudoers/stubs.c +++ b/plugins/sudoers/stubs.c @@ -80,6 +80,13 @@ get_interfaces(void) return &dummy; } +/* STUB */ +int +set_cmnd_path(const char *runchroot) +{ + return FOUND; +} + /* * Look up the hostname and set user_host and user_shost. */ diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c index 593c4ff59..933279b09 100644 --- a/plugins/sudoers/sudoers.c +++ b/plugins/sudoers/sudoers.c @@ -275,6 +275,59 @@ done: debug_return_str(iolog_path); } +static int +check_runchroot(void) +{ + debug_decl(check_runchroot, SUDOERS_DEBUG_PLUGIN); + + if (user_runchroot == NULL) + debug_return_bool(true); + + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_runchroot %s, user_runchroot %s", + def_runchroot ? def_runchroot : "NULL", user_runchroot); + + if (def_runchroot == NULL || (strcmp(def_runchroot, "*") != 0 && + strcmp(def_runchroot, user_runchroot) != 0)) { + audit_failure(NewArgv, + N_("user not allowed to change root directory to %s"), + user_runchroot); + sudo_warnx("%s", U_("you are not permitted to use the -R option")); + debug_return_bool(false); + } + free(def_runchroot); + if ((def_runchroot = strdup(user_runchroot)) == NULL) { + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + debug_return_bool(-1); + } + debug_return_bool(true); +} + +static int +check_runcwd(void) +{ + debug_decl(check_runcwd, SUDOERS_DEBUG_PLUGIN); + + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "def_runcwd %s, user_runcwd %s, user_cwd %s", + def_runcwd, user_runcwd, user_cwd); + + if (strcmp(user_cwd, user_runcwd) != 0) { + if (def_runcwd == NULL || strcmp(def_runcwd, "*") != 0) { + audit_failure(NewArgv, + N_("user not allowed to change directory to %s"), user_runcwd); + sudo_warnx("%s", U_("you are not permitted to use the -D option")); + debug_return_bool(false); + } + free(def_runcwd); + if ((def_runcwd = strdup(user_runcwd)) == NULL) { + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + debug_return_int(-1); + } + } + debug_return_bool(true); +} + int sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], bool verbose, void *closure) @@ -396,32 +449,24 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], } } - if (user_runchroot != NULL) { - if (def_runchroot == NULL || strcmp(def_runchroot, "*") != 0) { - audit_failure(NewArgv, - N_("user not allowed to change root directory to %s"), - user_runchroot); - sudo_warnx("%s", U_("you are not permitted to use the -R option")); - goto bad; - } - free(def_runchroot); - if ((def_runchroot = strdup(user_runchroot)) == NULL) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - goto done; - } + /* Check whether user_runchroot is permitted (if specified). */ + switch (check_runchroot()) { + case true: + break; + case false: + goto bad; + default: + goto done; } - if (strcmp(user_cwd, user_runcwd) != 0) { - if (def_runcwd == NULL || strcmp(def_runcwd, "*") != 0) { - audit_failure(NewArgv, - N_("user not allowed to change directory to %s"), user_runcwd); - sudo_warnx("%s", U_("you are not permitted to use the -D option")); - goto bad; - } - free(def_runcwd); - if ((def_runcwd = strdup(user_runcwd)) == NULL) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - goto done; - } + + /* Check whether user_runcwd is permitted (if specified). */ + switch (check_runcwd()) { + case true: + break; + case false: + goto bad; + default: + goto done; } /* @@ -861,6 +906,38 @@ init_vars(char * const envp[]) debug_return_bool(true); } +/* + * Fill in user_cmnd and user_stat variables. + * Does not fill in user_base. + */ +int +set_cmnd_path(const char *runchroot) +{ + char *path = user_path; + int ret; + debug_decl(set_cmnd_path, SUDOERS_DEBUG_PLUGIN); + + if (def_secure_path && !user_is_exempt()) + path = def_secure_path; + if (!set_perms(PERM_RUNAS)) + debug_return_int(NOT_FOUND_ERROR); + ret = find_path(NewArgv[0], &user_cmnd, user_stat, path, + runchroot, def_ignore_dot, NULL); + if (!restore_perms()) + debug_return_int(NOT_FOUND_ERROR); + if (ret == NOT_FOUND) { + /* Failed as root, try as invoking user. */ + if (!set_perms(PERM_USER)) + debug_return_int(false); + ret = find_path(NewArgv[0], &user_cmnd, user_stat, path, + runchroot, def_ignore_dot, NULL); + if (!restore_perms()) + debug_return_int(NOT_FOUND_ERROR); + } + + debug_return_int(ret); +} + /* * Fill in user_cmnd, user_args, user_base and user_stat variables * and apply any command-specific defaults entries. @@ -869,7 +946,6 @@ static int set_cmnd(void) { struct sudo_nss *nss; - char *path = user_path; int ret = FOUND; debug_decl(set_cmnd, SUDOERS_DEBUG_PLUGIN); @@ -886,23 +962,12 @@ set_cmnd(void) if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) { if (ISSET(sudo_mode, MODE_RUN | MODE_CHECK)) { - if (def_secure_path && !user_is_exempt()) - path = def_secure_path; - if (!set_perms(PERM_RUNAS)) - debug_return_int(-1); - ret = find_path(NewArgv[0], &user_cmnd, user_stat, path, - def_ignore_dot, NULL); - if (!restore_perms()) - debug_return_int(-1); - if (ret == NOT_FOUND) { - /* Failed as root, try as invoking user. */ - if (!set_perms(PERM_USER)) - debug_return_int(-1); - ret = find_path(NewArgv[0], &user_cmnd, user_stat, path, - def_ignore_dot, NULL); - if (!restore_perms()) - debug_return_int(-1); - } + const char *runchroot = user_runchroot; + if (runchroot == NULL && def_runchroot != NULL && + strcmp(def_runchroot, "*") != 0) + runchroot = def_runchroot; + + ret = set_cmnd_path(runchroot); if (ret == NOT_FOUND_ERROR) { if (errno == ENAMETOOLONG) { audit_failure(NewArgv, N_("command too long")); @@ -922,7 +987,7 @@ set_cmnd(void) size += strlen(*av) + 1; if (size == 0 || (user_args = malloc(size)) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - debug_return_int(-1); + debug_return_int(NOT_FOUND_ERROR); } if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) { /* @@ -944,7 +1009,7 @@ set_cmnd(void) n = strlcpy(to, *av, size - (to - user_args)); if (n >= size - (to - user_args)) { sudo_warnx(U_("internal error, %s overflow"), __func__); - debug_return_int(-1); + debug_return_int(NOT_FOUND_ERROR); } to += n; *to++ = ' '; diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index 0c493cc82..1dae3e7a3 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -263,11 +263,12 @@ struct timespec; #define YY_DECL int sudoerslex(void) /* goodpath.c */ -bool sudo_goodpath(const char *path, struct stat *sbp); +bool sudo_goodpath(const char *path, const char *runchroot, struct stat *sbp); /* findpath.c */ int find_path(const char *infile, char **outfile, struct stat *sbp, - const char *path, int ignore_dot, char * const *whitelist); + const char *path, const char *runchroot, int ignore_dot, + char * const *whitelist); /* check.c */ int check_user(int validate, int mode); @@ -394,6 +395,7 @@ bool matches_env_pattern(const char *pattern, const char *var, bool *full_match) /* sudoers.c */ FILE *open_sudoers(const char *, bool, bool *); +int set_cmnd_path(const char *runchroot); int sudoers_init(void *info, char * const envp[]); int sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], bool verbose, void *closure); void sudoers_cleanup(void); diff --git a/plugins/sudoers/testsudoers.c b/plugins/sudoers/testsudoers.c index effbd60f7..e88e5f1a0 100644 --- a/plugins/sudoers/testsudoers.c +++ b/plugins/sudoers/testsudoers.c @@ -336,7 +336,8 @@ main(int argc, char *argv[]) cs->runasuserlist, cs->runasgrouplist, NULL, NULL); if (runas_match == ALLOW) { puts("\trunas matched"); - cmnd_match = cmnd_matches(&parsed_policy, cs->cmnd); + cmnd_match = cmnd_matches(&parsed_policy, cs->cmnd, + cs->runchroot); if (cmnd_match != UNSPEC) match = cmnd_match; printf("\tcmnd %s\n", match == ALLOW ? "allowed" : @@ -499,6 +500,12 @@ restore_perms(void) return true; } +int +set_cmnd_path(const char *runchroot) +{ + return FOUND; +} + static bool print_defaults(struct sudo_lbuf *lbuf) {