Use getopt() and getopt_long() for sesh command line options.

This commit is contained in:
Todd C. Miller
2022-10-10 09:12:48 -06:00
parent 8d5e59c8a8
commit 3bdb585481
3 changed files with 194 additions and 158 deletions

View File

@@ -444,8 +444,7 @@ selinux_execve(int fd, const char *path, char *const argv[], char *envp[],
{
char **nargv;
const char *sesh;
int argc, nargc, serrno;
bool login_shell = false;
int argc, len, nargc, serrno;
debug_decl(selinux_execve, SUDO_DEBUG_SELINUX);
sesh = sudo_conf_sesh_path();
@@ -461,8 +460,6 @@ selinux_execve(int fd, const char *path, char *const argv[], char *envp[],
/*
* Build new argv with sesh as argv[0].
* If argv[0] ends in -noexec, sesh will disable execute
* for the command it runs.
*/
for (argc = 0; argv[argc] != NULL; argc++)
continue;
@@ -470,19 +467,15 @@ selinux_execve(int fd, const char *path, char *const argv[], char *envp[],
errno = EINVAL;
debug_return;
}
nargv = reallocarray(NULL, argc + 4, sizeof(char *));
nargv = reallocarray(NULL, 5 + argc + 1, sizeof(char *));
if (nargv == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return;
}
if (*argv[0] == '-')
login_shell = true;
if (ISSET(flags, CD_NOEXEC)) {
nargv[0] = (char *)(login_shell ? "-sesh-noexec" : "sesh-noexec");
CLR(flags, CD_NOEXEC);
} else {
nargv[0] = (char *)(login_shell ? "-sesh" : "sesh");
}
nargv[0] = (char *)"-sesh";
else
nargv[0] = (char *)"sesh";
nargc = 1;
if (ISSET(flags, CD_RBAC_SET_CWD)) {
const char *prefix = ISSET(flags, CD_CWD_OPTIONAL) ? "+" : "";
@@ -491,19 +484,27 @@ selinux_execve(int fd, const char *path, char *const argv[], char *envp[],
errno = EINVAL;
debug_return;
}
if (asprintf(&nargv[nargc++], "--chdir=%s%s", prefix, rundir) == -1) {
len = asprintf(&nargv[nargc++], "--directory=%s%s", prefix, rundir);
if (len == -1) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return;
}
}
if (fd != -1 && asprintf(&nargv[nargc++], "--execfd=%d", fd) == -1) {
if (fd != -1) {
len = asprintf(&nargv[nargc++], "--execfd=%d", fd);
if (len == -1) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return;
}
}
if (ISSET(flags, CD_NOEXEC)) {
CLR(flags, CD_NOEXEC);
nargv[nargc++] = (char *)"--noexec";
}
nargv[nargc++] = (char *)"--";
nargv[nargc++] = (char *)path;
memcpy(&nargv[nargc], &argv[1], argc * sizeof(char *)); /* copies NULL */
/* sesh will handle noexec for us. */
sudo_execve(-1, sesh, nargv, envp, -1, flags);
serrno = errno;
free(nargv);

View File

@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2008, 2010-2018, 2020-2021 Todd C. Miller <Todd.Miller@sudo.ws>
* Copyright (c) 2008, 2010-2018, 2020-2022 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
@@ -38,14 +38,48 @@
#else
# include "compat/stdbool.h"
#endif /* HAVE_STDBOOL_H */
#ifdef HAVE_GETOPT_LONG
# include <getopt.h>
# else
# include "compat/getopt.h"
#endif /* HAVE_GETOPT_LONG */
#include "sudo.h"
#include "sudo_exec.h"
#include "sudo_edit.h"
enum sesh_mode {
SESH_RUN_COMMAND,
SESH_EDIT_CREATE,
SESH_EDIT_INSTALL
};
static const char short_opts[] = "+cd:e:ihnw:";
static struct option long_opts[] = {
{ "edit-create", no_argument, NULL, 'c' },
{ "directory", required_argument, NULL, 'd' },
{ "execfd", required_argument, NULL, 'e' },
{ "edit-install", no_argument, NULL, 'i' },
{ "no-dereference", no_argument, NULL, 'h' },
{ "noexec", no_argument, NULL, 'n' },
{ "edit-checkdir", required_argument, NULL, 'w' },
{ NULL, no_argument, NULL, '\0' },
};
sudo_dso_public int main(int argc, char *argv[], char *envp[]);
static int sesh_sudoedit(int argc, char *argv[]);
static int sesh_sudoedit(enum sesh_mode mode, int flags, char *user, int argc,
char *argv[]);
sudo_noreturn void
usage(void)
{
(void)fprintf(stderr,
"usage: %s [-n] [-d directory] [-e fd] command [...]\n"
" %s [-cih] [-w uid:gids] file [...]\n",
getprogname(), getprogname());
exit(EXIT_FAILURE);
}
/*
* Exit codes defined in sudo_exec.h:
@@ -59,7 +93,11 @@ static int sesh_sudoedit(int argc, char *argv[]);
int
main(int argc, char *argv[], char *envp[])
{
int ret;
enum sesh_mode mode = SESH_RUN_COMMAND;
const char *errstr, *rundir = NULL;
int flags = CD_SUDOEDIT_FOLLOW;
char *edit_user = NULL;
int ch, ret, fd = -1;
debug_decl(main, SUDO_DEBUG_MAIN);
initprogname(argc > 0 ? argv[0] : "sesh");
@@ -68,8 +106,55 @@ main(int argc, char *argv[], char *envp[])
bindtextdomain(PACKAGE_NAME, LOCALEDIR);
textdomain(PACKAGE_NAME);
if (argc < 2)
sudo_fatalx("%s", U_("requires at least one argument"));
while ((ch = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (ch) {
case 'c':
if (mode != SESH_RUN_COMMAND) {
sudo_warnx("%s",
U_("Only one of the -c or -i options may be specified"));
usage();
}
mode = SESH_EDIT_CREATE;
break;
case 'd':
rundir = optarg;
if (*rundir == '+') {
SET(flags, CD_CWD_OPTIONAL);
rundir++;
}
break;
case 'e':
fd = sudo_strtonum(optarg, 0, INT_MAX, &errstr);
if (errstr != NULL)
sudo_fatalx(U_("invalid file descriptor number: %s"), optarg);
break;
case 'i':
if (mode != SESH_RUN_COMMAND) {
sudo_warnx("%s",
U_("Only one of the -c or -i options may be specified"));
usage();
}
mode = SESH_EDIT_INSTALL;
break;
case 'h':
CLR(flags, CD_SUDOEDIT_FOLLOW);
break;
case 'n':
SET(flags, CD_NOEXEC);
break;
case 'w':
SET(flags, CD_SUDOEDIT_CHECKDIR);
edit_user = optarg;
break;
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if (argc == 0)
usage();
/* Read sudo.conf and initialize the debug subsystem. */
if (sudo_conf_read(NULL, SUDO_CONF_DEBUG) == -1)
@@ -77,67 +162,57 @@ main(int argc, char *argv[], char *envp[])
sudo_debug_register(getprogname(), NULL, NULL,
sudo_conf_debug_files(getprogname()), -1);
if (strcmp(argv[1], "-e") == 0) {
ret = sesh_sudoedit(argc, argv);
if (mode != SESH_RUN_COMMAND) {
if (rundir != NULL) {
sudo_warnx(U_("The -%c option may not be used in edit mode."), 'd');
usage();
}
if (fd != -1) {
sudo_warnx(U_("The -%c option may not be used in edit mode."), 'e');
usage();
}
if (ISSET(flags, CD_NOEXEC)) {
sudo_warnx(U_("The -%c option may not be used in edit mode."), 'n');
usage();
}
ret = sesh_sudoedit(mode, flags, edit_user, argc, argv);
} else {
bool login_shell;
char *cp, *cmnd;
int flags = 0;
int fd = -1;
char *cmnd;
if (!ISSET(flags, CD_SUDOEDIT_FOLLOW)) {
sudo_warnx(U_("The -%c option may only be used in edit mode."),
'h');
usage();
}
if (edit_user != NULL) {
sudo_warnx(U_("The -%c option may only be used in edit mode."),
'w');
usage();
}
/* If the first char of argv[0] is '-', we are running a login shell. */
login_shell = argv[0][0] == '-';
/* If argv[0] ends in -noexec, pass the flag to sudo_execve() */
if ((cp = strrchr(argv[0], '-')) != NULL && cp != argv[0]) {
if (strcmp(cp, "-noexec") == 0)
SET(flags, CD_NOEXEC);
}
/* If argv[1] is --chdir=dir, change to specified dir. */
if (strncmp(argv[1], "--chdir=", sizeof("--chdir=") - 1) == 0) {
bool cwd_optional = false;
/* If run dir starts with a '+', ignore chdir(2) failure. */
cp = argv[1] + sizeof("--chdir=") - 1;
if (*cp == '+') {
cwd_optional = true;
cp++;
}
if (chdir(cp) == -1) {
sudo_warnx(U_("unable to change directory to %s"), cp);
if (!cwd_optional)
/* We must change the directory in sesh after the context changes. */
if (rundir != NULL && chdir(rundir) == -1) {
sudo_warnx(U_("unable to change directory to %s"), rundir);
if (!ISSET(flags, CD_CWD_OPTIONAL))
exit(EXIT_FAILURE);
}
argv++;
argc--;
}
/* If argv[1] is --execfd=%d, extract the fd to exec with. */
if (strncmp(argv[1], "--execfd=", sizeof("--execfd=") - 1) == 0) {
const char *errstr;
cp = argv[1] + sizeof("--execfd=") - 1;
fd = sudo_strtonum(cp, 0, INT_MAX, &errstr);
if (errstr != NULL)
sudo_fatalx(U_("invalid file descriptor number: %s"), cp);
argv++;
argc--;
}
/* Shift argv and make a copy of the command to execute. */
argv++;
argc--;
/* Make a copy of the command to execute. */
if ((cmnd = strdup(argv[0])) == NULL)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
/* If invoked as a login shell, modify argv[0] accordingly. */
if (login_shell) {
if ((cp = strrchr(argv[0], '/')) == NULL)
sudo_fatal(U_("unable to run %s as a login shell"), argv[0]);
char *cp = strrchr(argv[0], '/');
if (cp != NULL) {
*cp = '-';
argv[0] = cp;
}
}
sudo_execve(fd, cmnd, argv, envp, -1, flags);
sudo_warn(U_("unable to execute %s"), cmnd);
ret = SESH_ERR_FAILURE;
@@ -356,59 +431,25 @@ sesh_edit_copy_tfiles(int edit_flags, struct sudo_cred *user_cred,
}
static int
sesh_sudoedit(int argc, char *argv[])
sesh_sudoedit(enum sesh_mode mode, int flags, char *user,
int argc, char *argv[])
{
int edit_flags, post, ret;
struct sudo_cred user_cred, run_cred;
int ret;
debug_decl(sesh_sudoedit, SUDO_DEBUG_EDIT);
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 (argc > 2 && strcmp(argv[2], "-h") == 0) {
argv++;
argc--;
CLR(edit_flags, CD_SUDOEDIT_FOLLOW); // -V753
}
/* Check for -w flag (disallow directories writable by the user). */
if (argc > 2 && strcmp(argv[2], "-w") == 0) {
SET(edit_flags, CD_SUDOEDIT_CHECKDIR);
/* Parse uid:gid:gid1,gid2,... */
if (argv[3] == NULL || !parse_user(argv[3], &user_cred))
debug_return_int(SESH_ERR_FAILURE);
argv += 2;
argc -= 2;
}
if (argc < 3)
/* Parse user for -w option, "uid:gid:gid1,gid2,..." */
if (user != NULL && !parse_user(user, &user_cred))
debug_return_int(SESH_ERR_FAILURE);
/*
* We need to know whether we are performing the copy operation
* before or after the editing. Without this we would not know
* which files are temporary and which are the originals.
* post = 0 ... before
* post = 1 ... after
*/
if (strcmp(argv[2], "0") == 0)
post = 0;
else if (strcmp(argv[2], "1") == 0)
post = 1;
else /* invalid value */
debug_return_int(SESH_ERR_INVALID);
/* Align argv & argc to the beginning of the file list. */
argv += 3;
argc -= 3;
/* no files specified, nothing to do */
/* No files specified, nothing to do. */
if (argc == 0)
debug_return_int(SESH_SUCCESS);
/* odd number of paths specified */
/* Odd number of paths specified. */
if (argc & 1)
debug_return_int(SESH_ERR_BAD_PATHS);
@@ -441,8 +482,8 @@ sesh_sudoedit(int argc, char *argv[])
run_cred.groups = NULL;
}
ret = post ?
sesh_edit_copy_tfiles(edit_flags, &user_cred, &run_cred, argc, argv) :
sesh_edit_create_tfiles(edit_flags, &user_cred, &run_cred, argc, argv);
ret = mode == SESH_EDIT_CREATE ?
sesh_edit_create_tfiles(flags, &user_cred, &run_cred, argc, argv) :
sesh_edit_copy_tfiles(flags, &user_cred, &run_cred, argc, argv);
debug_return_int(ret);
}

View File

@@ -423,7 +423,6 @@ selinux_edit_create_tfiles(struct command_details *command_details,
if (nfiles < 1)
debug_return_int(0);
/* Construct common args for sesh */
sesh_nargs = 6 + (nfiles * 2) + 1;
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
if (sesh_args == NULL) {
@@ -431,18 +430,18 @@ selinux_edit_create_tfiles(struct command_details *command_details,
goto done;
}
*sesh_ap++ = "sesh";
*sesh_ap++ = "-e";
*sesh_ap++ = "--edit-create";
if (!ISSET(command_details->flags, CD_SUDOEDIT_FOLLOW))
*sesh_ap++ = "-h";
*sesh_ap++ = "--no-dereference";
if (ISSET(command_details->flags, CD_SUDOEDIT_CHECKDIR)) {
if ((user_str = selinux_fmt_sudo_user()) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto done;
}
*sesh_ap++ = "-w";
*sesh_ap++ = "--edit-checkdir";
*sesh_ap++ = user_str;
}
*sesh_ap++ = "0";
*sesh_ap++ = "--";
for (i = 0; i < nfiles; i++) {
char *tfile, *ofile = files[i];
@@ -471,7 +470,7 @@ selinux_edit_create_tfiles(struct command_details *command_details,
}
*sesh_ap = NULL;
/* Run sesh -e [-h] 0 <o1> <t1> ... <on> <tn> */
/* Run sesh -c [-h] [-w userstr] <o1> <t1> ... <on> <tn> */
error = selinux_run_helper(command_details->cred.uid,
command_details->cred.gid, command_details->cred.ngroups,
command_details->cred.groups, (char **)sesh_args, command_details->envp);
@@ -523,6 +522,7 @@ selinux_edit_copy_tfiles(struct command_details *command_details,
{
const char **sesh_args, **sesh_ap;
char *user_str = NULL;
bool run_helper = false;
int i, error, sesh_nargs, ret = 1;
int tfd = -1;
struct timespec ts;
@@ -532,9 +532,6 @@ selinux_edit_copy_tfiles(struct command_details *command_details,
if (nfiles < 1)
debug_return_int(0);
const int check_dir = ISSET(command_details->flags, CD_SUDOEDIT_CHECKDIR);
/* Construct common args for sesh */
sesh_nargs = 5 + (nfiles * 2) + 1;
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
if (sesh_args == NULL) {
@@ -542,18 +539,17 @@ selinux_edit_copy_tfiles(struct command_details *command_details,
goto done;
}
*sesh_ap++ = "sesh";
*sesh_ap++ = "-e";
if (check_dir) {
*sesh_ap++ = "--edit-install";
if (ISSET(command_details->flags, CD_SUDOEDIT_CHECKDIR)) {
if ((user_str = selinux_fmt_sudo_user()) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto done;
}
*sesh_ap++ = "-w";
*sesh_ap++ = "--edit-checkdir";
*sesh_ap++ = user_str;
}
*sesh_ap++ = "1";
*sesh_ap++ = "--";
/* Construct args for sesh -e 1 */
for (i = 0; i < nfiles; i++) {
if (tfd != -1)
close(tfd);
@@ -575,6 +571,7 @@ selinux_edit_copy_tfiles(struct command_details *command_details,
continue;
}
}
run_helper = true;
*sesh_ap++ = tf[i].tfile;
*sesh_ap++ = tf[i].ofile;
if (fchown(tfd, command_details->cred.uid, command_details->cred.gid) != 0) {
@@ -583,15 +580,11 @@ selinux_edit_copy_tfiles(struct command_details *command_details,
}
}
*sesh_ap = NULL;
if (tfd != -1)
close(tfd);
/*
* check dir adds two more args to the array
*/
if ((!check_dir && sesh_ap - sesh_args > 3)
|| (check_dir && sesh_ap - sesh_args > 5)) {
/* Run sesh -e 1 <t1> <o1> ... <tn> <on> */
if (!run_helper)
goto done;
/* Run sesh -i <t1> <o1> ... <tn> <on> */
error = selinux_run_helper(command_details->cred.uid,
command_details->cred.gid, command_details->cred.ngroups,
command_details->cred.groups, (char **)sesh_args, command_details->envp);
@@ -614,9 +607,10 @@ selinux_edit_copy_tfiles(struct command_details *command_details,
sudo_warnx(U_("sesh: unknown error %d"), error);
break;
}
}
done:
if (tfd != -1)
close(tfd);
/* Contents of tf will be freed by caller. */
free(sesh_args);
free(user_str);