diff --git a/src/edit_open.c b/src/edit_open.c index a30c5f4d1..3bbcc2cd8 100644 --- a/src/edit_open.c +++ b/src/edit_open.c @@ -47,7 +47,7 @@ switch_user(uid_t euid, gid_t egid, int ngroups, GETGROUPS_T *groups) sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "set uid:gid to %u:%u(%u)", (unsigned int)euid, (unsigned int)egid, - ngroups ? (unsigned int)groups[0] : (unsigned int)egid); + ngroups > 0 ? (unsigned int)groups[0] : (unsigned int)egid); /* When restoring root, change euid first; otherwise change it last. */ if (euid == ROOT_UID) { @@ -74,7 +74,7 @@ switch_user(uid_t euid, gid_t egid, int ngroups, GETGROUPS_T *groups) * Returns true if the open directory fd is owned or writable by the user. */ int -dir_is_writable(int dfd, struct user_details *ud, struct command_details *cd) +dir_is_writable(int dfd, struct sudo_cred *user_cred, struct sudo_cred *run_cred) { struct stat sb; int rc; @@ -84,29 +84,31 @@ dir_is_writable(int dfd, struct user_details *ud, struct command_details *cd) debug_return_int(-1); /* If the user owns the dir we always consider it writable. */ - if (sb.st_uid == ud->uid) { + if (sb.st_uid == user_cred->uid) { sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, - "user uid %u matches directory uid %u", (unsigned int)ud->uid, - (unsigned int)sb.st_uid); + "user uid %u matches directory uid %u", + (unsigned int)user_cred->uid, (unsigned int)sb.st_uid); debug_return_int(true); } /* Change uid/gid/groups to invoking user, usually needs root perms. */ - if (cd->euid != ROOT_UID) { + if (run_cred->euid != ROOT_UID) { if (seteuid(ROOT_UID) != 0) sudo_fatal("seteuid(ROOT_UID)"); } - switch_user(ud->uid, ud->gid, ud->ngroups, ud->groups); + switch_user(user_cred->uid, user_cred->gid, user_cred->ngroups, + user_cred->groups); /* Access checks are done using the euid/egid and group vector. */ rc = faccessat(dfd, ".", W_OK, AT_EACCESS); /* Change uid/gid/groups back to target user, may need root perms. */ - if (ud->uid != ROOT_UID) { + if (user_cred->uid != ROOT_UID) { if (seteuid(ROOT_UID) != 0) sudo_fatal("seteuid(ROOT_UID)"); } - switch_user(cd->euid, cd->egid, cd->ngroups, cd->groups); + switch_user(run_cred->euid, run_cred->egid, run_cred->ngroups, + run_cred->groups); if (rc == 0) debug_return_int(true); @@ -116,21 +118,21 @@ dir_is_writable(int dfd, struct user_details *ud, struct command_details *cd) } #else static bool -group_matches(gid_t target, gid_t gid, int ngroups, GETGROUPS_T *groups) +group_matches(gid_t target, struct sudo_cred *cred) { int i; debug_decl(group_matches, SUDO_DEBUG_EDIT); - if (target == gid) { + if (target == cred->gid) { sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, - "user gid %u matches directory gid %u", (unsigned int)gid, + "user gid %u matches directory gid %u", (unsigned int)cred->gid, (unsigned int)target); debug_return_bool(true); } - for (i = 0; i < ngroups; i++) { - if (target == groups[i]) { + for (i = 0; i < cred->ngroups; i++) { + if (target == cred->groups[i]) { sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, - "user gid %u matches directory gid %u", (unsigned int)gid, + "user gid %u matches directory gid %u", cred->groups[i], (unsigned int)target); debug_return_bool(true); } @@ -142,7 +144,7 @@ group_matches(gid_t target, gid_t gid, int ngroups, GETGROUPS_T *groups) * Returns true if the open directory fd is owned or writable by the user. */ int -dir_is_writable(int dfd, struct user_details *ud, struct command_details *cd) +dir_is_writable(int dfd, struct sudo_cred *user_cred, struct sudo_cred *run_cred) { struct stat sb; debug_decl(dir_is_writable, SUDO_DEBUG_EDIT); @@ -151,10 +153,10 @@ dir_is_writable(int dfd, struct user_details *ud, struct command_details *cd) debug_return_int(-1); /* If the user owns the dir we always consider it writable. */ - if (sb.st_uid == ud->uid) { + if (sb.st_uid == user_cred->uid) { sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, - "user uid %u matches directory uid %u", (unsigned int)ud->uid, - (unsigned int)sb.st_uid); + "user uid %u matches directory uid %u", + (unsigned int)user_cred->uid, (unsigned int)sb.st_uid); debug_return_int(true); } @@ -167,7 +169,7 @@ dir_is_writable(int dfd, struct user_details *ud, struct command_details *cd) /* Group writable? */ if (ISSET(sb.st_mode, S_IWGRP)) { - if (group_matches(sb.st_gid, ud->gid, ud->ngroups, ud->groups)) { + if (group_matches(sb.st_gid, user_cred)) { sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "directory is writable by one of the user's groups"); debug_return_int(true); @@ -270,7 +272,7 @@ done: static int sudo_edit_open_nonwritable(char *path, int oflags, mode_t mode, - struct user_details *ud, struct command_details *cd) + struct sudo_cred *user_cred, struct sudo_cred *run_cred) { const int dflags = DIR_OPEN_FLAGS; int dfd, fd, is_writable; @@ -295,7 +297,7 @@ sudo_edit_open_nonwritable(char *path, int oflags, mode_t mode, * Look up one component at a time, avoiding symbolic links in * writable directories. */ - is_writable = dir_is_writable(dfd, ud, cd); + is_writable = dir_is_writable(dfd, user_cred, run_cred); if (is_writable == -1) { close(dfd); debug_return_int(-1); @@ -336,17 +338,17 @@ sudo_edit_open_nonwritable(char *path, int oflags, mode_t mode, #ifdef O_NOFOLLOW int -sudo_edit_open(char *path, int oflags, mode_t mode, struct user_details *ud, - struct command_details *cd) +sudo_edit_open(char *path, int oflags, mode_t mode, int sflags, + struct sudo_cred *user_cred, struct sudo_cred *run_cred) { - const int sflags = cd ? cd->flags : 0; int fd; debug_decl(sudo_edit_open, SUDO_DEBUG_EDIT); if (!ISSET(sflags, CD_SUDOEDIT_FOLLOW)) oflags |= O_NOFOLLOW; - if (ISSET(sflags, CD_SUDOEDIT_CHECKDIR) && ud->uid != ROOT_UID) { - fd = sudo_edit_open_nonwritable(path, oflags|O_NONBLOCK, mode, ud, cd); + if (ISSET(sflags, CD_SUDOEDIT_CHECKDIR) && user_cred->uid != ROOT_UID) { + fd = sudo_edit_open_nonwritable(path, oflags|O_NONBLOCK, mode, + user_cred, run_cred); } else { fd = open(path, oflags|O_NONBLOCK, mode); } @@ -356,10 +358,9 @@ sudo_edit_open(char *path, int oflags, mode_t mode, struct user_details *ud, } #else int -sudo_edit_open(char *path, int oflags, mode_t mode, struct user_details *ud, - struct command_details *cd) +sudo_edit_open(char *path, int oflags, mode_t mode, int sflags, + struct sudo_cred *user_cred, struct sudo_cred *run_cred) { - const int sflags = cd ? cd->flags : 0; struct stat sb; int fd; debug_decl(sudo_edit_open, SUDO_DEBUG_EDIT); @@ -377,8 +378,9 @@ sudo_edit_open(char *path, int oflags, mode_t mode, struct user_details *ud, } } - if (ISSET(sflags, CD_SUDOEDIT_CHECKDIR) && ud->uid != ROOT_UID) { - fd = sudo_edit_open_nonwritable(path, oflags|O_NONBLOCK, mode, ud, cd); + if (ISSET(sflags, CD_SUDOEDIT_CHECKDIR) && user_cred->uid != ROOT_UID) { + fd = sudo_edit_open_nonwritable(path, oflags|O_NONBLOCK, mode, + user_cred, run_cred); } else { fd = open(path, oflags|O_NONBLOCK, mode); } @@ -409,11 +411,10 @@ sudo_edit_open(char *path, int oflags, mode_t mode, struct user_details *ud, * Does not modify the value of errno. */ bool -sudo_edit_parent_valid(char *path, struct user_details *ud, - struct command_details *cd) +sudo_edit_parent_valid(char *path, int sflags, struct sudo_cred *user_cred, + struct sudo_cred *run_cred) { const int serrno = errno; - const int sflags = cd->flags; struct stat sb; bool ret = false; char *slash; @@ -436,10 +437,8 @@ sudo_edit_parent_valid(char *path, struct user_details *ud, * The parent directory is allowed to be a symbolic link unless * *its* parent is writable and CD_SUDOEDIT_CHECK is set. */ - SET(cd->flags, CD_SUDOEDIT_FOLLOW); dfd = sudo_edit_open(path, DIR_OPEN_FLAGS, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, - ud, cd); - cd->flags = sflags; + sflags|CD_SUDOEDIT_FOLLOW, user_cred, run_cred); if (dfd != -1) { if (fstat(dfd, &sb) == 0 && S_ISDIR(sb.st_mode)) ret = true; diff --git a/src/exec.c b/src/exec.c index 3c4e749c1..ffee016aa 100644 --- a/src/exec.c +++ b/src/exec.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2009-2020 Todd C. Miller + * Copyright (c) 2009-2021 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -179,22 +179,22 @@ exec_setup(struct command_details *details, int errfd) unlimit_nproc(); #if defined(HAVE_SETRESUID) - if (setresuid(details->uid, details->euid, details->euid) != 0) { + if (setresuid(details->cred.uid, details->cred.euid, details->cred.euid) != 0) { sudo_warn(U_("unable to change to runas uid (%u, %u)"), - (unsigned int)details->uid, (unsigned int)details->euid); + (unsigned int)details->cred.uid, (unsigned int)details->cred.euid); goto done; } #elif defined(HAVE_SETREUID) - if (setreuid(details->uid, details->euid) != 0) { + if (setreuid(details->cred.uid, details->cred.euid) != 0) { sudo_warn(U_("unable to change to runas uid (%u, %u)"), - (unsigned int)details->uid, (unsigned int)details->euid); + (unsigned int)details->cred.uid, (unsigned int)details->cred.euid); goto done; } #else /* Cannot support real user-ID that is different from effective user-ID. */ - if (setuid(details->euid) != 0) { + if (setuid(details->cred.euid) != 0) { sudo_warn(U_("unable to change to runas uid (%u, %u)"), - (unsigned int)details->euid, (unsigned int)details->euid); + (unsigned int)details->cred.euid, (unsigned int)details->cred.euid); goto done; } #endif /* !HAVE_SETRESUID && !HAVE_SETREUID */ diff --git a/src/exec_pty.c b/src/exec_pty.c index 40ddbeb98..cfe4929dd 100644 --- a/src/exec_pty.c +++ b/src/exec_pty.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2009-2020 Todd C. Miller + * Copyright (c) 2009-2021 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -148,7 +148,7 @@ pty_setup(struct command_details *details, const char *tty) } if (!get_pty(&io_fds[SFD_LEADER], &io_fds[SFD_FOLLOWER], - ptyname, sizeof(ptyname), details->euid)) + ptyname, sizeof(ptyname), details->cred.euid)) sudo_fatal("%s", U_("unable to allocate pty")); /* Update tty name in command details (used by SELinux and AIX). */ diff --git a/src/sesh.c b/src/sesh.c index 3a1b596eb..4efe06556 100644 --- a/src/sesh.c +++ b/src/sesh.c @@ -131,7 +131,7 @@ main(int argc, char *argv[], char *envp[]) * On success, fills in ud and returns true, else false. */ static bool -parse_user(char *userstr, struct user_details *ud) +parse_user(char *userstr, struct sudo_cred *cred) { char *cp, *ep; const char *errstr; @@ -144,7 +144,7 @@ parse_user(char *userstr, struct user_details *ud) debug_return_bool(false); } *ep++ = '\0'; - ud->uid = sudo_strtoid(cp, &errstr); + cred->uid = cred->euid = sudo_strtoid(cp, &errstr); if (errstr != NULL) { sudo_warnx(U_("%s: %s"), cp, errstr); debug_return_bool(false); @@ -157,7 +157,7 @@ parse_user(char *userstr, struct user_details *ud) debug_return_bool(false); } *ep++ = '\0'; - ud->gid = sudo_strtoid(cp, &errstr); + cred->gid = cred->egid = sudo_strtoid(cp, &errstr); if (errstr != NULL) { sudo_warnx(U_("%s: %s"), cp, errstr); debug_return_bool(false); @@ -165,8 +165,8 @@ parse_user(char *userstr, struct user_details *ud) /* group vector */ cp = ep; - ud->ngroups = sudo_parse_gids(cp, NULL, &ud->groups); - if (ud->ngroups == -1) + cred->ngroups = sudo_parse_gids(cp, NULL, &cred->groups); + if (cred->ngroups == -1) debug_return_bool(false); debug_return_bool(true); @@ -176,30 +176,29 @@ static int sesh_sudoedit(int argc, char *argv[]) { int i, post, ret = SESH_ERR_FAILURE; - int fd_src = -1, fd_dst = -1; - struct command_details command_details; - struct user_details edit_user; + int edit_flags, fd_src = -1, fd_dst = -1; + struct sudo_cred user_cred, run_cred; struct timespec times[2]; struct stat sb; debug_decl(sesh_sudoedit, SUDO_DEBUG_EDIT); - memset(&edit_user, 0, sizeof(edit_user)); - memset(&command_details, 0, sizeof(command_details)); - command_details.flags = CD_SUDOEDIT_FOLLOW; + memset(&user_cred, 0, sizeof(user_cred)); + memset(&run_cred, 0, sizeof(run_cred)); + edit_flags = CD_SUDOEDIT_FOLLOW; /* Check for -h flag (don't follow links). */ if (argv[2] != NULL && strcmp(argv[2], "-h") == 0) { argv++; argc--; - CLR(command_details.flags, CD_SUDOEDIT_FOLLOW); + CLR(edit_flags, CD_SUDOEDIT_FOLLOW); } /* Check for -w flag (disallow directories writable by the user). */ if (argv[2] != NULL && strcmp(argv[2], "-w") == 0) { - SET(command_details.flags, CD_SUDOEDIT_CHECKDIR); + SET(edit_flags, CD_SUDOEDIT_CHECKDIR); /* Parse uid:gid:gid1,gid2,... */ - if (argv[3] == NULL || !parse_user(argv[3], &edit_user)) + if (argv[3] == NULL || !parse_user(argv[3], &user_cred)) debug_return_int(SESH_ERR_FAILURE); argv += 2; argc -= 2; @@ -237,18 +236,18 @@ sesh_sudoedit(int argc, char *argv[]) * sudoedit runs us with the effective user-ID and group-ID of * the target user as well as with the target user's group list. */ - command_details.uid = command_details.euid = geteuid(); - command_details.gid = command_details.egid = getegid(); - command_details.ngroups = getgroups(0, NULL); - if (command_details.ngroups > 0) { - command_details.groups = reallocarray(NULL, command_details.ngroups, + run_cred.uid = run_cred.euid = geteuid(); + run_cred.gid = run_cred.egid = getegid(); + run_cred.ngroups = getgroups(0, NULL); + if (run_cred.ngroups > 0) { + run_cred.groups = reallocarray(NULL, run_cred.ngroups, sizeof(GETGROUPS_T)); - if (command_details.groups == NULL) { + if (run_cred.groups == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_int(SESH_ERR_FAILURE); } - if (getgroups(command_details.ngroups, command_details.groups) < 0) { + if (getgroups(run_cred.ngroups, run_cred.groups) < 0) { sudo_warn("%s", U_("unable to get group list")); debug_return_int(SESH_ERR_FAILURE); } @@ -265,7 +264,7 @@ sesh_sudoedit(int argc, char *argv[]) fd_src = open(path_src, O_RDONLY|O_NONBLOCK|O_NOFOLLOW); } else { fd_src = sudo_edit_open(path_src, post ? O_RDONLY|O_NOFOLLOW : O_RDONLY, - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, &edit_user, &command_details); + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, &user_cred, &run_cred); } if (fd_src == -1) { if (post || errno != ENOENT) { @@ -285,12 +284,12 @@ sesh_sudoedit(int argc, char *argv[]) goto cleanup_0; } /* New file, verify parent dir exists and is not writable. */ - if (!sudo_edit_parent_valid(path_src, &edit_user, &command_details)) + if (!sudo_edit_parent_valid(path_src, edit_flags, &user_cred, &run_cred)) goto cleanup_0; } if (post) { /* Make sure the temporary file is safe and has the proper owner. */ - if (!sudo_check_temp_file(fd_src, path_src, command_details.uid, &sb)) { + if (!sudo_check_temp_file(fd_src, path_src, run_cred.uid, &sb)) { ret = SESH_ERR_SOME_FILES; goto nocleanup; } @@ -298,7 +297,8 @@ sesh_sudoedit(int argc, char *argv[]) /* Create destination file. */ fd_dst = sudo_edit_open(path_dst, O_WRONLY|O_CREAT, - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, &edit_user, &command_details); + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, edit_flags, &user_cred, + &run_cred); } else { if (fd_src == -1) { /* New file. */ diff --git a/src/sudo.c b/src/sudo.c index e4669b305..f2d8d666e 100644 --- a/src/sudo.c +++ b/src/sudo.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2009-2020 Todd C. Miller + * Copyright (c) 2009-2021 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -220,7 +220,7 @@ main(int argc, char *argv[], char *envp[]) /* Print sudo version early, in case of plugin init failure. */ if (ISSET(sudo_mode, MODE_VERSION)) { printf(_("Sudo version %s\n"), PACKAGE_VERSION); - if (user_details.uid == ROOT_UID) + if (user_details.cred.uid == ROOT_UID) (void) printf(_("Configure options: %s\n"), CONFIGURE_ARGS); } @@ -242,12 +242,12 @@ main(int argc, char *argv[], char *envp[]) switch (sudo_mode & MODE_MASK) { case MODE_VERSION: - policy_show_version(!user_details.uid); - iolog_show_version(!user_details.uid, settings, user_info, + policy_show_version(!user_details.cred.uid); + iolog_show_version(!user_details.cred.uid, settings, user_info, nargc, nargv, envp); - approval_show_version(!user_details.uid, settings, user_info, + approval_show_version(!user_details.cred.uid, settings, user_info, submit_optind, argv, envp); - audit_show_version(!user_details.uid); + audit_show_version(!user_details.cred.uid); break; case MODE_VALIDATE: case MODE_VALIDATE|MODE_INVALIDATE: @@ -383,7 +383,7 @@ fix_fds(void) * Returns 0 on success and -1 on failure. */ static int -fill_group_list(struct user_details *ud) +fill_group_list(const char *user, struct sudo_cred *cred) { int ret = -1; debug_decl(fill_group_list, SUDO_DEBUG_UTIL); @@ -392,41 +392,41 @@ fill_group_list(struct user_details *ud) * If user specified a max number of groups, use it, otherwise let * sudo_getgrouplist2() allocate the group vector. */ - ud->ngroups = sudo_conf_max_groups(); - if (ud->ngroups > 0) { - ud->groups = reallocarray(NULL, ud->ngroups, sizeof(GETGROUPS_T)); - if (ud->groups != NULL) { + cred->ngroups = sudo_conf_max_groups(); + if (cred->ngroups > 0) { + cred->groups = reallocarray(NULL, cred->ngroups, sizeof(GETGROUPS_T)); + if (cred->groups != NULL) { /* No error on insufficient space if user specified max_groups. */ - (void)sudo_getgrouplist2(ud->username, ud->gid, &ud->groups, - &ud->ngroups); + (void)sudo_getgrouplist2(user, cred->gid, + &cred->groups, &cred->ngroups); ret = 0; } } else { - ud->groups = NULL; - ret = sudo_getgrouplist2(ud->username, ud->gid, &ud->groups, - &ud->ngroups); + cred->groups = NULL; + ret = sudo_getgrouplist2(user, cred->gid, &cred->groups, + &cred->ngroups); } if (ret == -1) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, "%s: %s: unable to get groups via sudo_getgrouplist2()", - __func__, ud->username); + __func__, user); } else { sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %s: got %d groups via sudo_getgrouplist2()", - __func__, ud->username, ud->ngroups); + __func__, user, cred->ngroups); } debug_return_int(ret); } static char * -get_user_groups(struct user_details *ud) +get_user_groups(const char *user, struct sudo_cred *cred) { char *cp, *gid_list = NULL; size_t glsize; int i, len, group_source; debug_decl(get_user_groups, SUDO_DEBUG_UTIL); - ud->groups = NULL; + cred->groups = NULL; group_source = sudo_conf_group_source(); if (group_source != GROUP_SOURCE_DYNAMIC) { int maxgroups = (int)sysconf(_SC_NGROUPS_MAX); @@ -434,46 +434,46 @@ get_user_groups(struct user_details *ud) maxgroups = NGROUPS_MAX; /* Note that macOS may return ngroups > NGROUPS_MAX. */ - if ((ud->ngroups = getgroups(0, NULL)) > 0) { + if ((cred->ngroups = getgroups(0, NULL)) > 0) { /* Use groups from kernel if not at limit or source is static. */ - if (ud->ngroups != maxgroups || group_source == GROUP_SOURCE_STATIC) { - ud->groups = reallocarray(NULL, ud->ngroups, sizeof(GETGROUPS_T)); - if (ud->groups == NULL) + if (cred->ngroups != maxgroups || group_source == GROUP_SOURCE_STATIC) { + cred->groups = reallocarray(NULL, cred->ngroups, sizeof(GETGROUPS_T)); + if (cred->groups == NULL) goto done; - if (getgroups(ud->ngroups, ud->groups) < 0) { + if (getgroups(cred->ngroups, cred->groups) < 0) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, - "%s: %s: unable to get %d groups via getgroups()", - __func__, ud->username, ud->ngroups); - free(ud->groups); - ud->groups = NULL; + "%s: unable to get %d groups via getgroups()", + __func__, cred->ngroups); + free(cred->groups); + cred->groups = NULL; } else { sudo_debug_printf(SUDO_DEBUG_INFO, - "%s: %s: got %d groups via getgroups()", - __func__, ud->username, ud->ngroups); + "%s: got %d groups via getgroups()", + __func__, cred->ngroups); } } } } - if (ud->groups == NULL) { + if (cred->groups == NULL) { /* * Query group database if kernel list is too small or disabled. * Typically, this is because NFS can only support up to 16 groups. */ - if (fill_group_list(ud) == -1) + if (fill_group_list(user, cred) == -1) goto done; } /* * Format group list as a comma-separated string of gids. */ - glsize = sizeof("groups=") - 1 + (ud->ngroups * (MAX_UID_T_LEN + 1)); + glsize = sizeof("groups=") - 1 + (cred->ngroups * (MAX_UID_T_LEN + 1)); if ((gid_list = malloc(glsize)) == NULL) goto done; memcpy(gid_list, "groups=", sizeof("groups=") - 1); cp = gid_list + sizeof("groups=") - 1; - for (i = 0; i < ud->ngroups; i++) { + for (i = 0; i < cred->ngroups; i++) { len = snprintf(cp, glsize - (cp - gid_list), "%s%u", - i ? "," : "", (unsigned int)ud->groups[i]); + i ? "," : "", (unsigned int)cred->groups[i]); if (len < 0 || (size_t)len >= glsize - (cp - gid_list)) sudo_fatalx(U_("internal error, %s overflow"), __func__); cp += len; @@ -529,15 +529,15 @@ get_user_info(struct user_details *ud) if ((ud->sid = getsid(0)) == -1) ud->sid = 0; - ud->uid = getuid(); - ud->euid = geteuid(); - ud->gid = getgid(); - ud->egid = getegid(); + ud->cred.uid = getuid(); + ud->cred.euid = geteuid(); + ud->cred.gid = getgid(); + ud->cred.egid = getegid(); #ifdef HAVE_SETAUTHDB - aix_setauthdb(IDtouser(ud->uid), NULL); + aix_setauthdb(IDtouser(ud->cred.uid), NULL); #endif - pw = getpwuid(ud->uid); + pw = getpwuid(ud->cred.uid); #ifdef HAVE_SETAUTHDB aix_restoreauthdb(); #endif @@ -566,16 +566,16 @@ get_user_info(struct user_details *ud) goto oom; if (asprintf(&user_info[++i], "sid=%d", (int)ud->sid) == -1) goto oom; - if (asprintf(&user_info[++i], "uid=%u", (unsigned int)ud->uid) == -1) + if (asprintf(&user_info[++i], "uid=%u", (unsigned int)ud->cred.uid) == -1) goto oom; - if (asprintf(&user_info[++i], "euid=%u", (unsigned int)ud->euid) == -1) + if (asprintf(&user_info[++i], "euid=%u", (unsigned int)ud->cred.euid) == -1) goto oom; - if (asprintf(&user_info[++i], "gid=%u", (unsigned int)ud->gid) == -1) + if (asprintf(&user_info[++i], "gid=%u", (unsigned int)ud->cred.gid) == -1) goto oom; - if (asprintf(&user_info[++i], "egid=%u", (unsigned int)ud->egid) == -1) + if (asprintf(&user_info[++i], "egid=%u", (unsigned int)ud->cred.egid) == -1) goto oom; - if ((cp = get_user_groups(ud)) == NULL) + if ((cp = get_user_groups(ud->username, &ud->cred)) == NULL) goto oom; user_info[++i] = cp; @@ -749,7 +749,7 @@ command_info_to_details(char * const info[], struct command_details *details) id = sudo_strtoid(cp, &errstr); if (errstr != NULL) sudo_fatalx(U_("%s: %s"), info[i], U_(errstr)); - details->egid = (gid_t)id; + details->cred.egid = (gid_t)id; SET(details->flags, CD_SET_EGID); break; } @@ -758,7 +758,7 @@ command_info_to_details(char * const info[], struct command_details *details) id = sudo_strtoid(cp, &errstr); if (errstr != NULL) sudo_fatalx(U_("%s: %s"), info[i], U_(errstr)); - details->euid = (uid_t)id; + details->cred.euid = (uid_t)id; SET(details->flags, CD_SET_EUID); break; } @@ -767,15 +767,15 @@ command_info_to_details(char * const info[], struct command_details *details) id = sudo_strtoid(cp, &errstr); if (errstr != NULL) sudo_fatalx(U_("%s: %s"), info[i], U_(errstr)); - details->gid = (gid_t)id; + details->cred.gid = (gid_t)id; SET(details->flags, CD_SET_GID); break; } if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) { cp = info[i] + sizeof("runas_groups=") - 1; - details->ngroups = sudo_parse_gids(cp, NULL, &details->groups); + details->cred.ngroups = sudo_parse_gids(cp, NULL, &details->cred.groups); /* sudo_parse_gids() will print a warning on error. */ - if (details->ngroups == -1) + if (details->cred.ngroups == -1) exit(EXIT_FAILURE); /* XXX */ break; } @@ -784,7 +784,7 @@ command_info_to_details(char * const info[], struct command_details *details) id = sudo_strtoid(cp, &errstr); if (errstr != NULL) sudo_fatalx(U_("%s: %s"), info[i], U_(errstr)); - details->uid = (uid_t)id; + details->cred.uid = (uid_t)id; SET(details->flags, CD_SET_UID); break; } @@ -847,19 +847,19 @@ command_info_to_details(char * const info[], struct command_details *details) } if (!ISSET(details->flags, CD_SET_EUID)) - details->euid = details->uid; + details->cred.euid = details->cred.uid; if (!ISSET(details->flags, CD_SET_EGID)) - details->egid = details->gid; + details->cred.egid = details->cred.gid; if (!ISSET(details->flags, CD_SET_UMASK)) CLR(details->flags, CD_OVERRIDE_UMASK); #ifdef HAVE_SETAUTHDB - aix_setauthdb(IDtouser(details->euid), NULL); + aix_setauthdb(IDtouser(details->cred.euid), NULL); #endif if (details->runas_user != NULL) details->pw = getpwnam(details->runas_user); if (details->pw == NULL) - details->pw = getpwuid(details->euid); + details->pw = getpwuid(details->cred.euid); #ifdef HAVE_SETAUTHDB aix_restoreauthdb(); #endif @@ -933,23 +933,23 @@ set_user_groups(struct command_details *details) debug_decl(set_user_groups, SUDO_DEBUG_EXEC); if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) { - if (details->ngroups >= 0) { - if (sudo_setgroups(details->ngroups, details->groups) < 0) { + if (details->cred.ngroups >= 0) { + if (sudo_setgroups(details->cred.ngroups, details->cred.groups) < 0) { sudo_warn("%s", U_("unable to set supplementary group IDs")); goto done; } } } #ifdef HAVE_SETEUID - if (ISSET(details->flags, CD_SET_EGID) && setegid(details->egid)) { + if (ISSET(details->flags, CD_SET_EGID) && setegid(details->cred.egid)) { sudo_warn(U_("unable to set effective gid to runas gid %u"), - (unsigned int)details->egid); + (unsigned int)details->cred.egid); goto done; } #endif - if (ISSET(details->flags, CD_SET_GID) && setgid(details->gid)) { + if (ISSET(details->flags, CD_SET_GID) && setgid(details->cred.gid)) { sudo_warn(U_("unable to set gid to runas gid %u"), - (unsigned int)details->gid); + (unsigned int)details->cred.gid); goto done; } ret = true; diff --git a/src/sudo.h b/src/sudo.h index 06f47c544..7f48ece79 100644 --- a/src/sudo.h +++ b/src/sudo.h @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 1993-1996, 1998-2005, 2007-2016 + * Copyright (c) 1993-1996, 1998-2005, 2007-2021 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -92,23 +92,28 @@ struct sudo_settings { const char *value; }; +/* Sudo user credentials */ +struct sudo_cred { + uid_t uid; + uid_t euid; + uid_t gid; + uid_t egid; + int ngroups; + GETGROUPS_T *groups; +}; + struct user_details { + struct sudo_cred cred; pid_t pid; pid_t ppid; pid_t pgid; pid_t tcpgid; pid_t sid; - uid_t uid; - uid_t euid; - uid_t gid; - uid_t egid; const char *username; const char *cwd; const char *tty; const char *host; const char *shell; - GETGROUPS_T *groups; - int ngroups; int ts_rows; int ts_cols; }; @@ -143,21 +148,16 @@ struct preserved_fd { TAILQ_HEAD(preserved_fd_list, preserved_fd); struct command_details { - uid_t uid; - uid_t euid; - gid_t gid; - gid_t egid; + struct sudo_cred cred; mode_t umask; int priority; int timeout; - int ngroups; int closefrom; int flags; int execfd; int cwd_optional; struct preserved_fd_list preserved_fds; struct passwd *pw; - GETGROUPS_T *groups; const char *command; const char *runas_user; const char *cwd; diff --git a/src/sudo_edit.c b/src/sudo_edit.c index 8fae0fc59..27262762a 100644 --- a/src/sudo_edit.c +++ b/src/sudo_edit.c @@ -77,7 +77,7 @@ set_tmpdir(struct command_details *command_details) for (i = 0; tdir == NULL && i < nitems(tmpdirs); i++) { if ((dfd = open(tmpdirs[i], O_RDONLY)) != -1) { - if (dir_is_writable(dfd, &user_details, command_details) == true) + if (dir_is_writable(dfd, &user_details.cred, &command_details->cred) == true) tdir = tmpdirs[i]; close(dfd); } @@ -150,22 +150,23 @@ sudo_edit_create_tfiles(struct command_details *command_details, */ for (i = 0, j = 0; i < nfiles; i++) { rc = -1; - switch_user(command_details->euid, command_details->egid, - command_details->ngroups, command_details->groups); + switch_user(command_details->cred.euid, command_details->cred.egid, + command_details->cred.ngroups, command_details->cred.groups); ofd = sudo_edit_open(files[i], O_RDONLY, - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, &user_details, command_details); + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details->flags, + &user_details.cred, &command_details->cred); if (ofd != -1 || errno == ENOENT) { if (ofd != -1) { rc = fstat(ofd, &sb); } else { /* New file, verify parent dir exists and is not writable. */ memset(&sb, 0, sizeof(sb)); - if (sudo_edit_parent_valid(files[i], &user_details, command_details)) + if (sudo_edit_parent_valid(files[i], command_details->flags, &user_details.cred, &command_details->cred)) rc = 0; } } - switch_user(ROOT_UID, user_details.egid, - user_details.ngroups, user_details.groups); + switch_user(ROOT_UID, user_details.cred.egid, + user_details.cred.ngroups, user_details.cred.groups); if (ofd != -1 && !S_ISREG(sb.st_mode)) { sudo_warnx(U_("%s: not a regular file"), files[i]); close(ofd); @@ -190,9 +191,9 @@ sudo_edit_create_tfiles(struct command_details *command_details, tf[j].osize = sb.st_size; mtim_get(&sb, tf[j].omtim); sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, - "seteuid(%u)", (unsigned int)user_details.uid); - if (seteuid(user_details.uid) != 0) - sudo_fatal("seteuid(%u)", (unsigned int)user_details.uid); + "seteuid(%u)", (unsigned int)user_details.cred.uid); + if (seteuid(user_details.cred.uid) != 0) + sudo_fatal("seteuid(%u)", (unsigned int)user_details.cred.uid); tfd = sudo_edit_mktemp(tf[j].ofile, &tf[j].tfile); sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "seteuid(%u)", ROOT_UID); @@ -251,16 +252,16 @@ sudo_edit_copy_tfiles(struct command_details *command_details, /* Copy contents of temp files to real ones. */ for (i = 0; i < nfiles; i++) { sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, - "seteuid(%u)", (unsigned int)user_details.uid); - if (seteuid(user_details.uid) != 0) - sudo_fatal("seteuid(%u)", (unsigned int)user_details.uid); + "seteuid(%u)", (unsigned int)user_details.cred.uid); + if (seteuid(user_details.cred.uid) != 0) + sudo_fatal("seteuid(%u)", (unsigned int)user_details.cred.uid); tfd = sudo_edit_open(tf[i].tfile, O_RDONLY, - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, &user_details, NULL); + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, 0, &user_details.cred, NULL); if (seteuid(ROOT_UID) != 0) sudo_fatal("seteuid(ROOT_UID)"); sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "seteuid(%u)", ROOT_UID); - if (tfd == -1 || !sudo_check_temp_file(tfd, tf[i].tfile, user_details.uid, &sb)) { + if (tfd == -1 || !sudo_check_temp_file(tfd, tf[i].tfile, user_details.cred.uid, &sb)) { sudo_warnx(U_("%s left unmodified"), tf[i].ofile); if (tfd != -1) close(tfd); @@ -280,14 +281,15 @@ sudo_edit_copy_tfiles(struct command_details *command_details, continue; } } - switch_user(command_details->euid, command_details->egid, - command_details->ngroups, command_details->groups); + switch_user(command_details->cred.euid, command_details->cred.egid, + command_details->cred.ngroups, command_details->cred.groups); oldmask = umask(command_details->umask); ofd = sudo_edit_open(tf[i].ofile, O_WRONLY|O_CREAT, - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, &user_details, command_details); + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details->flags, + &user_details.cred, &command_details->cred); umask(oldmask); - switch_user(ROOT_UID, user_details.egid, - user_details.ngroups, user_details.groups); + switch_user(ROOT_UID, user_details.cred.egid, + user_details.cred.ngroups, user_details.cred.groups); if (ofd == -1) { sudo_warn(U_("unable to write to %s"), tf[i].ofile); goto bad; @@ -359,21 +361,21 @@ selinux_fmt_sudo_user(void) int i, len; debug_decl(selinux_fmt_sudo_user, SUDO_DEBUG_EDIT); - user_size = (MAX_UID_T_LEN + 1) * (2 + user_details.ngroups); + user_size = (MAX_UID_T_LEN + 1) * (2 + user_details.cred.ngroups); if ((user_str = malloc(user_size)) == NULL) debug_return_ptr(NULL); /* UID:GID: */ len = snprintf(user_str, user_size, "%u:%u:", - (unsigned int)user_details.uid, (unsigned int)user_details.gid); + (unsigned int)user_details.cred.uid, (unsigned int)user_details.cred.gid); if (len < 0 || (size_t)len >= user_size) sudo_fatalx(U_("internal error, %s overflow"), __func__); /* Supplementary GIDs */ cp = user_str + len; - for (i = 0; i < user_details.ngroups; i++) { + for (i = 0; i < user_details.cred.ngroups; i++) { len = snprintf(cp, user_size - (cp - user_str), "%s%u", - i ? "," : "", (unsigned int)user_details.groups[i]); + i ? "," : "", (unsigned int)user_details.cred.groups[i]); if (len < 0 || (size_t)len >= user_size - (cp - user_str)) sudo_fatalx(U_("internal error, %s overflow"), __func__); cp += len; @@ -443,8 +445,8 @@ selinux_edit_create_tfiles(struct command_details *command_details, *sesh_ap = NULL; /* Run sesh -e [-h] 0 ... */ - error = selinux_run_helper(command_details->uid, command_details->gid, - command_details->ngroups, command_details->groups, sesh_args, + error = selinux_run_helper(command_details->cred.uid, command_details->cred.gid, + command_details->cred.ngroups, command_details->cred.groups, sesh_args, command_details->envp); switch (error) { case SESH_SUCCESS: @@ -466,13 +468,13 @@ selinux_edit_create_tfiles(struct command_details *command_details, sudo_warn(U_("unable to open %s"), tf[i].tfile); goto done; } - if (!sudo_check_temp_file(tfd, tf[i].tfile, command_details->uid, NULL)) { + if (!sudo_check_temp_file(tfd, tf[i].tfile, command_details->cred.uid, NULL)) { close(tfd); goto done; } - if (fchown(tfd, user_details.uid, user_details.gid) != 0) { + if (fchown(tfd, user_details.cred.uid, user_details.cred.gid) != 0) { sudo_warn("unable to chown(%s) to %d:%d for editing", - tf[i].tfile, user_details.uid, user_details.gid); + tf[i].tfile, user_details.cred.uid, user_details.cred.gid); close(tfd); goto done; } @@ -529,7 +531,7 @@ selinux_edit_copy_tfiles(struct command_details *command_details, sudo_warn(U_("unable to open %s"), tf[i].tfile); continue; } - if (!sudo_check_temp_file(tfd, tf[i].tfile, user_details.uid, &sb)) + if (!sudo_check_temp_file(tfd, tf[i].tfile, user_details.cred.uid, &sb)) continue; mtim_get(&sb, ts); if (tf[i].osize == sb.st_size && sudo_timespeccmp(&tf[i].omtim, &ts, ==)) { @@ -545,9 +547,9 @@ selinux_edit_copy_tfiles(struct command_details *command_details, } *sesh_ap++ = tf[i].tfile; *sesh_ap++ = tf[i].ofile; - if (fchown(tfd, command_details->uid, command_details->gid) != 0) { + if (fchown(tfd, command_details->cred.uid, command_details->cred.gid) != 0) { sudo_warn("unable to chown(%s) back to %d:%d", tf[i].tfile, - command_details->uid, command_details->gid); + command_details->cred.uid, command_details->cred.gid); } } *sesh_ap = NULL; @@ -556,8 +558,8 @@ selinux_edit_copy_tfiles(struct command_details *command_details, if (sesh_ap - sesh_args > 3) { /* Run sesh -e 1 ... */ - error = selinux_run_helper(command_details->uid, command_details->gid, - command_details->ngroups, command_details->groups, sesh_args, + error = selinux_run_helper(command_details->cred.uid, command_details->cred.gid, + command_details->cred.ngroups, command_details->cred.groups, sesh_args, command_details->envp); switch (error) { case SESH_SUCCESS: @@ -689,12 +691,7 @@ sudo_edit(struct command_details *command_details) goto cleanup; } memcpy(&saved_command_details, command_details, sizeof(struct command_details)); - command_details->uid = user_details.uid; - command_details->euid = user_details.uid; - command_details->gid = user_details.gid; - command_details->egid = user_details.gid; - command_details->ngroups = user_details.ngroups; - command_details->groups = user_details.groups; + command_details->cred = user_details.cred; command_details->argv = nargv; ret = run_command(command_details); if (sudo_gettime_real(×[1]) == -1) { @@ -703,12 +700,7 @@ sudo_edit(struct command_details *command_details) } /* Restore saved command_details. */ - command_details->uid = saved_command_details.uid; - command_details->euid = saved_command_details.euid; - command_details->gid = saved_command_details.gid; - command_details->egid = saved_command_details.egid; - command_details->ngroups = saved_command_details.ngroups; - command_details->groups = saved_command_details.groups; + command_details->cred = saved_command_details.cred; command_details->argv = saved_command_details.argv; /* Copy contents of temp files to real ones. */ diff --git a/src/sudo_edit.h b/src/sudo_edit.h index 69f62b1ce..0714642b0 100644 --- a/src/sudo_edit.h +++ b/src/sudo_edit.h @@ -46,9 +46,10 @@ int sudo_copy_file(const char *src, int src_fd, off_t src_len, const char *dst, bool sudo_check_temp_file(int tfd, const char *tname, uid_t uid, struct stat *sb); /* edit_open.c */ +struct sudo_cred; void switch_user(uid_t euid, gid_t egid, int ngroups, GETGROUPS_T *groups); -int sudo_edit_open(char *path, int oflags, mode_t mode, struct user_details *ud, struct command_details *cd); -int dir_is_writable(int dfd, struct user_details *ud, struct command_details *cd); -bool sudo_edit_parent_valid(char *path, struct user_details *ud, struct command_details *cd); +int sudo_edit_open(char *path, int oflags, mode_t mode, int sflags, struct sudo_cred *user_cred, struct sudo_cred *run_cred); +int dir_is_writable(int dfd, struct sudo_cred *user_cred, struct sudo_cred *run_cred); +bool sudo_edit_parent_valid(char *path, int sflags, struct sudo_cred *user_cred, struct sudo_cred *run_cred); #endif /* SUDO_EDIT_H */ diff --git a/src/tgetpass.c b/src/tgetpass.c index 5ccb5136e..87f01dc38 100644 --- a/src/tgetpass.c +++ b/src/tgetpass.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 1996, 1998-2005, 2007-2018 + * Copyright (c) 1996, 1998-2005, 2007-2021 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -323,12 +323,12 @@ sudo_askpass(const char *askpass, const char *prompt) restore_limits(); /* But avoid a setuid() failure on Linux due to RLIMIT_NPROC. */ unlimit_nproc(); - if (setgid(user_details.gid)) { - sudo_warn(U_("unable to set gid to %u"), (unsigned int)user_details.gid); + if (setgid(user_details.cred.gid)) { + sudo_warn(U_("unable to set gid to %u"), (unsigned int)user_details.cred.gid); _exit(255); } - if (setuid(user_details.uid)) { - sudo_warn(U_("unable to set uid to %u"), (unsigned int)user_details.uid); + if (setuid(user_details.cred.uid)) { + sudo_warn(U_("unable to set uid to %u"), (unsigned int)user_details.cred.uid); _exit(255); } restore_nproc();