[Midnightbsd-cvs] CVS Commit: contrib/sudo: Updating sudo to latest version from OpenBSD.

archite at midnightbsd.org archite at midnightbsd.org
Sat Aug 18 21:58:35 EDT 2007


Log Message:
-----------
Updating sudo to latest version from OpenBSD.

Modified Files:
--------------
    src/contrib/sudo:
        BUGS (r1.1 -> r1.2)
        CHANGES (r1.1 -> r1.2)
        HISTORY (r1.1 -> r1.2)
        INSTALL (r1.1 -> r1.2)
        INSTALL.configure (r1.1 -> r1.2)
        LICENSE (r1.1 -> r1.2)
        Makefile.in (r1.2 -> r1.3)
        PORTING (r1.1 -> r1.2)
        README (r1.1 -> r1.2)
        README.LDAP (r1.1 -> r1.2)
        TROUBLESHOOTING (r1.1 -> r1.2)
        UPGRADE (r1.1 -> r1.2)
        aclocal.m4 (r1.1 -> r1.2)
        alloc.c (r1.1 -> r1.2)
        check.c (r1.1 -> r1.2)
        compat.h (r1.1 -> r1.2)
        config.guess (r1.1 -> r1.2)
        config.h (r1.1 -> r1.2)
        config.h.in (r1.1 -> r1.2)
        config.sub (r1.1 -> r1.2)
        configure (r1.1 -> r1.2)
        configure.in (r1.1 -> r1.2)
        def_data.c (r1.1 -> r1.2)
        def_data.h (r1.1 -> r1.2)
        def_data.in (r1.1 -> r1.2)
        defaults.c (r1.1 -> r1.2)
        env.c (r1.2 -> r1.3)
        fileops.c (r1.1 -> r1.2)
        find_path.c (r1.1 -> r1.2)
        getspwuid.c (r1.1 -> r1.2)
        gettime.c (r1.1 -> r1.2)
        goodpath.c (r1.1 -> r1.2)
        ins_csops.h (r1.1 -> r1.2)
        install-sh (r1.1 -> r1.2)
        interfaces.c (r1.1 -> r1.2)
        interfaces.h (r1.1 -> r1.2)
        logging.c (r1.2 -> r1.3)
        logging.h (r1.1 -> r1.2)
        parse.c (r1.1 -> r1.2)
        parse.h (r1.1 -> r1.2)
        parse.lex (r1.1 -> r1.2)
        parse.yacc (r1.1 -> r1.2)
        pathnames.h (r1.1 -> r1.2)
        pathnames.h.in (r1.1 -> r1.2)
        set_perms.c (r1.1 -> r1.2)
        sudo.c (r1.1 -> r1.2)
        sudo.h (r1.1 -> r1.2)
        sudo_edit.c (r1.1 -> r1.2)
        sudo_noexec.c (r1.1 -> r1.2)
        sudoers (r1.1 -> r1.2)
        sudoers2ldif (r1.1 -> r1.2)
        testsudoers.c (r1.1 -> r1.2)
        tgetpass.c (r1.1 -> r1.2)
        version.h (r1.2 -> r1.3)
        visudo.c (r1.1 -> r1.2)
        zero_bytes.c (r1.1 -> r1.2)
    src/contrib/sudo/auth:
        afs.c (r1.1 -> r1.2)
        aix_auth.c (r1.1 -> r1.2)
        bsdauth.c (r1.1 -> r1.2)
        dce.c (r1.1 -> r1.2)
        fwtk.c (r1.1 -> r1.2)
        kerb4.c (r1.1 -> r1.2)
        kerb5.c (r1.1 -> r1.2)
        pam.c (r1.1 -> r1.2)
        passwd.c (r1.1 -> r1.2)
        rfc1938.c (r1.1 -> r1.2)
        secureware.c (r1.1 -> r1.2)
        securid.c (r1.1 -> r1.2)
        securid5.c (r1.1 -> r1.2)
        sia.c (r1.1 -> r1.2)
        sudo_auth.c (r1.1 -> r1.2)
        sudo_auth.h (r1.1 -> r1.2)

Added Files:
-----------
    src/contrib/sudo:
        ldap.c (r1.1)
        memrchr.c (r1.1)
        schema.OpenLDAP (r1.1)
        schema.iPlanet (r1.1)
        sudo.pod (r1.1)
        sudoers.pod (r1.1)
        varsub (r1.1)
        visudo.pod (r1.1)
    src/contrib/sudo/lib:
        Makefile (r1.1)
    src/contrib/sudo/noexec:
        Makefile (r1.1)
        shlib_version (r1.1)
    src/contrib/sudo/sudo:
        Makefile (r1.1)
    src/contrib/sudo/visudo:
        Makefile (r1.1)

-------------- next part --------------
Index: sudo_edit.c
===================================================================
RCS file: /home/cvs/src/contrib/sudo/sudo_edit.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lcontrib/sudo/sudo_edit.c -Lcontrib/sudo/sudo_edit.c -u -r1.1 -r1.2
--- contrib/sudo/sudo_edit.c
+++ contrib/sudo/sudo_edit.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004 Todd C. Miller <Todd.Miller at courtesan.com>
+ * Copyright (c) 2004-2005 Todd C. Miller <Todd.Miller at courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "config.h"
+#include <config.h>
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -48,32 +48,40 @@
 #endif /* HAVE_ERR_H */
 #include <ctype.h>
 #include <pwd.h>
+#include <grp.h>
 #include <signal.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <time.h>
+#if TIME_WITH_SYS_TIME
+# include <time.h>
+#endif
+#ifndef HAVE_TIMESPEC
+# include <emul/timespec.h>
+#endif
 
 #include "sudo.h"
 
 #ifndef lint
-static const char rcsid[] = "$Sudo: sudo_edit.c,v 1.16 2004/09/15 16:16:20 millert Exp $";
+__unused static const char rcsid[] = "$Sudo: sudo_edit.c,v 1.6.2.7 2007/07/08 18:44:41 millert Exp $";
 #endif /* lint */
 
 extern sigaction_t saved_sa_int, saved_sa_quit, saved_sa_tstp, saved_sa_chld;
+extern char **environ;
 
 /*
  * Wrapper to allow users to edit privileged files with their own uid.
  */
-int sudo_edit(argc, argv)
+int sudo_edit(argc, argv, envp)
     int argc;
     char **argv;
+    char **envp;
 {
     ssize_t nread, nwritten;
     pid_t kidpid, pid;
     const char *tmpdir;
     char **nargv, **ap, *editor, *cp;
     char buf[BUFSIZ];
-    int i, ac, ofd, tfd, nargc, rval, tmplen;
+    int error, i, ac, ofd, tfd, nargc, rval, tmplen, wasblank;
     sigaction_t sa;
     struct stat sb;
     struct timespec ts1, ts2;
@@ -102,36 +110,38 @@
     /*
      * For each file specified by the user, make a temporary version
      * and copy the contents of the original to it.
-     * XXX - It would be nice to lock the original files but that means
-     *       keeping an extra fd open for each file.
      */
     tf = emalloc2(argc - 1, sizeof(*tf));
     memset(tf, 0, (argc - 1) * sizeof(*tf));
     for (i = 0, ap = argv + 1; i < argc - 1 && *ap != NULL; i++, ap++) {
+	error = -1;
 	set_perms(PERM_RUNAS);
-	ofd = open(*ap, O_RDONLY, 0644);
-	if (ofd != -1) {
+
+	/*
+	 * We close the password file before we try to open the user-specified
+	 * path to prevent the opening of things like /dev/fd/4.
+	 */
+	endpwent();
+	if ((ofd = open(*ap, O_RDONLY, 0644)) != -1 || errno == ENOENT) {
+	    if (ofd == -1) {
+		memset(&sb, 0, sizeof(sb));		/* new file */
+		error = 0;
+	    } else {
 #ifdef HAVE_FSTAT
-	    if (fstat(ofd, &sb) != 0) {
+		error = fstat(ofd, &sb);
 #else
-	    if (stat(tf[i].ofile, &sb) != 0) {
+		error = stat(tf[i].ofile, &sb);
 #endif
-		close(ofd);	/* XXX - could reset errno */
-		ofd = -1;
 	    }
 	}
 	set_perms(PERM_ROOT);
-	if (ofd == -1) {
-	    if (errno != ENOENT) {
+	if (error || !S_ISREG(sb.st_mode)) {
+	    if (error)
 		warn("%s", *ap);
-		argc--;
-		i--;
-		continue;
-	    }
-	    memset(&sb, 0, sizeof(sb));
-	} else if (!S_ISREG(sb.st_mode)) {
-	    warnx("%s: not a regular file", *ap);
-	    close(ofd);
+	    else
+		warnx("%s: not a regular file", *ap);
+	    if (ofd != -1)
+		close(ofd);
 	    argc--;
 	    i--;
 	    continue;
@@ -189,6 +199,7 @@
      * based on def_env_editor or def_editor since the editor runs with
      * the uid of the invoking user, not the runas (privileged) user.
      */
+    environ = envp;
     if (((editor = getenv("VISUAL")) != NULL && *editor != '\0') ||
 	((editor = getenv("EDITOR")) != NULL && *editor != '\0')) {
 	editor = estrdup(editor);
@@ -204,9 +215,13 @@
      * line args so look for those and alloc space for them too.
      */
     nargc = argc;
-    for (cp = editor + 1; *cp != '\0'; cp++) {
-	if (isblank((unsigned char)cp[0]) && !isblank((unsigned char)cp[-1]))
+    for (wasblank = FALSE, cp = editor; *cp != '\0'; cp++) {
+	if (isblank((unsigned char) *cp))
+	    wasblank = TRUE;
+	else if (wasblank) {
+	    wasblank = FALSE;
 	    nargc++;
+	}
     }
     nargv = (char **) emalloc2(nargc + 1, sizeof(char *));
     ac = 0;
@@ -238,6 +253,9 @@
 	(void) sigaction(SIGQUIT, &saved_sa_quit, NULL);
 	(void) sigaction(SIGCHLD, &saved_sa_chld, NULL);
 	set_perms(PERM_FULL_USER);
+	endpwent();
+	endgrent();
+	closefrom(STDERR_FILENO + 1);
 	execvp(nargv[0], nargv);
 	warn("unable to execute %s", nargv[0]);
 	_exit(127);
@@ -246,7 +264,7 @@
     /*
      * Wait for status from the child.  Most modern kernels
      * will not let an unprivileged child process send a
-     * signal to its privileged parent to we have to request
+     * signal to its privileged parent so we have to request
      * status when the child is stopped and then send the
      * same signal to our own pid.
      */
@@ -271,42 +289,44 @@
 
     /* Copy contents of temp files to real ones */
     for (i = 0; i < argc - 1; i++) {
+	error = -1;
 	set_perms(PERM_USER);
-	tfd = open(tf[i].tfile, O_RDONLY, 0644);
+	if ((tfd = open(tf[i].tfile, O_RDONLY, 0644)) != -1) {
+#ifdef HAVE_FSTAT
+	    error = fstat(tfd, &sb);
+#else
+	    error = stat(tf[i].tfile, &sb);
+#endif
+	}
 	set_perms(PERM_ROOT);
-	if (tfd < 0) {
-	    warn("unable to read %s", tf[i].tfile);
+	if (error || !S_ISREG(sb.st_mode)) {
+	    if (error)
+		warn("%s", tf[i].tfile);
+	    else
+		warnx("%s: not a regular file", tf[i].tfile);
 	    warnx("%s left unmodified", tf[i].ofile);
+	    if (tfd != -1)
+		close(tfd);
 	    continue;
 	}
-#ifdef HAVE_FSTAT
-	if (fstat(tfd, &sb) == 0) {
-	    if (!S_ISREG(sb.st_mode)) {
-		warnx("%s: not a regular file", tf[i].tfile);
-		warnx("%s left unmodified", tf[i].ofile);
-		continue;
-	    }
-	    if (tf[i].osize == sb.st_size &&
-		tf[i].omtim.tv_sec == mtim_getsec(sb) &&
-		tf[i].omtim.tv_nsec == mtim_getnsec(sb)) {
-		/*
-		 * If mtime and size match but the user spent no measurable
-		 * time in the editor we can't tell if the file was changed.
-		 */
+	if (tf[i].osize == sb.st_size && tf[i].omtim.tv_sec == mtim_getsec(sb)
+	    && tf[i].omtim.tv_nsec == mtim_getnsec(sb)) {
+	    /*
+	     * If mtime and size match but the user spent no measurable
+	     * time in the editor we can't tell if the file was changed.
+	     */
 #ifdef HAVE_TIMESPECSUB2
-		timespecsub(&ts1, &ts2);
+	    timespecsub(&ts1, &ts2);
 #else
-		timespecsub(&ts1, &ts2, &ts2);
+	    timespecsub(&ts1, &ts2, &ts2);
 #endif
-		if (timespecisset(&ts2)) {
-		    warnx("%s unchanged", tf[i].ofile);
-		    unlink(tf[i].tfile);
-		    close(tfd);
-		    continue;
-		}
+	    if (timespecisset(&ts2)) {
+		warnx("%s unchanged", tf[i].ofile);
+		unlink(tf[i].tfile);
+		close(tfd);
+		continue;
 	    }
 	}
-#endif
 	set_perms(PERM_RUNAS);
 	ofd = open(tf[i].ofile, O_WRONLY|O_TRUNC|O_CREAT, 0644);
 	set_perms(PERM_ROOT);
Index: def_data.h
===================================================================
RCS file: /home/cvs/src/contrib/sudo/def_data.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lcontrib/sudo/def_data.h -Lcontrib/sudo/def_data.h -u -r1.1 -r1.2
--- contrib/sudo/def_data.h
+++ contrib/sudo/def_data.h
@@ -114,6 +114,8 @@
 #define I_ENV_KEEP              56
 #define def_ignore_local_sudoers (sudo_defs_table[57].sd_un.flag)
 #define I_IGNORE_LOCAL_SUDOERS  57
+#define def_setenv              (sudo_defs_table[58].sd_un.flag)
+#define I_SETENV                58
 
 enum def_tupple {
 	never,
--- /dev/null
+++ contrib/sudo/sudoers.pod
@@ -0,0 +1,1323 @@
+=cut
+Copyright (c) 1994-1996, 1998-2005, 2007
+	Todd C. Miller <Todd.Miller at courtesan.com>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Sponsored in part by the Defense Advanced Research Projects
+Agency (DARPA) and Air Force Research Laboratory, Air Force
+Materiel Command, USAF, under agreement number F39502-99-1-0512.
+
+$Sudo: sudoers.pod,v 1.95.2.19 2007/08/13 16:30:02 millert Exp $
+=pod
+
+=head1 NAME
+
+sudoers - list of which users may execute what
+
+=head1 DESCRIPTION
+
+The I<sudoers> file is composed of two types of entries: aliases
+(basically variables) and user specifications (which specify who
+may run what).
+
+When multiple entries match for a user, they are applied in order.
+Where there are multiple matches, the last match is used (which is
+not necessarily the most specific match).
+
+The I<sudoers> grammar will be described below in Extended Backus-Naur
+Form (EBNF).  Don't despair if you don't know what EBNF is; it is
+fairly simple, and the definitions below are annotated.
+
+=head2 Quick guide to EBNF
+
+EBNF is a concise and exact way of describing the grammar of a language.
+Each EBNF definition is made up of I<production rules>.  E.g.,
+
+ symbol ::= definition | alternate1 | alternate2 ...
+
+Each I<production rule> references others and thus makes up a
+grammar for the language.  EBNF also contains the following
+operators, which many readers will recognize from regular
+expressions.  Do not, however, confuse them with "wildcard"
+characters, which have different meanings.
+
+=over 4
+
+=item C<?>
+
+Means that the preceding symbol (or group of symbols) is optional.
+That is, it may appear once or not at all.
+
+=item C<*>
+
+Means that the preceding symbol (or group of symbols) may appear
+zero or more times.
+
+=item C<+>
+
+Means that the preceding symbol (or group of symbols) may appear
+one or more times.
+
+=back
+
+Parentheses may be used to group symbols together.  For clarity,
+we will use single quotes ('') to designate what is a verbatim character
+string (as opposed to a symbol name).
+
+=head2 Aliases
+
+There are four kinds of aliases: C<User_Alias>, C<Runas_Alias>,
+C<Host_Alias> and C<Cmnd_Alias>.
+
+ Alias ::= 'User_Alias'  User_Alias (':' User_Alias)* |
+	   'Runas_Alias' Runas_Alias (':' Runas_Alias)* |
+	   'Host_Alias'  Host_Alias (':' Host_Alias)* |
+	   'Cmnd_Alias'  Cmnd_Alias (':' Cmnd_Alias)*
+
+ User_Alias ::= NAME '=' User_List
+
+ Runas_Alias ::= NAME '=' Runas_List
+
+ Host_Alias ::= NAME '=' Host_List
+
+ Cmnd_Alias ::= NAME '=' Cmnd_List
+
+ NAME ::= [A-Z]([A-Z][0-9]_)*
+
+Each I<alias> definition is of the form
+
+ Alias_Type NAME = item1, item2, ...
+
+where I<Alias_Type> is one of C<User_Alias>, C<Runas_Alias>, C<Host_Alias>,
+or C<Cmnd_Alias>.  A C<NAME> is a string of uppercase letters, numbers,
+and underscore characters ('_').  A C<NAME> B<must> start with an
+uppercase letter.  It is possible to put several alias definitions
+of the same type on a single line, joined by a colon (':').  E.g.,
+
+ Alias_Type NAME = item1, item2, item3 : NAME = item4, item5
+
+The definitions of what constitutes a valid I<alias> member follow.
+
+ User_List ::= User |
+	       User ',' User_List
+
+ User ::= '!'* username |
+	  '!'* '%'group |
+	  '!'* '+'netgroup |
+	  '!'* User_Alias
+
+A C<User_List> is made up of one or more usernames, system groups
+(prefixed with '%'), netgroups (prefixed with '+') and other aliases.
+Each list item may be prefixed with one or more '!' operators.
+An odd number of '!' operators negate the value of the item; an even
+number just cancel each other out.
+
+ Runas_List ::= Runas_User |
+		Runas_User ',' Runas_List
+
+ Runas_User ::= '!'* username |
+	        '!'* '#'uid |
+	        '!'* '%'group |
+	        '!'* +netgroup |
+	        '!'* Runas_Alias
+
+A C<Runas_List> is similar to a C<User_List> except that it can
+also contain uids (prefixed with '#') and instead of C<User_Alias>es
+it can contain C<Runas_Alias>es.  Note that usernames and groups
+are matched as strings.  In other words, two users (groups) with
+the same uid (gid) are considered to be distinct.  If you wish to
+match all usernames with the same uid (e.g.E<nbsp>root and toor), you
+can use a uid instead (#0 in the example given).
+
+ Host_List ::= Host |
+	       Host ',' Host_List
+
+ Host ::= '!'* hostname |
+	  '!'* ip_addr |
+	  '!'* network(/netmask)? |
+	  '!'* '+'netgroup |
+	  '!'* Host_Alias
+
+A C<Host_List> is made up of one or more hostnames, IP addresses,
+network numbers, netgroups (prefixed with '+') and other aliases.
+Again, the value of an item may be negated with the '!' operator.
+If you do not specify a netmask along with the network number,
+B<sudo> will query each of the local host's network interfaces and,
+if the network number corresponds to one of the hosts's network
+interfaces, the corresponding netmask will be used.  The netmask
+may be specified either in standard IP address notation
+(e.g.E<nbsp>255.255.255.0 or ffff:ffff:ffff:ffff::),
+or CIDR notation (number of bits, e.g.E<nbsp>24 or 64).  A hostname may
+include shell-style wildcards (see the L<Wildcards> section below),
+but unless the C<hostname> command on your machine returns the fully
+qualified hostname, you'll need to use the I<fqdn> option for
+wildcards to be useful.
+
+ Cmnd_List ::= Cmnd |
+	       Cmnd ',' Cmnd_List
+
+ commandname ::= filename |
+	         filename args |
+	         filename '""'
+
+ Cmnd ::= '!'* commandname |
+	  '!'* directory |
+	  '!'* "sudoedit" |
+	  '!'* Cmnd_Alias
+
+A C<Cmnd_List> is a list of one or more commandnames, directories, and other
+aliases.  A commandname is a fully qualified filename which may include
+shell-style wildcards (see the L<Wildcards> section below).  A simple
+filename allows the user to run the command with any arguments he/she
+wishes.  However, you may also specify command line arguments (including
+wildcards).  Alternately, you can specify C<""> to indicate that the command
+may only be run B<without> command line arguments.  A directory is a
+fully qualified pathname ending in a '/'.  When you specify a directory
+in a C<Cmnd_List>, the user will be able to run any file within that directory
+(but not in any subdirectories therein).
+
+If a C<Cmnd> has associated command line arguments, then the arguments
+in the C<Cmnd> must match exactly those given by the user on the command line
+(or match the wildcards if there are any).  Note that the following
+characters must be escaped with a '\' if they are used in command
+arguments: ',', ':', '=', '\'.  The special command C<"sudoedit">
+is used to permit a user to run B<sudo> with the B<-e> flag (or
+as B<sudoedit>).  It may take command line arguments just as
+a normal command does.
+
+=head2 Defaults
+
+Certain configuration options may be changed from their default
+values at runtime via one or more C<Default_Entry> lines.  These
+may affect all users on any host, all users on a specific host, a
+specific user, or commands being run as a specific user.
+
+ Default_Type ::= 'Defaults' |
+		  'Defaults' '@' Host |
+		  'Defaults' ':' User |
+		  'Defaults' '>' RunasUser
+
+ Default_Entry ::= Default_Type Parameter_List
+
+ Parameter_List ::= Parameter |
+		    Parameter ',' Parameter_List
+
+ Parameter ::= Parameter '=' Value |
+	       Parameter '+=' Value |
+	       Parameter '-=' Value |
+	       '!'* Parameter
+
+Parameters may be B<flags>, B<integer> values, B<strings>, or B<lists>.
+Flags are implicitly boolean and can be turned off via the '!'
+operator.  Some integer, string and list parameters may also be
+used in a boolean context to disable them.  Values may be enclosed
+in double quotes (C<">) when they contain multiple words.  Special
+characters may be escaped with a backslash (C<\>).
+
+Lists have two additional assignment operators, C<+=> and C<-=>.
+These operators are used to add to and delete from a list respectively.
+It is not an error to use the C<-=> operator to remove an element
+that does not exist in a list.
+
+See L</"SUDOERS OPTIONS"> for a list of supported Defaults parameters.
+
+=head2 User Specification
+
+ User_Spec ::= User_List Host_List '=' Cmnd_Spec_List \
+	       (':' Host_List '=' Cmnd_Spec_List)*
+
+ Cmnd_Spec_List ::= Cmnd_Spec |
+		    Cmnd_Spec ',' Cmnd_Spec_List
+
+ Cmnd_Spec ::= Runas_Spec? Tag_Spec* Cmnd
+
+ Runas_Spec ::= '(' Runas_List ')'
+
+ Tag_Spec ::= ('NOPASSWD:' | 'PASSWD:' | 'NOEXEC:' | 'EXEC:' |
+	       'SETENV:' | 'NOSETENV:')
+
+A B<user specification> determines which commands a user may run
+(and as what user) on specified hosts.  By default, commands are
+run as B<root>, but this can be changed on a per-command basis.
+
+Let's break that down into its constituent parts:
+
+=head2 Runas_Spec
+
+A C<Runas_Spec> is simply a C<Runas_List> (as defined above)
+enclosed in a set of parentheses.  If you do not specify a
+C<Runas_Spec> in the user specification, a default C<Runas_Spec>
+of B<root> will be used.  A C<Runas_Spec> sets the default for
+commands that follow it.  What this means is that for the entry:
+
+ dgb	boulder = (operator) /bin/ls, /bin/kill, /usr/bin/lprm
+
+The user B<dgb> may run F</bin/ls>, F</bin/kill>, and
+F</usr/bin/lprm> -- but only as B<operator>.  E.g.,
+
+ $ sudo -u operator /bin/ls.
+
+It is also possible to override a C<Runas_Spec> later on in an
+entry.  If we modify the entry like so:
+
+ dgb	boulder = (operator) /bin/ls, (root) /bin/kill, /usr/bin/lprm
+
+Then user B<dgb> is now allowed to run F</bin/ls> as B<operator>,
+but  F</bin/kill> and F</usr/bin/lprm> as B<root>.
+
+=head2 Tag_Spec
+
+A command may have zero or more tags associated with it.  There are
+six possible tag values, C<NOPASSWD>, C<PASSWD>, C<NOEXEC>, C<EXEC>,
+C<SETENV> and C<NOSETENV>.
+Once a tag is set on a C<Cmnd>, subsequent C<Cmnd>s in the
+C<Cmnd_Spec_List>, inherit the tag unless it is overridden by the
+opposite tag (i.e.: C<PASSWD> overrides C<NOPASSWD> and C<NOEXEC>
+overrides C<EXEC>).
+
+=head3 NOPASSWD and PASSWD
+
+By default, B<sudo> requires that a user authenticate him or herself
+before running a command.  This behavior can be modified via the
+C<NOPASSWD> tag.  Like a C<Runas_Spec>, the C<NOPASSWD> tag sets
+a default for the commands that follow it in the C<Cmnd_Spec_List>.
+Conversely, the C<PASSWD> tag can be used to reverse things.
+For example:
+
+ ray	rushmore = NOPASSWD: /bin/kill, /bin/ls, /usr/bin/lprm
+
+would allow the user B<ray> to run F</bin/kill>, F</bin/ls>, and
+F</usr/bin/lprm> as root on the machine rushmore as B<root> without
+authenticating himself.  If we only want B<ray> to be able to
+run F</bin/kill> without a password the entry would be:
+
+ ray	rushmore = NOPASSWD: /bin/kill, PASSWD: /bin/ls, /usr/bin/lprm
+
+Note, however, that the C<PASSWD> tag has no effect on users who are
+in the group specified by the I<exempt_group> option.
+
+By default, if the C<NOPASSWD> tag is applied to any of the entries
+for a user on the current host, he or she will be able to run
+C<sudo -l> without a password.  Additionally, a user may only run
+C<sudo -v> without a password if the C<NOPASSWD> tag is present
+for all a user's entries that pertain to the current host.
+This behavior may be overridden via the verifypw and listpw options.
+
+=head3 NOEXEC and EXEC
+
+If B<sudo> has been compiled with I<noexec> support and the underlying
+operating system supports it, the C<NOEXEC> tag can be used to prevent
+a dynamically-linked executable from running further commands itself.
+
+In the following example, user B<aaron> may run F</usr/bin/more>
+and F</usr/bin/vi> but shell escapes will be disabled.
+
+ aaron	shanty = NOEXEC: /usr/bin/more, /usr/bin/vi
+
+See the L<PREVENTING SHELL ESCAPES> section below for more details
+on how C<NOEXEC> works and whether or not it will work on your system.
+
+=head3 SETENV and NOSETENV
+
+These tags override the value of the I<setenv> option on a per-command
+basis.  Note that if C<SETENV> has been set for a command, any
+environment variables set on the command line way are not subject
+to the restrictions imposed by I<env_check>, I<env_delete>, or
+I<env_keep>.  As such, only trusted users should be allowed to set
+variables in this manner.
+
+=head2 Wildcards
+
+B<sudo> allows shell-style I<wildcards> (aka meta or glob characters)
+to be used in pathnames as well as command line arguments in the
+I<sudoers> file.  Wildcard matching is done via the B<POSIX>
+L<fnmatch(3)> routine.  Note that these are I<not> regular expressions.
+
+=over 8
+
+=item C<*>
+
+Matches any set of zero or more characters.
+
+=item C<?>
+
+Matches any single character.
+
+=item C<[...]>
+
+Matches any character in the specified range.
+
+=item C<[!...]>
+
+Matches any character B<not> in the specified range.
+
+=item C<\x>
+
+For any character "x", evaluates to "x".  This is used to
+escape special characters such as: "*", "?", "[", and "}".
+
+=back
+
+Note that a forward slash ('/') will B<not> be matched by
+wildcards used in the pathname.  When matching the command
+line arguments, however, a slash B<does> get matched by
+wildcards.  This is to make a path like:
+
+    /usr/bin/*
+
+match F</usr/bin/who> but not F</usr/bin/X11/xterm>.
+
+=head2 Exceptions to wildcard rules
+
+The following exceptions apply to the above rules:
+
+=over 8
+
+=item C<"">
+
+If the empty string C<""> is the only command line argument in the
+I<sudoers> entry it means that command is not allowed to be run
+with B<any> arguments.
+
+=back
+
+=head2 Other special characters and reserved words
+
+The pound sign ('#') is used to indicate a comment (unless it is
+part of a #include directive or unless it occurs in the context of
+a user name and is followed by one or more digits, in which case
+it is treated as a uid).  Both the comment character and any text
+after it, up to the end of the line, are ignored.
+
+The reserved word B<ALL> is a built-in I<alias> that always causes
+a match to succeed.  It can be used wherever one might otherwise
+use a C<Cmnd_Alias>, C<User_Alias>, C<Runas_Alias>, or C<Host_Alias>.
+You should not try to define your own I<alias> called B<ALL> as the
+built-in alias will be used in preference to your own.  Please note
+that using B<ALL> can be dangerous since in a command context, it
+allows the user to run B<any> command on the system.
+
+An exclamation point ('!') can be used as a logical I<not> operator
+both in an I<alias> and in front of a C<Cmnd>.  This allows one to
+exclude certain values.  Note, however, that using a C<!> in
+conjunction with the built-in C<ALL> alias to allow a user to
+run "all but a few" commands rarely works as intended (see SECURITY
+NOTES below).
+
+Long lines can be continued with a backslash ('\') as the last
+character on the line.
+
+Whitespace between elements in a list as well as special syntactic
+characters in a I<User Specification> ('=', ':', '(', ')') is optional.
+
+The following characters must be escaped with a backslash ('\') when
+used as part of a word (e.g.E<nbsp>a username or hostname):
+'@', '!', '=', ':', ',', '(', ')', '\'.
+
+=head1 SUDOERS OPTIONS
+
+B<sudo>'s behavior can be modified by C<Default_Entry> lines, as
+explained earlier.  A list of all supported Defaults parameters,
+grouped by type, are listed below.
+
+B<Flags>:
+
+=over 16
+
+=item always_set_home
+
+If set, B<sudo> will set the C<HOME> environment variable to the home
+directory of the target user (which is root unless the B<-u> option is used).
+This effectively means that the B<-H> flag is always implied.
+This flag is I<off> by default.
+
+=item authenticate
+
+If set, users must authenticate themselves via a password (or other
+means of authentication) before they may run commands.  This default
+may be overridden via the C<PASSWD> and C<NOPASSWD> tags.
+This flag is I<on> by default.
+
+=item env_editor
+
+If set, B<visudo> will use the value of the EDITOR or VISUAL
+environment variables before falling back on the default editor list.
+Note that this may create a security hole as it allows the user to
+run any arbitrary command as root without logging.  A safer alternative
+is to place a colon-separated list of editors in the C<editor>
+variable.  B<visudo> will then only use the EDITOR or VISUAL if
+they match a value specified in C<editor>.  This flag is I<@env_editor@> by
+default.
+
+=item env_reset
+
+If set, B<sudo> will reset the environment to only contain the
+LOGNAME, SHELL, USER, USERNAME and the C<SUDO_*> variables.  Any
+variables in the caller's environment that match the C<env_keep>
+and C<env_check> lists are then added.  The default contents of the
+C<env_keep> and C<env_check> lists are displayed when B<sudo> is
+run by root with the I<-V> option.  If B<sudo> was compiled with
+the C<SECURE_PATH> option, its value will be used for the C<PATH>
+environment variable.  This flag is I<on> by default.
+
+=item fqdn
+
+Set this flag if you want to put fully qualified hostnames in the
+I<sudoers> file.  I.e., instead of myhost you would use myhost.mydomain.edu.
+You may still use the short form if you wish (and even mix the two).
+Beware that turning on I<fqdn> requires B<sudo> to make DNS lookups
+which may make B<sudo> unusable if DNS stops working (for example
+if the machine is not plugged into the network).  Also note that
+you must use the host's official name as DNS knows it.  That is,
+you may not use a host alias (C<CNAME> entry) due to performance
+issues and the fact that there is no way to get all aliases from
+DNS.  If your machine's hostname (as returned by the C<hostname>
+command) is already fully qualified you shouldn't need to set
+I<fqdn>.  This flag is I<@fqdn@> by default.
+
+=item ignore_dot
+
+If set, B<sudo> will ignore '.' or '' (current dir) in the C<PATH>
+environment variable; the C<PATH> itself is not modified.  This
+flag is I<@ignore_dot@> by default.  Currently, while it is possible
+to set I<ignore_dot> in I<sudoers>, its value is not used.  This option
+should be considered read-only (it will be fixed in a future version
+of B<sudo>).
+
+=item ignore_local_sudoers
+
+If set via LDAP, parsing of @sysconfdir@/sudoers will be skipped.
+This is intended for Enterprises that wish to prevent the usage of local
+sudoers files so that only LDAP is used.  This thwarts the efforts of
+rogue operators who would attempt to add roles to @sysconfdir@/sudoers.
+When this option is present, @sysconfdir@/sudoers does not even need to exist. 
+Since this option tells B<sudo> how to behave when no specific LDAP entries
+have been matched, this sudoOption is only meaningful for the cn=defaults
+section.  This flag is I<off> by default.
+
+=item insults
+
+If set, B<sudo> will insult users when they enter an incorrect
+password.  This flag is I<@insults@> by default.
+
+=item log_host
+
+If set, the hostname will be logged in the (non-syslog) B<sudo> log file.
+This flag is I<off> by default.
+
+=item log_year
+
+If set, the four-digit year will be logged in the (non-syslog) B<sudo> log file.
+This flag is I<off> by default.
+
+=item long_otp_prompt
+
+When validating with a One Time Password (OPT) scheme such as
+B<S/Key> or B<OPIE>, a two-line prompt is used to make it easier
+to cut and paste the challenge to a local window.  It's not as
+pretty as the default but some people find it more convenient.  This
+flag is I<@long_otp_prompt@> by default.
+
+=item mail_always
+
+Send mail to the I<mailto> user every time a users runs B<sudo>.
+This flag is I<off> by default.
+
+=item mail_badpass
+
+Send mail to the I<mailto> user if the user running B<sudo> does not
+enter the correct password.  This flag is I<off> by default.
+
+=item mail_no_host
+
+If set, mail will be sent to the I<mailto> user if the invoking
+user exists in the I<sudoers> file, but is not allowed to run
+commands on the current host.  This flag is I<@mail_no_host@> by default.
+
+=item mail_no_perms
+
+If set, mail will be sent to the I<mailto> user if the invoking
+user is allowed to use B<sudo> but the command they are trying is not
+listed in their I<sudoers> file entry or is explicitly denied.
+This flag is I<@mail_no_perms@> by default.
+
+=item mail_no_user
+
+If set, mail will be sent to the I<mailto> user if the invoking
+user is not in the I<sudoers> file.  This flag is I<@mail_no_user@>
+by default.
+
+=item noexec
+
+If set, all commands run via B<sudo> will behave as if the C<NOEXEC>
+tag has been set, unless overridden by a C<EXEC> tag.  See the
+description of I<NOEXEC and EXEC> below as well as the L<PREVENTING SHELL
+ESCAPES> section at the end of this manual.  This flag is I<off> by default.
+
+=item path_info
+
+Normally, B<sudo> will tell the user when a command could not be
+found in their C<PATH> environment variable.  Some sites may wish
+to disable this as it could be used to gather information on the
+location of executables that the normal user does not have access
+to.  The disadvantage is that if the executable is simply not in
+the user's C<PATH>, B<sudo> will tell the user that they are not
+allowed to run it, which can be confusing.  This flag is I<@path_info@>
+by default.
+
+=item preserve_groups
+
+By default B<sudo> will initialize the group vector to the list of
+groups the target user is in.  When I<preserve_groups> is set, the
+user's existing group vector is left unaltered.  The real and
+effective group IDs, however, are still set to match the target
+user.  This flag is I<off> by default.
+
+=item requiretty
+
+If set, B<sudo> will only run when the user is logged in to a real
+tty.  This will disallow things like C<"rsh somehost sudo ls"> since
+L<rsh(1)> does not allocate a tty.  Because it is not possible to turn
+off echo when there is no tty present, some sites may wish to set
+this flag to prevent a user from entering a visible password.  This
+flag is I<off> by default.
+
+=item root_sudo
+
+If set, root is allowed to run B<sudo> too.  Disabling this prevents users
+from "chaining" B<sudo> commands to get a root shell by doing something
+like C<"sudo sudo /bin/sh">.  Note, however, that turning off I<root_sudo>
+will also prevent root and from running B<sudoedit>.
+Disabling I<root_sudo> provides no real additional security; it
+exists purely for historical reasons.
+This flag is I<@root_sudo@> by default.
+
+=item rootpw
+
+If set, B<sudo> will prompt for the root password instead of the password
+of the invoking user.  This flag is I<off> by default.
+
+=item runaspw
+
+If set, B<sudo> will prompt for the password of the user defined by the
+I<runas_default> option (defaults to C<@runas_default@>) instead of the
+password of the invoking user.  This flag is I<off> by default.
+
+=item set_home
+
+If set and B<sudo> is invoked with the B<-s> flag the C<HOME>
+environment variable will be set to the home directory of the target
+user (which is root unless the B<-u> option is used).  This effectively
+makes the B<-s> flag imply B<-H>.  This flag is I<off> by default.
+
+=item set_logname
+
+Normally, B<sudo> will set the C<LOGNAME>, C<USER> and C<USERNAME>
+environment variables to the name of the target user (usually root
+unless the B<-u> flag is given).  However, since some programs
+(including the RCS revision control system) use C<LOGNAME> to
+determine the real identity of the user, it may be desirable to
+change this behavior.  This can be done by negating the set_logname
+option.  Note that if the I<env_reset> option has not been disabled,
+entries in the I<env_keep> list will override the value of
+I<set_logname>.  This flag is I<off> by default.
+
+=item setenv
+
+Allow the user to disable the I<env_reset> option from the command
+line.  Additionally, environment variables set via the command line
+are not subject to the restrictions imposed by I<env_check>,
+I<env_delete>, or I<env_keep>.  As such, only trusted users should
+be allowed to set variables in this manner.  This flag is I<off>
+by default.
+
+=item shell_noargs
+
+If set and B<sudo> is invoked with no arguments it acts as if the
+B<-s> flag had been given.  That is, it runs a shell as root (the
+shell is determined by the C<SHELL> environment variable if it is
+set, falling back on the shell listed in the invoking user's
+/etc/passwd entry if not).  This flag is I<off> by default.
+
+=item stay_setuid
+
+Normally, when B<sudo> executes a command the real and effective
+UIDs are set to the target user (root by default).  This option
+changes that behavior such that the real UID is left as the invoking
+user's UID.  In other words, this makes B<sudo> act as a setuid
+wrapper.  This can be useful on systems that disable some potentially
+dangerous functionality when a program is run setuid.  This option
+is only effective on systems with either the setreuid() or setresuid()
+function.  This flag is I<off> by default.
+
+=item targetpw
+
+If set, B<sudo> will prompt for the password of the user specified by
+the B<-u> flag (defaults to C<root>) instead of the password of the
+invoking user.  Note that this precludes the use of a uid not listed
+in the passwd database as an argument to the B<-u> flag.
+This flag is I<off> by default.
+
+=item tty_tickets
+
+If set, users must authenticate on a per-tty basis.  Normally,
+B<sudo> uses a directory in the ticket dir with the same name as
+the user running it.  With this flag enabled, B<sudo> will use a
+file named for the tty the user is logged in on in that directory.
+This flag is I<@tty_tickets@> by default.
+
+=item use_loginclass
+
+If set, B<sudo> will apply the defaults specified for the target user's
+login class if one exists.  Only available if B<sudo> is configured with
+the --with-logincap option.  This flag is I<off> by default.
+
+=back
+
+B<Integers>:
+
+=over 16
+
+=item passwd_tries
+
+The number of tries a user gets to enter his/her password before
+B<sudo> logs the failure and exits.  The default is C<@passwd_tries@>.
+
+=back
+
+B<Integers that can be used in a boolean context>:
+
+=over 16
+
+=item loglinelen
+
+Number of characters per line for the file log.  This value is used
+to decide when to wrap lines for nicer log files.  This has no
+effect on the syslog log file, only the file log.  The default is
+C<@loglen@> (use 0 or negate the option to disable word wrap).
+
+=item passwd_timeout
+
+Number of minutes before the B<sudo> password prompt times out.
+The default is C<@password_timeout@>; set this to C<0> for no password timeout.
+
+=item timestamp_timeout
+
+Number of minutes that can elapse before B<sudo> will ask for a
+passwd again.  The default is C<@timeout@>.  Set this to C<0> to always
+prompt for a password.
+If set to a value less than C<0> the user's timestamp will never
+expire.  This can be used to allow users to create or delete their
+own timestamps via C<sudo -v> and C<sudo -k> respectively.
+
+=item umask
+
+Umask to use when running the command.  Negate this option or set
+it to 0777 to preserve the user's umask.  The default is C<@sudo_umask@>.
+
+=back
+
+B<Strings>:
+
+=over 16
+
+=item badpass_message
+
+Message that is displayed if a user enters an incorrect password.
+The default is C<@badpass_message@> unless insults are enabled.
+
+=item editor
+
+A colon (':') separated list of editors allowed to be used with
+B<visudo>.  B<visudo> will choose the editor that matches the user's
+EDITOR environment variable if possible, or the first editor in the
+list that exists and is executable.  The default is the path to vi
+on your system.
+
+=item mailsub
+
+Subject of the mail sent to the I<mailto> user. The escape C<%h>
+will expand to the hostname of the machine.
+Default is C<@mailsub@>.
+
+=item noexec_file
+
+Path to a shared library containing dummy versions of the execv(),
+execve() and fexecve() library functions that just return an error.
+This is used to implement the I<noexec> functionality on systems that
+support C<LD_PRELOAD> or its equivalent.  Defaults to F<@noexec_file@>.
+
+=item passprompt
+
+The default prompt to use when asking for a password; can be overridden
+via the B<-p> option or the C<SUDO_PROMPT> environment variable.
+The following percent (`C<%>') escapes are supported:
+
+=over 4
+
+=item C<%H>
+
+expanded to the local hostname including the domain name
+(on if the machine's hostname is fully qualified or the I<fqdn>
+option is set)
+
+=item C<%h>
+
+expanded to the local hostname without the domain name
+
+=item C<%U>
+
+expanded to the login name of the user the command will
+be run as (defaults to root)
+
+=item C<%u>
+
+expanded to the invoking user's login name
+
+=item C<%%>
+
+two consecutive C<%> characters are collapsed into a single C<%> character
+
+=back
+
+The default value is C<@passprompt@>.
+
+=item runas_default
+
+The default user to run commands as if the B<-u> flag is not specified
+on the command line.  This defaults to C<@runas_default@>.
+Note that if I<runas_default> is set it B<must> occur before
+any C<Runas_Alias> specifications.
+
+=item syslog_badpri
+
+Syslog priority to use when user authenticates unsuccessfully.
+Defaults to C<@badpri@>.
+
+=item syslog_goodpri
+
+Syslog priority to use when user authenticates successfully.
+Defaults to C<@goodpri@>.
+
+=item timestampdir
+
+The directory in which B<sudo> stores its timestamp files.
+The default is F<@timedir@>.
+
+=item timestampowner
+
+The owner of the timestamp directory and the timestamps stored therein.
+The default is C<root>.
+
+=back
+
+B<Strings that can be used in a boolean context>:
+
+=over 12
+
+=item exempt_group
+
+Users in this group are exempt from password and PATH requirements.
+This is not set by default.
+
+=item lecture
+
+This option controls when a short lecture will be printed along with
+the password prompt.  It has the following possible values:
+
+=over 8
+
+=item always
+
+Always lecture the user.
+
+=item never
+
+Never lecture the user.
+
+=item once
+
+Only lecture the user the first time they run B<sudo>.
+
+=back
+
+If no value is specified, a value of I<once> is implied.
+Negating the option results in a value of I<never> being used.
+The default value is I<@lecture@>.
+
+=item lecture_file
+
+Path to a file containing an alternate B<sudo> lecture that will
+be used in place of the standard lecture if the named file exists.
+By default, B<sudo> uses a built-in lecture.
+
+=item listpw
+
+This option controls when a password will be required when a
+user runs B<sudo> with the B<-l> flag.  It has the following possible values:
+
+=over 8
+
+=item all
+
+All the user's I<sudoers> entries for the current host must have
+the C<NOPASSWD> flag set to avoid entering a password.
+
+=item always
+
+The user must always enter a password to use the B<-l> flag.
+
+=item any
+
+At least one of the user's I<sudoers> entries for the current host
+must have the C<NOPASSWD> flag set to avoid entering a password.
+
+=item never
+
+The user need never enter a password to use the B<-l> flag.
+
+=back
+
+If no value is specified, a value of I<any> is implied.
+Negating the option results in a value of I<never> being used.
+The default value is I<any>.
+
+=item logfile
+
+Path to the B<sudo> log file (not the syslog log file).  Setting a path
+turns on logging to a file; negating this option turns it off.
+By default, B<sudo> logs via syslog.
+
+=item mailerflags
+
+Flags to use when invoking mailer. Defaults to B<-t>.
+
+=item mailerpath
+
+Path to mail program used to send warning mail.
+Defaults to the path to sendmail found at configure time.
+
+=item mailto
+
+Address to send warning and error mail to.  The address should
+be enclosed in double quotes (C<">) to protect against B<sudo>
+interpreting the C<@> sign.  Defaults to C<@mailto@>.
+
+=item syslog
+
+Syslog facility if syslog is being used for logging (negate to
+disable syslog logging).  Defaults to C<@logfac@>.
+
+=item verifypw
+
+This option controls when a password will be required when a user runs
+B<sudo> with the B<-v> flag.  It has the following possible values:
+
+=over 8
+
+=item all
+
+All the user's I<sudoers> entries for the current host must have
+the C<NOPASSWD> flag set to avoid entering a password.
+
+=item always
+
+The user must always enter a password to use the B<-v> flag.
+
+=item any
+
+At least one of the user's I<sudoers> entries for the current host
+must have the C<NOPASSWD> flag set to avoid entering a password.
+
+=item never
+
+The user need never enter a password to use the B<-v> flag.
+
+=back
+
+If no value is specified, a value of I<all> is implied.
+Negating the option results in a value of I<never> being used.
+The default value is I<all>.
+
+=back
+
+B<Lists that can be used in a boolean context>:
+
+=over 16
+
+=item env_check
+
+Environment variables to be removed from the user's environment if
+the variable's value contains C<%> or C</> characters.  This can
+be used to guard against printf-style format vulnerabilities in
+poorly-written programs.  The argument may be a double-quoted,
+space-separated list or a single value without double-quotes.  The
+list can be replaced, added to, deleted from, or disabled by using
+the C<=>, C<+=>, C<-=>, and C<!> operators respectively.  Regardless
+of whether the C<env_reset> option is enabled or disabled, variables
+specified by C<env_check> will be preserved in the environment if
+they pass the aforementioned check.  The default list of environment
+variables to check is displayed when B<sudo> is run by root with
+the I<-V> option.
+
+=item env_delete
+
+Environment variables to be removed from the user's environment.
+The argument may be a double-quoted, space-separated list or a
+single value without double-quotes.  The list can be replaced, added
+to, deleted from, or disabled by using the C<=>, C<+=>, C<-=>, and
+C<!> operators respectively.  The default list of environment
+variables to remove is displayed when B<sudo> is run by root with the
+I<-V> option.  Note that many operating systems will remove potentially
+dangerous variables from the environment of any setuid process (such
+as B<sudo>).
+
+=item env_keep
+
+Environment variables to be preserved in the user's environment
+when the I<env_reset> option is in effect.  This allows fine-grained
+control over the environment B<sudo>-spawned processes will receive.
+The argument may be a double-quoted, space-separated list or a
+single value without double-quotes.  The list can be replaced, added
+to, deleted from, or disabled by using the C<=>, C<+=>, C<-=>, and
+C<!> operators respectively.  The default list of variables to keep
+is displayed when B<sudo> is run by root with the I<-V> option.
+
+=back
+
+When logging via L<syslog(3)>, B<sudo> accepts the following values
+for the syslog facility (the value of the B<syslog> Parameter):
+B<authpriv> (if your OS supports it), B<auth>, B<daemon>, B<user>,
+B<local0>, B<local1>, B<local2>, B<local3>, B<local4>, B<local5>,
+B<local6>, and B<local7>.  The following syslog priorities are
+supported: B<alert>, B<crit>, B<debug>, B<emerg>, B<err>, B<info>,
+B<notice>, and B<warning>.
+
+=head1 FILES
+
+=over 4
+
+=item F<@sysconfdir@/sudoers>C<		>
+List of who can run what
+
+=item F</etc/group>C<		>
+Local groups file
+
+=item F</etc/netgroup>C<		>
+List of network groups
+
+=back
+
+=head1 EXAMPLES
+
+Since the I<sudoers> file is parsed in a single pass, order is
+important.  In general, you should structure I<sudoers> such that
+the C<Host_Alias>, C<User_Alias>, and C<Cmnd_Alias> specifications
+come first, followed by any C<Default_Entry> lines, and finally the
+C<Runas_Alias> and user specifications.  The basic rule of thumb
+is you cannot reference an Alias that has not already been defined.
+
+Below are example I<sudoers> entries.  Admittedly, some of
+these are a bit contrived.  First, we define our I<aliases>:
+
+ # User alias specification
+ User_Alias	FULLTIMERS = millert, mikef, dowdy
+ User_Alias	PARTTIMERS = bostley, jwfox, crawl
+ User_Alias	WEBMASTERS = will, wendy, wim
+
+ # Runas alias specification
+ Runas_Alias	OP = root, operator
+ Runas_Alias	DB = oracle, sybase
+
+ # Host alias specification
+ Host_Alias	SPARC = bigtime, eclipse, moet, anchor :\
+		SGI = grolsch, dandelion, black :\
+		ALPHA = widget, thalamus, foobar :\
+		HPPA = boa, nag, python
+ Host_Alias	CUNETS = 128.138.0.0/255.255.0.0
+ Host_Alias	CSNETS = 128.138.243.0, 128.138.204.0/24, 128.138.242.0
+ Host_Alias	SERVERS = master, mail, www, ns
+ Host_Alias	CDROM = orion, perseus, hercules
+
+ # Cmnd alias specification
+ Cmnd_Alias	DUMPS = /usr/bin/mt, /usr/sbin/dump, /usr/sbin/rdump,\
+			/usr/sbin/restore, /usr/sbin/rrestore
+ Cmnd_Alias	KILL = /usr/bin/kill
+ Cmnd_Alias	PRINTING = /usr/sbin/lpc, /usr/bin/lprm
+ Cmnd_Alias	SHUTDOWN = /usr/sbin/shutdown
+ Cmnd_Alias	HALT = /usr/sbin/halt
+ Cmnd_Alias	REBOOT = /usr/sbin/reboot
+ Cmnd_Alias	SHELLS = /usr/bin/sh, /usr/bin/csh, /usr/bin/ksh, \
+			 /usr/local/bin/tcsh, /usr/bin/rsh, \
+			 /usr/local/bin/zsh
+ Cmnd_Alias	SU = /usr/bin/su
+ Cmnd_Alias	PAGERS = /usr/bin/more, /usr/bin/pg, /usr/bin/less
+
+Here we override some of the compiled in default values.  We want
+B<sudo> to log via L<syslog(3)> using the I<auth> facility in all
+cases.  We don't want to subject the full time staff to the B<sudo>
+lecture, user B<millert> need not give a password, and we don't
+want to reset the C<LOGNAME>, C<USER> or C<USERNAME> environment
+variables when running commands as root.  Additionally, on the
+machines in the I<SERVERS> C<Host_Alias>, we keep an additional
+local log file and make sure we log the year in each log line since
+the log entries will be kept around for several years.  Lastly, we
+disable shell escapes for the commands in the PAGERS C<Cmnd_Alias>
+(F</usr/bin/more>, F</usr/bin/pg> and F</usr/bin/less>).
+
+ # Override built-in defaults
+ Defaults		syslog=auth
+ Defaults>root		!set_logname
+ Defaults:FULLTIMERS	!lecture
+ Defaults:millert	!authenticate
+ Defaults at SERVERS	log_year, logfile=/var/log/sudo.log
+ Defaults!PAGERS	noexec
+
+The I<User specification> is the part that actually determines who may
+run what.
+
+ root		ALL = (ALL) ALL
+ %wheel		ALL = (ALL) ALL
+
+We let B<root> and any user in group B<wheel> run any command on any
+host as any user.
+
+ FULLTIMERS	ALL = NOPASSWD: ALL
+
+Full time sysadmins (B<millert>, B<mikef>, and B<dowdy>) may run any
+command on any host without authenticating themselves.
+
+ PARTTIMERS	ALL = ALL
+
+Part time sysadmins (B<bostley>, B<jwfox>, and B<crawl>) may run any
+command on any host but they must authenticate themselves first
+(since the entry lacks the C<NOPASSWD> tag).
+
+ jack		CSNETS = ALL
+
+The user B<jack> may run any command on the machines in the I<CSNETS> alias
+(the networks C<128.138.243.0>, C<128.138.204.0>, and C<128.138.242.0>).
+Of those networks, only C<128.138.204.0> has an explicit netmask (in
+CIDR notation) indicating it is a class C network.  For the other
+networks in I<CSNETS>, the local machine's netmask will be used
+during matching.
+
+ lisa		CUNETS = ALL
+
+The user B<lisa> may run any command on any host in the I<CUNETS> alias
+(the class B network C<128.138.0.0>).
+
+ operator	ALL = DUMPS, KILL, SHUTDOWN, HALT, REBOOT, PRINTING,\
+		sudoedit /etc/printcap, /usr/oper/bin/
+
+The B<operator> user may run commands limited to simple maintenance.
+Here, those are commands related to backups, killing processes, the
+printing system, shutting down the system, and any commands in the
+directory F</usr/oper/bin/>.
+
+ joe		ALL = /usr/bin/su operator
+
+The user B<joe> may only L<su(1)> to operator.
+
+ pete		HPPA = /usr/bin/passwd [A-z]*, !/usr/bin/passwd root
+
+The user B<pete> is allowed to change anyone's password except for
+root on the I<HPPA> machines.  Note that this assumes L<passwd(1)>
+does not take multiple usernames on the command line.
+
+ bob		SPARC = (OP) ALL : SGI = (OP) ALL
+
+The user B<bob> may run anything on the I<SPARC> and I<SGI> machines
+as any user listed in the I<OP> C<Runas_Alias> (B<root> and B<operator>).
+
+ jim		+biglab = ALL
+
+The user B<jim> may run any command on machines in the I<biglab> netgroup.
+B<sudo> knows that "biglab" is a netgroup due to the '+' prefix.
+
+ +secretaries	ALL = PRINTING, /usr/bin/adduser, /usr/bin/rmuser
+
+Users in the B<secretaries> netgroup need to help manage the printers
+as well as add and remove users, so they are allowed to run those
+commands on all machines.
+
+ fred		ALL = (DB) NOPASSWD: ALL
+
+The user B<fred> can run commands as any user in the I<DB> C<Runas_Alias>
+(B<oracle> or B<sybase>) without giving a password.
+
+ john		ALPHA = /usr/bin/su [!-]*, !/usr/bin/su *root*
+
+On the I<ALPHA> machines, user B<john> may su to anyone except root
+but he is not allowed to give L<su(1)> any flags.
+
+ jen		ALL, !SERVERS = ALL
+
+The user B<jen> may run any command on any machine except for those
+in the I<SERVERS> C<Host_Alias> (master, mail, www and ns).
+
+ jill		SERVERS = /usr/bin/, !SU, !SHELLS
+
+For any machine in the I<SERVERS> C<Host_Alias>, B<jill> may run
+any commands in the directory F</usr/bin/> except for those commands
+belonging to the I<SU> and I<SHELLS> C<Cmnd_Aliases>.
+
+ steve		CSNETS = (operator) /usr/local/op_commands/
+
+The user B<steve> may run any command in the directory /usr/local/op_commands/
+but only as user operator.
+
+ matt		valkyrie = KILL
+
+On his personal workstation, valkyrie, B<matt> needs to be able to
+kill hung processes.
+
+ WEBMASTERS	www = (www) ALL, (root) /usr/bin/su www
+
+On the host www, any user in the I<WEBMASTERS> C<User_Alias> (will,
+wendy, and wim), may run any command as user www (which owns the
+web pages) or simply L<su(1)> to www.
+
+ ALL		CDROM = NOPASSWD: /sbin/umount /CDROM,\
+		/sbin/mount -o nosuid\,nodev /dev/cd0a /CDROM
+
+Any user may mount or unmount a CD-ROM on the machines in the CDROM
+C<Host_Alias> (orion, perseus, hercules) without entering a password.
+This is a bit tedious for users to type, so it is a prime candidate
+for encapsulating in a shell script.
+
+=head1 SECURITY NOTES
+
+It is generally not effective to "subtract" commands from C<ALL>
+using the '!' operator.  A user can trivially circumvent this
+by copying the desired command to a different name and then
+executing that.  For example:
+
+    bill	ALL = ALL, !SU, !SHELLS
+
+Doesn't really prevent B<bill> from running the commands listed in
+I<SU> or I<SHELLS> since he can simply copy those commands to a
+different name, or use a shell escape from an editor or other
+program.  Therefore, these kind of restrictions should be considered
+advisory at best (and reinforced by policy).
+
+=head1 PREVENTING SHELL ESCAPES
+
+Once B<sudo> executes a program, that program is free to do whatever
+it pleases, including run other programs.  This can be a security
+issue since it is not uncommon for a program to allow shell escapes,
+which lets a user bypass B<sudo>'s access control and logging.
+Common programs that permit shell escapes include shells (obviously),
+editors, paginators, mail and terminal programs.
+
+There are two basic approaches to this problem:
+
+=over 10
+
+=item restrict
+
+Avoid giving users access to commands that allow the user to run
+arbitrary commands.  Many editors have a restricted mode where shell
+escapes are disabled, though B<sudoedit> is a better solution to
+running editors via B<sudo>.  Due to the large number of programs that
+offer shell escapes, restricting users to the set of programs that
+do not if often unworkable.
+
+=item noexec
+
+Many systems that support shared libraries have the ability to
+override default library functions by pointing an environment
+variable (usually C<LD_PRELOAD>) to an alternate shared library.
+On such systems, B<sudo>'s I<noexec> functionality can be used to
+prevent a program run by B<sudo> from executing any other programs.
+Note, however, that this applies only to native dynamically-linked
+executables.  Statically-linked executables and foreign executables
+running under binary emulation are not affected.
+
+To tell whether or not B<sudo> supports I<noexec>, you can run
+the following as root:
+
+    sudo -V | grep "dummy exec"
+
+If the resulting output contains a line that begins with:
+
+    File containing dummy exec functions:
+
+then B<sudo> may be able to replace the exec family of functions
+in the standard library with its own that simply return an error.
+Unfortunately, there is no foolproof way to know whether or not
+I<noexec> will work at compile-time.  I<noexec> should work on
+SunOS, Solaris, *BSD, Linux, IRIX, Tru64 UNIX, MacOS X, and HP-UX
+11.x.  It is known B<not> to work on AIX and UnixWare.  I<noexec>
+is expected to work on most operating systems that support the
+C<LD_PRELOAD> environment variable.  Check your operating system's
+manual pages for the dynamic linker (usually ld.so, ld.so.1, dyld,
+dld.sl, rld, or loader) to see if C<LD_PRELOAD> is supported.
+
+To enable I<noexec> for a command, use the C<NOEXEC> tag as documented
+in the User Specification section above.  Here is that example again:
+
+ aaron	shanty = NOEXEC: /usr/bin/more, /usr/bin/vi
+
+This allows user B<aaron> to run F</usr/bin/more> and F</usr/bin/vi>
+with I<noexec> enabled.  This will prevent those two commands from
+executing other commands (such as a shell).  If you are unsure
+whether or not your system is capable of supporting I<noexec> you
+can always just try it out and see if it works.
+
+=back
+
+Note that restricting shell escapes is not a panacea.  Programs
+running as root are still capable of many potentially hazardous
+operations (such as changing or overwriting files) that could lead
+to unintended privilege escalation.  In the specific case of an
+editor, a safer approach is to give the user permission to run
+B<sudoedit>.
+
+=head1 SEE ALSO
+
+L<rsh(1)>, L<su(1)>, L<fnmatch(3)>, L<sudo(8)>, L<visudo(8)>
+
+=head1 CAVEATS
+
+The I<sudoers> file should B<always> be edited by the B<visudo>
+command which locks the file and does grammatical checking. It is
+imperative that I<sudoers> be free of syntax errors since B<sudo>
+will not run with a syntactically incorrect I<sudoers> file.
+
+When using netgroups of machines (as opposed to users), if you
+store fully qualified hostnames in the netgroup (as is usually the
+case), you either need to have the machine's hostname be fully qualified
+as returned by the C<hostname> command or use the I<fqdn> option in
+I<sudoers>.
+
+=head1 BUGS
+
+If you feel you have found a bug in B<sudo>, please submit a bug report
+at http://www.sudo.ws/sudo/bugs/
+
+=head1 SUPPORT
+
+Limited free support is available via the sudo-users mailing list,
+see http://www.sudo.ws/mailman/listinfo/sudo-users to subscribe or
+search the archives.
+
+=head1 DISCLAIMER
+
+B<sudo> is provided ``AS IS'' and any express or implied warranties,
+including, but not limited to, the implied warranties of merchantability
+and fitness for a particular purpose are disclaimed.  See the LICENSE
+file distributed with B<sudo> or http://www.sudo.ws/sudo/license.html
+for complete details.
Index: ins_csops.h
===================================================================
RCS file: /home/cvs/src/contrib/sudo/ins_csops.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lcontrib/sudo/ins_csops.h -Lcontrib/sudo/ins_csops.h -u -r1.1 -r1.2
--- contrib/sudo/ins_csops.h
+++ contrib/sudo/ins_csops.h
@@ -13,7 +13,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $Sudo: ins_csops.h,v 1.29 2004/09/14 21:43:31 millert Exp $
+ * $Sudo: ins_csops.h,v 1.28.2.1 2007/06/10 16:57:35 millert Exp $
  */
 
 #ifndef _SUDO_INS_CSOPS_H
Index: alloc.c
===================================================================
RCS file: /home/cvs/src/contrib/sudo/alloc.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lcontrib/sudo/alloc.c -Lcontrib/sudo/alloc.c -u -r1.1 -r1.2
--- contrib/sudo/alloc.c
+++ contrib/sudo/alloc.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999-2003 Todd C. Miller <Todd.Miller at courtesan.com>
+ * Copyright (c) 1999-2005 Todd C. Miller <Todd.Miller at courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -18,7 +18,7 @@
  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
  */
 
-#include "config.h"
+#include <config.h>
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -50,7 +50,7 @@
 #include "sudo.h"
 
 #ifndef lint
-static const char rcsid[] = "$Sudo: alloc.c,v 1.23 2004/06/01 16:23:32 millert Exp $";
+__unused static const char rcsid[] = "$Sudo: alloc.c,v 1.23.2.3 2007/06/12 01:43:01 millert Exp $";
 #endif /* lint */
 
 /*
@@ -179,7 +179,9 @@
 #ifdef __STDC__
 easprintf(char **ret, const char *fmt, ...)
 #else
-easprintf(va_alist)
+easprintf(ret, fmt, va_alist)
+    char **ret;
+    const char *fmt;
     va_dcl
 #endif
 {
@@ -188,12 +190,7 @@
 #ifdef __STDC__
     va_start(ap, fmt);
 #else
-    char **ret;
-    const char *fmt;
-
     va_start(ap);
-    ret = va_arg(ap, char **);
-    fmt = va_arg(ap, const char *);
 #endif
     len = vasprintf(ret, fmt, ap);
     va_end(ap);
@@ -219,3 +216,14 @@
 	errx(1, "unable to allocate memory");
     return(len);
 }
+
+/*
+ * Wrapper for free(3) so we can depend on C89 semantics.
+ */
+void
+efree(ptr)
+    VOID *ptr;
+{
+    if (ptr != NULL)
+	free(ptr);
+}
--- /dev/null
+++ contrib/sudo/ldap.c
@@ -0,0 +1,1037 @@
+/*
+ * Copyright (c) 2003-2005 Todd C. Miller <Todd.Miller at courtesan.com>
+ *
+ * This code is derived from software contributed by Aaron Spangler.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif /* STDC_HEADERS */
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# ifdef HAVE_STRINGS_H
+#  include <strings.h>
+# endif
+#endif /* HAVE_STRING_H */
+#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
+# include <malloc.h>
+#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#ifdef HAVE_ERR_H
+# include <err.h>
+#else
+# include "emul/err.h"
+#endif /* HAVE_ERR_H */
+#include <errno.h>
+#ifdef HAVE_LBER_H
+# include <lber.h>
+#endif
+#include <ldap.h>
+
+#include "sudo.h"
+#include "parse.h"
+
+#ifndef lint
+__unused static const char rcsid[] = "$Sudo: ldap.c,v 1.11.2.15 2007/07/18 11:13:50 millert Exp $";
+#endif /* lint */
+
+#ifndef LINE_MAX
+# define LINE_MAX 2048
+#endif
+
+#ifndef LDAP_OPT_SUCCESS
+# define LDAP_OPT_SUCCESS LDAP_SUCCESS
+#endif
+
+#if defined(LDAP_X_OPT_CONNECT_TIMEOUT) && !defined(LDAP_OPT_X_CONNECT_TIMEOUT)
+#define LDAP_OPT_X_CONNECT_TIMEOUT	LDAP_OPT_X_CONNECT_TIMEOUT
+#endif
+
+#define	DPRINTF(args, level)	if (ldap_conf.debug >= level) warnx args
+
+/* ldap configuration structure */
+struct ldap_config {
+    int port;
+    int version;
+    int debug;
+    int tls_checkpeer;
+    int timelimit;
+    int bind_timelimit;
+    char *host;
+    char *uri;
+    char *binddn;
+    char *bindpw;
+    char *rootbinddn;
+    char *base;
+    char *ssl;
+    char *tls_cacertfile;
+    char *tls_cacertdir;
+    char *tls_random_file;
+    char *tls_cipher_suite;
+    char *tls_certfile;
+    char *tls_keyfile;
+} ldap_conf;
+
+static void sudo_ldap_update_defaults __P((LDAP *));
+static void sudo_ldap_close __P((LDAP *));
+static LDAP *sudo_ldap_open __P((void));
+
+/*
+ * Walk through search results and return TRUE if we have a matching
+ * netgroup, else FALSE.
+ */
+int
+sudo_ldap_check_user_netgroup(ld, entry)
+    LDAP *ld;
+    LDAPMessage *entry;
+{
+    char **v = NULL, **p = NULL;
+    int ret = FALSE;
+
+    if (!entry)
+	return(ret);
+
+    /* get the values from the entry */
+    v = ldap_get_values(ld, entry, "sudoUser");
+
+    /* walk through values */
+    for (p = v; p && *p && !ret; p++) {
+	/* match any */
+	if (netgr_matches(*p, NULL, NULL, user_name))
+	    ret = TRUE;
+	DPRINTF(("ldap sudoUser netgroup '%s' ... %s", *p,
+	    ret ? "MATCH!" : "not"), 2);
+    }
+
+    if (v)
+	ldap_value_free(v);	/* cleanup */
+
+    return(ret);
+}
+
+/*
+ * Walk through search results and return TRUE if we have a
+ * host match, else FALSE.
+ */
+int
+sudo_ldap_check_host(ld, entry)
+    LDAP *ld;
+    LDAPMessage *entry;
+{
+    char **v = NULL, **p = NULL;
+    int ret = FALSE;
+
+    if (!entry)
+	return(ret);
+
+    /* get the values from the entry */
+    v = ldap_get_values(ld, entry, "sudoHost");
+
+    /* walk through values */
+    for (p = v; p && *p && !ret; p++) {
+	/* match any or address or netgroup or hostname */
+	if (!strcasecmp(*p, "ALL") || addr_matches(*p) ||
+	    netgr_matches(*p, user_host, user_shost, NULL) ||
+	    !hostname_matches(user_shost, user_host, *p))
+	    ret = TRUE;
+	DPRINTF(("ldap sudoHost '%s' ... %s", *p,
+	    ret ? "MATCH!" : "not"), 2);
+    }
+
+    if (v)
+	ldap_value_free(v);	/* cleanup */
+
+    return(ret);
+}
+
+/*
+ * Walk through search results and return TRUE if we have a runas match,
+ * else FALSE.
+ * Since the runas directive in /etc/sudoers is optional, so is sudoRunAs.
+ */
+int
+sudo_ldap_check_runas(ld, entry)
+    LDAP *ld;
+    LDAPMessage *entry;
+{
+    char **v = NULL, **p = NULL;
+    int ret = FALSE;
+
+    if (!entry)
+	return(ret);
+
+    /* get the values from the entry */
+    v = ldap_get_values(ld, entry, "sudoRunAs");
+
+    /*
+     * BUG:
+     * 
+     * if runas is not specified on the command line, the only information
+     * as to which user to run as is in the runas_default option.  We should
+     * check to see if we have the local option present.  Unfortunately we
+     * don't parse these options until after this routine says yes or no.
+     * The query has already returned, so we could peek at the attribute
+     * values here though.
+     * 
+     * For now just require users to always use -u option unless its set
+     * in the global defaults. This behaviour is no different than the global
+     * /etc/sudoers.
+     *
+     * Sigh - maybe add this feature later
+     *
+     */
+
+    /*
+     * If there are no runas entries, match runas_default against
+     * what the user specified on the command line.
+     */
+    if (!v)
+	ret = !strcasecmp(*user_runas, def_runas_default);
+
+    /* walk through values returned, looking for a match */
+    for (p = v; p && *p && !ret; p++) {
+	if (!strcasecmp(*p, *user_runas) || !strcasecmp(*p, "ALL")) 
+	    ret = TRUE;
+	DPRINTF(("ldap sudoRunAs '%s' ... %s", *p,
+	    ret ? "MATCH!" : "not"), 2);
+    }
+
+    if (v)
+	ldap_value_free(v);	/* cleanup */
+
+    return(ret);
+}
+
+/*
+ * Walk through search results and return TRUE if we have a command match.
+ */
+int
+sudo_ldap_check_command(ld, entry)
+    LDAP *ld;
+    LDAPMessage *entry;
+{
+    char *allowed_cmnd, *allowed_args, **v = NULL, **p = NULL;
+    int foundbang, ret = FALSE;
+
+    if (!entry)
+	return(ret);
+
+    v = ldap_get_values(ld, entry, "sudoCommand");
+
+    /* get_first_entry */
+    for (p = v; p && *p && ret >= 0; p++) {
+	/* Match against ALL ? */
+	if (!strcasecmp(*p, "ALL")) {
+	    ret = TRUE;
+	    DPRINTF(("ldap sudoCommand '%s' ... MATCH!", *p), 2);
+	    continue;
+	}
+
+	/* check for !command */
+	if (**p == '!') {
+	    foundbang = TRUE;
+	    allowed_cmnd = estrdup(1 + *p);	/* !command */
+	} else {
+	    foundbang = FALSE;
+	    allowed_cmnd = estrdup(*p);		/* command */
+	}
+
+	/* split optional args away from command */
+	allowed_args = strchr(allowed_cmnd, ' ');
+	if (allowed_args)
+	    *allowed_args++ = '\0';
+
+	/* check the command like normal */
+	if (command_matches(allowed_cmnd, allowed_args)) {
+	    /*
+	     * If allowed (no bang) set ret but keep on checking.
+	     * If disallowed (bang), exit loop.
+	     */
+	    ret = foundbang ? -1 : TRUE;
+	}
+	DPRINTF(("ldap sudoCommand '%s' ... %s", *p,
+	    ret == TRUE ? "MATCH!" : "not"), 2);
+
+	efree(allowed_cmnd);	/* cleanup */
+    }
+
+    if (v)
+	ldap_value_free(v);	/* more cleanup */
+
+    /* return TRUE if we found at least one ALLOW and no DENY */
+    return(ret > 0);
+}
+
+/*
+ * Read sudoOption and modify the defaults as we go.  This is used once
+ * from the cn=defaults entry and also once when a final sudoRole is matched.
+ */
+void
+sudo_ldap_parse_options(ld, entry)
+    LDAP *ld;
+    LDAPMessage *entry;
+{
+    char op, *var, *val, **v = NULL, **p = NULL;
+
+    if (!entry)
+	return;
+
+    v = ldap_get_values(ld, entry, "sudoOption");
+
+    /* walk through options */
+    for (p = v; p && *p; p++) {
+
+	DPRINTF(("ldap sudoOption: '%s'", *p), 2);
+	var = estrdup(*p);
+
+	/* check for equals sign past first char */
+	val = strchr(var, '=');
+	if (val > var) {
+	    *val++ = '\0';	/* split on = and truncate var */
+	    op = *(val - 2);	/* peek for += or -= cases */
+	    if (op == '+' || op == '-') {
+		*(val - 2) = '\0';	/* found, remove extra char */
+		/* case var+=val or var-=val */
+		set_default(var, val, (int) op);
+	    } else {
+		/* case var=val */
+		set_default(var, val, TRUE);
+	    }
+	} else if (*var == '!') {
+	    /* case !var Boolean False */
+	    set_default(var + 1, NULL, FALSE);
+	} else {
+	    /* case var Boolean True */
+	    set_default(var, NULL, TRUE);
+	}
+	efree(var);
+    }
+
+    if (v)
+	ldap_value_free(v);
+}
+
+/*
+ * Concatenate strings, dynamically growing them as necessary.
+ * Strings can be arbitrarily long and are allocated/reallocated on
+ * the fly.  Make sure to free them when you are done.
+ *
+ * Usage:
+ *
+ * char *s=NULL;
+ * size_t sz;
+ *
+ * ncat(&s,&sz,"This ");
+ * ncat(&s,&sz,"is ");
+ * ncat(&s,&sz,"an ");
+ * ncat(&s,&sz,"arbitrarily ");
+ * ncat(&s,&sz,"long ");
+ * ncat(&s,&sz,"string!");
+ *
+ * printf("String Value='%s', but has %d bytes allocated\n",s,sz);
+ *
+ */
+void
+ncat(s, sz, src)
+    char **s;
+    size_t *sz;
+    char *src;
+{
+    size_t nsz;
+
+    /* handle initial alloc */
+    if (*s == NULL) {
+	*s = estrdup(src);
+	*sz = strlen(src) + 1;
+	return;
+    }
+    /* handle realloc */
+    nsz = strlen(*s) + strlen(src) + 1;
+    if (*sz < nsz)
+	*s = erealloc((void *) *s, *sz = nsz * 2);
+    strlcat(*s, src, *sz);
+}
+
+/*
+ * builds together a filter to check against ldap
+ */
+char *
+sudo_ldap_build_pass1()
+{
+    struct group *grp;
+    size_t sz;
+    char *b = NULL;
+    int i;
+
+    /* global OR */
+    ncat(&b, &sz, "(|");
+
+    /* build filter sudoUser=user_name */
+    ncat(&b, &sz, "(sudoUser=");
+    ncat(&b, &sz, user_name);
+    ncat(&b, &sz, ")");
+
+    /* Append primary group */
+    grp = getgrgid(user_gid);
+    if (grp != NULL) {
+	ncat(&b, &sz, "(sudoUser=%");
+	ncat(&b, &sz, grp -> gr_name);
+	ncat(&b, &sz, ")");
+    }
+
+    /* Append supplementary groups */
+    for (i = 0; i < user_ngroups; i++) {
+	if ((grp = getgrgid(user_groups[i])) != NULL) {
+	    ncat(&b, &sz, "(sudoUser=%");
+	    ncat(&b, &sz, grp -> gr_name);
+	    ncat(&b, &sz, ")");
+	}
+    }
+
+    /* Add ALL to list */
+    ncat(&b, &sz, "(sudoUser=ALL)");
+
+    /* End of OR List */
+    ncat(&b, &sz, ")");
+
+    return(b);
+}
+
+/*
+ * Map yes/true/on to TRUE, no/false/off to FALSE, else -1
+ */
+int
+_atobool(s)
+    const char *s;
+{
+    switch (*s) {
+	case 'y':
+	case 'Y':
+	    if (strcasecmp(s, "yes") == 0)
+		return(TRUE);
+	    break;
+	case 't':
+	case 'T':
+	    if (strcasecmp(s, "true") == 0)
+		return(TRUE);
+	    break;
+	case 'o':
+	case 'O':
+	    if (strcasecmp(s, "on") == 0)
+		return(TRUE);
+	    if (strcasecmp(s, "off") == 0)
+		return(FALSE);
+	    break;
+	case 'n':
+	case 'N':
+	    if (strcasecmp(s, "no") == 0)
+		return(FALSE);
+	    break;
+	case 'f':
+	case 'F':
+	    if (strcasecmp(s, "false") == 0)
+		return(FALSE);
+	    break;
+    }
+    return(-1);
+}
+
+int
+sudo_ldap_read_config()
+{
+    FILE *f;
+    char buf[LINE_MAX], *c, *keyword, *value;
+
+    /* defaults */
+    ldap_conf.version = 3;
+    ldap_conf.port = 389;
+    ldap_conf.tls_checkpeer = -1;
+    ldap_conf.timelimit = -1;
+    ldap_conf.bind_timelimit = -1;
+
+    if ((f = fopen(_PATH_LDAP_CONF, "r")) == NULL)
+	return(FALSE);
+    while (fgets(buf, sizeof(buf), f)) {
+	/* ignore text after comment character */
+	if ((c = strchr(buf, '#')) != NULL)
+	    *c = '\0';
+
+	/* skip leading whitespace */
+	for (c = buf; isspace((unsigned char) *c); c++)
+	    /* nothing */;
+
+	if (*c == '\0' || *c == '\n')
+	    continue;		/* skip empty line */
+
+	/* properly terminate keyword string */
+	keyword = c;
+	while (*c && !isspace((unsigned char) *c))
+	    c++;
+	if (*c)
+	    *c++ = '\0';	/* terminate keyword */
+
+	/* skip whitespace before value */
+	while (isspace((unsigned char) *c))
+	    c++;
+	value = c;
+
+	/* trim whitespace after value */
+	while (*c)
+	    c++;		/* wind to end */
+	while (--c > value && isspace((unsigned char) *c))
+	    *c = '\0';
+
+	/* The following macros make the code much more readable */
+
+#define MATCH_S(x,y) if (!strcasecmp(keyword,x)) \
+    { efree(y); y=estrdup(value); }
+#define MATCH_I(x,y) if (!strcasecmp(keyword,x)) { y=atoi(value); }
+#define MATCH_B(x,y) if (!strcasecmp(keyword,x)) { y=_atobool(value); }
+
+	/*
+	 * Parse values using a continues chain of if else if else if else if
+	 * else ...
+	 */
+	MATCH_S("host", ldap_conf.host)
+	    else
+	MATCH_I("port", ldap_conf.port)
+	    else
+	MATCH_S("ssl", ldap_conf.ssl)
+	    else
+	MATCH_B("tls_checkpeer", ldap_conf.tls_checkpeer)
+	    else
+	MATCH_S("tls_cacertfile", ldap_conf.tls_cacertfile)
+	    else
+	MATCH_S("tls_cacertdir", ldap_conf.tls_cacertdir)
+	    else
+	MATCH_S("tls_randfile", ldap_conf.tls_random_file)
+	    else
+	MATCH_S("tls_ciphers", ldap_conf.tls_cipher_suite)
+	    else
+	MATCH_S("tls_cert", ldap_conf.tls_certfile)
+	    else
+	MATCH_S("tls_key", ldap_conf.tls_keyfile)
+	    else
+	MATCH_I("ldap_version", ldap_conf.version)
+	    else
+	MATCH_I("bind_timelimit", ldap_conf.bind_timelimit)
+	    else
+	MATCH_I("timelimit", ldap_conf.timelimit)
+	    else
+	MATCH_S("uri", ldap_conf.uri)
+	    else
+	MATCH_S("binddn", ldap_conf.binddn)
+	    else
+	MATCH_S("bindpw", ldap_conf.bindpw)
+	    else
+	MATCH_S("rootbinddn", ldap_conf.rootbinddn)
+	    else
+	MATCH_S("sudoers_base", ldap_conf.base)
+	    else
+	MATCH_I("sudoers_debug", ldap_conf.debug)
+	    else {
+
+	    /*
+	     * The keyword was unrecognized.  Since this config file is
+	     * shared by multiple programs, it is appropriate to silently
+	     * ignore options this program does not understand
+	     */
+	}
+
+    }
+    fclose(f);
+
+    if (!ldap_conf.host)
+	ldap_conf.host = estrdup("localhost");
+
+    if (ldap_conf.bind_timelimit > 0)
+	ldap_conf.bind_timelimit *= 1000;	/* convert to ms */
+
+    if (ldap_conf.debug > 1) {
+	fprintf(stderr, "LDAP Config Summary\n");
+	fprintf(stderr, "===================\n");
+#ifdef HAVE_LDAP_INITIALIZE
+	if (ldap_conf.uri) {
+	    fprintf(stderr, "uri          %s\n", ldap_conf.uri);
+	} else
+#endif
+	{
+	    fprintf(stderr, "host         %s\n", ldap_conf.host ?
+		ldap_conf.host : "(NONE)");
+	    fprintf(stderr, "port         %d\n", ldap_conf.port);
+	}
+	fprintf(stderr, "ldap_version %d\n", ldap_conf.version);
+
+	fprintf(stderr, "sudoers_base %s\n", ldap_conf.base ?
+	    ldap_conf.base : "(NONE) <---Sudo will ignore ldap)");
+	fprintf(stderr, "binddn       %s\n", ldap_conf.binddn ?
+	    ldap_conf.binddn : "(anonymous)");
+	fprintf(stderr, "bindpw       %s\n", ldap_conf.bindpw ?
+	    ldap_conf.bindpw : "(anonymous)");
+	fprintf(stderr, "bind_timelimit  %d\n", ldap_conf.bind_timelimit);
+	fprintf(stderr, "timelimit    %d\n", ldap_conf.timelimit);
+#ifdef HAVE_LDAP_START_TLS_S
+	fprintf(stderr, "ssl          %s\n", ldap_conf.ssl ?
+	    ldap_conf.ssl : "(no)");
+#endif
+	fprintf(stderr, "===================\n");
+    }
+    if (!ldap_conf.base)
+	return(FALSE);		/* if no base is defined, ignore LDAP */
+
+    /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
+    if (ldap_conf.rootbinddn) {
+	if ((f = fopen(_PATH_LDAP_SECRET, "r")) != NULL) {
+	    if (fgets(buf, sizeof(buf), f) != NULL) {
+		/* removing trailing newlines */
+		for (c = buf; *c != '\0'; c++)
+		    continue;
+		while (--c > buf && *c == '\n')
+		    *c = '\0';
+		/* copy to bindpw and binddn */
+		efree(ldap_conf.bindpw);
+		ldap_conf.bindpw = estrdup(buf);
+		efree(ldap_conf.binddn);
+		ldap_conf.binddn = ldap_conf.rootbinddn;
+		ldap_conf.rootbinddn = NULL;
+	    }
+	    fclose(f);
+	}
+    }
+    return(TRUE);
+}
+
+/*
+ * like perl's join(sep, at ARGS)
+ */
+char *
+ _ldap_join_values(sep, v)
+    char *sep;
+    char **v;
+{
+    char *b = NULL, **p = NULL;
+    size_t sz = 0;
+
+    /* paste values together */
+    for (p = v; p && *p; p++) {
+	if (p != v && sep != NULL)
+	    ncat(&b, &sz, sep);	/* append seperator */
+	ncat(&b, &sz, *p);	/* append value */
+    }
+
+    /* sanity check */
+    if (b[0] == '\0') {
+	/* something went wrong, put something here */
+	ncat(&b, &sz, "(empty list)");	/* append value */
+    }
+
+    return(b);
+}
+
+char *sudo_ldap_cm_list = NULL;
+size_t sudo_ldap_cm_list_size;
+
+#define SAVE_LIST(x) ncat(&sudo_ldap_cm_list,&sudo_ldap_cm_list_size,(x))
+/*
+ * Walks through search result and returns TRUE if we have a
+ * command match
+ */
+int
+sudo_ldap_add_match(ld, entry, pwflag)
+    LDAP *ld;
+    LDAPMessage *entry;
+    int pwflag;
+{
+    char *dn, **edn, **v = NULL;
+
+    /* if we are not collecting matches, then don't save them */
+    if (pwflag != I_LISTPW)
+	return(TRUE);
+
+    /* collect the dn, only show the rdn */
+    dn = ldap_get_dn(ld, entry);
+    edn = dn ? ldap_explode_dn(dn, 1) : NULL;
+    SAVE_LIST("\nLDAP Role: ");
+    SAVE_LIST((edn && *edn) ? *edn : "UNKNOWN");
+    SAVE_LIST("\n");
+    if (dn)
+	ldap_memfree(dn);
+    if (edn)
+	ldap_value_free(edn);
+
+    /* get the Runas Values from the entry */
+    v = ldap_get_values(ld, entry, "sudoRunAs");
+    if (v && *v) {
+	SAVE_LIST("  RunAs: (");
+	SAVE_LIST(_ldap_join_values(", ", v));
+	SAVE_LIST(")\n");
+    }
+    if (v)
+	ldap_value_free(v);
+
+    /* get the Command Values from the entry */
+    v = ldap_get_values(ld, entry, "sudoCommand");
+    if (v && *v) {
+	SAVE_LIST("  Commands:\n    ");
+	SAVE_LIST(_ldap_join_values("\n    ", v));
+	SAVE_LIST("\n");
+    } else {
+	SAVE_LIST("  Commands: NONE\n");
+    }
+    if (v)
+	ldap_value_free(v);
+
+    return(FALSE);		/* Don't stop at the first match */
+}
+#undef SAVE_LIST
+
+void
+sudo_ldap_list_matches()
+{
+    if (sudo_ldap_cm_list != NULL)
+	printf("%s", sudo_ldap_cm_list);
+}
+
+/* macros to set option, error on failure plus consistent debugging */
+#define SET_OPTS(opt, val) do { \
+    if (ldap_conf.val != NULL) { \
+	if (ldap_conf.debug > 1) \
+	    fprintf(stderr, \
+		"ldap_set_option(LDAP_OPT_%s, \"%s\")\n", #opt, ldap_conf.val);\
+	rc = ldap_set_option(ld, LDAP_OPT_ ## opt, ldap_conf.val); \
+	if (rc != LDAP_OPT_SUCCESS) { \
+	    fprintf(stderr,"ldap_set_option(LDAP_OPT_%s, \"%s\")=%d: %s\n", \
+		#opt, ldap_conf.val, rc, ldap_err2string(rc)); \
+	    return(NULL); \
+	} \
+    } \
+} while(0)
+#define SET_OPTI(opt, val) do { \
+    if (ldap_conf.val >= 0) { \
+	if (ldap_conf.debug > 1) \
+	    fprintf(stderr, \
+		"ldap_set_option(LDAP_OPT_%s, %d)\n", #opt, ldap_conf.val); \
+	rc = ldap_set_option(ld, LDAP_OPT_ ## opt, &ldap_conf.val); \
+	if (rc != LDAP_OPT_SUCCESS) { \
+	    fprintf(stderr,"ldap_set_option(LDAP_OPT_%s, %d)=%d: %s\n", \
+		#opt, ldap_conf.val, rc, ldap_err2string(rc)); \
+	    return(NULL); \
+	} \
+    } \
+} while(0)
+
+/*
+ * Open a connection to the LDAP server.
+ */
+static LDAP *
+sudo_ldap_open()
+{
+    LDAP *ld = NULL;
+    int rc;
+
+    if (!sudo_ldap_read_config())
+	return(NULL);
+
+    /* attempt to setup ssl options */
+#ifdef LDAP_OPT_X_TLS_CACERTFILE
+    SET_OPTS(X_TLS_CACERTFILE, tls_cacertfile);
+#endif /* LDAP_OPT_X_TLS_CACERTFILE */
+
+#ifdef LDAP_OPT_X_TLS_CACERTDIR
+    SET_OPTS(X_TLS_CACERTDIR, tls_cacertdir);
+#endif /* LDAP_OPT_X_TLS_CACERTDIR */
+
+#ifdef LDAP_OPT_X_TLS_CERTFILE
+    SET_OPTS(X_TLS_CERTFILE, tls_certfile);
+#endif /* LDAP_OPT_X_TLS_CERTFILE */
+
+#ifdef LDAP_OPT_X_TLS_KEYFILE
+    SET_OPTS(X_TLS_KEYFILE, tls_keyfile);
+#endif /* LDAP_OPT_X_TLS_KEYFILE */
+
+#ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
+    SET_OPTS(X_TLS_CIPHER_SUITE, tls_cipher_suite);
+#endif /* LDAP_OPT_X_TLS_CIPHER_SUITE */
+
+#ifdef LDAP_OPT_X_TLS_RANDOM_FILE
+    SET_OPTS(X_TLS_RANDOM_FILE, tls_random_file);
+#endif /* LDAP_OPT_X_TLS_RANDOM_FILE */
+
+#ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
+    /* check the server certificate? */
+    SET_OPTI(X_TLS_REQUIRE_CERT, tls_checkpeer);
+#endif /* LDAP_OPT_X_TLS_REQUIRE_CERT */
+
+    /* set timelimit options */
+    SET_OPTI(TIMELIMIT, timelimit);
+
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+    if (ldap_conf.bind_timelimit > 0) {
+	struct timeval tv;
+	tv.tv_sec = ldap_conf.bind_timelimit / 1000;
+	tv.tv_usec = 0;
+	if (ldap_conf.debug > 1)
+	    fprintf(stderr, "ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %ld)\n",
+	    tv.tv_sec);
+	rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
+	if (rc != LDAP_OPT_SUCCESS) {
+	    fprintf(stderr,"ldap_set_option(NETWORK_TIMEOUT, %ld)=%d: %s\n",
+	    tv.tv_sec, rc, ldap_err2string(rc));
+	    return(NULL);
+	}
+    }
+#endif
+
+    /* attempt connect */
+#ifdef HAVE_LDAP_INITIALIZE
+    if (ldap_conf.uri) {
+
+	DPRINTF(("ldap_initialize(ld,%s)", ldap_conf.uri), 2);
+
+	rc = ldap_initialize(&ld, ldap_conf.uri);
+	if (rc) {
+	    fprintf(stderr, "ldap_initialize()=%d : %s\n",
+		rc, ldap_err2string(rc));
+	    return(NULL);
+	}
+    } else
+#endif /* HAVE_LDAP_INITIALIZE */
+    if (ldap_conf.host) {
+
+	DPRINTF(("ldap_init(%s,%d)", ldap_conf.host, ldap_conf.port), 2);
+
+	if ((ld = ldap_init(ldap_conf.host, ldap_conf.port)) == NULL) {
+	    fprintf(stderr, "ldap_init(): errno=%d : %s\n",
+		errno, strerror(errno));
+	    return(NULL);
+	}
+    }
+#ifdef LDAP_OPT_PROTOCOL_VERSION
+
+    /* Set the LDAP Protocol version */
+    SET_OPTI(PROTOCOL_VERSION, version);
+
+#endif /* LDAP_OPT_PROTOCOL_VERSION */
+
+#ifdef HAVE_LDAP_START_TLS_S
+    /* Turn on TLS */
+    if (ldap_conf.ssl && !strcasecmp(ldap_conf.ssl, "start_tls")) {
+	rc = ldap_start_tls_s(ld, NULL, NULL);
+	if (rc != LDAP_SUCCESS) {
+	    fprintf(stderr, "ldap_start_tls_s(): %d: %s\n", rc,
+		ldap_err2string(rc));
+	    ldap_unbind(ld);
+	    return(NULL);
+	}
+	DPRINTF(("ldap_start_tls_s() ok"), 1);
+    }
+#endif /* HAVE_LDAP_START_TLS_S */
+
+    /* Actually connect */
+    if ((rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw))) {
+	fprintf(stderr, "ldap_simple_bind_s()=%d : %s\n",
+	    rc, ldap_err2string(rc));
+	return(NULL);
+    }
+    DPRINTF(("ldap_bind() ok"), 1);
+
+    return(ld);
+}
+
+static void
+sudo_ldap_update_defaults(ld)
+    LDAP *ld;
+{
+    LDAPMessage *entry = NULL, *result = NULL;	 /* used for searches */
+    int rc;					 /* temp return value */
+
+    rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE,
+	"cn=defaults", NULL, 0, &result);
+    if (!rc && (entry = ldap_first_entry(ld, result))) {
+	DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
+	sudo_ldap_parse_options(ld, entry);
+    } else
+	DPRINTF(("no default options found!"), 1);
+
+    if (result)
+	ldap_msgfree(result);
+}
+
+/*
+ * like sudoers_lookup() - only LDAP style
+ */
+int
+sudo_ldap_check(pwflag)
+    int pwflag;
+{
+    LDAP *ld;
+    LDAPMessage *entry = NULL, *result = NULL;	/* used for searches */
+    char *filt;					/* used to parse attributes */
+    int rc, ret = FALSE, do_netgr;		/* temp/final return values */
+    int ldap_user_matches = FALSE, ldap_host_matches = FALSE; /* flags */
+
+    /* Open a connection to the LDAP server. */
+    if ((ld = sudo_ldap_open()) == NULL)
+	return(VALIDATE_ERROR);
+
+    /* Parse Default options. */
+    sudo_ldap_update_defaults(ld);
+
+    /*
+     * Okay - time to search for anything that matches this user
+     * Lets limit it to only two queries of the LDAP server
+     *
+     * The first pass will look by the username, groups, and
+     * the keyword ALL.  We will then inspect the results that
+     * came back from the query.  We don't need to inspect the
+     * sudoUser in this pass since the LDAP server already scanned
+     * it for us.
+     *
+     * The second pass will return all the entries that contain
+     * user netgroups.  Then we take the netgroups returned and
+     * try to match them against the username.
+     */
+
+    for (do_netgr = 0; !ret && do_netgr < 2; do_netgr++) {
+	filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1();
+	DPRINTF(("ldap search '%s'", filt), 1);
+	rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt,
+	    NULL, 0, &result);
+	if (rc)
+	    DPRINTF(("nothing found for '%s'", filt), 1);
+	efree(filt);
+
+	/* parse each entry returned from this most recent search */
+	entry = rc ? NULL : ldap_first_entry(ld, result);
+	while (entry != NULL) {
+	    DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
+	    if (
+	    /* first verify user netgroup matches - only if in pass 2 */
+		(!do_netgr || sudo_ldap_check_user_netgroup(ld, entry)) &&
+	    /* remember that user matched */
+		(ldap_user_matches = -1) &&
+	    /* verify host match */
+		sudo_ldap_check_host(ld, entry) &&
+	    /* remember that host matched */
+		(ldap_host_matches = -1) &&
+	    /* add matches for listing later */
+		sudo_ldap_add_match(ld, entry, pwflag) &&
+	    /* verify command match */
+		sudo_ldap_check_command(ld, entry) &&
+	    /* verify runas match */
+		sudo_ldap_check_runas(ld, entry)
+		) {
+		/* We have a match! */
+		DPRINTF(("Perfect Matched!"), 1);
+		/* pick up any options */
+		sudo_ldap_parse_options(ld, entry);
+		/* make sure we don't reenter loop */
+		ret = VALIDATE_OK;
+		/* break from inside for loop */
+		break;
+	    }
+	    entry = ldap_next_entry(ld, entry);
+	}
+	if (result)
+	    ldap_msgfree(result);
+	result = NULL;
+    }
+
+    sudo_ldap_close(ld);		/* shut down connection */
+
+    DPRINTF(("user_matches=%d", ldap_user_matches), 1);
+    DPRINTF(("host_matches=%d", ldap_host_matches), 1);
+
+    /* Check for special case for -v, -k, -l options */
+    if (pwflag && ldap_user_matches && ldap_host_matches) {
+	/*
+         * Handle verifypw & listpw
+         *
+         * To be extra paranoid, since we haven't read any NOPASSWD options
+         * in /etc/sudoers yet, but we have to make the decission now, lets
+         * assume the worst and prefer to prompt for password unless the setting
+         * is "never". (example verifypw=never or listpw=never)
+         *
+         */
+	ret = VALIDATE_OK;
+	if (pwflag == -1) {
+	    SET(ret, FLAG_NOPASS);		/* -k or -K */
+	} else {
+	    switch (sudo_defs_table[pwflag].sd_un.tuple) {
+	    case never:
+		SET(ret, FLAG_NOPASS);
+		break;
+	    case always:
+		if (def_authenticate)
+		    SET(ret, FLAG_CHECK_USER);
+		break;
+	    default:
+		break;
+	    }
+	}
+    }
+    if (ISSET(ret, VALIDATE_OK)) {
+	/* we have a match, should we check the password? */
+	if (!def_authenticate)
+	    SET(ret, FLAG_NOPASS);
+	if (def_noexec)
+	    SET(ret, FLAG_NOEXEC);
+	if (def_setenv)
+	    SET(ret, FLAG_SETENV);
+    } else {
+	/* we do not have a match */
+	ret = VALIDATE_NOT_OK;
+	if (pwflag)
+	    SET(ret, FLAG_NO_CHECK);
+	else if (!ldap_user_matches)
+	    SET(ret, FLAG_NO_USER);
+	else if (!ldap_host_matches)
+	    SET(ret, FLAG_NO_HOST);
+    }
+    DPRINTF(("sudo_ldap_check(%d)=0x%02x", pwflag, ret), 1);
+
+    return(ret);
+}
+
+/*
+ * shut down LDAP connection
+ */
+static void
+sudo_ldap_close(LDAP *ld)
+{
+    if (ld)
+	ldap_unbind_s(ld);
+}
Index: defaults.c
===================================================================
RCS file: /home/cvs/src/contrib/sudo/defaults.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lcontrib/sudo/defaults.c -Lcontrib/sudo/defaults.c -u -r1.1 -r1.2
--- contrib/sudo/defaults.c
+++ contrib/sudo/defaults.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999-2001, 2003-2004 Todd C. Miller <Todd.Miller at courtesan.com>
+ * Copyright (c) 1999-2005 Todd C. Miller <Todd.Miller at courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -18,7 +18,7 @@
  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
  */
 
-#include "config.h"
+#include <config.h>
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -52,7 +52,7 @@
 #include "sudo.h"
 
 #ifndef lint
-static const char rcsid[] = "$Sudo: defaults.c,v 1.48 2004/06/06 23:58:10 millert Exp $";
+__unused static const char rcsid[] = "$Sudo: defaults.c,v 1.48.2.5 2007/06/18 15:51:35 millert Exp $";
 #endif /* lint */
 
 /*
@@ -375,10 +375,8 @@
 	for (def = sudo_defs_table; def->name; def++)
 	    switch (def->type & T_MASK) {
 		case T_STR:
-		    if (def->sd_un.str) {
-			free(def->sd_un.str);
-			def->sd_un.str = NULL;
-		    }
+		    efree(def->sd_un.str);
+		    def->sd_un.str = NULL;
 		    break;
 		case T_LIST:
 		    list_op(NULL, 0, def, freeall);
@@ -438,6 +436,7 @@
 #ifdef ENV_EDITOR
     def_env_editor = TRUE;
 #endif
+    def_env_reset = TRUE;
     def_set_logname = TRUE;
 
     /* Syslog options need special care since they both strings and ints */
@@ -585,8 +584,7 @@
     int op;
 {
 
-    if (def->sd_un.str)
-	free(def->sd_un.str);
+    efree(def->sd_un.str);
     if (op == FALSE)
 	def->sd_un.str = NULL;
     else
@@ -735,8 +733,8 @@
 	for (cur = def->sd_un.list; cur; ) {
 	    tmp = cur;
 	    cur = tmp->next;
-	    free(tmp->value);
-	    free(tmp);
+	    efree(tmp->value);
+	    efree(tmp);
 	}
 	def->sd_un.list = NULL;
 	return;
@@ -753,8 +751,8 @@
 		prev->next = cur->next;
 	    else
 		def->sd_un.list = cur->next;
-	    free(cur->value);
-	    free(cur);
+	    efree(cur->value);
+	    efree(cur);
 	    break;
 	}
     }
Index: interfaces.h
===================================================================
RCS file: /home/cvs/src/contrib/sudo/interfaces.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lcontrib/sudo/interfaces.h -Lcontrib/sudo/interfaces.h -u -r1.1 -r1.2
--- contrib/sudo/interfaces.h
+++ contrib/sudo/interfaces.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996,1998-2001,2003 Todd C. Miller <Todd.Miller at courtesan.com>
+ * Copyright (c) 1996, 1998-2004 Todd C. Miller <Todd.Miller at courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -17,7 +17,7 @@
  * Agency (DARPA) and Air Force Research Laboratory, Air Force
  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
  *
- * $Sudo: interfaces.h,v 1.8 2004/02/13 21:36:43 millert Exp $
+ * $Sudo: interfaces.h,v 1.8.2.2 2007/08/13 16:30:02 millert Exp $
  */
 
 #ifndef _SUDO_INTERFACES_H
@@ -27,8 +27,19 @@
  * IP address and netmask pairs for checking against local interfaces.
  */
 struct interface {
-    struct in_addr addr;
-    struct in_addr netmask;
+    int family;	/* AF_INET or AF_INET6 */
+    union {
+	struct in_addr ip4;
+#ifdef AF_INET6
+	struct in6_addr ip6;
+#endif
+    } addr;
+    union {
+	struct in_addr ip4;
+#ifdef AF_INET6
+	struct in6_addr ip6;
+#endif
+    } netmask;
 };
 
 /*
Index: goodpath.c
===================================================================
RCS file: /home/cvs/src/contrib/sudo/goodpath.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lcontrib/sudo/goodpath.c -Lcontrib/sudo/goodpath.c -u -r1.1 -r1.2
--- contrib/sudo/goodpath.c
+++ contrib/sudo/goodpath.c
@@ -1,6 +1,5 @@
 /*
- * Copyright (c) 1996, 1998, 1999, 2001
- *	Todd C. Miller <Todd.Miller at courtesan.com>.
+ * Copyright (c) 1996, 1998-2005 Todd C. Miller <Todd.Miller at courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -19,7 +18,7 @@
  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
  */
 
-#include "config.h"
+#include <config.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -40,7 +39,7 @@
 #include "sudo.h"
 
 #ifndef lint
-static const char rcsid[] = "$Sudo: goodpath.c,v 1.41 2004/08/24 18:01:13 millert Exp $";
+__unused static const char rcsid[] = "$Sudo: goodpath.c,v 1.40.2.3 2007/06/12 01:28:41 millert Exp $";
 #endif /* lint */
 
 /*
Index: sudo.h
===================================================================
RCS file: /home/cvs/src/contrib/sudo/sudo.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lcontrib/sudo/sudo.h -Lcontrib/sudo/sudo.h -u -r1.1 -r1.2
--- contrib/sudo/sudo.h
+++ contrib/sudo/sudo.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1993-1996,1998-2004 Todd C. Miller <Todd.Miller at courtesan.com>
+ * Copyright (c) 1993-1996,1998-2007 Todd C. Miller <Todd.Miller at courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -17,7 +17,7 @@
  * Agency (DARPA) and Air Force Research Laboratory, Air Force
  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
  *
- * $Sudo: sudo.h,v 1.213 2004/09/08 15:48:23 millert Exp $
+ * $Sudo: sudo.h,v 1.209.2.10 2007/07/06 14:14:34 millert Exp $
  */
 
 #ifndef _SUDO_SUDO_H
@@ -49,6 +49,9 @@
     char *cmnd_base;
     char *cmnd_safe;
     char *class_name;
+    int ngroups;
+    gid_t *groups;
+    struct list_member *env_vars;
 };
 
 /*
@@ -65,6 +68,7 @@
 #define FLAG_NO_HOST		0x080
 #define FLAG_NO_CHECK		0x100
 #define FLAG_NOEXEC		0x200
+#define FLAG_SETENV		0x400
 
 /*
  * Pseudo-boolean values
@@ -86,35 +90,35 @@
 #define NOT_FOUND_DOT		-1
 
 /*
- * Various modes sudo can be in (based on arguments) in octal
+ * Various modes sudo can be in (based on arguments) in hex
  */
-#define MODE_RUN                 000001
-#define MODE_VALIDATE            000002
-#define MODE_INVALIDATE          000004
-#define MODE_KILL                000010
-#define MODE_VERSION             000020
-#define MODE_HELP                000040
-#define MODE_LIST                000100
-#define MODE_LISTDEFS            000200
-#define MODE_BACKGROUND          000400
-#define MODE_SHELL               001000
-#define MODE_LOGIN_SHELL         002000
-#define MODE_IMPLIED_SHELL       004000
-#define MODE_RESET_HOME          010000
-#define MODE_PRESERVE_GROUPS     020000
-#define MODE_EDIT                040000
+#define MODE_RUN		0x0001
+#define MODE_EDIT		0x0002
+#define MODE_VALIDATE		0x0004
+#define MODE_INVALIDATE		0x0008
+#define MODE_KILL		0x0010
+#define MODE_VERSION		0x0020
+#define MODE_HELP		0x0040
+#define MODE_LIST		0x0080
+#define MODE_LISTDEFS		0x0100
+#define MODE_BACKGROUND		0x0200
+#define MODE_SHELL		0x0400
+#define MODE_LOGIN_SHELL	0x0800
+#define MODE_IMPLIED_SHELL	0x1000
+#define MODE_RESET_HOME		0x2000
+#define MODE_PRESERVE_GROUPS	0x4000
+#define MODE_PRESERVE_ENV	0x8000
 
 /*
  * Used with set_perms()
  */
 #define PERM_ROOT                0x00
-#define PERM_FULL_ROOT           0x01
-#define PERM_USER                0x02
-#define PERM_FULL_USER           0x03
-#define PERM_SUDOERS             0x04
-#define PERM_RUNAS               0x05
-#define PERM_FULL_RUNAS          0x06
-#define PERM_TIMESTAMP           0x07
+#define PERM_USER                0x01
+#define PERM_FULL_USER           0x02
+#define PERM_SUDOERS             0x03
+#define PERM_RUNAS               0x04
+#define PERM_FULL_RUNAS          0x05
+#define PERM_TIMESTAMP           0x06
 
 /*
  * Shortcuts for sudo_user contents.
@@ -125,6 +129,8 @@
 #define user_gid		(sudo_user.pw->pw_gid)
 #define user_dir		(sudo_user.pw->pw_dir)
 #define user_shell		(sudo_user.shell)
+#define user_ngroups		(sudo_user.ngroups)
+#define user_groups		(sudo_user.groups)
 #define user_tty		(sudo_user.tty)
 #define user_cwd		(sudo_user.cwd)
 #define user_runas		(sudo_user.runas)
@@ -183,16 +189,20 @@
 int futimes		__P((int, const struct timeval *));
 #endif
 #ifndef HAVE_SNPRINTF
-int snprintf		__P((char *, size_t, const char *, ...));
+int snprintf		__P((char *, size_t, const char *, ...))
+			    __printflike(3, 4);
 #endif
 #ifndef HAVE_VSNPRINTF
-int vsnprintf		__P((char *, size_t, const char *, va_list));
+int vsnprintf		__P((char *, size_t, const char *, va_list))
+			    __printflike(3, 0);
 #endif
 #ifndef HAVE_ASPRINTF
-int asprintf		__P((char **, const char *, ...));
+int asprintf		__P((char **, const char *, ...))
+			    __printflike(2, 3);
 #endif
 #ifndef HAVE_VASPRINTF
-int vasprintf		__P((char **, const char *, va_list));
+int vasprintf		__P((char **, const char *, va_list))
+			    __printflike(2, 0);
 #endif
 #ifndef HAVE_STRCASECMP
 int strcasecmp		__P((const char *, const char *));
@@ -203,6 +213,12 @@
 #ifndef HAVE_STRLCPY
 size_t strlcpy		__P((char *, const char *, size_t));
 #endif
+#ifndef HAVE_MEMRCHR
+VOID *memrchr		__P((const VOID *, int, size_t));
+#endif
+#ifndef HAVE_MKSTEMP
+int mkstemp		__P((char *));
+#endif
 char *sudo_goodpath	__P((const char *, struct stat *));
 char *tgetpass		__P((const char *, int, int));
 int find_path		__P((char *, char **, struct stat *, char *));
@@ -213,9 +229,7 @@
 int sudo_ldap_check	__P((int));
 void sudo_ldap_list_matches __P((void));
 #endif
-void set_perms_nosuid	__P((int));
-void set_perms_posix	__P((int));
-void set_perms_suid	__P((int));
+void set_perms		__P((int));
 void remove_timestamp	__P((int));
 int check_secureware	__P((char *));
 void sia_attempt_auth	__P((void));
@@ -227,8 +241,11 @@
 VOID *erealloc		__P((VOID *, size_t));
 VOID *erealloc3		__P((VOID *, size_t, size_t));
 char *estrdup		__P((const char *));
-int easprintf		__P((char **, const char *, ...));
-int evasprintf		__P((char **, const char *, va_list));
+int easprintf		__P((char **, const char *, ...))
+			    __printflike(2, 3);
+int evasprintf		__P((char **, const char *, va_list))
+			    __printflike(2, 0);
+void efree		__P((VOID *));
 void dump_defaults	__P((void));
 void dump_auth_methods	__P((void));
 void init_envtables	__P((void));
@@ -251,8 +268,6 @@
 extern FILE *sudoers_fp;
 extern int tgetpass_flags;
 extern uid_t timestamp_uid;
-
-extern void (*set_perms) __P((int));
 #endif
 #ifndef errno
 extern int errno;
Index: zero_bytes.c
===================================================================
RCS file: /home/cvs/src/contrib/sudo/zero_bytes.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lcontrib/sudo/zero_bytes.c -Lcontrib/sudo/zero_bytes.c -u -r1.1 -r1.2
--- contrib/sudo/zero_bytes.c
+++ contrib/sudo/zero_bytes.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2001 Todd C. Miller <Todd.Miller at courtesan.com>
+ * Copyright (c) 2003-2005 Todd C. Miller <Todd.Miller at courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -16,10 +16,11 @@
 
 #include <sys/types.h>
 
-#include "config.h"
+#include <config.h>
+#include <compat.h>
 
 #ifndef lint
-static const char rcsid[] = "$Sudo: zero_bytes.c,v 1.2 2004/02/13 21:36:44 millert Exp $";
+__unused static const char rcsid[] = "$Sudo: zero_bytes.c,v 1.2.2.2 2007/06/12 01:28:42 millert Exp $";
 #endif /* lint */
 
 /*
Index: sudo.c
===================================================================
RCS file: /home/cvs/src/contrib/sudo/sudo.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lcontrib/sudo/sudo.c -Lcontrib/sudo/sudo.c -u -r1.1 -r1.2
--- contrib/sudo/sudo.c
+++ contrib/sudo/sudo.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1993-1996,1998-2004 Todd C. Miller <Todd.Miller at courtesan.com>
+ * Copyright (c) 1993-1996,1998-2007 Todd C. Miller <Todd.Miller at courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -27,7 +27,7 @@
 # include <floss.h>
 #endif
 
-#include "config.h"
+#include <config.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -69,7 +69,12 @@
 #include <fcntl.h>
 #include <signal.h>
 #include <grp.h>
-#include <time.h>
+#if TIME_WITH_SYS_TIME
+# include <time.h>
+#endif
+#ifdef HAVE_SETLOCALE
+# include <locale.h>
+#endif
 #include <netinet/in.h>
 #include <netdb.h>
 #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
@@ -87,30 +92,39 @@
 #  define LOGIN_DEFROOTCLASS	"daemon"
 # endif
 #endif
+#ifdef HAVE_PROJECT_H
+# include <project.h>
+# include <sys/task.h>
+#endif
 
 #include "sudo.h"
 #include "interfaces.h"
 #include "version.h"
 
 #ifndef lint
-static const char rcsid[] = "$Sudo: sudo.c,v 1.370 2004/08/24 18:01:13 millert Exp $";
+__unused __unused static const char rcsid[] = "$Sudo: sudo.c,v 1.369.2.29 2007/08/15 13:48:56 millert Exp $";
 #endif /* lint */
 
 /*
  * Prototypes
  */
-static int init_vars			__P((int));
+static int init_vars			__P((int, char **));
 static int parse_args			__P((int, char **));
 static void check_sudoers		__P((void));
 static void initial_setup		__P((void));
 static void set_loginclass		__P((struct passwd *));
-static void usage			__P((int));
+static void set_project			__P((struct passwd *));
+static void usage			__P((int))
+					    __attribute__((__noreturn__));
+static void usage_excl			__P((int))
+					    __attribute__((__noreturn__));
 static void usage_excl			__P((int));
 static struct passwd *get_authpw	__P((void));
-extern int sudo_edit			__P((int, char **));
+extern int sudo_edit			__P((int, char **, char **));
 extern void list_matches		__P((void));
 extern char **rebuild_env		__P((char **, int, int));
-extern char **zero_env			__P((char **));
+extern void validate_env_vars		__P((struct list_member *));
+extern char **insert_env_vars		__P((char **, struct list_member *));
 extern struct passwd *sudo_getpwnam	__P((const char *));
 extern struct passwd *sudo_getpwuid	__P((uid_t));
 extern struct passwd *sudo_pwdup	__P((const struct passwd *));
@@ -139,7 +153,6 @@
 char *login_style;
 #endif /* HAVE_BSD_AUTH_H */
 sigaction_t saved_sa_int, saved_sa_quit, saved_sa_tstp, saved_sa_chld;
-void (*set_perms) __P((int));
 
 
 int
@@ -153,11 +166,14 @@
     int cmnd_status;
     int sudo_mode;
     int pwflag;
-    char **new_environ;
     sigaction_t sa;
     extern int printmatches;
     extern char **environ;
 
+#ifdef HAVE_SETLOCALE
+    setlocale(LC_ALL, "");
+#endif
+
     Argv = argv;
     if ((Argc = argc) < 1)
 	usage(1);
@@ -170,9 +186,6 @@
 # endif
 #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
 
-    /* Zero out the environment. */
-    environ = zero_env(envp);
-
     if (geteuid() != 0)
 	errx(1, "must be setuid root");
 
@@ -192,7 +205,7 @@
     (void) sigaction(SIGCHLD, &sa, &saved_sa_chld);
 
     /*
-     * Turn off core dumps, close open files and setup set_perms().
+     * Turn off core dumps and close open files.
      */
     initial_setup();
     setpwent();
@@ -217,6 +230,7 @@
 		(void) printf("Sudo version %s\n", version);
 		if (getuid() == 0) {
 		    putchar('\n');
+		    (void) printf("Sudoers path: %s\n", _PATH_SUDOERS);
 		    dump_auth_methods();
 		    dump_defaults();
 		    dump_interfaces();
@@ -250,7 +264,7 @@
     if (user_cmnd == NULL && NewArgc == 0)
 	usage(1);
 
-    cmnd_status = init_vars(sudo_mode);
+    cmnd_status = init_vars(sudo_mode, environ);
 
 #ifdef HAVE_LDAP
     validated = sudo_ldap_check(pwflag);
@@ -276,23 +290,7 @@
 	validated = sudoers_lookup(pwflag);
     }
     if (safe_cmnd == NULL)
-	safe_cmnd = user_cmnd;
-
-    /*
-     * If we are using set_perms_posix() and the stay_setuid flag was not set,
-     * set the real, effective and saved uids to 0 and use set_perms_nosuid()
-     * instead of set_perms_posix().
-     */
-#if !defined(HAVE_SETRESUID) && !defined(HAVE_SETREUID) && \
-    !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
-    if (!def_stay_setuid && set_perms == set_perms_posix) {
-	if (setuid(0)) {
-	    perror("setuid(0)");
-	    exit(1);
-	}
-	set_perms = set_perms_nosuid;
-    }
-#endif
+	safe_cmnd = estrdup(user_cmnd);
 
     /*
      * Look up the timestamp dir owner if one is specified.
@@ -336,11 +334,6 @@
     if (ISSET(sudo_mode, MODE_IMPLIED_SHELL) && !def_shell_noargs)
 	usage(1);
 
-    /* May need to set $HOME to target user if we are running a command. */
-    if (ISSET(sudo_mode, MODE_RUN) && (def_always_set_home ||
-	(ISSET(sudo_mode, MODE_SHELL) && def_set_home)))
-	SET(sudo_mode, MODE_RESET_HOME);
-
     /* Bail if a tty is required and we don't have one.  */
     if (def_requiretty) {
 	if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1)
@@ -349,29 +342,30 @@
 	    (void) close(fd);
     }
 
+    /* User may have overriden environment resetting via the -E flag. */
+    if (ISSET(sudo_mode, MODE_PRESERVE_ENV) && ISSET(validated, FLAG_SETENV))
+	def_env_reset = FALSE;
+
+    /* Build a new environment that avoids any nasty bits. */
+    environ = rebuild_env(environ, sudo_mode, ISSET(validated, FLAG_NOEXEC));
+
     /* Fill in passwd struct based on user we are authenticating as.  */
     auth_pw = get_authpw();
 
     /* Require a password if sudoers says so.  */
     if (!ISSET(validated, FLAG_NOPASS))
-	check_user(ISSET(validated, FLAG_CHECK_USER));
+	check_user(validated);
 
     /* If run as root with SUDO_USER set, set sudo_user.pw to that user. */
     if (user_uid == 0 && prev_user != NULL && strcmp(prev_user, "root") != 0) {
 	    struct passwd *pw;
 
 	    if ((pw = sudo_getpwnam(prev_user)) != NULL) {
-		    free(sudo_user.pw);
+		    efree(sudo_user.pw);
 		    sudo_user.pw = pw;
 	    }
     }
 
-    /* Build a new environment that avoids any nasty bits if we have a cmnd. */
-    if (ISSET(sudo_mode, MODE_RUN))
-	new_environ = rebuild_env(envp, sudo_mode, ISSET(validated, FLAG_NOEXEC));
-    else
-	new_environ = envp;
-
     if (ISSET(validated, VALIDATE_OK)) {
 	/* Finally tell the user if the command did not exist. */
 	if (cmnd_status == NOT_FOUND_DOT) {
@@ -382,6 +376,15 @@
 	    exit(1);
 	}
 
+	/* If user specified env vars make sure sudoers allows it. */
+	if (ISSET(sudo_mode, MODE_RUN) && !ISSET(validated, FLAG_SETENV)) {
+	    if (ISSET(sudo_mode, MODE_PRESERVE_ENV))
+		log_error(NO_MAIL,
+		    "sorry, you are not allowed to preserve the environment");
+	    else
+		validate_env_vars(sudo_user.env_vars);
+	}
+
 	log_auth(validated, 1);
 	if (sudo_mode == MODE_VALIDATE)
 	    exit(0);
@@ -410,9 +413,6 @@
 	endpwent();
 	endgrent();
 
-	/* Install the real environment. */
-	environ = new_environ;
-
 	if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
 	    char *p;
 
@@ -428,7 +428,10 @@
 	}
 
 	if (ISSET(sudo_mode, MODE_EDIT))
-	    exit(sudo_edit(NewArgc, NewArgv));
+	    exit(sudo_edit(NewArgc, NewArgv, envp));
+
+	/* Insert user-specified environment variables. */
+	environ = insert_env_vars(environ, sudo_user.env_vars);
 
 	/* Restore signal handlers before we exec. */
 	(void) sigaction(SIGINT, &saved_sa_int, NULL);
@@ -440,13 +443,19 @@
 	if (ISSET(sudo_mode, MODE_BACKGROUND) && fork() > 0)
 	    exit(0);
 	else
-	    EXECV(safe_cmnd, NewArgv);	/* run the command */
+	    execve(safe_cmnd, NewArgv, environ);
 #else
 	exit(0);
 #endif /* PROFILING */
 	/*
 	 * If we got here then the exec() failed...
 	 */
+	if (errno == ENOEXEC) {
+	    NewArgv--;			/* at least one extra slot... */
+	    NewArgv[0] = "sh";
+	    NewArgv[1] = safe_cmnd;
+	    execve(_PATH_BSHELL, NewArgv, environ);
+	}
 	warn("unable to execute %s", safe_cmnd);
 	exit(127);
     } else if (ISSET(validated, FLAG_NO_USER) || (validated & FLAG_NO_HOST)) {
@@ -485,10 +494,11 @@
  * load the ``interfaces'' array.
  */
 static int
-init_vars(sudo_mode)
+init_vars(sudo_mode, envp)
     int sudo_mode;
+    char **envp;
 {
-    char *p, thost[MAXHOSTNAMELEN];
+    char *p, **ep, thost[MAXHOSTNAMELEN];
     int nohostname, rval;
 
     /* Sanity check command from user. */
@@ -536,6 +546,24 @@
     } else
 	user_tty = "unknown";
 
+    for (ep = envp; *ep; ep++) {
+	switch (**ep) {
+	    case 'P':
+		if (strncmp("PATH=", *ep, 5) == 0)
+		    user_path = *ep + 5;
+		break;
+	    case 'S':
+		if (strncmp("SHELL=", *ep, 6) == 0)
+		    user_shell = *ep + 6;
+		else if (!user_prompt && strncmp("SUDO_PROMPT=", *ep, 12) == 0)
+		    user_prompt = *ep + 12;
+		else if (strncmp("SUDO_USER=", *ep, 10) == 0)
+		    prev_user = *ep + 10;
+		break;
+
+	    }
+    }
+
     /*
      * Get a local copy of the user's struct passwd with the shadow password
      * if necessary.  It is assumed that euid is 0 at this point so we
@@ -566,6 +594,15 @@
 
     /* It is now safe to use log_error() and set_perms() */
 
+#ifdef HAVE_GETGROUPS
+    if ((user_ngroups = getgroups(0, NULL)) > 0) {
+	user_groups = emalloc2(user_ngroups, MAX(sizeof(gid_t), sizeof(int)));
+	if (getgroups(user_ngroups, user_groups) < 0)
+	    log_error(USE_ERRNO|MSG_ONLY, "can't get group vector");
+    } else
+	user_ngroups = 0;
+#endif
+
     if (def_fqdn)
 	set_fqdn();			/* may call log_error() */
 
@@ -596,7 +633,9 @@
     if ((sudo_mode & (MODE_SHELL | MODE_EDIT))) {
 	char **dst, **src = NewArgv;
 
-	NewArgv = (char **) emalloc2((++NewArgc + 1), sizeof(char *));
+	/* Allocate an extra slot for execve() failure (ENOEXEC). */
+	NewArgv = (char **) emalloc2((++NewArgc + 2), sizeof(char *));
+	NewArgv++;
 	if (ISSET(sudo_mode, MODE_EDIT))
 	    NewArgv[0] = "sudoedit";
 	else if (ISSET(sudo_mode, MODE_LOGIN_SHELL))
@@ -608,12 +647,15 @@
 
 	/* copy the args from NewArgv */
 	for (dst = NewArgv + 1; (*dst = *src) != NULL; ++src, ++dst)
-	    ;
+	    continue;
     }
 
     /* Set login class if applicable. */
     set_loginclass(sudo_user.pw);
 
+    /* Set project if applicable. */
+    set_project(runas_pw);
+
     /* Resolve the path and return. */
     rval = FOUND;
     user_stat = emalloc(sizeof(struct stat));
@@ -686,160 +728,179 @@
     } else
 	rval = MODE_RUN;
 
-    if (NewArgc == 0 && rval == MODE_RUN) {	/* no options and no command */
-	SET(rval, (MODE_IMPLIED_SHELL | MODE_SHELL));
-	return(rval);
-    }
+    while (NewArgc > 0) {
+	if (NewArgv[0][0] == '-') {
+	    if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0')
+		warnx("please use single character options");
+
+	    switch (NewArgv[0][1]) {
+		case 'p':
+		    /* Must have an associated prompt. */
+		    if (NewArgv[1] == NULL)
+			usage(1);
 
-    while (NewArgc > 0 && NewArgv[0][0] == '-') {
-	if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0')
-	    warnx("please use single character options");
-
-	switch (NewArgv[0][1]) {
-	    case 'p':
-		/* Must have an associated prompt. */
-		if (NewArgv[1] == NULL)
-		    usage(1);
+		    user_prompt = NewArgv[1];
 
-		user_prompt = NewArgv[1];
-
-		NewArgc--;
-		NewArgv++;
-		break;
-	    case 'u':
-		/* Must have an associated runas user. */
-		if (NewArgv[1] == NULL)
-		    usage(1);
+		    NewArgc--;
+		    NewArgv++;
+		    break;
+		case 'u':
+		    /* Must have an associated runas user. */
+		    if (NewArgv[1] == NULL)
+			usage(1);
 
-		user_runas = &NewArgv[1];
+		    user_runas = &NewArgv[1];
 
-		NewArgc--;
-		NewArgv++;
-		break;
+		    NewArgc--;
+		    NewArgv++;
+		    break;
 #ifdef HAVE_BSD_AUTH_H
-	    case 'a':
-		/* Must have an associated authentication style. */
-		if (NewArgv[1] == NULL)
-		    usage(1);
+		case 'a':
+		    /* Must have an associated authentication style. */
+		    if (NewArgv[1] == NULL)
+			usage(1);
 
-		login_style = NewArgv[1];
+		    login_style = NewArgv[1];
 
-		NewArgc--;
-		NewArgv++;
-		break;
+		    NewArgc--;
+		    NewArgv++;
+		    break;
 #endif
 #ifdef HAVE_LOGIN_CAP_H
-	    case 'c':
-		/* Must have an associated login class. */
-		if (NewArgv[1] == NULL)
-		    usage(1);
+		case 'c':
+		    /* Must have an associated login class. */
+		    if (NewArgv[1] == NULL)
+			usage(1);
 
-		login_class = NewArgv[1];
-		def_use_loginclass = TRUE;
+		    login_class = NewArgv[1];
+		    def_use_loginclass = TRUE;
 
-		NewArgc--;
-		NewArgv++;
-		break;
+		    NewArgc--;
+		    NewArgv++;
+		    break;
 #endif
-	    case 'b':
-		SET(rval, MODE_BACKGROUND);
-		break;
-	    case 'e':
-		rval = MODE_EDIT;
-		if (excl && excl != 'e')
-		    usage_excl(1);
-		excl = 'e';
-		break;
-	    case 'v':
-		rval = MODE_VALIDATE;
-		if (excl && excl != 'v')
-		    usage_excl(1);
-		excl = 'v';
-		break;
-	    case 'i':
-		SET(rval, (MODE_LOGIN_SHELL | MODE_SHELL));
-		def_env_reset = TRUE;
-		if (excl && excl != 'i')
-		    usage_excl(1);
-		excl = 'i';
-		break;
-	    case 'k':
-		rval = MODE_INVALIDATE;
-		if (excl && excl != 'k')
-		    usage_excl(1);
-		excl = 'k';
-		break;
-	    case 'K':
-		rval = MODE_KILL;
-		if (excl && excl != 'K')
-		    usage_excl(1);
-		excl = 'K';
-		break;
-	    case 'L':
-		rval = MODE_LISTDEFS;
-		if (excl && excl != 'L')
-		    usage_excl(1);
-		excl = 'L';
-		break;
-	    case 'l':
-		rval = MODE_LIST;
-		if (excl && excl != 'l')
-		    usage_excl(1);
-		excl = 'l';
-		break;
-	    case 'V':
-		rval = MODE_VERSION;
-		if (excl && excl != 'V')
-		    usage_excl(1);
-		excl = 'V';
-		break;
-	    case 'h':
-		rval = MODE_HELP;
-		if (excl && excl != 'h')
-		    usage_excl(1);
-		excl = 'h';
-		break;
-	    case 's':
-		SET(rval, MODE_SHELL);
-		if (excl && excl != 's')
-		    usage_excl(1);
-		excl = 's';
-		break;
-	    case 'H':
-		SET(rval, MODE_RESET_HOME);
-		break;
-	    case 'P':
-		SET(rval, MODE_PRESERVE_GROUPS);
-		break;
-	    case 'S':
-		SET(tgetpass_flags, TGP_STDIN);
-		break;
-	    case '-':
-		NewArgc--;
-		NewArgv++;
-		if (rval == MODE_RUN)
-		    SET(rval, (MODE_IMPLIED_SHELL | MODE_SHELL));
-		return(rval);
-	    case '\0':
-		warnx("'-' requires an argument");
-		usage(1);
-	    default:
-		warnx("illegal option `%s'", NewArgv[0]);
-		usage(1);
+		case 'b':
+		    SET(rval, MODE_BACKGROUND);
+		    break;
+		case 'e':
+		    rval = MODE_EDIT;
+		    if (excl && excl != 'e')
+			usage_excl(1);
+		    excl = 'e';
+		    break;
+		case 'v':
+		    rval = MODE_VALIDATE;
+		    if (excl && excl != 'v')
+			usage_excl(1);
+		    excl = 'v';
+		    break;
+		case 'i':
+		    SET(rval, (MODE_LOGIN_SHELL | MODE_SHELL));
+		    def_env_reset = TRUE;
+		    if (excl && excl != 'i')
+			usage_excl(1);
+		    excl = 'i';
+		    break;
+		case 'k':
+		    rval = MODE_INVALIDATE;
+		    if (excl && excl != 'k')
+			usage_excl(1);
+		    excl = 'k';
+		    break;
+		case 'K':
+		    rval = MODE_KILL;
+		    if (excl && excl != 'K')
+			usage_excl(1);
+		    excl = 'K';
+		    break;
+		case 'L':
+		    rval = MODE_LISTDEFS;
+		    if (excl && excl != 'L')
+			usage_excl(1);
+		    excl = 'L';
+		    break;
+		case 'l':
+		    rval = MODE_LIST;
+		    if (excl && excl != 'l')
+			usage_excl(1);
+		    excl = 'l';
+		    break;
+		case 'V':
+		    rval = MODE_VERSION;
+		    if (excl && excl != 'V')
+			usage_excl(1);
+		    excl = 'V';
+		    break;
+		case 'h':
+		    rval = MODE_HELP;
+		    if (excl && excl != 'h')
+			usage_excl(1);
+		    excl = 'h';
+		    break;
+		case 's':
+		    SET(rval, MODE_SHELL);
+		    if (excl && excl != 's')
+			usage_excl(1);
+		    excl = 's';
+		    break;
+		case 'H':
+		    SET(rval, MODE_RESET_HOME);
+		    break;
+		case 'P':
+		    SET(rval, MODE_PRESERVE_GROUPS);
+		    break;
+		case 'S':
+		    SET(tgetpass_flags, TGP_STDIN);
+		    break;
+		case 'E':
+		    SET(rval, MODE_PRESERVE_ENV);
+		    break;
+		case '-':
+		    NewArgc--;
+		    NewArgv++;
+		    goto args_done;
+		case '\0':
+		    warnx("'-' requires an argument");
+		    usage(1);
+		default:
+		    warnx("illegal option `%s'", NewArgv[0]);
+		    usage(1);
+	    }
+	} else if (NewArgv[0][0] != '/' && strchr(NewArgv[0], '=') != NULL) {
+	    /* Could be an environment variable. */
+	    struct list_member *ev;
+	    ev = emalloc(sizeof(*ev));
+	    ev->value = NewArgv[0];
+	    ev->next = sudo_user.env_vars;
+	    sudo_user.env_vars = ev;
+	} else {
+	    /* Not an arg */
+	    break;
 	}
 	NewArgc--;
 	NewArgv++;
     }
+args_done:
+
+    if (ISSET(rval, MODE_EDIT) &&
+	(ISSET(rval, MODE_PRESERVE_ENV) || sudo_user.env_vars != NULL)) {
+	if (ISSET(rval, MODE_PRESERVE_ENV))
+	    warnx("the `-E' option is not valid in edit mode");
+	if (sudo_user.env_vars != NULL)
+	    warnx("you may not specify environment variables in edit mode");
+	usage(1);
+    }
 
     if (user_runas != NULL && !ISSET(rval, (MODE_EDIT|MODE_RUN))) {
 	if (excl != '\0')
 	    warnx("the `-u' and '-%c' options may not be used together", excl);
 	usage(1);
     }
-
     if ((NewArgc == 0 && (rval & MODE_EDIT)) ||
 	(NewArgc > 0 && !(rval & (MODE_RUN | MODE_EDIT))))
 	usage(1);
+    if (NewArgc == 0 && rval == MODE_RUN)
+	SET(rval, (MODE_IMPLIED_SHELL | MODE_SHELL));
 
     return(rval);
 }
@@ -853,7 +914,6 @@
 {
     struct stat statbuf;
     int rootstat, i;
-    char c;
 
     /*
      * Fix the mode and group on sudoers file from old default.
@@ -892,7 +952,8 @@
 	log_error(0, "%s is zero length", _PATH_SUDOERS);
     else if ((statbuf.st_mode & 07777) != SUDOERS_MODE)
 	log_error(0, "%s is mode 0%o, should be 0%o", _PATH_SUDOERS,
-	    (statbuf.st_mode & 07777), SUDOERS_MODE);
+	    (unsigned int) (statbuf.st_mode & 07777),
+	    (unsigned int) SUDOERS_MODE);
     else if (statbuf.st_uid != SUDOERS_UID)
 	log_error(0, "%s is owned by uid %lu, should be %lu", _PATH_SUDOERS,
 	    (unsigned long) statbuf.st_uid, (unsigned long) SUDOERS_UID);
@@ -904,7 +965,9 @@
 	for (i = 0; i < 10 ; i++) {
 	    errno = 0;
 	    if ((sudoers_fp = fopen(_PATH_SUDOERS, "r")) == NULL ||
-		fread(&c, sizeof(c), 1, sudoers_fp) != 1) {
+		fgetc(sudoers_fp) == EOF) {
+		if (sudoers_fp != NULL)
+		    fclose(sudoers_fp);
 		sudoers_fp = NULL;
 		if (errno != EAGAIN && errno != EWOULDBLOCK)
 		    break;
@@ -921,11 +984,11 @@
 
 /*
  * Close all open files (except std*) and turn off core dumps.
- * Also sets the set_perms() pointer to the correct function.
  */
 static void
 initial_setup()
 {
+    int miss[3], devnull = -1;
 #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
     struct rlimit rl;
 
@@ -938,25 +1001,24 @@
     (void) setrlimit(RLIMIT_CORE, &rl);
 #endif /* RLIMIT_CORE && !SUDO_DEVEL */
 
-    closefrom(STDERR_FILENO + 1);
-
     /*
-     * Make set_perms point to the correct function.
-     * If we are using setresuid() or setreuid() we only need to set this
-     * once.  If we are using POSIX saved uids we will switch to
-     * set_perms_nosuid after sudoers has been parsed if the "stay_suid"
-     * option is not set.
+     * stdin, stdout and stderr must be open; set them to /dev/null
+     * if they are closed and close all other fds.
      */
-#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID)
-    set_perms = set_perms_suid;
-#else
-# if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
-    if (sysconf(_SC_SAVED_IDS) == 1 && sysconf(_SC_VERSION) >= 199009)
-	set_perms = set_perms_posix;
-    else
-# endif
-	set_perms = set_perms_nosuid;
-#endif /* HAVE_SETRESUID || HAVE_SETREUID */
+    miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F