Stash the "safe" path (ie: the one listed in sudoers) to the command instead

of stashing the struct stat.  Should be safer.
This commit is contained in:
Todd C. Miller
1999-04-10 04:10:01 +00:00
parent 0ef9c81f16
commit 59b0cff8cf
9 changed files with 91 additions and 106 deletions

View File

@@ -1060,3 +1060,6 @@ Sudo 1.5.9 released.
332) You can now specifiy a host list instead of just a host or alias 332) You can now specifiy a host list instead of just a host or alias
in a privilege list. Ie: user=host1,host2,ALIAS,!host3 /bin/ls in a privilege list. Ie: user=host1,host2,ALIAS,!host3 /bin/ls
333) Stash the "safe" path to the command instead of stashing the struct
stat. Should be safer.

View File

@@ -251,12 +251,6 @@ void log_error(code)
SUDOERS_MODE); SUDOERS_MODE);
break; break;
case SPOOF_ATTEMPT:
(void) sprintf(p,
"probable spoofing attempt; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=",
tty, cwd, runas_user);
break;
case BAD_STAMPDIR: case BAD_STAMPDIR:
(void) sprintf(p, (void) sprintf(p,
"%s owned by non-root or not mode 0700; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=", "%s owned by non-root or not mode 0700; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=",
@@ -653,12 +647,6 @@ void inform_user(code)
SUDOERS_MODE); SUDOERS_MODE);
break; break;
case SPOOF_ATTEMPT:
(void) fprintf(stderr,
"%s is not the same command that was validated, disallowing.\n",
cmnd);
break;
case BAD_STAMPDIR: case BAD_STAMPDIR:
(void) fprintf(stderr, (void) fprintf(stderr,
"Timestamp directory has wrong permissions, ignoring.\n"); "Timestamp directory has wrong permissions, ignoring.\n");
@@ -729,7 +717,6 @@ static int appropriate(code)
*/ */
case VALIDATE_ERROR: case VALIDATE_ERROR:
case NO_SUDOERS_FILE: case NO_SUDOERS_FILE:
case SPOOF_ATTEMPT:
case BAD_STAMPDIR: case BAD_STAMPDIR:
case BAD_STAMPFILE: case BAD_STAMPFILE:
case BAD_ALLOCATION: case BAD_ALLOCATION:

83
parse.c
View File

@@ -209,26 +209,27 @@ int command_matches(cmnd, user_args, path, sudoers_args)
char *sudoers_args; char *sudoers_args;
{ {
int plen; int plen;
static struct stat cst;
struct stat pst; struct stat pst;
DIR *dirp; DIR *dirp;
struct dirent *dent; struct dirent *dent;
char buf[MAXPATHLEN]; char buf[MAXPATHLEN];
static char *c; static char *cmnd_base;
/* don't bother with pseudo commands like "validate" */ /* Don't bother with pseudo commands like "validate" */
if (strchr(cmnd, '/') == NULL) if (strchr(cmnd, '/') == NULL)
return(FALSE); return(FALSE);
plen = strlen(path); plen = strlen(path);
/* only need to stat cmnd once since it never changes */ /* Only need to stat cmnd once since it never changes */
if (cmnd_st.st_dev == 0) { if (cst.st_dev == 0) {
if (stat(cmnd, &cmnd_st) < 0) if (stat(cmnd, &cst) == -1)
return(FALSE); return(FALSE);
if ((c = strrchr(cmnd, '/')) == NULL) if ((cmnd_base = strrchr(cmnd, '/')) == NULL)
c = cmnd; cmnd_base = cmnd;
else else
c++; cmnd_base++;
} }
/* /*
@@ -237,21 +238,29 @@ int command_matches(cmnd, user_args, path, sudoers_args)
*/ */
if (has_meta(path)) { if (has_meta(path)) {
/* /*
* Return true if fnmatch(3) succeeds and there are no args * Return true if fnmatch(3) succeeds AND
* (in sudoers or command) or if the args match; * 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. * else return false.
*/ */
if (fnmatch(path, cmnd, FNM_PATHNAME)) if (fnmatch(path, cmnd, FNM_PATHNAME) != 0)
return(FALSE); return(FALSE);
if (!sudoers_args) if (!sudoers_args ||
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
(sudoers_args && fnmatch(sudoers_args, user_args ? user_args : "",
0) == 0)) {
if (cmnd_safe)
free(cmnd_safe);
cmnd_safe = estrdup(cmnd);
return(TRUE); return(TRUE);
else if (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) } else
return(TRUE);
else if (sudoers_args)
return((fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0));
else
return(FALSE); return(FALSE);
} else { } else {
/*
* No meta characters
* Check to make sure this is not a directory spec (doesn't end in '/')
*/
if (path[plen - 1] != '/') { if (path[plen - 1] != '/') {
char *p; char *p;
@@ -260,26 +269,26 @@ int command_matches(cmnd, user_args, path, sudoers_args)
p = path; p = path;
else else
p++; p++;
if (strcmp(c, p)) if (strcmp(cmnd_base, p) != 0 || stat(path, &pst) == -1)
return(FALSE);
if (stat(path, &pst) < 0)
return(FALSE); return(FALSE);
/* /*
* Return true if inode/device matches and there are no args * Return true if inode/device matches AND
* (in sudoers or command) or if the args match; * a) there are no args in sudoers OR
* else return false. * b) there are no args on command line and none req by sudoers OR
* c) there are args in sudoers and on command line and they match
*/ */
if (cmnd_st.st_dev != pst.st_dev || cmnd_st.st_ino != pst.st_ino) if (cst.st_dev != pst.st_dev || cst.st_ino != pst.st_ino)
return(FALSE); return(FALSE);
if (!sudoers_args) if (!sudoers_args ||
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
(sudoers_args &&
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
if (cmnd_safe)
free(cmnd_safe);
cmnd_safe = estrdup(path);
return(TRUE); return(TRUE);
else if (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) } else
return(TRUE);
else if (sudoers_args)
return((fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0));
else
return(FALSE); return(FALSE);
} }
@@ -298,13 +307,17 @@ int command_matches(cmnd, user_args, path, sudoers_args)
strcat(buf, dent->d_name); strcat(buf, dent->d_name);
/* only stat if basenames are not the same */ /* only stat if basenames are not the same */
if (strcmp(c, dent->d_name)) if (strcmp(cmnd_base, dent->d_name))
continue; continue;
if (stat(buf, &pst) < 0) if (stat(buf, &pst) == -1)
continue; continue;
if (cmnd_st.st_dev == pst.st_dev && cmnd_st.st_ino == pst.st_ino) if (cst.st_dev == pst.st_dev && cst.st_ino == pst.st_ino) {
if (cmnd_safe)
free(cmnd_safe);
cmnd_safe = estrdup(buf);
break; break;
} }
}
closedir(dirp); closedir(dirp);
return(dent != NULL); return(dent != NULL);
@@ -411,7 +424,7 @@ int netgr_matches(netgr, host, user)
/* get the domain name (if any) */ /* get the domain name (if any) */
if (domain == (char *) -1) { if (domain == (char *) -1) {
domain = (char *) emalloc(MAXHOSTNAMELEN); domain = (char *) emalloc(MAXHOSTNAMELEN);
if (getdomainname(domain, MAXHOSTNAMELEN) != 0 || *domain == '\0') { if (getdomainname(domain, MAXHOSTNAMELEN) == -1 || *domain == '\0') {
(void) free(domain); (void) free(domain);
domain = NULL; domain = NULL;
} }

View File

@@ -475,8 +475,10 @@ cmnd : ALL {
expand_match_list(); expand_match_list();
} }
cmnd_matches = TRUE; $$ = cmnd_matches = TRUE;
$$ = TRUE; if (cmnd_safe)
free(cmnd_safe);
cmnd_safe = estrdup(cmnd);
} }
| ALIAS { | ALIAS {
if (printmatches == TRUE && in_alias == TRUE) { if (printmatches == TRUE && in_alias == TRUE) {

31
sudo.c
View File

@@ -122,6 +122,7 @@ char **NewArgv = NULL;
struct passwd *user_pw_ent; struct passwd *user_pw_ent;
char *runas_user = RUNAS_DEFAULT; char *runas_user = RUNAS_DEFAULT;
char *cmnd = NULL; char *cmnd = NULL;
char *cmnd_safe = NULL;
char *cmnd_args = NULL; char *cmnd_args = NULL;
char *tty = "unknown"; char *tty = "unknown";
char *prompt; char *prompt;
@@ -129,7 +130,6 @@ char host[MAXHOSTNAMELEN];
char *shost; char *shost;
char cwd[MAXPATHLEN]; char cwd[MAXPATHLEN];
FILE *sudoers_fp = NULL; FILE *sudoers_fp = NULL;
struct stat cmnd_st;
static char *runas_homedir = NULL; static char *runas_homedir = NULL;
extern struct interface *interfaces; extern struct interface *interfaces;
extern int num_interfaces; extern int num_interfaces;
@@ -334,33 +334,10 @@ int main(argc, argv)
(void) sudo_setenv("HOME", runas_homedir); (void) sudo_setenv("HOME", runas_homedir);
#ifndef PROFILING #ifndef PROFILING
if ((sudo_mode & MODE_BACKGROUND) && fork() > 0) { if ((sudo_mode & MODE_BACKGROUND) && fork() > 0)
exit(0); exit(0);
} else { else
/* EXEC(cmnd_safe, NewArgv); /* run the command */
* Make sure we are not being spoofed. The stat should
* be cheap enough to make this almost bulletproof.
*/
if (cmnd_st.st_dev) {
struct stat st;
if (stat(cmnd, &st) < 0) {
(void) fprintf(stderr, "%s: unable to stat %s: ",
Argv[0], cmnd);
perror("");
exit(1);
}
if (st.st_dev != cmnd_st.st_dev ||
st.st_ino != cmnd_st.st_ino) {
/* log and send mail, then bitch */
log_error(SPOOF_ATTEMPT);
inform_user(SPOOF_ATTEMPT);
exit(1);
}
}
EXEC(cmnd, NewArgv); /* run the command */
}
#else #else
exit(0); exit(0);
#endif /* PROFILING */ #endif /* PROFILING */

11
sudo.h
View File

@@ -158,12 +158,11 @@ struct generic_alias {
#define SUDOERS_WRONG_OWNER ( 0x09 | GLOBAL_PROBLEM ) #define SUDOERS_WRONG_OWNER ( 0x09 | GLOBAL_PROBLEM )
#define SUDOERS_WRONG_MODE ( 0x0A | GLOBAL_PROBLEM ) #define SUDOERS_WRONG_MODE ( 0x0A | GLOBAL_PROBLEM )
#define SUDOERS_NOT_FILE ( 0x0B | GLOBAL_PROBLEM ) #define SUDOERS_NOT_FILE ( 0x0B | GLOBAL_PROBLEM )
#define SPOOF_ATTEMPT 0x0C #define BAD_STAMPDIR 0x0C
#define BAD_STAMPDIR 0x0D #define BAD_STAMPFILE 0x0D
#define BAD_STAMPFILE 0x0E #define BAD_ALLOCATION 0x0E
#define BAD_ALLOCATION 0x0F
#ifdef HAVE_KERB5 #ifdef HAVE_KERB5
#define GLOBAL_KRB5_INIT_ERR ( 0x10 | GLOBAL_PROBLEM ) #define GLOBAL_KRB5_INIT_ERR ( 0x0F | GLOBAL_PROBLEM )
#endif /* HAVE_KERB5 */ #endif /* HAVE_KERB5 */
/* /*
@@ -268,9 +267,9 @@ extern struct passwd *user_pw_ent;
extern char *runas_user; extern char *runas_user;
extern char *tty; extern char *tty;
extern char *cmnd; extern char *cmnd;
extern char *cmnd_safe;
extern char *cmnd_args; extern char *cmnd_args;
extern char *prompt; extern char *prompt;
extern struct stat cmnd_st;
extern int Argc; extern int Argc;
extern char **Argv; extern char **Argv;
extern int NewArgc; extern int NewArgc;

View File

@@ -497,7 +497,7 @@ short *yyss;
short *yysslim; short *yysslim;
YYSTYPE *yyvs; YYSTYPE *yyvs;
int yystacksize; int yystacksize;
#line 659 "parse.yacc" #line 661 "parse.yacc"
typedef struct { typedef struct {
@@ -1393,12 +1393,14 @@ case 44:
expand_match_list(); expand_match_list();
} }
cmnd_matches = TRUE; yyval.BOOLEAN = cmnd_matches = TRUE;
yyval.BOOLEAN = TRUE; if (cmnd_safe)
free(cmnd_safe);
cmnd_safe = estrdup(cmnd);
} }
break; break;
case 45: case 45:
#line 481 "parse.yacc" #line 483 "parse.yacc"
{ {
if (printmatches == TRUE && in_alias == TRUE) { if (printmatches == TRUE && in_alias == TRUE) {
append(yyvsp[0].string, &ga_list[ga_list_len-1].entries, append(yyvsp[0].string, &ga_list[ga_list_len-1].entries,
@@ -1420,7 +1422,7 @@ case 45:
} }
break; break;
case 46: case 46:
#line 500 "parse.yacc" #line 502 "parse.yacc"
{ {
if (printmatches == TRUE && in_alias == TRUE) { if (printmatches == TRUE && in_alias == TRUE) {
append(yyvsp[0].command.cmnd, &ga_list[ga_list_len-1].entries, append(yyvsp[0].command.cmnd, &ga_list[ga_list_len-1].entries,
@@ -1456,11 +1458,11 @@ case 46:
} }
break; break;
case 49: case 49:
#line 539 "parse.yacc" #line 541 "parse.yacc"
{ push; } { push; }
break; break;
case 50: case 50:
#line 539 "parse.yacc" #line 541 "parse.yacc"
{ {
if (host_matches == TRUE && if (host_matches == TRUE &&
add_alias(yyvsp[-3].string, HOST_ALIAS) == FALSE) add_alias(yyvsp[-3].string, HOST_ALIAS) == FALSE)
@@ -1469,7 +1471,7 @@ case 50:
} }
break; break;
case 55: case 55:
#line 555 "parse.yacc" #line 557 "parse.yacc"
{ {
push; push;
if (printmatches == TRUE) { if (printmatches == TRUE) {
@@ -1481,7 +1483,7 @@ case 55:
} }
break; break;
case 56: case 56:
#line 563 "parse.yacc" #line 565 "parse.yacc"
{ {
if (cmnd_matches == TRUE && if (cmnd_matches == TRUE &&
add_alias(yyvsp[-3].string, CMND_ALIAS) == FALSE) add_alias(yyvsp[-3].string, CMND_ALIAS) == FALSE)
@@ -1494,11 +1496,11 @@ case 56:
} }
break; break;
case 57: case 57:
#line 575 "parse.yacc" #line 577 "parse.yacc"
{ ; } { ; }
break; break;
case 61: case 61:
#line 583 "parse.yacc" #line 585 "parse.yacc"
{ {
push; push;
if (printmatches == TRUE) { if (printmatches == TRUE) {
@@ -1510,7 +1512,7 @@ case 61:
} }
break; break;
case 62: case 62:
#line 591 "parse.yacc" #line 593 "parse.yacc"
{ {
if (runas_matches > 0 && if (runas_matches > 0 &&
add_alias(yyvsp[-3].string, RUNAS_ALIAS) == FALSE) add_alias(yyvsp[-3].string, RUNAS_ALIAS) == FALSE)
@@ -1523,11 +1525,11 @@ case 62:
} }
break; break;
case 65: case 65:
#line 607 "parse.yacc" #line 609 "parse.yacc"
{ push; } { push; }
break; break;
case 66: case 66:
#line 607 "parse.yacc" #line 609 "parse.yacc"
{ {
if (user_matches == TRUE && if (user_matches == TRUE &&
add_alias(yyvsp[-3].string, USER_ALIAS) == FALSE) add_alias(yyvsp[-3].string, USER_ALIAS) == FALSE)
@@ -1537,17 +1539,17 @@ case 66:
} }
break; break;
case 67: case 67:
#line 616 "parse.yacc" #line 618 "parse.yacc"
{ ; } { ; }
break; break;
case 70: case 70:
#line 621 "parse.yacc" #line 623 "parse.yacc"
{ {
push; push;
} }
break; break;
case 71: case 71:
#line 623 "parse.yacc" #line 625 "parse.yacc"
{ {
pop; pop;
if (user_matched == TRUE) if (user_matched == TRUE)
@@ -1557,7 +1559,7 @@ case 71:
} }
break; break;
case 72: case 72:
#line 631 "parse.yacc" #line 633 "parse.yacc"
{ {
if (strcmp(yyvsp[0].string, user_name) == 0) if (strcmp(yyvsp[0].string, user_name) == 0)
user_matches = TRUE; user_matches = TRUE;
@@ -1565,7 +1567,7 @@ case 72:
} }
break; break;
case 73: case 73:
#line 636 "parse.yacc" #line 638 "parse.yacc"
{ {
if (usergr_matches(yyvsp[0].string, user_name)) if (usergr_matches(yyvsp[0].string, user_name))
user_matches = TRUE; user_matches = TRUE;
@@ -1573,7 +1575,7 @@ case 73:
} }
break; break;
case 74: case 74:
#line 641 "parse.yacc" #line 643 "parse.yacc"
{ {
if (netgr_matches(yyvsp[0].string, NULL, user_name)) if (netgr_matches(yyvsp[0].string, NULL, user_name))
user_matches = TRUE; user_matches = TRUE;
@@ -1581,7 +1583,7 @@ case 74:
} }
break; break;
case 75: case 75:
#line 646 "parse.yacc" #line 648 "parse.yacc"
{ {
/* could be an all-caps username */ /* could be an all-caps username */
if (find_alias(yyvsp[0].string, USER_ALIAS) == TRUE || if (find_alias(yyvsp[0].string, USER_ALIAS) == TRUE ||
@@ -1591,12 +1593,12 @@ case 75:
} }
break; break;
case 76: case 76:
#line 653 "parse.yacc" #line 655 "parse.yacc"
{ {
user_matches = TRUE; user_matches = TRUE;
} }
break; break;
#line 1600 "sudo.tab.c" #line 1602 "sudo.tab.c"
} }
yyssp -= yym; yyssp -= yym;
yystate = *yyssp; yystate = *yyssp;

View File

@@ -78,6 +78,7 @@ extern int num_interfaces;
char *cmnd = NULL; char *cmnd = NULL;
char *cmnd_args = NULL; char *cmnd_args = NULL;
char *cmnd_safe = NULL;
char *runas_user = RUNAS_DEFAULT; char *runas_user = RUNAS_DEFAULT;
char host[MAXHOSTNAMELEN]; char host[MAXHOSTNAMELEN];
char *shost; char *shost;

View File

@@ -113,6 +113,7 @@ char *runas_user = RUNAS_DEFAULT;
char host[] = ""; char host[] = "";
char *shost = ""; char *shost = "";
char *cmnd = ""; char *cmnd = "";
char *cmnd_safe = NULL;
char *cmnd_args = NULL; char *cmnd_args = NULL;
struct passwd *user_pw_ent; struct passwd *user_pw_ent;