Use LH_FOREACH_REV when checking permission and short-circuit on

the first non-UNSPEC hit we get for the command.  This means that
instead of cycling through the all the parsed sudoers entries we
start at the end and work backwards and quit after the first positive
or negative match.
This commit is contained in:
Todd C. Miller
2007-08-31 01:21:26 +00:00
parent 270ba11dbf
commit 10e5d4e708
3 changed files with 49 additions and 36 deletions

18
match.c
View File

@@ -111,7 +111,7 @@ userlist_matches(pw, list)
struct alias *a;
int rval, matched = UNSPEC;
for (m = list->first; m != NULL; m = m->next) {
LH_FOREACH_REV(list, m) {
switch (m->type) {
case ALL:
matched = !m->negated;
@@ -137,6 +137,8 @@ userlist_matches(pw, list)
matched = !m->negated;
break;
}
if (matched != UNSPEC)
break;
}
return(matched);
}
@@ -157,7 +159,7 @@ runaslist_matches(list)
if (list == NULL)
return(userpw_matches(def_runas_default, runas_pw->pw_name, runas_pw));
for (m = list->first; m != NULL; m = m->next) {
LH_FOREACH_REV(list, m) {
switch (m->type) {
case ALL:
matched = !m->negated;
@@ -183,6 +185,8 @@ runaslist_matches(list)
matched = !m->negated;
break;
}
if (matched != UNSPEC)
break;
}
return(matched);
}
@@ -199,7 +203,7 @@ hostlist_matches(list)
struct alias *a;
int rval, matched = UNSPEC;
for (m = list->first; m != NULL; m = m->next) {
LH_FOREACH_REV(list, m) {
switch (m->type) {
case ALL:
matched = !m->negated;
@@ -225,6 +229,8 @@ hostlist_matches(list)
matched = !m->negated;
break;
}
if (matched != UNSPEC)
break;
}
return(matched);
}
@@ -272,10 +278,12 @@ cmndlist_matches(list)
struct member *m;
int rval, matched = UNSPEC;
for (m = list->first; m != NULL; m = m->next) {
LH_FOREACH_REV(list, m) {
rval = cmnd_matches(m);
if (rval != UNSPEC)
if (rval != UNSPEC) {
matched = m->negated ? !rval : rval;
break;
}
}
return(matched);
}

48
parse.c
View File

@@ -110,15 +110,13 @@ sudoers_lookup(pwflag)
CLR(validated, FLAG_NO_USER);
CLR(validated, FLAG_NO_HOST);
match = DENY;
/* XXX - it should be faster to start from the bottom and
work our way up and then just stop at the first match. */
LH_FOREACH_FWD(&userspecs, us) {
LH_FOREACH_REV(&userspecs, us) {
if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
continue;
LH_FOREACH_FWD(&us->privileges, priv) {
LH_FOREACH_REV(&us->privileges, priv) {
if (hostlist_matches(&priv->hostlist) != ALLOW)
continue;
LH_FOREACH_FWD(&priv->cmndlist, cs) {
LH_FOREACH_REV(&priv->cmndlist, cs) {
/* Only check the command when listing another user. */
if (user_uid == 0 || list_pw == NULL ||
user_uid == list_pw->pw_uid ||
@@ -127,9 +125,12 @@ sudoers_lookup(pwflag)
if ((pwcheck == any && nopass != TRUE) ||
(pwcheck == all && nopass == TRUE))
nopass = cs->tags.nopasswd;
if (match == ALLOW)
goto matched_pseudo;
}
}
}
matched_pseudo:
if (match == ALLOW || user_uid == 0) {
/* User has an entry for this host. */
CLR(validated, VALIDATE_NOT_OK);
@@ -145,34 +146,34 @@ sudoers_lookup(pwflag)
/* Need to be runas user while stat'ing things. */
set_perms(PERM_RUNAS);
/* XXX - it should be faster to start from the bottom and
work our way up and then just stop at the first match. */
match = UNSPEC;
LH_FOREACH_FWD(&userspecs, us) {
LH_FOREACH_REV(&userspecs, us) {
if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
continue;
CLR(validated, FLAG_NO_USER);
LH_FOREACH_FWD(&us->privileges, priv) {
LH_FOREACH_REV(&us->privileges, priv) {
host_match = hostlist_matches(&priv->hostlist);
if (host_match == UNSPEC)
continue;
if (host_match == ALLOW)
CLR(validated, FLAG_NO_HOST);
else
continue;
runas = NULL;
LH_FOREACH_FWD(&priv->cmndlist, cs) {
LH_FOREACH_REV(&priv->cmndlist, cs) {
if (!LH_EMPTY(&cs->runaslist))
runas = &cs->runaslist;
runas_match = runaslist_matches(runas);
if (runas_match != UNSPEC) {
if (runas_match == ALLOW) {
cmnd_match = cmnd_matches(cs->cmnd);
if (cmnd_match != UNSPEC)
match = host_match && runas_match && cmnd_match;
if (match == ALLOW)
if (cmnd_match != UNSPEC) {
match = cmnd_match;
tags = &cs->tags;
goto matched2;
}
}
}
}
}
matched2:
if (match == ALLOW) {
CLR(validated, VALIDATE_NOT_OK);
SET(validated, VALIDATE_OK);
@@ -433,28 +434,31 @@ display_cmnd(v, pw)
#endif
if (rval != 0 && !def_ignore_local_sudoers) {
match = NULL;
LH_FOREACH_FWD(&userspecs, us) {
LH_FOREACH_REV(&userspecs, us) {
if (userlist_matches(pw, &us->users) != ALLOW)
continue;
LH_FOREACH_FWD(&us->privileges, priv) {
LH_FOREACH_REV(&us->privileges, priv) {
host_match = hostlist_matches(&priv->hostlist);
if (host_match == UNSPEC)
if (host_match != ALLOW)
continue;
runas = NULL;
LH_FOREACH_FWD(&priv->cmndlist, cs) {
LH_FOREACH_REV(&priv->cmndlist, cs) {
if (!LH_EMPTY(&cs->runaslist) != NULL)
runas = &cs->runaslist;
runas_match = runaslist_matches(runas);
if (runas_match != UNSPEC) {
if (runas_match == ALLOW) {
cmnd_match = cmnd_matches(cs->cmnd);
if (cmnd_match != UNSPEC)
if (cmnd_match != UNSPEC) {
match = host_match && runas_match ?
cs->cmnd : NULL;
goto matched;
}
}
}
}
}
matched:
if (match != NULL && !match->negated) {
printf("%s%s%s\n", safe_cmnd, user_args ? " " : "",
user_args ? user_args : "");

View File

@@ -269,20 +269,20 @@ main(argc, argv)
/* This loop must match the one in sudoers_lookup() */
printf("\nEntries for user %s:\n", user_name);
matched = UNSPEC;
LH_FOREACH_FWD(&userspecs, us) {
if (userlist_matches(sudo_user.pw, &us->users) != TRUE)
LH_FOREACH_REV(&userspecs, us) {
if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
continue;
LH_FOREACH_FWD(&us->privileges, priv) {
LH_FOREACH_REV(&us->privileges, priv) {
putchar('\n');
print_privilege(priv); /* XXX */
putchar('\n');
if (hostlist_matches(&priv->hostlist) == TRUE) {
if (hostlist_matches(&priv->hostlist) == ALLOW) {
puts("\thost matched");
runas = NULL;
LH_FOREACH_FWD(&priv->cmndlist, cs) {
LH_FOREACH_REV(&priv->cmndlist, cs) {
if (!LH_EMPTY(&cs->runaslist))
runas = &cs->runaslist;
if (runaslist_matches(runas) == TRUE) {
if (runaslist_matches(runas) == ALLOW) {
puts("\trunas matched");
rval = cmnd_matches(cs->cmnd);
if (rval != UNSPEC)
@@ -291,11 +291,12 @@ main(argc, argv)
rval == DENY ? "denied" : "unmatched");
}
}
} else puts("\thost unmatched");
} else
puts("\thost unmatched");
}
}
printf("\nCommand %s\n", matched == TRUE ? "allowed" :
matched == FALSE ? "denied" : "unmatched");
printf("\nCommand %s\n", matched == ALLOW ? "allowed" :
matched == DENY ? "denied" : "unmatched");
exit(0);
}