diff --git a/MANIFEST b/MANIFEST index 1bcd47c61..30514a1f5 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1091,6 +1091,10 @@ plugins/sudoers/regress/testsudoers/test23.out.ok plugins/sudoers/regress/testsudoers/test23.sh plugins/sudoers/regress/testsudoers/test24.out.ok plugins/sudoers/regress/testsudoers/test24.sh +plugins/sudoers/regress/testsudoers/test25.out.ok +plugins/sudoers/regress/testsudoers/test25.sh +plugins/sudoers/regress/testsudoers/test26.out.ok +plugins/sudoers/regress/testsudoers/test26.sh plugins/sudoers/regress/testsudoers/test3.out.ok plugins/sudoers/regress/testsudoers/test3.sh plugins/sudoers/regress/testsudoers/test4.out.ok diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index ab1bfeb17..40f759bf5 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -213,9 +213,10 @@ REPLAY_OBJS = getdate.o sudoreplay.o REPLAY_IOBJS = $(REPLAY_OBJS:.o=.i) -TEST_OBJS = fmtsudoers.lo fmtsudoers_cvt.lo group_plugin.lo interfaces.lo \ - ldap_util.lo locale.lo lookup.lo net_ifs.o parse_ldif.o \ - sudo_printf.o testsudoers.o testsudoers_pwutil.o tsgetgrpw.o +TEST_OBJS = check_util.lo fmtsudoers.lo fmtsudoers_cvt.lo group_plugin.lo \ + interfaces.lo ldap_util.lo locale.lo lookup.lo net_ifs.o \ + parse_ldif.o sudo_printf.o testsudoers.o testsudoers_pwutil.o \ + tsgetgrpw.o IOBJS = $(LIBPARSESUDOERS_IOBJS) $(SUDOERS_IOBJS) $(VISUDO_IOBJS) \ $(CVTSUDOERS_IOBJS) $(REPLAY_IOBJS) diff --git a/plugins/sudoers/regress/testsudoers/test25.out.ok b/plugins/sudoers/regress/testsudoers/test25.out.ok new file mode 100644 index 000000000..dfb390cab --- /dev/null +++ b/plugins/sudoers/regress/testsudoers/test25.out.ok @@ -0,0 +1,63 @@ +A simple sudoers rule should not allow the user to set the cwd: +Parses OK + +Entries for user root: + +ALL = /bin/ls + host allowed + runas allowed + cmnd allowed + +User root is not allowed to change directory to / + +Command denied + +User can run commands with cwd set to sudoers value: +Parses OK + +Entries for user root: + +ALL = CWD=/some/where/else /bin/ls + host allowed + runas allowed + cmnd allowed + +Command allowed + +User cannot override the sudoers cwd: +Parses OK + +Entries for user root: + +ALL = CWD=/some/where/else /bin/ls + host allowed + runas allowed + cmnd allowed + +User root is not allowed to change directory to / + +Command denied + +User can set cwd if sudoers rule sets cwd to '*': +Parses OK + +Entries for user root: + +ALL = CWD=* /bin/ls + host allowed + runas allowed + cmnd allowed + +Command allowed + +User can set cwd runcwd Defaults is '*': +Parses OK + +Entries for user root: + +ALL = /bin/ls + host allowed + runas allowed + cmnd allowed + +Command allowed diff --git a/plugins/sudoers/regress/testsudoers/test25.sh b/plugins/sudoers/regress/testsudoers/test25.sh new file mode 100644 index 000000000..9912e31f1 --- /dev/null +++ b/plugins/sudoers/regress/testsudoers/test25.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# +# Test user-specified cwd handling +# + +: ${TESTSUDOERS=testsudoers} + +exec 2>&1 +cd / + +retval=0 + +# Sudo used to allow the user to set the cwd to the current value. +# Now, a cwd must be explicitly set in sudoers to use the -D option. +printf "A simple sudoers rule should not allow the user to set the cwd:\n" +$TESTSUDOERS -D / root /bin/ls <<'EOF' +root ALL = /bin/ls +EOF +if [ $? -eq 0 ]; then + retval=1 +fi + +printf "\nUser can run commands with cwd set to sudoers value:\n" +$TESTSUDOERS -D /some/where/else root /bin/ls <<'EOF' +root ALL = CWD=/some/where/else /bin/ls +EOF +if [ $? -ne 0 ]; then + retval=$? +fi + +printf "\nUser cannot override the sudoers cwd:\n" +$TESTSUDOERS -D / root /bin/ls <<'EOF' +root ALL = CWD=/some/where/else /bin/ls +EOF +if [ $? -eq 0 ]; then + retval=1 +fi + +printf "\nUser can set cwd if sudoers rule sets cwd to '*':\n" +$TESTSUDOERS -D /usr root /bin/ls <<'EOF' +root ALL = CWD=* /bin/ls +EOF +if [ $? -ne 0 ]; then + retval=$? +fi + +printf "\nUser can set cwd runcwd Defaults is '*':\n" +$TESTSUDOERS -D /usr root /bin/ls <<'EOF' +Defaults runcwd = "*" +root ALL = /bin/ls +EOF +if [ $? -ne 0 ]; then + retval=$? +fi + +exit $retval diff --git a/plugins/sudoers/regress/testsudoers/test26.out.ok b/plugins/sudoers/regress/testsudoers/test26.out.ok new file mode 100644 index 000000000..633a557fd --- /dev/null +++ b/plugins/sudoers/regress/testsudoers/test26.out.ok @@ -0,0 +1,61 @@ +A simple sudoers rule should not allow the user to chroot: +Parses OK + +Entries for user root: + +ALL = /bin/ls + host allowed + runas allowed + cmnd allowed + +User root is not allowed to change root directory to / + +Command denied + +User can run commands with chroot set to sudoers value: +Parses OK + +Entries for user root: + +ALL = CHROOT=/some/where/else /bin/ls + host allowed + runas allowed + cmnd allowed + +Command allowed + +User cannot override the sudoers chroot: +Parses OK + +Entries for user root: + +ALL = CHROOT=/some/where/else /bin/ls + host allowed + runas allowed + cmnd unmatched + +Command unmatched + +User can chroot if sudoers rule sets chroot to '*': +Parses OK + +Entries for user root: + +ALL = CHROOT=* /bin/ls + host allowed + runas allowed + cmnd allowed + +Command allowed + +User can chroot if runchroot Defaults is '*': +Parses OK + +Entries for user root: + +ALL = /bin/ls + host allowed + runas allowed + cmnd allowed + +Command allowed diff --git a/plugins/sudoers/regress/testsudoers/test26.sh b/plugins/sudoers/regress/testsudoers/test26.sh new file mode 100644 index 000000000..982ee1acc --- /dev/null +++ b/plugins/sudoers/regress/testsudoers/test26.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# +# Test user-specified chroot handling +# + +: ${TESTSUDOERS=testsudoers} + +exec 2>&1 +cd / + +retval=0 + +printf "A simple sudoers rule should not allow the user to chroot:\n" +$TESTSUDOERS -R / root /bin/ls <<'EOF' +root ALL = /bin/ls +EOF +if [ $? -eq 0 ]; then + retval=1 +fi + +printf "\nUser can run commands with chroot set to sudoers value:\n" +$TESTSUDOERS -R /some/where/else root /bin/ls <<'EOF' +root ALL = CHROOT=/some/where/else /bin/ls +EOF +if [ $? -ne 0 ]; then + retval=$? +fi + +# Because command_matches() uses the per-rule CHROOT, this results in +# an unmatched rule instead of a matched rule that is rejected later. +# This is different from the CWD checking which is performed after +# matching is done. +printf "\nUser cannot override the sudoers chroot:\n" +$TESTSUDOERS -R / root /bin/ls <<'EOF' +root ALL = CHROOT=/some/where/else /bin/ls +EOF +if [ $? -eq 0 ]; then + retval=1 +fi + +printf "\nUser can chroot if sudoers rule sets chroot to '*':\n" +$TESTSUDOERS -R /usr root /bin/ls <<'EOF' +root ALL = CHROOT=* /bin/ls +EOF +if [ $? -ne 0 ]; then + retval=$? +fi + +printf "\nUser can chroot if runchroot Defaults is '*':\n" +$TESTSUDOERS -R /usr root /bin/ls <<'EOF' +Defaults runchroot = "*" +root ALL = /bin/ls +EOF +if [ $? -ne 0 ]; then + retval=$? +fi + +exit $retval diff --git a/plugins/sudoers/testsudoers.c b/plugins/sudoers/testsudoers.c index c2514b9e7..4ce1839c9 100644 --- a/plugins/sudoers/testsudoers.c +++ b/plugins/sudoers/testsudoers.c @@ -112,6 +112,7 @@ main(int argc, char *argv[]) const char *errstr; int ch, dflag, exitcode = EXIT_FAILURE; int validated, status = FOUND; + char cwdbuf[PATH_MAX]; time_t now; id_t id; debug_decl(main, SUDOERS_DEBUG_MAIN); @@ -140,8 +141,11 @@ main(int argc, char *argv[]) dflag = 0; grfile = pwfile = NULL; - while ((ch = getopt(argc, argv, "+dg:G:h:i:P:p:T:tu:U:")) != -1) { + while ((ch = getopt(argc, argv, "+D:dg:G:h:i:P:p:R:T:tu:U:")) != -1) { switch (ch) { + case 'D': + user_runcwd = optarg; + break; case 'd': dflag = 1; break; @@ -179,6 +183,9 @@ main(int argc, char *argv[]) if (now == -1) sudo_fatalx("invalid time: %s", optarg); break; + case 'R': + user_runchroot = optarg; + break; case 't': trace_print = testsudoers_error; break; @@ -229,6 +236,10 @@ main(int argc, char *argv[]) sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); user_base = sudo_basename(user_cmnd); + if (getcwd(cwdbuf, sizeof(cwdbuf)) == NULL) + strlcpy(cwdbuf, "/", sizeof(cwdbuf)); + user_cwd = cwdbuf; + if ((sudo_user.pw = sudo_getpwnam(user_name)) == NULL) sudo_fatalx(U_("unknown user %s"), user_name); @@ -342,6 +353,22 @@ main(int argc, char *argv[]) validated = sudoers_lookup(&snl, sudo_user.pw, now, &callbacks, &status, false); + /* Validate user-specified chroot or cwd (if any). */ + if (ISSET(validated, VALIDATE_SUCCESS)) { + if (check_user_runchroot() != true) { + printf("\nUser %s is not allowed to change root directory to %s\n", + user_name, user_runchroot); + CLR(validated, VALIDATE_SUCCESS); + SET(validated, VALIDATE_FAILURE); + } + if (check_user_runcwd() != true) { + printf("\nUser %s is not allowed to change directory to %s\n", + user_name, user_runcwd); + CLR(validated, VALIDATE_SUCCESS); + SET(validated, VALIDATE_FAILURE); + } + } + /* * Exit codes: * 0 - parsed OK and command matched.