version 1.12, 2004/09/28 15:10:51 |
version 1.13, 2007/07/26 16:10:16 |
|
|
/* |
/* |
* 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 |
* Permission to use, copy, modify, and distribute this software for any |
* purpose with or without fee is hereby granted, provided that the above |
* purpose with or without fee is hereby granted, provided that the above |
|
|
* Materiel Command, USAF, under agreement number F39502-99-1-0512. |
* Materiel Command, USAF, under agreement number F39502-99-1-0512. |
*/ |
*/ |
|
|
#include "config.h" |
#include <config.h> |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/param.h> |
#include <sys/param.h> |
|
|
#ifdef HAVE_FNMATCH |
#ifdef HAVE_FNMATCH |
# include <fnmatch.h> |
# include <fnmatch.h> |
#endif /* HAVE_FNMATCH */ |
#endif /* HAVE_FNMATCH */ |
|
#ifdef HAVE_EXTENDED_GLOB |
|
# include <glob.h> |
|
#endif /* HAVE_EXTENDED_GLOB */ |
#ifdef HAVE_NETGROUP_H |
#ifdef HAVE_NETGROUP_H |
# include <netgroup.h> |
# include <netgroup.h> |
#endif /* HAVE_NETGROUP_H */ |
#endif /* HAVE_NETGROUP_H */ |
|
|
#ifndef HAVE_FNMATCH |
#ifndef HAVE_FNMATCH |
# include "emul/fnmatch.h" |
# include "emul/fnmatch.h" |
#endif /* HAVE_FNMATCH */ |
#endif /* HAVE_FNMATCH */ |
|
#ifndef HAVE_EXTENDED_GLOB |
|
# include "emul/glob.h" |
|
#endif /* HAVE_EXTENDED_GLOB */ |
|
|
#ifndef lint |
#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.10 2007/07/06 19:34:20 millert Exp $"; |
#endif /* lint */ |
#endif /* lint */ |
|
|
/* |
/* |
|
|
int pwflag; |
int pwflag; |
{ |
{ |
int error, nopass; |
int error, nopass; |
enum def_tupple pwcheck; |
|
|
|
/* We opened _PATH_SUDOERS in check_sudoers() so just rewind it. */ |
/* We opened _PATH_SUDOERS in check_sudoers() so just rewind it. */ |
rewind(sudoers_fp); |
rewind(sudoers_fp); |
|
|
/* Allocate space for data structures in the parser. */ |
/* Allocate space for data structures in the parser. */ |
init_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) |
if (pwflag > 0) |
keepall = TRUE; |
keepall = TRUE; |
|
|
|
|
} |
} |
|
|
/* |
/* |
* 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 |
* Assume the worst. If the stack is empty the user was |
* not mentioned at all. |
* not mentioned at all. |
*/ |
*/ |
|
|
error = VALIDATE_NOT_OK; |
error = VALIDATE_NOT_OK; |
else |
else |
error = VALIDATE_NOT_OK | FLAG_NOPASS; |
error = VALIDATE_NOT_OK | FLAG_NOPASS; |
if (pwcheck) { |
if (pwflag) { |
SET(error, FLAG_NO_CHECK); |
SET(error, FLAG_NO_CHECK); |
} else { |
} else { |
SET(error, FLAG_NO_HOST); |
SET(error, FLAG_NO_HOST); |
|
|
nopass = -1; |
nopass = -1; |
if (pwflag) { |
if (pwflag) { |
int found; |
int found; |
|
enum def_tupple pwcheck; |
|
|
|
pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple; |
|
|
if (pwcheck == always && def_authenticate) |
if (pwcheck == always && def_authenticate) |
nopass = FLAG_CHECK_USER; |
nopass = FLAG_CHECK_USER; |
else if (pwcheck == never || !def_authenticate) |
else if (pwcheck == never || !def_authenticate) |
|
|
set_perms(PERM_ROOT); |
set_perms(PERM_ROOT); |
return(VALIDATE_OK | |
return(VALIDATE_OK | |
(no_passwd == TRUE ? FLAG_NOPASS : 0) | |
(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) || |
} else if ((runas_matches == TRUE && cmnd_matches == FALSE) || |
(runas_matches == FALSE && cmnd_matches == TRUE)) { |
(runas_matches == FALSE && cmnd_matches == TRUE)) { |
/* |
/* |
|
|
set_perms(PERM_ROOT); |
set_perms(PERM_ROOT); |
return(VALIDATE_NOT_OK | |
return(VALIDATE_NOT_OK | |
(no_passwd == TRUE ? FLAG_NOPASS : 0) | |
(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--; |
top--; |
|
|
{ |
{ |
struct stat sudoers_stat; |
struct stat sudoers_stat; |
struct dirent *dent; |
struct dirent *dent; |
char buf[PATH_MAX]; |
char **ap, *base, buf[PATH_MAX]; |
|
glob_t gl; |
DIR *dirp; |
DIR *dirp; |
|
|
/* Check for pseudo-commands */ |
/* Check for pseudo-commands */ |
|
|
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || |
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || |
(sudoers_args && |
(sudoers_args && |
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { |
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { |
if (safe_cmnd) |
efree(safe_cmnd); |
free(safe_cmnd); |
|
safe_cmnd = estrdup(sudoers_cmnd); |
safe_cmnd = estrdup(sudoers_cmnd); |
return(TRUE); |
return(TRUE); |
} else |
} else |
|
|
*/ |
*/ |
if (has_meta(sudoers_cmnd)) { |
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 |
* a) there are no args in sudoers OR |
* b) there are no args on command line and none required by 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 |
* c) there are args in sudoers and on command line and they match |
* else return false. |
* 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); |
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 || |
if (!sudoers_args || |
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || |
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || |
(sudoers_args && |
(sudoers_args && |
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { |
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { |
if (safe_cmnd) |
efree(safe_cmnd); |
free(safe_cmnd); |
|
safe_cmnd = estrdup(user_cmnd); |
safe_cmnd = estrdup(user_cmnd); |
return(TRUE); |
return(TRUE); |
} else |
} else |
|
|
* Check to make sure this is not a directory spec (doesn't end in '/') |
* Check to make sure this is not a directory spec (doesn't end in '/') |
*/ |
*/ |
if (sudoers_cmnd[dlen - 1] != '/') { |
if (sudoers_cmnd[dlen - 1] != '/') { |
char *base; |
|
|
|
/* Only proceed if user_base and basename(sudoers_cmnd) match */ |
/* Only proceed if user_base and basename(sudoers_cmnd) match */ |
if ((base = strrchr(sudoers_cmnd, '/')) == NULL) |
if ((base = strrchr(sudoers_cmnd, '/')) == NULL) |
base = sudoers_cmnd; |
base = sudoers_cmnd; |
|
|
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || |
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || |
(sudoers_args && |
(sudoers_args && |
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { |
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { |
if (safe_cmnd) |
efree(safe_cmnd); |
free(safe_cmnd); |
|
safe_cmnd = estrdup(sudoers_cmnd); |
safe_cmnd = estrdup(sudoers_cmnd); |
return(TRUE); |
return(TRUE); |
} else |
} else |
|
|
continue; |
continue; |
if (user_stat->st_dev == sudoers_stat.st_dev && |
if (user_stat->st_dev == sudoers_stat.st_dev && |
user_stat->st_ino == sudoers_stat.st_ino) { |
user_stat->st_ino == sudoers_stat.st_ino) { |
if (safe_cmnd) |
efree(safe_cmnd); |
free(safe_cmnd); |
|
safe_cmnd = estrdup(buf); |
safe_cmnd = estrdup(buf); |
break; |
break; |
} |
} |
|
|
struct group *grp; |
struct group *grp; |
gid_t pw_gid; |
gid_t pw_gid; |
char **cur; |
char **cur; |
|
int n; |
|
|
/* make sure we have a valid usergroup, sudo style */ |
/* make sure we have a valid usergroup, sudo style */ |
if (*group++ != '%') |
if (*group++ != '%') |
|
|
if (grp->gr_gid == pw_gid) |
if (grp->gr_gid == pw_gid) |
return(TRUE); |
return(TRUE); |
|
|
/* check to see if user is explicitly listed in the group */ |
/* |
for (cur = grp->gr_mem; *cur; cur++) { |
* If the user has a supplementary group vector, check it first. |
if (strcmp(*cur, user) == 0) |
*/ |
|
for (n = user_ngroups; n != 0; n--) { |
|
if (grp->gr_gid == user_groups[n]) |
return(TRUE); |
return(TRUE); |
} |
} |
|
if (grp->gr_mem != NULL) { |
|
for (cur = grp->gr_mem; *cur; cur++) { |
|
if (strcmp(*cur, user) == 0) |
|
return(TRUE); |
|
} |
|
} |
|
|
return(FALSE); |
return(FALSE); |
} |
} |
|
|
char *shost; |
char *shost; |
char *user; |
char *user; |
{ |
{ |
|
static char *domain; |
#ifdef HAVE_GETDOMAINNAME |
#ifdef HAVE_GETDOMAINNAME |
static char *domain = (char *) -1; |
static int initialized; |
#else |
#endif |
static char *domain = NULL; |
|
#endif /* HAVE_GETDOMAINNAME */ |
|
|
|
/* make sure we have a valid netgroup, sudo style */ |
/* make sure we have a valid netgroup, sudo style */ |
if (*netgr++ != '+') |
if (*netgr++ != '+') |
|
|
|
|
#ifdef HAVE_GETDOMAINNAME |
#ifdef HAVE_GETDOMAINNAME |
/* get the domain name (if any) */ |
/* get the domain name (if any) */ |
if (domain == (char *) -1) { |
if (!initialized) { |
domain = (char *) emalloc(MAXHOSTNAMELEN); |
domain = (char *) emalloc(MAXHOSTNAMELEN); |
if (getdomainname(domain, MAXHOSTNAMELEN) == -1 || *domain == '\0') { |
if (getdomainname(domain, MAXHOSTNAMELEN) == -1 || *domain == '\0') { |
free(domain); |
efree(domain); |
domain = NULL; |
domain = NULL; |
} |
} |
|
initialized = 1; |
} |
} |
#endif /* HAVE_GETDOMAINNAME */ |
#endif /* HAVE_GETDOMAINNAME */ |
|
|