Imported Upstream version 1.8.31p2 upstream/1.8.31p2
authorJinWang An <jinwang.an@samsung.com>
Mon, 14 Dec 2020 08:18:26 +0000 (17:18 +0900)
committerJinWang An <jinwang.an@samsung.com>
Mon, 14 Dec 2020 08:18:26 +0000 (17:18 +0900)
15 files changed:
ChangeLog
NEWS
config.h.in
configure
configure.ac
doc/sudo.man.in
doc/sudo.mdoc.in
include/sudo_compat.h
plugins/sudoers/ldap.c
plugins/sudoers/parse.c
plugins/sudoers/sssd.c
src/exec_pty.c
src/parse_args.c
src/sudo.c
src/sudo.h

index 87719c3..5752605 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,98 @@
+2020-06-12  Todd C. Miller  <Todd.Miller@sudo.ws>
+
+       * .hgtags:
+       Added tag SUDO_1_8_31p2 for changeset e7b18fb9bded
+       [bef3b405b502] [tip] <1.8>
+
+2020-06-11  Todd C. Miller  <Todd.Miller@sudo.ws>
+
+       * NEWS, configure, configure.ac, src/sudo.c:
+       sudo 1.8.31p2
+       [e7b18fb9bded] [SUDO_1_8_31p2] <1.8>
+
+2020-06-06  Todd C. Miller  <Todd.Miller@sudo.ws>
+
+       * config.h.in, configure, configure.ac, include/sudo_compat.h:
+       Declare getdelim(3) if it exists in libc but is not prototyped in
+       stdio.h. This can happen on systems with a gcc packages that was
+       built on and older versions of the OS where getdelim(3) was not
+       present.
+       [8a9ae71f2f0b] <1.8>
+
+2020-06-04  Todd C. Miller  <Todd.Miller@sudo.ws>
+
+       * plugins/sudoers/ldap.c, plugins/sudoers/parse.c,
+       plugins/sudoers/sssd.c:
+       Avoid passing NULL to printf in match debug code for LDAP/SSSD. The
+       file name in struct userspec was not set for the LDAP and SSSD
+       backends. There is no actual file in this case so set the name to
+       LDAP/SSSD. Also add a guard to make sure we don't try to print NULL
+       in sudoers_lookup_check() if name is left unset.
+       [783c8193ca63] <1.8>
+
+2020-06-03  Todd C. Miller  <Todd.Miller@sudo.ws>
+
+       * plugins/sudoers/ldap.c, plugins/sudoers/sssd.c:
+       When converting LDAP to sudoers, ignore entries with no sudoHost
+       attribute. Otherwise, sudo_ldap_role_to_priv() will treat a NULL
+       host list as as the "ALL" wildcard. This regression was introduced
+       in sudo 1.8.23, which was the first version to convert LDAP sudoRole
+       objects to sudoers internal data structures. Thanks to Andreas
+       Mueller for reporting and debugging this problem.
+       [c49fb26a6435] <1.8>
+
+2020-06-02  Todd C. Miller  <Todd.Miller@sudo.ws>
+
+       * src/exec_pty.c:
+       If event loop fails due to ENXIO, remove /dev/tty events and
+       recover. This fixes an issue on Solaris 11.4 (and probably others)
+       with "sudo reboot" when I/O logging is enabled. Previously, sudo
+       would kill the command if it was still running after the event loop
+       terminated, leaving the system in a half-dead state.
+       [b9b95c6778b8] <1.8>
+
+2020-06-01  Todd C. Miller  <Todd.Miller@sudo.ws>
+
+       * src/exec_pty.c:
+       Don't try to suspend sudo if the user's tty has gone away. Fixes a
+       problem on Solaris 11.4 (and possibly others) where sudo continually
+       tries to put itself in the background after the user's terminal has
+       been revoked.
+       [ed49c3e168ff] <1.8>
+
+2020-05-06  Todd C. Miller  <Todd.Miller@sudo.ws>
+
+       * doc/sudo.man.in, doc/sudo.mdoc.in, src/parse_args.c:
+       Don't allow duplicate values for command line options that take an
+       argument. Previously, if multiple instances of the same command line
+       option were specified, the last one would be used. This meant that,
+       for example, "sudo -u someuser -u otheruser id" would run the
+       command as "otheruser". This has the potential to cause problems for
+       programs that run sudo with a user-specified command that do not use
+       the "--" option to indicate that no more options should be
+       processed. While this is a bug in the calling program, there is
+       little downside to erroring out when multiple options of the same
+       type are specified on the command line. Bug #924
+       [e9ecdf650c0a] <1.8>
+
+2020-03-31  Todd C. Miller  <Todd.Miller@sudo.ws>
+
+       * src/exec_pty.c:
+       Don't kill the command just because the loop exited unexpectedly. We
+       currently have no good way to distinguish between an error executing
+       the command and an error while the command is running.
+
+       In the future, we should have additional status codes so we can tell
+       what type of condition caused the loop to exit.
+
+       For now, only kill the command if cstat is left uninitialized.
+       [f02ac319b458] <1.8>
+
 2020-03-14  Todd C. Miller  <Todd.Miller@sudo.ws>
 
        * .hgtags:
        Added tag SUDO_1_8_31p1 for changeset c80864e62917
-       [ec1850cd11a2] [tip] <1.8>
+       [ec1850cd11a2] <1.8>
 
        * NEWS, configure, configure.ac:
        Sudo 1.8.31p1
        outside the container.
        [314df33eea97] <1.8>
 
+2020-02-08  Todd C. Miller  <Todd.Miller@sudo.ws>
+
+       * src/parse_args.c, src/sudo.h:
+       Mark main sudo usage() function __noreturn__. This splits the usage
+       printing out into display_usage().
+       [8b0f8828c019] <1.8>
+
 2020-01-30  Todd C. Miller  <Todd.Miller@sudo.ws>
 
        * .hgtags:
diff --git a/NEWS b/NEWS
index 1c7ecd0..cdb5047 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,29 @@
+What's new in Sudo 1.8.31p2
+
+ * Sudo command line options that take a value may only be specified
+   once.  This is to help guard against problems caused by poorly
+   written scripts that invoke sudo with user-controlled input.
+   Bug #924.
+
+ * When running a command in a pty, sudo will no longer try to
+   suspend itself if the user's tty has been revoked (for instance
+   when the parent ssh daemon is killed).  This fixes a bug where
+   sudo would continuously suspend the command (which would succeed),
+   then suspend itself (which would fail due to the missing tty)
+   and then resume the command.
+
+ * If sudo's event loop fails due to the tty being revoked, remove
+   the user's tty events and restart the event loop (once).  This
+   fixes a problem when running "sudo reboot" in a pty on some
+   systems.  When the event loop exited unexpectedly, sudo would
+   kill the command running in the pty, which in the case of "reboot",
+   could lead to the system being in a half-rebooted state.
+
+ * Fixed a regression introduced in sudo 1.8.23 in the LDAP and
+   SSSD back-ends where a missing sudoHost attribute was treated
+   as an "ALL" wildcard value.  A sudoRole with no sudoHost attribute
+   is now ignored as it was prior to version 1.8.23.
+
 What's new in Sudo 1.8.31p1
 
  * Sudo once again ignores a failure to restore the RLIMIT_CORE
index a05b4a1..1799a02 100644 (file)
    */
 #undef HAVE_DECL_ERRNO
 
+/* Define to 1 if you have the declaration of `getdelim', and to 0 if you
+   don't. */
+#undef HAVE_DECL_GETDELIM
+
 /* Define to 1 if you have the declaration of `getdomainname', and to 0 if you
    don't. */
 #undef HAVE_DECL_GETDOMAINNAME
index cdf9397..13659ff 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for sudo 1.8.31p1.
+# Generated by GNU Autoconf 2.69 for sudo 1.8.31p2.
 #
 # Report bugs to <https://bugzilla.sudo.ws/>.
 #
@@ -590,8 +590,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='sudo'
 PACKAGE_TARNAME='sudo'
-PACKAGE_VERSION='1.8.31p1'
-PACKAGE_STRING='sudo 1.8.31p1'
+PACKAGE_VERSION='1.8.31p2'
+PACKAGE_STRING='sudo 1.8.31p2'
 PACKAGE_BUGREPORT='https://bugzilla.sudo.ws/'
 PACKAGE_URL=''
 
@@ -825,6 +825,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -1042,6 +1043,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1294,6 +1296,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1431,7 +1442,7 @@ fi
 for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
                datadir sysconfdir sharedstatedir localstatedir includedir \
                oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-               libdir localedir mandir
+               libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1544,7 +1555,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures sudo 1.8.31p1 to adapt to many kinds of systems.
+\`configure' configures sudo 1.8.31p2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1584,6 +1595,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -1609,7 +1621,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of sudo 1.8.31p1:";;
+     short | recursive ) echo "Configuration of sudo 1.8.31p2:";;
    esac
   cat <<\_ACEOF
 
@@ -1875,7 +1887,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-sudo configure 1.8.31p1
+sudo configure 1.8.31p2
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2584,7 +2596,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by sudo $as_me 1.8.31p1, which was
+It was created by sudo $as_me 1.8.31p2, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -19380,6 +19392,19 @@ if test "x$ac_cv_func_getdelim" = xyes; then :
 #define HAVE_GETDELIM 1
 _ACEOF
 
+    # Out of date gcc fixed includes may result in missing getdelim() prototype
+    ac_fn_c_check_decl "$LINENO" "getdelim" "ac_cv_have_decl_getdelim" "$ac_includes_default"
+if test "x$ac_cv_have_decl_getdelim" = xyes; then :
+  ac_have_decl=1
+else
+  ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_DECL_GETDELIM $ac_have_decl
+_ACEOF
+
+
 else
 
     case " $LIBOBJS " in
@@ -27543,7 +27568,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by sudo $as_me 1.8.31p1, which was
+This file was extended by sudo $as_me 1.8.31p2, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -27609,7 +27634,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-sudo config.status 1.8.31p1
+sudo config.status 1.8.31p2
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
index 20802a6..e8389e8 100644 (file)
@@ -18,7 +18,7 @@ dnl ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 dnl OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 dnl
 AC_PREREQ([2.59])
-AC_INIT([sudo], [1.8.31p1], [https://bugzilla.sudo.ws/], [sudo])
+AC_INIT([sudo], [1.8.31p2], [https://bugzilla.sudo.ws/], [sudo])
 AC_CONFIG_HEADER([config.h pathnames.h])
 AC_CONFIG_SRCDIR([src/sudo.c])
 dnl
@@ -2560,7 +2560,10 @@ AC_CHECK_FUNCS([getgrouplist], [], [
     esac
     SUDO_APPEND_COMPAT_EXP(sudo_getgrouplist)
 ])
-AC_CHECK_FUNCS([getdelim], [], [
+AC_CHECK_FUNCS([getdelim], [
+    # Out of date gcc fixed includes may result in missing getdelim() prototype
+    AC_CHECK_DECLS([getdelim])
+], [
     AC_LIBOBJ(getdelim)
     SUDO_APPEND_COMPAT_EXP(sudo_getdelim)
     COMPAT_TEST_PROGS="${COMPAT_TEST_PROGS}${COMPAT_TEST_PROGS+ }getdelim_test"
index 98f543b..90c7d68 100644 (file)
@@ -25,7 +25,7 @@
 .nr BA @BAMAN@
 .nr LC @LCMAN@
 .nr PS @PSMAN@
-.TH "SUDO" "@mansectsu@" "October 20, 2019" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
+.TH "SUDO" "@mansectsu@" "May 6, 2020" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
 .nh
 .if n .ad l
 .SH "NAME"
@@ -686,6 +686,12 @@ option indicates that
 \fBsudo\fR
 should stop processing command line arguments.
 .PP
+Options that take a value may only be specified once.
+This is to help guard against problems caused by poorly written
+scripts that invoke
+\fBsudo\fR
+with user-controlled input.
+.PP
 Environment variables to be set for the command may also be passed
 on the command line in the form of
 \fIVAR\fR=\fIvalue\fR,
index 4ee2f00..9b16a79 100644 (file)
@@ -24,7 +24,7 @@
 .nr BA @BAMAN@
 .nr LC @LCMAN@
 .nr PS @PSMAN@
-.Dd October 20, 2019
+.Dd May 6, 2020
 .Dt SUDO @mansectsu@
 .Os Sudo @PACKAGE_VERSION@
 .Sh NAME
@@ -638,6 +638,12 @@ option indicates that
 should stop processing command line arguments.
 .El
 .Pp
+Options that take a value may only be specified once.
+This is to help guard against problems caused by poorly written
+scripts that invoke
+.Nm sudo
+with user-controlled input.
+.Pp
 Environment variables to be set for the command may also be passed
 on the command line in the form of
 .Ar VAR Ns = Ns Ar value ,
index 396e1cb..0388a86 100644 (file)
@@ -408,10 +408,13 @@ __dso_public int sudo_getgrouplist(const char *name, GETGROUPS_T basegid, GETGRO
 # undef getgrouplist
 # define getgrouplist(_a, _b, _c, _d) sudo_getgrouplist((_a), (_b), (_c), (_d))
 #endif /* GETGROUPLIST */
-#ifndef HAVE_GETDELIM
+#if !defined(HAVE_GETDELIM)
 __dso_public ssize_t sudo_getdelim(char **bufp, size_t *bufsizep, int delim, FILE *fp);
 # undef getdelim
 # define getdelim(_a, _b, _c, _d) sudo_getdelim((_a), (_b), (_c), (_d))
+#elif defined(HAVE_DECL_GETDELIM) && !HAVE_DECL_GETDELIM
+/* getdelim present in libc but missing prototype (old gcc fixed includes?) */
+ssize_t getdelim(char **bufp, size_t *bufsizep, int delim, FILE *fp);
 #endif /* HAVE_GETDELIM */
 #ifndef HAVE_GETUSERSHELL
 __dso_public char *sudo_getusershell(void);
index 583af5d..89d197b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * SPDX-License-Identifier: ISC
  *
- * Copyright (c) 2003-2019 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2003-2020 Todd C. Miller <Todd.Miller@sudo.ws>
  *
  * This code is derived from software contributed by Aaron Spangler.
  *
@@ -363,16 +363,21 @@ sudo_ldap_check_non_unix_group(LDAP *ld, LDAPMessage *entry, struct passwd *pw)
  * Extract the dn from an entry and return the first rdn from it.
  */
 static char *
-sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry)
+sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry, int *rc)
 {
 #ifdef HAVE_LDAP_STR2DN
     char *dn, *rdn = NULL;
     LDAPDN tmpDN;
     debug_decl(sudo_ldap_get_first_rdn, SUDOERS_DEBUG_LDAP)
 
-    if ((dn = ldap_get_dn(ld, entry)) == NULL)
+    if ((dn = ldap_get_dn(ld, entry)) == NULL) {
+       int optrc = ldap_get_option(ld, LDAP_OPT_RESULT_CODE, rc);
+       if (optrc != LDAP_OPT_SUCCESS)
+           *rc = optrc;
        debug_return_str(NULL);
-    if (ldap_str2dn(dn, &tmpDN, LDAP_DN_FORMAT_LDAP) == LDAP_SUCCESS) {
+    }
+    *rc = ldap_str2dn(dn, &tmpDN, LDAP_DN_FORMAT_LDAP);
+    if (*rc == LDAP_SUCCESS) {
        ldap_rdn2str(tmpDN[0], &rdn, LDAP_DN_FORMAT_UFN);
        ldap_dnfree(tmpDN);
     }
@@ -382,11 +387,20 @@ sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry)
     char *dn, **edn;
     debug_decl(sudo_ldap_get_first_rdn, SUDOERS_DEBUG_LDAP)
 
-    if ((dn = ldap_get_dn(ld, entry)) == NULL)
+    if ((dn = ldap_get_dn(ld, entry)) == NULL) {
+       int optrc = ldap_get_option(ld, LDAP_OPT_RESULT_CODE, rc);
+       if (optrc != LDAP_OPT_SUCCESS)
+           *rc = optrc;
        debug_return_str(NULL);
+    }
     edn = ldap_explode_dn(dn, 1);
     ldap_memfree(dn);
-    debug_return_str(edn ? edn[0] : NULL);
+    if (edn == NULL) {
+       *rc = LDAP_NO_MEMORY;
+       debug_return_str(NULL);
+    }
+    *rc = LDAP_SUCCESS;
+    debug_return_str(edn[0]);
 #endif
 }
 
@@ -405,13 +419,21 @@ sudo_ldap_parse_options(LDAP *ld, LDAPMessage *entry, struct defaults_list *defs
 
     bv = sudo_ldap_get_values_len(ld, entry, "sudoOption", &rc);
     if (bv == NULL) {
-       if (rc == LDAP_NO_MEMORY)
+       if (rc == LDAP_NO_MEMORY) {
+           sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
            debug_return_bool(false);
+       }
        debug_return_bool(true);
     }
 
     /* Use sudoRole in place of file name in defaults. */
-    cn = sudo_ldap_get_first_rdn(ld, entry);
+    cn = sudo_ldap_get_first_rdn(ld, entry, &rc);
+    if (cn == NULL) {
+       if (rc == LDAP_NO_MEMORY) {
+           sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+           goto done;
+       }
+    }
     if (asprintf(&cp, "sudoRole %s", cn ? cn : "UNKNOWN") == -1) {
        sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
        goto done;
@@ -1133,6 +1155,88 @@ berval_iter(void **vp)
     return *bv ? (*bv)->bv_val : NULL;
 }
 
+/*
+ * Wrapper for sudo_ldap_role_to_priv() that takes an LDAPMessage.
+ * Returns a struct privilege on success or NULL on failure.
+ */
+static struct privilege *
+ldap_entry_to_priv(LDAP *ld, LDAPMessage *entry, int *rc_out)
+{
+    struct berval **cmnds = NULL, **hosts = NULL;
+    struct berval **runasusers = NULL, **runasgroups = NULL;
+    struct berval **opts = NULL, **notbefore = NULL, **notafter = NULL;
+    struct privilege *priv = NULL;
+    char *cn = NULL;
+    int rc;
+    debug_decl(ldap_entry_to_priv, SUDOERS_DEBUG_LDAP);
+
+    /* Ignore sudoRole without sudoCommand or sudoHost. */
+    cmnds = sudo_ldap_get_values_len(ld, entry, "sudoCommand", &rc);
+    if (cmnds == NULL)
+       goto cleanup;
+    hosts = sudo_ldap_get_values_len(ld, entry, "sudoHost", &rc);
+    if (hosts == NULL)
+       goto cleanup;
+
+    /* Get the entry's dn for long format printing. */
+    if ((cn = sudo_ldap_get_first_rdn(ld, entry, &rc)) == NULL)
+       goto cleanup;
+
+    /* Get sudoRunAsUser / sudoRunAsGroup */
+    runasusers = sudo_ldap_get_values_len(ld, entry, "sudoRunAsUser", &rc);
+    if (runasusers == NULL) {
+       if (rc != LDAP_NO_MEMORY)
+           runasusers = sudo_ldap_get_values_len(ld, entry, "sudoRunAs", &rc);
+       if (rc == LDAP_NO_MEMORY)
+           goto cleanup;
+    }
+    runasgroups = sudo_ldap_get_values_len(ld, entry, "sudoRunAsGroup", &rc);
+    if (rc == LDAP_NO_MEMORY)
+       goto cleanup;
+
+    /* Get sudoNotBefore / sudoNotAfter */
+    notbefore = sudo_ldap_get_values_len(ld, entry, "sudoNotBefore", &rc);
+    if (rc == LDAP_NO_MEMORY)
+       goto cleanup;
+    notafter = sudo_ldap_get_values_len(ld, entry, "sudoNotAfter", &rc);
+    if (rc == LDAP_NO_MEMORY)
+       goto cleanup;
+
+    /* Parse sudoOptions. */
+    opts = sudo_ldap_get_values_len(ld, entry, "sudoOption", &rc);
+    if (rc == LDAP_NO_MEMORY)
+       goto cleanup;
+
+    priv = sudo_ldap_role_to_priv(cn, hosts, runasusers, runasgroups,
+       cmnds, opts, notbefore ? notbefore[0]->bv_val : NULL,
+       notafter ? notafter[0]->bv_val : NULL, false, true, berval_iter);
+    if (priv == NULL) {
+       rc = LDAP_NO_MEMORY;
+       goto cleanup;
+    }
+
+cleanup:
+    if (cn != NULL)
+       ldap_memfree(cn);
+    if (cmnds != NULL)
+       ldap_value_free_len(cmnds);
+    if (hosts != NULL)
+       ldap_value_free_len(hosts);
+    if (runasusers != NULL)
+       ldap_value_free_len(runasusers);
+    if (runasgroups != NULL)
+       ldap_value_free_len(runasgroups);
+    if (opts != NULL)
+       ldap_value_free_len(opts);
+    if (notbefore != NULL)
+       ldap_value_free_len(notbefore);
+    if (notafter != NULL)
+       ldap_value_free_len(notafter);
+
+    *rc_out = rc;
+    debug_return_ptr(priv);
+}
+
 static bool
 ldap_to_sudoers(LDAP *ld, struct ldap_result *lres,
     struct userspec_list *ldap_userspecs)
@@ -1146,6 +1250,7 @@ ldap_to_sudoers(LDAP *ld, struct ldap_result *lres,
     /* We only have a single userspec */
     if ((us = calloc(1, sizeof(*us))) == NULL)
        goto oom;
+    us->file = rcstr_dup("LDAP");
     TAILQ_INIT(&us->users);
     TAILQ_INIT(&us->privileges);
     STAILQ_INIT(&us->comments);
@@ -1157,81 +1262,16 @@ ldap_to_sudoers(LDAP *ld, struct ldap_result *lres,
     m->type = ALL;
     TAILQ_INSERT_TAIL(&us->users, m, entries);
 
-    /* Treat each sudoRole as a separate privilege. */
+    /* Treat each entry as a separate privilege. */
     for (i = 0; i < lres->nentries; i++) {
-       LDAPMessage *entry = lres->entries[i].entry;
-       struct berval **cmnds = NULL, **hosts = NULL;
-       struct berval **runasusers = NULL, **runasgroups = NULL;
-       struct berval **opts = NULL, **notbefore = NULL, **notafter = NULL;
-       struct privilege *priv = NULL;
-       char *cn = NULL;
-
-       /* Ignore sudoRole without sudoCommand. */
-       cmnds = sudo_ldap_get_values_len(ld, entry, "sudoCommand", &rc);
-       if (cmnds == NULL) {
-           if (rc == LDAP_NO_MEMORY)
-               goto cleanup;
-           continue;
-       }
+       struct privilege *priv;
 
-       /* Get the entry's dn for long format printing. */
-       if ((cn = sudo_ldap_get_first_rdn(ld, entry)) == NULL)
-           goto cleanup;
-
-       /* Get sudoHost */
-       hosts = sudo_ldap_get_values_len(ld, entry, "sudoHost", &rc);
-       if (rc == LDAP_NO_MEMORY)
-           goto cleanup;
-
-       /* Get sudoRunAsUser / sudoRunAsGroup */
-       runasusers = sudo_ldap_get_values_len(ld, entry, "sudoRunAsUser", &rc);
-       if (runasusers == NULL) {
-           if (rc != LDAP_NO_MEMORY)
-               runasusers = sudo_ldap_get_values_len(ld, entry, "sudoRunAs", &rc);
+       priv = ldap_entry_to_priv(ld, lres->entries[i].entry, &rc);
+       if (priv == NULL) {
            if (rc == LDAP_NO_MEMORY)
-               goto cleanup;
+               goto oom;
+           continue;
        }
-       runasgroups = sudo_ldap_get_values_len(ld, entry, "sudoRunAsGroup", &rc);
-       if (rc == LDAP_NO_MEMORY)
-           goto cleanup;
-
-       /* Get sudoNotBefore / sudoNotAfter */
-       notbefore = sudo_ldap_get_values_len(ld, entry, "sudoNotBefore", &rc);
-       if (rc == LDAP_NO_MEMORY)
-           goto cleanup;
-       notafter = sudo_ldap_get_values_len(ld, entry, "sudoNotAfter", &rc);
-       if (rc == LDAP_NO_MEMORY)
-           goto cleanup;
-
-       /* Parse sudoOptions. */
-       opts = sudo_ldap_get_values_len(ld, entry, "sudoOption", &rc);
-       if (rc == LDAP_NO_MEMORY)
-           goto cleanup;
-
-       priv = sudo_ldap_role_to_priv(cn, hosts, runasusers, runasgroups,
-           cmnds, opts, notbefore ? notbefore[0]->bv_val : NULL,
-           notafter ? notafter[0]->bv_val : NULL, false, true, berval_iter);
-
-    cleanup:
-       if (cn != NULL)
-           ldap_memfree(cn);
-       if (cmnds != NULL)
-           ldap_value_free_len(cmnds);
-       if (hosts != NULL)
-           ldap_value_free_len(hosts);
-       if (runasusers != NULL)
-           ldap_value_free_len(runasusers);
-       if (runasgroups != NULL)
-           ldap_value_free_len(runasgroups);
-       if (opts != NULL)
-           ldap_value_free_len(opts);
-       if (notbefore != NULL)
-           ldap_value_free_len(notbefore);
-       if (notafter != NULL)
-           ldap_value_free_len(notafter);
-
-       if (priv == NULL)
-           goto oom;
        TAILQ_INSERT_TAIL(&us->privileges, priv, entries);
     }
 
index 2853093..c44f5fe 100644 (file)
@@ -169,7 +169,8 @@ sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw,
                        *matching_cs = cs;
                        *defs = &priv->defaults;
                        sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
-                           "userspec matched @ %s:%d %s", us->file, us->lineno,
+                           "userspec matched @ %s:%d %s",
+                           us->file ? us->file : "???", us->lineno,
                            cmnd_match ? "allowed" : "denied");
                        debug_return_int(cmnd_match);
                    }
index f859d03..f25c4f5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * SPDX-License-Identifier: ISC
  *
- * Copyright (c) 2003-2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2003-2020 Todd C. Miller <Todd.Miller@sudo.ws>
  * Copyright (c) 2011 Daniel Kopecek <dkopecek@redhat.com>
  *
  * This code is derived from software contributed by Aaron Spangler.
@@ -240,6 +240,126 @@ val_array_iter(void **vp)
     return *val_array;
 }
 
+/*
+ * Wrapper for sudo_ldap_role_to_priv() that takes an sss rule..
+ * Returns a struct privilege on success or NULL on failure.
+ */
+static struct privilege *
+sss_rule_to_priv(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule,
+    int *rc_out)
+{
+    char **cmnds, **runasusers = NULL, **runasgroups = NULL;
+    char **opts = NULL, **notbefore = NULL, **notafter = NULL;
+    char **hosts = NULL, **cn_array = NULL, *cn = NULL;
+    struct privilege *priv = NULL;
+    int rc;
+    debug_decl(sss_rule_to_priv, SUDOERS_DEBUG_SSSD);
+
+    /* Ignore sudoRole without sudoCommand or sudoHost. */
+    rc = handle->fn_get_values(rule, "sudoCommand", &cmnds);
+    if (rc != 0)
+       goto cleanup;
+    rc = handle->fn_get_values(rule, "sudoHost", &hosts);
+    if (rc != 0)
+       goto cleanup;
+
+    /* Get the entry's dn for long format printing. */
+    rc = handle->fn_get_values(rule, "cn", &cn_array);
+    if (rc != 0)
+       goto cleanup;
+    cn = cn_array[0];
+
+    /* Get sudoRunAsUser / sudoRunAs */
+    rc = handle->fn_get_values(rule, "sudoRunAsUser", &runasusers);
+    switch (rc) {
+    case 0:
+       break;
+    case ENOENT:
+       rc = handle->fn_get_values(rule, "sudoRunAs", &runasusers);
+       switch (rc) {
+           case 0:
+           case ENOENT:
+               break;
+           default:
+               goto cleanup;
+       }
+       break;
+    default:
+       goto cleanup;
+    }
+
+    /* Get sudoRunAsGroup */
+    rc = handle->fn_get_values(rule, "sudoRunAsGroup", &runasgroups);
+    switch (rc) {
+    case 0:
+    case ENOENT:
+       break;
+    default:
+       goto cleanup;
+    }
+
+    /* Get sudoNotBefore */
+    rc = handle->fn_get_values(rule, "sudoNotBefore", &notbefore);
+    switch (rc) {
+    case 0:
+    case ENOENT:
+       break;
+    default:
+       goto cleanup;
+    }
+
+    /* Get sudoNotAfter */
+    rc = handle->fn_get_values(rule, "sudoNotAfter", &notafter);
+    switch (rc) {
+    case 0:
+    case ENOENT:
+       break;
+    default:
+       goto cleanup;
+    }
+
+    /* Parse sudoOptions. */
+    rc = handle->fn_get_values(rule, "sudoOption", &opts);
+    switch (rc) {
+    case 0:
+    case ENOENT:
+       break;
+    default:
+       goto cleanup;
+    }
+
+    priv = sudo_ldap_role_to_priv(cn, hosts, runasusers, runasgroups,
+       cmnds, opts, notbefore ? notbefore[0] : NULL,
+       notafter ? notafter[0] : NULL, false, true, val_array_iter);
+    if (priv == NULL) {
+       rc = ENOMEM;
+       goto cleanup;
+    }
+    rc = 0;
+
+cleanup:
+    if (cn_array != NULL)
+       handle->fn_free_values(cn_array);
+    if (cmnds != NULL)
+       handle->fn_free_values(cmnds);
+    if (hosts != NULL)
+       handle->fn_free_values(hosts);
+    if (runasusers != NULL)
+       handle->fn_free_values(runasusers);
+    if (runasgroups != NULL)
+       handle->fn_free_values(runasgroups);
+    if (opts != NULL)
+       handle->fn_free_values(opts);
+    if (notbefore != NULL)
+       handle->fn_free_values(notbefore);
+    if (notafter != NULL)
+       handle->fn_free_values(notafter);
+
+    *rc_out = rc;
+
+    debug_return_ptr(priv);
+}
+
 static bool
 sss_to_sudoers(struct sudo_sss_handle *handle,
     struct sss_sudo_result *sss_result)
@@ -252,6 +372,7 @@ sss_to_sudoers(struct sudo_sss_handle *handle,
     /* We only have a single userspec */
     if ((us = calloc(1, sizeof(*us))) == NULL)
        goto oom;
+    us->file = rcstr_dup("SSSD");
     TAILQ_INIT(&us->users);
     TAILQ_INIT(&us->privileges);
     STAILQ_INIT(&us->comments);
@@ -264,7 +385,7 @@ sss_to_sudoers(struct sudo_sss_handle *handle,
     TAILQ_INSERT_TAIL(&us->users, m, entries);
 
     /*
-     * Treat each sudoRole as a separate privilege.
+     * Treat each rule as a separate privilege.
      *
      * Sssd has already sorted the rules in descending order.
      * The conversion to a sudoers parse tree requires that entries be
@@ -272,10 +393,8 @@ sss_to_sudoers(struct sudo_sss_handle *handle,
      */
     for (i = sss_result->num_rules; i-- > 0; ) {
        struct sss_sudo_rule *rule = sss_result->rules + i;
-       char **cmnds, **runasusers = NULL, **runasgroups = NULL;
-       char **opts = NULL, **notbefore = NULL, **notafter = NULL;
-       char **hosts = NULL, **cn_array = NULL, *cn = NULL;
-       struct privilege *priv = NULL;
+       struct privilege *priv;
+       int rc;
 
        /*
         * We don't know whether a rule was included due to a user/group
@@ -284,113 +403,11 @@ sss_to_sudoers(struct sudo_sss_handle *handle,
        if (!sudo_sss_check_user(handle, rule))
            continue;
 
-       switch (handle->fn_get_values(rule, "sudoCommand", &cmnds)) {
-       case 0:
-           break;
-       case ENOENT:
-           /* Ignore sudoRole without sudoCommand. */
+       if ((priv = sss_rule_to_priv(handle, rule, &rc)) == NULL) {
+           if (rc == ENOMEM)
+               goto oom;
            continue;
-       default:
-           goto cleanup;
        }
-
-       /* Get the entry's dn for long format printing. */
-       switch (handle->fn_get_values(rule, "cn", &cn_array)) {
-       case 0:
-           cn = cn_array[0];
-           break;
-       case ENOENT:
-           break;
-       default:
-           goto cleanup;
-       }
-
-       /* Get sudoHost */
-       switch (handle->fn_get_values(rule, "sudoHost", &hosts)) {
-       case 0:
-       case ENOENT:
-           break;
-       default:
-           goto cleanup;
-       }
-
-       /* Get sudoRunAsUser / sudoRunAs */
-       switch (handle->fn_get_values(rule, "sudoRunAsUser", &runasusers)) {
-       case 0:
-           break;
-       case ENOENT:
-           switch (handle->fn_get_values(rule, "sudoRunAs", &runasusers)) {
-               case 0:
-               case ENOENT:
-                   break;
-               default:
-                   goto cleanup;
-           }
-           break;
-       default:
-           goto cleanup;
-       }
-
-       /* Get sudoRunAsGroup */
-       switch (handle->fn_get_values(rule, "sudoRunAsGroup", &runasgroups)) {
-       case 0:
-       case ENOENT:
-           break;
-       default:
-           goto cleanup;
-       }
-
-       /* Get sudoNotBefore */
-       switch (handle->fn_get_values(rule, "sudoNotBefore", &notbefore)) {
-       case 0:
-       case ENOENT:
-           break;
-       default:
-           goto cleanup;
-       }
-
-       /* Get sudoNotAfter */
-       switch (handle->fn_get_values(rule, "sudoNotAfter", &notafter)) {
-       case 0:
-       case ENOENT:
-           break;
-       default:
-           goto cleanup;
-       }
-
-       /* Parse sudoOptions. */
-       switch (handle->fn_get_values(rule, "sudoOption", &opts)) {
-       case 0:
-       case ENOENT:
-           break;
-       default:
-           goto cleanup;
-       }
-
-       priv = sudo_ldap_role_to_priv(cn, hosts, runasusers, runasgroups,
-           cmnds, opts, notbefore ? notbefore[0] : NULL,
-           notafter ? notafter[0] : NULL, false, true, val_array_iter);
-
-    cleanup:
-       if (cn_array != NULL)
-           handle->fn_free_values(cn_array);
-       if (cmnds != NULL)
-           handle->fn_free_values(cmnds);
-       if (hosts != NULL)
-           handle->fn_free_values(hosts);
-       if (runasusers != NULL)
-           handle->fn_free_values(runasusers);
-       if (runasgroups != NULL)
-           handle->fn_free_values(runasgroups);
-       if (opts != NULL)
-           handle->fn_free_values(opts);
-       if (notbefore != NULL)
-           handle->fn_free_values(notbefore);
-       if (notafter != NULL)
-           handle->fn_free_values(notafter);
-
-       if (priv == NULL)
-           goto oom;
        TAILQ_INSERT_TAIL(&us->privileges, priv, entries);
     }
 
index 8d04fa1..7e33bca 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * SPDX-License-Identifier: ISC
  *
- * Copyright (c) 2009-2019 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2009-2020 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
@@ -116,7 +116,7 @@ static void del_io_events(bool nonblocking);
 static void sync_ttysize(struct exec_closure_pty *ec);
 static int safe_close(int fd);
 static void ev_free_by_fd(struct sudo_event_base *evbase, int fd);
-static void check_foreground(struct exec_closure_pty *ec);
+static pid_t check_foreground(struct exec_closure_pty *ec);
 static void add_io_events(struct sudo_event_base *evbase);
 static void schedule_signal(struct exec_closure_pty *ec, int signo);
 
@@ -456,22 +456,25 @@ log_winchange(unsigned int rows, unsigned int cols)
 
 /*
  * Check whether we are running in the foregroup.
- * Updates the foreground global and does lazy init of the
- * the pty slave as needed.
+ * Updates the foreground global and updates the window size.
+ * Returns 0 if there is no tty, the foreground process group ID
+ * on success, or -1 on failure (tty revoked).
  */
-static void
+static pid_t
 check_foreground(struct exec_closure_pty *ec)
 {
+    int ret = 0;
     debug_decl(check_foreground, SUDO_DEBUG_EXEC);
 
     if (io_fds[SFD_USERTTY] != -1) {
-       foreground = tcgetpgrp(io_fds[SFD_USERTTY]) == ec->ppgrp;
+       if ((ret = tcgetpgrp(io_fds[SFD_USERTTY])) != -1) {
+           foreground = ret == ec->ppgrp;
 
-       /* Also check for window size changes. */
-       sync_ttysize(ec);
+           /* Also check for window size changes. */
+           sync_ttysize(ec);
+       }
     }
-
-    debug_return;
+    debug_return_int(ret);
 }
 
 /*
@@ -494,8 +497,12 @@ suspend_sudo(struct exec_closure_pty *ec, int signo)
         * If sudo is already the foreground process, just resume the command
         * in the foreground.  If not, we'll suspend sudo and resume later.
         */
-       if (!foreground)
-           check_foreground(ec);
+       if (!foreground) {
+           if (check_foreground(ec) == -1) {
+               /* User's tty was revoked. */
+               break;
+           }
+       }
        if (foreground) {
            if (ttymode != TERM_RAW) {
                if (sudo_term_raw(io_fds[SFD_USERTTY], 0))
@@ -537,7 +544,10 @@ suspend_sudo(struct exec_closure_pty *ec, int signo)
        log_suspend(SIGCONT);
 
        /* Check foreground/background status on resume. */
-       check_foreground(ec);
+       if (check_foreground(ec) == -1) {
+           /* User's tty was revoked. */
+           break;
+       }
 
        /*
         * We always resume the command in the foreground if sudo itself
@@ -872,6 +882,9 @@ schedule_signal(struct exec_closure_pty *ec, int signo)
     char signame[SIG2STR_MAX];
     debug_decl(schedule_signal, SUDO_DEBUG_EXEC)
 
+    if (signo == 0)
+       debug_return;
+
     if (signo == SIGCONT_FG)
        strlcpy(signame, "CONT_FG", sizeof(signame));
     else if (signo == SIGCONT_BG)
@@ -1159,6 +1172,10 @@ fill_exec_closure_pty(struct exec_closure_pty *ec, struct command_status *cstat,
     ec->cols = user_details.ts_cols;
     TAILQ_INIT(&ec->monitor_messages);
 
+    /* Reset cstat for running the command. */
+    cstat->type = CMD_INVALID;
+    cstat->val = 0;
+
     /* Setup event base and events. */
     ec->evbase = sudo_ev_base_alloc();
     if (ec->evbase == NULL)
@@ -1300,6 +1317,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
     bool interpose[3] = { false, false, false };
     struct exec_closure_pty ec = { 0 };
     struct plugin_container *plugin;
+    int evloop_retries = -1;
     sigset_t set, oset;
     struct sigaction sa;
     struct stat sb;
@@ -1576,15 +1594,38 @@ exec_pty(struct command_details *details, struct command_status *cstat)
     /*
      * In the event loop we pass input from user tty to master
      * and pass output from master to stdout and IO plugin.
+     * Try to recover on ENXIO, it means the tty was revoked.
      */
     add_io_events(ec.evbase);
-    if (sudo_ev_dispatch(ec.evbase) == -1)
-       sudo_warn(U_("error in event loop"));
-    if (sudo_ev_got_break(ec.evbase)) {
-       /* error from callback or monitor died */
-       sudo_debug_printf(SUDO_DEBUG_ERROR, "event loop exited prematurely");
-       /* XXX - may need to terminate command if cmnd_pid != -1 */
-    }
+    do {
+       if (sudo_ev_dispatch(ec.evbase) == -1)
+           sudo_warn(U_("error in event loop"));
+       if (sudo_ev_got_break(ec.evbase)) {
+           /* error from callback or monitor died */
+           sudo_debug_printf(SUDO_DEBUG_ERROR, "event loop exited prematurely");
+           /* XXX: no good way to know if we should terminate the command. */
+           if (cstat->val == CMD_INVALID && ec.cmnd_pid != -1) {
+               /* no status message, kill command */
+               terminate_command(ec.cmnd_pid, true);
+               ec.cmnd_pid = -1;
+               /* TODO: need way to pass an error to the sudo front end */
+               cstat->type = CMD_WSTATUS;
+               cstat->val = W_EXITCODE(1, SIGKILL);
+           }
+       } else if (!sudo_ev_got_exit(ec.evbase)) {
+           switch (errno) {
+           case ENXIO:
+           case EIO:
+           case EBADF:
+               /* /dev/tty was revoked, remove tty events and retry (once) */
+               if (evloop_retries == -1 && io_fds[SFD_USERTTY] != -1) {
+                   ev_free_by_fd(ec.evbase, io_fds[SFD_USERTTY]);
+                   evloop_retries = 1;
+               }
+               break;
+           }
+       }
+    } while (evloop_retries-- > 0);
 
     /* Flush any remaining output, free I/O bufs and events, do logout. */
     pty_finish(cstat);
index f870359..504b723 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * SPDX-License-Identifier: ISC
  *
- * Copyright (c) 1993-1996, 1998-2017 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 1993-1996, 1998-2020 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
@@ -59,7 +59,7 @@ int tgetpass_flags;
  * Local functions.
  */
 static void help(void) __attribute__((__noreturn__));
-static void usage_excl(int);
+static void usage_excl(void) __attribute__((__noreturn__));
 
 /*
  * Mapping of command line flags to name/value settings.
@@ -226,7 +226,7 @@ parse_env_list(struct environment *e, char *list)
        (cp = strtok_r(NULL, ",", &last))) {
        if (strchr(cp, '=') != NULL) {
            sudo_warnx(U_("invalid environment variable name: %s"), cp);
-           usage(1);
+           usage();
        }
        if ((val = getenv(cp)) != NULL)
            env_set(e, cp, val);
@@ -249,15 +249,13 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv,
     int valid_flags = DEFAULT_VALID_FLAGS;
     int ch, i;
     char *cp;
-    const char *runas_user = NULL;
-    const char *runas_group = NULL;
     const char *progname;
     int proglen;
     debug_decl(parse_args, SUDO_DEBUG_ARGS)
 
     /* Is someone trying something funny? */
     if (argc <= 0)
-       usage(1);
+       usage();
 
     /* Pass progname to plugin so it can call initprogname() */
     progname = getprogname();
@@ -313,7 +311,9 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv,
                case 'a':
                    assert(optarg != NULL);
                    if (*optarg == '\0')
-                       usage(1);
+                       usage();
+                   if (sudo_settings[ARG_BSDAUTH_TYPE].value != NULL)
+                       usage();
                    sudo_settings[ARG_BSDAUTH_TYPE].value = optarg;
                    break;
 #endif
@@ -327,15 +327,19 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv,
                    assert(optarg != NULL);
                    if (sudo_strtonum(optarg, 3, INT_MAX, NULL) == 0) {
                        sudo_warnx(U_("the argument to -C must be a number greater than or equal to 3"));
-                       usage(1);
+                       usage();
                    }
+                   if (sudo_settings[ARG_CLOSEFROM].value != NULL)
+                       usage();
                    sudo_settings[ARG_CLOSEFROM].value = optarg;
                    break;
 #ifdef HAVE_LOGIN_CAP_H
                case 'c':
                    assert(optarg != NULL);
                    if (*optarg == '\0')
-                       usage(1);
+                       usage();
+                   if (sudo_settings[ARG_LOGIN_CLASS].value != NULL)
+                       usage();
                    sudo_settings[ARG_LOGIN_CLASS].value = optarg;
                    break;
 #endif
@@ -352,12 +356,14 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv,
                        sudo_settings[ARG_PRESERVE_ENVIRONMENT].value = "true";
                        SET(flags, MODE_PRESERVE_ENV);
                    } else {
+                       if (extra_env.env_len != 0)
+                           usage();
                        parse_env_list(&extra_env, optarg);
                    }
                    break;
                case 'e':
                    if (mode && mode != MODE_EDIT)
-                       usage_excl(1);
+                       usage_excl();
                    mode = MODE_EDIT;
                    sudo_settings[ARG_SUDOEDIT].value = "true";
                    valid_flags = MODE_NONINTERACTIVE;
@@ -365,8 +371,9 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv,
                case 'g':
                    assert(optarg != NULL);
                    if (*optarg == '\0')
-                       usage(1);
-                   runas_group = optarg;
+                       usage();
+                   if (sudo_settings[ARG_RUNAS_GROUP].value != NULL)
+                       usage();
                    sudo_settings[ARG_RUNAS_GROUP].value = optarg;
                    break;
                case 'H':
@@ -381,12 +388,14 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv,
                         */
                        if (got_host_flag && !is_envar &&
                            argv[optind] != NULL && argv[optind][0] != '-') {
+                           if (sudo_settings[ARG_REMOTE_HOST].value != NULL)
+                               usage();
                            sudo_settings[ARG_REMOTE_HOST].value = argv[optind++];
                            continue;
                        }
                        if (mode && mode != MODE_HELP) {
                            if (strcmp(progname, "sudoedit") != 0)
-                               usage_excl(1);
+                               usage_excl();
                        }
                        mode = MODE_HELP;
                        valid_flags = 0;
@@ -396,7 +405,9 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv,
                case OPT_HOSTNAME:
                    assert(optarg != NULL);
                    if (*optarg == '\0')
-                       usage(1);
+                       usage();
+                   if (sudo_settings[ARG_REMOTE_HOST].value != NULL)
+                       usage();
                    sudo_settings[ARG_REMOTE_HOST].value = optarg;
                    break;
                case 'i':
@@ -409,7 +420,7 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv,
                case 'K':
                    sudo_settings[ARG_IGNORE_TICKET].value = "true";
                    if (mode && mode != MODE_KILL)
-                       usage_excl(1);
+                       usage_excl();
                    mode = MODE_KILL;
                    valid_flags = 0;
                    break;
@@ -418,7 +429,7 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv,
                        if (mode == MODE_LIST)
                            SET(flags, MODE_LONG_LIST);
                        else
-                           usage_excl(1);
+                           usage_excl();
                    }
                    mode = MODE_LIST;
                    valid_flags = MODE_NONINTERACTIVE|MODE_LONG_LIST;
@@ -433,25 +444,33 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv,
                case 'p':
                    /* An empty prompt is allowed. */
                    assert(optarg != NULL);
+                   if (sudo_settings[ARG_PROMPT].value != NULL)
+                       usage();
                    sudo_settings[ARG_PROMPT].value = optarg;
                    break;
 #ifdef HAVE_SELINUX
                case 'r':
                    assert(optarg != NULL);
                    if (*optarg == '\0')
-                       usage(1);
+                       usage();
+                   if (sudo_settings[ARG_SELINUX_ROLE].value != NULL)
+                       usage();
                    sudo_settings[ARG_SELINUX_ROLE].value = optarg;
                    break;
                case 't':
                    assert(optarg != NULL);
                    if (*optarg == '\0')
-                       usage(1);
+                       usage();
+                   if (sudo_settings[ARG_SELINUX_TYPE].value != NULL)
+                       usage();
                    sudo_settings[ARG_SELINUX_TYPE].value = optarg;
                    break;
 #endif
                case 'T':
                    /* Plugin determines whether empty timeout is allowed. */
                    assert(optarg != NULL);
+                   if (sudo_settings[ARG_TIMEOUT].value != NULL)
+                       usage();
                    sudo_settings[ARG_TIMEOUT].value = optarg;
                    break;
                case 'S':
@@ -463,31 +482,32 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv,
                    break;
                case 'U':
                    assert(optarg != NULL);
-                   if (*optarg == '\0')
-                       usage(1);
+                   if (list_user != NULL || *optarg == '\0')
+                       usage();
                    list_user = optarg;
                    break;
                case 'u':
                    assert(optarg != NULL);
                    if (*optarg == '\0')
-                       usage(1);
-                   runas_user = optarg;
+                       usage();
+                   if (sudo_settings[ARG_RUNAS_USER].value != NULL)
+                       usage();
                    sudo_settings[ARG_RUNAS_USER].value = optarg;
                    break;
                case 'v':
                    if (mode && mode != MODE_VALIDATE)
-                       usage_excl(1);
+                       usage_excl();
                    mode = MODE_VALIDATE;
                    valid_flags = MODE_NONINTERACTIVE;
                    break;
                case 'V':
                    if (mode && mode != MODE_VERSION)
-                       usage_excl(1);
+                       usage_excl();
                    mode = MODE_VERSION;
                    valid_flags = 0;
                    break;
                default:
-                   usage(1);
+                   usage();
            }
        } else if (!got_end_of_args && is_envar) {
            /* Insert key=value pair, crank optind and resume getopt. */
@@ -521,39 +541,40 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv,
     if (ISSET(flags, MODE_LOGIN_SHELL)) {
        if (ISSET(flags, MODE_SHELL)) {
            sudo_warnx(U_("you may not specify both the `-i' and `-s' options"));
-           usage(1);
+           usage();
        }
        if (ISSET(flags, MODE_PRESERVE_ENV)) {
            sudo_warnx(U_("you may not specify both the `-i' and `-E' options"));
-           usage(1);
+           usage();
        }
        SET(flags, MODE_SHELL);
     }
     if ((flags & valid_flags) != flags)
-       usage(1);
+       usage();
     if (mode == MODE_EDIT &&
        (ISSET(flags, MODE_PRESERVE_ENV) || extra_env.env_len != 0)) {
        if (ISSET(mode, MODE_PRESERVE_ENV))
            sudo_warnx(U_("the `-E' option is not valid in edit mode"));
        if (extra_env.env_len != 0)
            sudo_warnx(U_("you may not specify environment variables in edit mode"));
-       usage(1);
+       usage();
     }
-    if ((runas_user != NULL || runas_group != NULL) &&
+    if ((sudo_settings[ARG_RUNAS_USER].value != NULL ||
+        sudo_settings[ARG_RUNAS_GROUP].value != NULL) &&
        !ISSET(mode, MODE_EDIT | MODE_RUN | MODE_CHECK | MODE_VALIDATE)) {
-       usage(1);
+       usage();
     }
     if (list_user != NULL && mode != MODE_LIST && mode != MODE_CHECK) {
        sudo_warnx(U_("the `-U' option may only be used with the `-l' option"));
-       usage(1);
+       usage();
     }
     if (ISSET(tgetpass_flags, TGP_STDIN) && ISSET(tgetpass_flags, TGP_ASKPASS)) {
        sudo_warnx(U_("the `-A' and `-S' options may not be used together"));
-       usage(1);
+       usage();
     }
     if ((argc == 0 && mode == MODE_EDIT) ||
        (argc > 0 && !ISSET(mode, MODE_RUN | MODE_EDIT | MODE_CHECK)))
-       usage(1);
+       usage();
     if (argc == 0 && mode == MODE_RUN && !ISSET(flags, MODE_SHELL)) {
        SET(flags, (MODE_IMPLIED_SHELL | MODE_SHELL));
        sudo_settings[ARG_IMPLIED_SHELL].value = "true";
@@ -648,11 +669,11 @@ usage_out(const char *buf)
 }
 
 /*
- * Give usage message and exit.
+ * Display usage message.
  * The actual usage strings are in sudo_usage.h for configure substitution.
  */
-void
-usage(int fatal)
+static void
+display_usage(int (*output)(const char *))
 {
     struct sudo_lbuf lbuf;
     char *uvec[6];
@@ -678,27 +699,35 @@ usage(int fatal)
      * tty width.
      */
     ulen = (int)strlen(getprogname()) + 8;
-    sudo_lbuf_init(&lbuf, fatal ? usage_err : usage_out, ulen, NULL,
+    sudo_lbuf_init(&lbuf, output, ulen, NULL,
        user_details.ts_cols);
     for (i = 0; uvec[i] != NULL; i++) {
        sudo_lbuf_append(&lbuf, "usage: %s%s", getprogname(), uvec[i]);
        sudo_lbuf_print(&lbuf);
     }
     sudo_lbuf_destroy(&lbuf);
-    if (fatal)
-       exit(1);
+}
+
+/*
+ * Display usage message and exit.
+ */
+void
+usage(void)
+{
+    display_usage(usage_err);
+    exit(1);
 }
 
 /*
  * Tell which options are mutually exclusive and exit.
  */
 static void
-usage_excl(int fatal)
+usage_excl(void)
 {
     debug_decl(usage_excl, SUDO_DEBUG_ARGS)
 
     sudo_warnx(U_("Only one of the -e, -h, -i, -K, -l, -s, -v or -V options may be specified"));
-    usage(fatal);
+    usage();
 }
 
 static void
@@ -716,7 +745,7 @@ help(void)
        sudo_lbuf_append(&lbuf, _("%s - execute a command as another user\n\n"), pname);
     sudo_lbuf_print(&lbuf);
 
-    usage(0);
+    display_usage(usage_out);
 
     sudo_lbuf_append(&lbuf, _("\nOptions:\n"));
     sudo_lbuf_append(&lbuf, "  -A, --askpass                 %s\n",
index 9c4b4bc..c6d8f62 100644 (file)
@@ -217,7 +217,7 @@ main(int argc, char *argv[], char *envp[])
     ok = policy_open(&policy_plugin, settings, user_info, envp);
     if (ok != 1) {
        if (ok == -2)
-           usage(1);
+           usage();
        else
            sudo_fatalx(U_("unable to initialize policy plugin"));
     }
@@ -255,7 +255,7 @@ main(int argc, char *argv[], char *envp[])
            sudo_debug_printf(SUDO_DEBUG_INFO, "policy plugin returns %d", ok);
            if (ok != 1) {
                if (ok == -2)
-                   usage(1);
+                   usage();
                exit(EXIT_FAILURE); /* plugin printed error message */
            }
            /* Reset nargv/nargc based on argv_out. */
@@ -276,7 +276,7 @@ main(int argc, char *argv[], char *envp[])
                    iolog_unlink(plugin);
                    break;
                case -2:
-                   usage(1);
+                   usage();
                    break;
                default:
                    sudo_fatalx(U_("error initializing I/O plugin %s"),
index ac0b323..7745139 100644 (file)
@@ -232,7 +232,7 @@ extern int sudo_debug_instance;
 int sudo_edit(struct command_details *details);
 
 /* parse_args.c */
-void usage(int);
+void usage(void) __attribute__((__noreturn__));
 
 /* openbsd.c */
 int os_init_openbsd(int argc, char *argv[], char *envp[]);