[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