/*
- * Copyright (c) 1996, 1998-2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 1996, 1998-2005, 2007
+ * Todd C. Miller <Todd.Miller@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
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
-#include "config.h"
+#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <stdio.h>
#ifdef STDC_HEADERS
#ifdef HAVE_FNMATCH
# include <fnmatch.h>
#endif /* HAVE_FNMATCH */
+#ifdef HAVE_EXTENDED_GLOB
+# include <glob.h>
+#endif /* HAVE_EXTENDED_GLOB */
#ifdef HAVE_NETGROUP_H
# include <netgroup.h>
#endif /* HAVE_NETGROUP_H */
#ifndef HAVE_FNMATCH
# include "emul/fnmatch.h"
#endif /* HAVE_FNMATCH */
+#ifndef HAVE_EXTENDED_GLOB
+# include "emul/glob.h"
+#endif /* HAVE_EXTENDED_GLOB */
#ifndef lint
-static const char rcsid[] = "$Sudo: parse.c,v 1.161 2004/08/24 18:01:13 millert Exp $";
+__unused static const char rcsid[] = "$Sudo: parse.c,v 1.160.2.16 2008/02/09 14:44:48 millert Exp $";
#endif /* lint */
/*
int pwflag;
{
int error, nopass;
- enum def_tupple pwcheck;
/* We opened _PATH_SUDOERS in check_sudoers() so just rewind it. */
rewind(sudoers_fp);
/* Allocate space for data structures in the parser. */
init_parser();
- /* If pwcheck *could* be "all" or "any", keep more state. */
+ /* Keep more state for pseudo-commands so that listpw and verifypw work */
if (pwflag > 0)
keepall = TRUE;
return(VALIDATE_ERROR);
}
- /*
- * The pw options may have changed during sudoers parse so we
- * wait until now to set this.
- */
- if (pwflag)
- pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
- else
- pwcheck = 0;
-
/*
* Assume the worst. If the stack is empty the user was
* not mentioned at all.
error = VALIDATE_NOT_OK;
else
error = VALIDATE_NOT_OK | FLAG_NOPASS;
- if (pwcheck) {
+ if (pwflag) {
SET(error, FLAG_NO_CHECK);
} else {
SET(error, FLAG_NO_HOST);
nopass = -1;
if (pwflag) {
int found;
+ enum def_tupple pwcheck;
+
+ pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
if (pwcheck == always && def_authenticate)
nopass = FLAG_CHECK_USER;
/*
* User was granted access to cmnd on host as user.
*/
+#ifdef HAVE_SELINUX
+ /* Set role and type if not specified on command line. */
+ if (user_role == NULL) {
+ if (match[top-1].role != NULL)
+ user_role = match[top-1].role;
+ else
+ user_role = def_role;
+ }
+ if (user_type == NULL) {
+ if (match[top-1].type != NULL)
+ user_type = match[top-1].type;
+ else
+ user_type = def_type;
+ }
+#endif
set_perms(PERM_ROOT);
return(VALIDATE_OK |
(no_passwd == TRUE ? FLAG_NOPASS : 0) |
- (no_execve == TRUE ? FLAG_NOEXEC : 0));
+ (no_execve == TRUE ? FLAG_NOEXEC : 0) |
+ (setenv_ok >= TRUE ? FLAG_SETENV : 0));
} else if ((runas_matches == TRUE && cmnd_matches == FALSE) ||
(runas_matches == FALSE && cmnd_matches == TRUE)) {
/*
set_perms(PERM_ROOT);
return(VALIDATE_NOT_OK |
(no_passwd == TRUE ? FLAG_NOPASS : 0) |
- (no_execve == TRUE ? FLAG_NOEXEC : 0));
+ (no_execve == TRUE ? FLAG_NOEXEC : 0) |
+ (setenv_ok >= TRUE ? FLAG_SETENV : 0));
}
}
top--;
{
struct stat sudoers_stat;
struct dirent *dent;
- char buf[PATH_MAX];
+ char **ap, *base, buf[PATH_MAX];
+ glob_t gl;
DIR *dirp;
/* Check for pseudo-commands */
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
(sudoers_args &&
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
- if (safe_cmnd)
- free(safe_cmnd);
+ efree(safe_cmnd);
safe_cmnd = estrdup(sudoers_cmnd);
return(TRUE);
} else
*/
if (has_meta(sudoers_cmnd)) {
/*
- * Return true if fnmatch(3) succeeds AND
+ * Return true if we find a match in the glob(3) results AND
* a) there are no args in sudoers OR
* b) there are no args on command line and none required by sudoers OR
* c) there are args in sudoers and on command line and they match
* else return false.
+ *
+ * Could optimize patterns ending in "/*" to "/user_base"
*/
- if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
+#define GLOB_FLAGS (GLOB_NOSORT | GLOB_MARK | GLOB_BRACE | GLOB_TILDE)
+ if (glob(sudoers_cmnd, GLOB_FLAGS, NULL, &gl) != 0) {
+ globfree(&gl);
+ return(FALSE);
+ }
+ /* For each glob match, compare basename, st_dev and st_ino. */
+ for (ap = gl.gl_pathv; *ap != NULL; ap++) {
+ /* only stat if basenames are the same */
+ if ((base = strrchr(*ap, '/')) != NULL)
+ base++;
+ else
+ base = *ap;
+ if (strcmp(user_base, base) != 0 ||
+ stat(*ap, &sudoers_stat) == -1)
+ continue;
+ if (user_stat->st_dev == sudoers_stat.st_dev &&
+ user_stat->st_ino == sudoers_stat.st_ino) {
+ efree(safe_cmnd);
+ safe_cmnd = estrdup(*ap);
+ break;
+ }
+ }
+ globfree(&gl);
+ if (*ap == NULL)
return(FALSE);
+
if (!sudoers_args ||
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
(sudoers_args &&
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
- if (safe_cmnd)
- free(safe_cmnd);
+ efree(safe_cmnd);
safe_cmnd = estrdup(user_cmnd);
return(TRUE);
} else
* Check to make sure this is not a directory spec (doesn't end in '/')
*/
if (sudoers_cmnd[dlen - 1] != '/') {
- char *base;
-
/* Only proceed if user_base and basename(sudoers_cmnd) match */
if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
base = sudoers_cmnd;
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
(sudoers_args &&
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
- if (safe_cmnd)
- free(safe_cmnd);
+ efree(safe_cmnd);
safe_cmnd = estrdup(sudoers_cmnd);
return(TRUE);
} else
continue;
if (user_stat->st_dev == sudoers_stat.st_dev &&
user_stat->st_ino == sudoers_stat.st_ino) {
- if (safe_cmnd)
- free(safe_cmnd);
+ efree(safe_cmnd);
safe_cmnd = estrdup(buf);
break;
}
}
}
-/*
- * Returns TRUE if "n" is one of our ip addresses or if
- * "n" is a network that we are on, else returns FALSE.
- */
-int
-addr_matches(n)
+static int
+addr_matches_if(n)
char *n;
{
int i;
+ struct in_addr addr;
+ struct interface *ifp;
+#ifdef HAVE_IN6_ADDR
+ struct in6_addr addr6;
+ int j;
+#endif
+ int family;
+
+#ifdef HAVE_IN6_ADDR
+ if (inet_pton(AF_INET6, n, &addr6) > 0) {
+ family = AF_INET6;
+ } else
+#endif
+ {
+ family = AF_INET;
+ addr.s_addr = inet_addr(n);
+ }
+
+ for (i = 0; i < num_interfaces; i++) {
+ ifp = &interfaces[i];
+ if (ifp->family != family)
+ continue;
+ switch(family) {
+ case AF_INET:
+ if (ifp->addr.ip4.s_addr == addr.s_addr ||
+ (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr)
+ == addr.s_addr)
+ return(TRUE);
+ break;
+#ifdef HAVE_IN6_ADDR
+ case AF_INET6:
+ if (memcmp(ifp->addr.ip6.s6_addr, addr6.s6_addr,
+ sizeof(addr6.s6_addr)) == 0)
+ return(TRUE);
+ for (j = 0; j < sizeof(addr6.s6_addr); j++) {
+ if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr6.s6_addr[j])
+ break;
+ }
+ if (j == sizeof(addr6.s6_addr))
+ return(TRUE);
+#endif /* HAVE_IN6_ADDR */
+ }
+ }
+
+ return(FALSE);
+}
+
+static int
+addr_matches_if_netmask(n, m)
+ char *n;
char *m;
+{
+ int i;
struct in_addr addr, mask;
+ struct interface *ifp;
+#ifdef HAVE_IN6_ADDR
+ struct in6_addr addr6, mask6;
+ int j;
+#endif
+ int family;
- /* If there's an explicit netmask, use it. */
- if ((m = strchr(n, '/'))) {
- *m++ = '\0';
+#ifdef HAVE_IN6_ADDR
+ if (inet_pton(AF_INET6, n, &addr6) > 0)
+ family = AF_INET6;
+ else
+#endif
+ {
+ family = AF_INET;
addr.s_addr = inet_addr(n);
+ }
+
+ if (family == AF_INET) {
if (strchr(m, '.'))
mask.s_addr = inet_addr(m);
else {
mask.s_addr <<= i;
mask.s_addr = htonl(mask.s_addr);
}
- *(m - 1) = '/';
-
- for (i = 0; i < num_interfaces; i++)
- if ((interfaces[i].addr.s_addr & mask.s_addr) == addr.s_addr)
- return(TRUE);
- } else {
- addr.s_addr = inet_addr(n);
-
- for (i = 0; i < num_interfaces; i++)
- if (interfaces[i].addr.s_addr == addr.s_addr ||
- (interfaces[i].addr.s_addr & interfaces[i].netmask.s_addr)
- == addr.s_addr)
- return(TRUE);
+ }
+#ifdef HAVE_IN6_ADDR
+ else {
+ if (inet_pton(AF_INET6, m, &mask6) <= 0) {
+ j = atoi(m);
+ for (i = 0; i < 16; i++) {
+ if (j < i * 8)
+ mask6.s6_addr[i] = 0;
+ else if (i * 8 + 8 <= j)
+ mask6.s6_addr[i] = 0xff;
+ else
+ mask6.s6_addr[i] = 0xff00 >> (j - i * 8);
+ }
+ }
+ }
+#endif /* HAVE_IN6_ADDR */
+
+ for (i = 0; i < num_interfaces; i++) {
+ ifp = &interfaces[i];
+ if (ifp->family != family)
+ continue;
+ switch(family) {
+ case AF_INET:
+ if ((ifp->addr.ip4.s_addr & mask.s_addr) == addr.s_addr)
+ return(TRUE);
+#ifdef HAVE_IN6_ADDR
+ case AF_INET6:
+ for (j = 0; j < sizeof(addr6.s6_addr); j++) {
+ if ((ifp->addr.ip6.s6_addr[j] & mask6.s6_addr[j]) != addr6.s6_addr[j])
+ break;
+ }
+ if (j == sizeof(addr6.s6_addr))
+ return(TRUE);
+#endif /* HAVE_IN6_ADDR */
+ }
}
return(FALSE);
}
+/*
+ * Returns TRUE if "n" is one of our ip addresses or if
+ * "n" is a network that we are on, else returns FALSE.
+ */
+int
+addr_matches(n)
+ char *n;
+{
+ char *m;
+ int retval;
+
+ /* If there's an explicit netmask, use it. */
+ if ((m = strchr(n, '/'))) {
+ *m++ = '\0';
+ retval = addr_matches_if_netmask(n, m);
+ *(m - 1) = '/';
+ } else
+ retval = addr_matches_if(n);
+
+ return(retval);
+}
+
/*
* Returns 0 if the hostname matches the pattern and non-zero otherwise.
*/
struct group *grp;
gid_t pw_gid;
char **cur;
+ int i;
/* make sure we have a valid usergroup, sudo style */
if (*group++ != '%')
if (grp->gr_gid == pw_gid)
return(TRUE);
- /* check to see if user is explicitly listed in the group */
- for (cur = grp->gr_mem; *cur; cur++) {
- if (strcmp(*cur, user) == 0)
+ /*
+ * If the user has a supplementary group vector, check it first.
+ */
+ for (i = 0; i < user_ngroups; i++) {
+ if (grp->gr_gid == user_groups[i])
return(TRUE);
}
+ if (grp->gr_mem != NULL) {
+ for (cur = grp->gr_mem; *cur; cur++) {
+ if (strcmp(*cur, user) == 0)
+ return(TRUE);
+ }
+ }
return(FALSE);
}
char *shost;
char *user;
{
+ static char *domain;
#ifdef HAVE_GETDOMAINNAME
- static char *domain = (char *) -1;
-#else
- static char *domain = NULL;
-#endif /* HAVE_GETDOMAINNAME */
+ static int initialized;
+#endif
/* make sure we have a valid netgroup, sudo style */
if (*netgr++ != '+')
#ifdef HAVE_GETDOMAINNAME
/* get the domain name (if any) */
- if (domain == (char *) -1) {
+ if (!initialized) {
domain = (char *) emalloc(MAXHOSTNAMELEN);
if (getdomainname(domain, MAXHOSTNAMELEN) == -1 || *domain == '\0') {
- free(domain);
+ efree(domain);
domain = NULL;
}
+ initialized = 1;
}
#endif /* HAVE_GETDOMAINNAME */