--- embedaddon/sudo/plugins/sudoers/match.c 2013/10/14 07:56:34 1.1.1.5 +++ embedaddon/sudo/plugins/sudoers/match.c 2014/06/15 16:12:54 1.1.1.6 @@ -91,14 +91,14 @@ #include "sha2.h" #include -static struct member_list empty; +static struct member_list empty = TAILQ_HEAD_INITIALIZER(empty); -static bool command_matches_dir(char *, size_t); +static bool command_matches_dir(const char *sudoers_dir, size_t dlen); #ifndef SUDOERS_NAME_MATCH -static bool command_matches_glob(char *, char *); +static bool command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args); #endif -static bool command_matches_fnmatch(char *, char *); -static bool command_matches_normal(char *, char *, struct sudo_digest *); +static bool command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args); +static bool command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest); /* * Returns true if string 's' contains meta characters. @@ -110,14 +110,14 @@ static bool command_matches_normal(char *, char *, str * Returns ALLOW, DENY or UNSPEC. */ int -userlist_matches(struct passwd *pw, struct member_list *list) +userlist_matches(const struct passwd *pw, const struct member_list *list) { struct member *m; struct alias *a; int rval, matched = UNSPEC; debug_decl(userlist_matches, SUDO_DEBUG_MATCH) - tq_foreach_rev(list, m) { + TAILQ_FOREACH_REVERSE(m, list, member_list, entries) { switch (m->type) { case ALL: matched = !m->negated; @@ -156,8 +156,8 @@ userlist_matches(struct passwd *pw, struct member_list * Returns ALLOW, DENY or UNSPEC. */ int -runaslist_matches(struct member_list *user_list, - struct member_list *group_list, struct member **matching_user, +runaslist_matches(const struct member_list *user_list, + const struct member_list *group_list, struct member **matching_user, struct member **matching_group) { struct member *m; @@ -169,47 +169,49 @@ runaslist_matches(struct member_list *user_list, if (runas_pw != NULL) { /* If no runas user or runas group listed in sudoers, use default. */ - if (tq_empty(user_list) && tq_empty(group_list)) + if (user_list == NULL && group_list == NULL) debug_return_int(userpw_matches(def_runas_default, runas_pw->pw_name, runas_pw)); - tq_foreach_rev(user_list, m) { - switch (m->type) { - case ALL: - user_matched = !m->negated; - break; - case NETGROUP: - if (netgr_matches(m->name, NULL, NULL, runas_pw->pw_name)) + if (user_list != NULL) { + TAILQ_FOREACH_REVERSE(m, user_list, member_list, entries) { + switch (m->type) { + case ALL: user_matched = !m->negated; - break; - case USERGROUP: - if (usergr_matches(m->name, runas_pw->pw_name, runas_pw)) - user_matched = !m->negated; - break; - case ALIAS: - if ((a = alias_get(m->name, RUNASALIAS)) != NULL) { - rval = runaslist_matches(&a->members, &empty, - matching_user, NULL); - if (rval != UNSPEC) - user_matched = m->negated ? !rval : rval; - alias_put(a); break; - } - /* FALLTHROUGH */ - case WORD: - if (userpw_matches(m->name, runas_pw->pw_name, runas_pw)) - user_matched = !m->negated; + case NETGROUP: + if (netgr_matches(m->name, NULL, NULL, runas_pw->pw_name)) + user_matched = !m->negated; + break; + case USERGROUP: + if (usergr_matches(m->name, runas_pw->pw_name, runas_pw)) + user_matched = !m->negated; + break; + case ALIAS: + if ((a = alias_get(m->name, RUNASALIAS)) != NULL) { + rval = runaslist_matches(&a->members, &empty, + matching_user, NULL); + if (rval != UNSPEC) + user_matched = m->negated ? !rval : rval; + alias_put(a); + break; + } + /* FALLTHROUGH */ + case WORD: + if (userpw_matches(m->name, runas_pw->pw_name, runas_pw)) + user_matched = !m->negated; + break; + case MYSELF: + if (!ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED) || + strcmp(user_name, runas_pw->pw_name) == 0) + user_matched = !m->negated; + break; + } + if (user_matched != UNSPEC) { + if (matching_user != NULL && m->type != ALIAS) + *matching_user = m; break; - case MYSELF: - if (!ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED) || - strcmp(user_name, runas_pw->pw_name) == 0) - user_matched = !m->negated; - break; + } } - if (user_matched != UNSPEC) { - if (matching_user != NULL && m->type != ALIAS) - *matching_user = m; - break; - } } } @@ -218,31 +220,33 @@ runaslist_matches(struct member_list *user_list, if (runas_pw == NULL || strcmp(runas_pw->pw_name, user_name) == 0) user_matched = ALLOW; /* only changing group */ } - tq_foreach_rev(group_list, m) { - switch (m->type) { - case ALL: - group_matched = !m->negated; - break; - case ALIAS: - if ((a = alias_get(m->name, RUNASALIAS)) != NULL) { - rval = runaslist_matches(&empty, &a->members, - NULL, matching_group); - if (rval != UNSPEC) - group_matched = m->negated ? !rval : rval; - alias_put(a); - break; - } - /* FALLTHROUGH */ - case WORD: - if (group_matches(m->name, runas_gr)) + if (group_list != NULL) { + TAILQ_FOREACH_REVERSE(m, group_list, member_list, entries) { + switch (m->type) { + case ALL: group_matched = !m->negated; + break; + case ALIAS: + if ((a = alias_get(m->name, RUNASALIAS)) != NULL) { + rval = runaslist_matches(&empty, &a->members, + NULL, matching_group); + if (rval != UNSPEC) + group_matched = m->negated ? !rval : rval; + alias_put(a); + break; + } + /* FALLTHROUGH */ + case WORD: + if (group_matches(m->name, runas_gr)) + group_matched = !m->negated; + break; + } + if (group_matched != UNSPEC) { + if (matching_group != NULL && m->type != ALIAS) + *matching_group = m; break; + } } - if (group_matched != UNSPEC) { - if (matching_group != NULL && m->type != ALIAS) - *matching_group = m; - break; - } } if (group_matched == UNSPEC) { if (runas_pw != NULL && runas_pw->pw_gid == runas_gr->gr_gid) @@ -262,14 +266,14 @@ runaslist_matches(struct member_list *user_list, * Returns ALLOW, DENY or UNSPEC. */ int -hostlist_matches(struct member_list *list) +hostlist_matches(const struct member_list *list) { struct member *m; struct alias *a; int rval, matched = UNSPEC; debug_decl(hostlist_matches, SUDO_DEBUG_MATCH) - tq_foreach_rev(list, m) { + TAILQ_FOREACH_REVERSE(m, list, member_list, entries) { switch (m->type) { case ALL: matched = !m->negated; @@ -307,13 +311,13 @@ hostlist_matches(struct member_list *list) * Returns ALLOW, DENY or UNSPEC. */ int -cmndlist_matches(struct member_list *list) +cmndlist_matches(const struct member_list *list) { struct member *m; int matched = UNSPEC; debug_decl(cmndlist_matches, SUDO_DEBUG_MATCH) - tq_foreach_rev(list, m) { + TAILQ_FOREACH_REVERSE(m, list, member_list, entries) { matched = cmnd_matches(m); if (matched != UNSPEC) break; @@ -326,7 +330,7 @@ cmndlist_matches(struct member_list *list) * Returns ALLOW, DENY or UNSPEC. */ int -cmnd_matches(struct member *m) +cmnd_matches(const struct member *m) { struct alias *a; struct sudo_command *c; @@ -355,7 +359,7 @@ cmnd_matches(struct member *m) } static bool -command_args_match(char *sudoers_cmnd, char *sudoers_args) +command_args_match(const char *sudoers_cmnd, const char *sudoers_args) { int flags = 0; debug_decl(command_args_match, SUDO_DEBUG_MATCH) @@ -386,8 +390,9 @@ command_args_match(char *sudoers_cmnd, char *sudoers_a * otherwise, return true if user_cmnd names one of the inodes in path. */ bool -command_matches(char *sudoers_cmnd, char *sudoers_args, struct sudo_digest *digest) +command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest) { + bool rc = false; debug_decl(command_matches, SUDO_DEBUG_MATCH) /* Check for pseudo-commands */ @@ -398,15 +403,14 @@ command_matches(char *sudoers_cmnd, char *sudoers_args * 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 (strcmp(sudoers_cmnd, "sudoedit") != 0 || - strcmp(user_cmnd, "sudoedit") != 0) - debug_return_bool(false); - if (command_args_match(sudoers_cmnd, sudoers_args)) { + if (strcmp(sudoers_cmnd, "sudoedit") == 0 && + strcmp(user_cmnd, "sudoedit") == 0 && + command_args_match(sudoers_cmnd, sudoers_args)) { efree(safe_cmnd); safe_cmnd = estrdup(sudoers_cmnd); - debug_return_bool(true); - } else - debug_return_bool(false); + rc = true; + } + goto done; } if (has_meta(sudoers_cmnd)) { @@ -415,18 +419,27 @@ command_matches(char *sudoers_cmnd, char *sudoers_args * use glob(3) and/or fnmatch(3) to do the matching. */ #ifdef SUDOERS_NAME_MATCH - debug_return_bool(command_matches_fnmatch(sudoers_cmnd, sudoers_args)); + rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args); #else if (def_fast_glob) - debug_return_bool(command_matches_fnmatch(sudoers_cmnd, sudoers_args)); - debug_return_bool(command_matches_glob(sudoers_cmnd, sudoers_args)); + rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args); + else + rc = command_matches_glob(sudoers_cmnd, sudoers_args); #endif + } else { + rc = command_matches_normal(sudoers_cmnd, sudoers_args, digest); } - debug_return_bool(command_matches_normal(sudoers_cmnd, sudoers_args, digest)); +done: + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, + "user command \"%s%s%s\" matches sudoers command \"%s%s%s\": %s", + user_cmnd, user_args ? " " : "", user_args ? user_args : "", + sudoers_cmnd, sudoers_args ? " " : "", sudoers_args ? sudoers_args : "", + rc ? "true" : "false"); + debug_return_bool(rc); } static bool -command_matches_fnmatch(char *sudoers_cmnd, char *sudoers_args) +command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args) { debug_decl(command_matches_fnmatch, SUDO_DEBUG_MATCH) @@ -450,7 +463,7 @@ command_matches_fnmatch(char *sudoers_cmnd, char *sudo #ifndef SUDOERS_NAME_MATCH static bool -command_matches_glob(char *sudoers_cmnd, char *sudoers_args) +command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args) { struct stat sudoers_stat; size_t dlen; @@ -523,7 +536,7 @@ command_matches_glob(char *sudoers_cmnd, char *sudoers #ifdef SUDOERS_NAME_MATCH static bool -command_matches_normal(char *sudoers_cmnd, char *sudoers_args, struct sudo_digest *digest) +command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest) { size_t dlen; debug_decl(command_matches_normal, SUDO_DEBUG_MATCH) @@ -582,7 +595,7 @@ static struct digest_function { }; static bool -digest_matches(char *file, struct sudo_digest *sd) +digest_matches(const char *file, const struct sudo_digest *sd) { unsigned char file_digest[SHA512_DIGEST_LENGTH]; unsigned char sudoers_digest[SHA512_DIGEST_LENGTH]; @@ -601,7 +614,7 @@ digest_matches(char *file, struct sudo_digest *sd) } } if (func == NULL) { - warningx(_("unsupported digest type %d for %s"), sd->digest_type, file); + warningx(U_("unsupported digest type %d for %s"), sd->digest_type, file); debug_return_bool(false); } if (strlen(sd->digest_str) == func->digest_len * 2) { @@ -631,25 +644,30 @@ digest_matches(char *file, struct sudo_digest *sd) func->update(&ctx, buf, nread); } if (ferror(fp)) { - warningx(_("%s: read error"), file); + warningx(U_("%s: read error"), file); fclose(fp); debug_return_bool(false); } fclose(fp); func->final(file_digest, &ctx); - debug_return_bool(memcmp(file_digest, sudoers_digest, func->digest_len) == 0); + if (memcmp(file_digest, sudoers_digest, func->digest_len) == 0) + debug_return_bool(true); + sudo_debug_printf(SUDO_DEBUG_DIAG|SUDO_DEBUG_LINENO, + "%s digest mismatch for %s, expecting %s", + func->digest_name, file, sd->digest_str); + debug_return_bool(false); bad_format: - warningx(_("digest for %s (%s) is not in %s form"), file, + warningx(U_("digest for %s (%s) is not in %s form"), file, sd->digest_str, func->digest_name); debug_return_bool(false); } static bool -command_matches_normal(char *sudoers_cmnd, char *sudoers_args, struct sudo_digest *digest) +command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct sudo_digest *digest) { struct stat sudoers_stat; - char *base; + const char *base; size_t dlen; debug_decl(command_matches_normal, SUDO_DEBUG_MATCH) @@ -696,7 +714,7 @@ command_matches_normal(char *sudoers_cmnd, char *sudoe * Note that sudoers_dir include the trailing '/' */ static bool -command_matches_dir(char *sudoers_dir, size_t dlen) +command_matches_dir(const char *sudoers_dir, size_t dlen) { debug_decl(command_matches_dir, SUDO_DEBUG_MATCH) debug_return_bool(strncmp(user_cmnd, sudoers_dir, dlen) == 0); @@ -706,7 +724,7 @@ command_matches_dir(char *sudoers_dir, size_t dlen) * Return true if user_cmnd names one of the inodes in dir, else false. */ static bool -command_matches_dir(char *sudoers_dir, size_t dlen) +command_matches_dir(const char *sudoers_dir, size_t dlen) { struct stat sudoers_stat; struct dirent *dent; @@ -753,71 +771,95 @@ command_matches_dir(char *sudoers_dir, size_t dlen) * Returns true if the hostname matches the pattern, else false */ bool -hostname_matches(char *shost, char *lhost, char *pattern) +hostname_matches(const char *shost, const char *lhost, const char *pattern) { debug_decl(hostname_matches, SUDO_DEBUG_MATCH) + const char *host; + bool rc; + host = strchr(pattern, '.') != NULL ? lhost : shost; if (has_meta(pattern)) { - if (strchr(pattern, '.')) - debug_return_bool(!fnmatch(pattern, lhost, FNM_CASEFOLD)); - else - debug_return_bool(!fnmatch(pattern, shost, FNM_CASEFOLD)); + rc = !fnmatch(pattern, host, FNM_CASEFOLD); } else { - if (strchr(pattern, '.')) - debug_return_bool(!strcasecmp(lhost, pattern)); - else - debug_return_bool(!strcasecmp(shost, pattern)); + rc = !strcasecmp(host, pattern); } + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, + "host %s matches sudoers pattern %s: %s", + host, pattern, rc ? "true" : "false"); + debug_return_bool(rc); } /* - * Returns true if the user/uid from sudoers matches the specified user/uid, - * else returns false. + * Returns true if the user/uid from sudoers matches the specified user/uid, + * else returns false. */ bool -userpw_matches(char *sudoers_user, char *user, struct passwd *pw) +userpw_matches(const char *sudoers_user, const char *user, const struct passwd *pw) { + const char *errstr; + uid_t uid; + bool rc; debug_decl(userpw_matches, SUDO_DEBUG_MATCH) if (pw != NULL && *sudoers_user == '#') { - uid_t uid = (uid_t) atoi(sudoers_user + 1); - if (uid == pw->pw_uid) - debug_return_bool(true); + uid = (uid_t) atoid(sudoers_user + 1, NULL, NULL, &errstr); + if (errstr == NULL && uid == pw->pw_uid) { + rc = true; + goto done; + } } - debug_return_bool(strcmp(sudoers_user, user) == 0); + rc = strcmp(sudoers_user, user) == 0; +done: + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, + "user %s matches sudoers user %s: %s", + user, sudoers_user, rc ? "true" : "false"); + debug_return_bool(rc); } /* - * Returns true if the group/gid from sudoers matches the specified group/gid, - * else returns false. + * Returns true if the group/gid from sudoers matches the specified group/gid, + * else returns false. */ bool -group_matches(char *sudoers_group, struct group *gr) +group_matches(const char *sudoers_group, const struct group *gr) { + const char *errstr; + gid_t gid; + bool rc; debug_decl(group_matches, SUDO_DEBUG_MATCH) if (*sudoers_group == '#') { - gid_t gid = (gid_t) atoi(sudoers_group + 1); - if (gid == gr->gr_gid) - debug_return_bool(true); + gid = (gid_t) atoid(sudoers_group + 1, NULL, NULL, &errstr); + if (errstr == NULL && gid == gr->gr_gid) { + rc = true; + goto done; + } } - debug_return_bool(strcmp(gr->gr_name, sudoers_group) == 0); + rc = strcmp(gr->gr_name, sudoers_group) == 0; +done: + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, + "group %s matches sudoers group %s: %s", + gr->gr_name, sudoers_group, rc ? "true" : "false"); + debug_return_bool(rc); } /* - * Returns true if the given user belongs to the named group, - * else returns false. + * Returns true if the given user belongs to the named group, + * else returns false. */ bool -usergr_matches(char *group, char *user, struct passwd *pw) +usergr_matches(const char *group, const char *user, const struct passwd *pw) { int matched = false; struct passwd *pw0 = NULL; debug_decl(usergr_matches, SUDO_DEBUG_MATCH) /* make sure we have a valid usergroup, sudo style */ - if (*group++ != '%') + if (*group++ != '%') { + sudo_debug_printf(SUDO_DEBUG_DIAG, "user group %s has no leading '%%'", + group); goto done; + } if (*group == ':' && def_group_plugin) { matched = group_plugin_query(user, group + 1, pw); @@ -826,8 +868,11 @@ usergr_matches(char *group, char *user, struct passwd /* look up user's primary gid in the passwd file */ if (pw == NULL) { - if ((pw0 = sudo_getpwnam(user)) == NULL) + if ((pw0 = sudo_getpwnam(user)) == NULL) { + sudo_debug_printf(SUDO_DEBUG_DIAG, "unable to find %s in passwd db", + user); goto done; + } pw = pw0; } @@ -846,6 +891,8 @@ done: if (pw0 != NULL) sudo_pw_delref(pw0); + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, + "user %s matches group %s: %s", user, group, matched ? "true" : "false"); debug_return_bool(matched); } @@ -880,24 +927,31 @@ sudo_getdomainname(void) /* * Returns true if "host" and "user" belong to the netgroup "netgr", - * else return false. Either of "host", "shost" or "user" may be NULL + * else return false. Either of "lhost", "shost" or "user" may be NULL * in which case that argument is not checked... - * - * XXX - swap order of host & shost */ bool -netgr_matches(char *netgr, char *lhost, char *shost, char *user) +netgr_matches(const char *netgr, const char *lhost, const char *shost, const char *user) { #ifdef HAVE_INNETGR static char *domain; static int initialized; #endif + bool rc = false; debug_decl(netgr_matches, SUDO_DEBUG_MATCH) + if (!def_use_netgroups) { + sudo_debug_printf(SUDO_DEBUG_INFO, "netgroups are disabled"); + debug_return_bool(false); + } + #ifdef HAVE_INNETGR /* make sure we have a valid netgroup, sudo style */ - if (*netgr++ != '+') + if (*netgr++ != '+') { + sudo_debug_printf(SUDO_DEBUG_DIAG, "netgroup %s has no leading '+'", + netgr); debug_return_bool(false); + } /* get the domain name (if any) */ if (!initialized) { @@ -906,10 +960,15 @@ netgr_matches(char *netgr, char *lhost, char *shost, c } if (innetgr(netgr, lhost, user, domain)) - debug_return_bool(true); + rc = true; else if (lhost != shost && innetgr(netgr, shost, user, domain)) - debug_return_bool(true); + rc = true; #endif /* HAVE_INNETGR */ - debug_return_bool(false); + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, + "netgroup %s matches (%s|%s, %s, %s): %s", netgr, lhost ? lhost : "", + shost ? shost : "", user ? user : "", domain ? domain : "", + rc ? "true" : "false"); + + debug_return_bool(rc); }