diff -up ./plugins/sudoers/auth/passwd.c.rowhammer ./plugins/sudoers/auth/passwd.c --- ./plugins/sudoers/auth/passwd.c.rowhammer 2020-12-17 02:33:44.000000000 +0100 +++ ./plugins/sudoers/auth/passwd.c 2024-01-22 16:01:16.331874669 +0100 @@ -62,7 +62,7 @@ sudo_passwd_verify(struct passwd *pw, ch char sav, *epass; char *pw_epasswd = auth->data; size_t pw_len; - int matched = 0; + int ret; debug_decl(sudo_passwd_verify, SUDOERS_DEBUG_AUTH); /* An empty plain-text password must match an empty encrypted password. */ @@ -85,27 +85,37 @@ sudo_passwd_verify(struct passwd *pw, ch */ epass = (char *) crypt(pass, pw_epasswd); pass[8] = sav; + ret = AUTH_FAILURE; if (epass != NULL) { - if (HAS_AGEINFO(pw_epasswd, pw_len) && strlen(epass) == DESLEN) - matched = !strncmp(pw_epasswd, epass, DESLEN); - else - matched = !strcmp(pw_epasswd, epass); + if (HAS_AGEINFO(pw_epasswd, pw_len) && strlen(epass) == DESLEN) { + if (strncmp(pw_epasswd, epass, DESLEN) == 0) + ret = AUTH_SUCCESS; + } else { + if (strcmp(pw_epasswd, epass) == 0) + ret = AUTH_SUCCESS; + } } - debug_return_int(matched ? AUTH_SUCCESS : AUTH_FAILURE); + explicit_bzero(des_pass, sizeof(des_pass)); + + debug_return_int(ret); } #else int sudo_passwd_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback) { char *pw_passwd = auth->data; - int matched; + int ret; debug_decl(sudo_passwd_verify, SUDOERS_DEBUG_AUTH); /* Simple string compare for systems without crypt(). */ matched = !strcmp(pass, pw_passwd); + if (strcmp(pass, pw_passwd) == 0) + ret = AUTH_SUCCESS; + else + ret = AUTH_FAILURE; - debug_return_int(matched ? AUTH_SUCCESS : AUTH_FAILURE); + debug_return_int(ret); } #endif diff -up ./plugins/sudoers/auth/sudo_auth.c.rowhammer ./plugins/sudoers/auth/sudo_auth.c --- ./plugins/sudoers/auth/sudo_auth.c.rowhammer 2020-12-17 02:33:43.000000000 +0100 +++ ./plugins/sudoers/auth/sudo_auth.c 2024-01-22 16:01:16.331874669 +0100 @@ -112,10 +112,16 @@ sudo_auth_init(struct passwd *pw) if (auth->init && !IS_DISABLED(auth)) { /* Disable if it failed to init unless there was a fatal error. */ status = (auth->init)(pw, auth); - if (status == AUTH_FAILURE) - SET(auth->flags, FLAG_DISABLED); - else if (status == AUTH_FATAL) - break; /* assume error msg already printed */ + switch (status) { + case AUTH_SUCCESS: + break; + case AUTH_FAILURE: + SET(auth->flags, FLAG_DISABLED); + break; + default: + /* Assume error msg already printed. */ + debug_return_int(-1); + } } } @@ -161,7 +167,7 @@ sudo_auth_init(struct passwd *pw) } } - debug_return_int(status == AUTH_FATAL ? -1 : 0); + debug_return_int(0); } /* @@ -202,7 +208,7 @@ sudo_auth_cleanup(struct passwd *pw, boo for (auth = auth_switch; auth->name; auth++) { if (auth->cleanup && !IS_DISABLED(auth)) { int status = (auth->cleanup)(pw, auth, force); - if (status == AUTH_FATAL) { + if (status != AUTH_SUCCESS) { /* Assume error msg already printed. */ debug_return_int(-1); } @@ -297,7 +303,7 @@ verify_user(struct passwd *pw, char *pro status = (auth->setup)(pw, &prompt, auth); if (status == AUTH_FAILURE) SET(auth->flags, FLAG_DISABLED); - else if (status == AUTH_FATAL || user_interrupted()) + else if (status != AUTH_SUCCESS || user_interrupted()) goto done; /* assume error msg already printed */ } } @@ -348,7 +354,6 @@ done: log_auth_failure(validated, ntries); ret = false; break; - case AUTH_FATAL: default: log_auth_failure(validated, 0); ret = -1; @@ -360,24 +365,32 @@ done: /* * Call authentication method begin session hooks. - * Returns 1 on success and -1 on error. + * Returns true on success, false on failure and -1 on error. */ int sudo_auth_begin_session(struct passwd *pw, char **user_env[]) { sudo_auth *auth; + int ret = true; debug_decl(sudo_auth_begin_session, SUDOERS_DEBUG_AUTH); for (auth = auth_switch; auth->name; auth++) { if (auth->begin_session && !IS_DISABLED(auth)) { int status = (auth->begin_session)(pw, user_env, auth); - if (status != AUTH_SUCCESS) { - /* Assume error msg already printed. */ - debug_return_int(-1); + switch (status) { + case AUTH_SUCCESS: + break; + case AUTH_FAILURE: + ret = false; + break; + default: + /* Assume error msg already printed. */ + ret = -1; + break; } } } - debug_return_int(1); + debug_return_int(ret); } bool @@ -398,25 +411,33 @@ sudo_auth_needs_end_session(void) /* * Call authentication method end session hooks. - * Returns 1 on success and -1 on error. + * Returns true on success, false on failure and -1 on error. */ int sudo_auth_end_session(struct passwd *pw) { sudo_auth *auth; + int ret = true; int status; debug_decl(sudo_auth_end_session, SUDOERS_DEBUG_AUTH); for (auth = auth_switch; auth->name; auth++) { if (auth->end_session && !IS_DISABLED(auth)) { status = (auth->end_session)(pw, auth); - if (status == AUTH_FATAL) { - /* Assume error msg already printed. */ - debug_return_int(-1); - } + switch (status) { + case AUTH_SUCCESS: + break; + case AUTH_FAILURE: + ret = false; + break; + default: + /* Assume error msg already printed. */ + ret = -1; + break; + } } } - debug_return_int(1); + debug_return_int(ret); } /* diff -up ./plugins/sudoers/auth/sudo_auth.h.rowhammer ./plugins/sudoers/auth/sudo_auth.h --- ./plugins/sudoers/auth/sudo_auth.h.rowhammer 2020-12-17 02:33:43.000000000 +0100 +++ ./plugins/sudoers/auth/sudo_auth.h 2024-01-22 16:01:16.332874679 +0100 @@ -19,11 +19,11 @@ #ifndef SUDO_AUTH_H #define SUDO_AUTH_H -/* Auth function return values. */ -#define AUTH_SUCCESS 0 -#define AUTH_FAILURE 1 -#define AUTH_INTR 2 -#define AUTH_FATAL 3 +/* Auth function return values (rowhammer resistent). */ +#define AUTH_SUCCESS 0x52a2925 /* 0101001010100010100100100101 */ +#define AUTH_FAILURE 0xad5d6da /* 1010110101011101011011011010 */ +#define AUTH_INTR 0x69d61fc8 /* 1101001110101100001111111001000 */ +#define AUTH_FATAL 0x1629e037 /* 0010110001010011110000000110111 */ typedef struct sudo_auth { int flags; /* various flags, see below */ diff -up ./plugins/sudoers/cvtsudoers.c.rowhammer ./plugins/sudoers/cvtsudoers.c --- ./plugins/sudoers/cvtsudoers.c.rowhammer 2024-01-22 18:30:09.585081693 +0100 +++ ./plugins/sudoers/cvtsudoers.c 2024-01-22 18:32:35.238519869 +0100 @@ -685,7 +685,7 @@ userlist_matches_filter(struct sudoers_p pw.pw_uid = (uid_t)-1; pw.pw_gid = (gid_t)-1; - if (user_matches(parse_tree, &pw, m) == true) + if (user_matches(parse_tree, &pw, m) == ALLOW) matched = true; } else { STAILQ_FOREACH(s, &filters->users, entries) { @@ -711,7 +711,7 @@ userlist_matches_filter(struct sudoers_p if (pw == NULL) continue; - if (user_matches(parse_tree, pw, m) == true) + if (user_matches(parse_tree, pw, m) == ALLOW) matched = true; sudo_pw_delref(pw); @@ -787,7 +787,7 @@ hostlist_matches_filter(struct sudoers_p /* Only need one host in the filter to match. */ /* XXX - can't use netgroup_tuple with NULL pw */ - if (host_matches(parse_tree, NULL, lhost, shost, m) == true) { + if (host_matches(parse_tree, NULL, lhost, shost, m) == ALLOW) { matched = true; break; } diff -up ./plugins/sudoers/match.c.rowhammer ./plugins/sudoers/match.c --- ./plugins/sudoers/match.c.rowhammer 2020-12-17 02:33:44.000000000 +0100 +++ ./plugins/sudoers/match.c 2024-01-22 16:01:16.332874679 +0100 @@ -26,6 +26,7 @@ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com */ +#include "parse.h" #include #include @@ -70,37 +71,42 @@ user_matches(struct sudoers_parse_tree * { const char *lhost = parse_tree->lhost ? parse_tree->lhost : user_runhost; const char *shost = parse_tree->shost ? parse_tree->shost : user_srunhost; - int matched = UNSPEC; + int rc, matched = UNSPEC; struct alias *a; debug_decl(user_matches, SUDOERS_DEBUG_MATCH); switch (m->type) { case ALL: - matched = !m->negated; + matched = m->negated ? DENY : ALLOW; break; case NETGROUP: if (netgr_matches(m->name, def_netgroup_tuple ? lhost : NULL, def_netgroup_tuple ? shost : NULL, pw->pw_name)) - matched = !m->negated; + matched = m->negated ? DENY : ALLOW; break; case USERGROUP: if (usergr_matches(m->name, pw->pw_name, pw)) - matched = !m->negated; + matched = m->negated ? DENY : ALLOW; break; case ALIAS: if ((a = alias_get(parse_tree, m->name, USERALIAS)) != NULL) { /* XXX */ - int rc = userlist_matches(parse_tree, pw, &a->members); - if (rc != UNSPEC) - matched = m->negated ? !rc : rc; + rc = userlist_matches(parse_tree, pw, &a->members); + if (SPECIFIED(rc)) { + if (m->negated) { + matched = rc == ALLOW ? DENY : ALLOW; + } else { + matched = rc; + } + } alias_put(a); break; } FALLTHROUGH; case WORD: if (userpw_matches(m->name, pw->pw_name, pw)) - matched = !m->negated; + matched = m->negated ? DENY : ALLOW; break; } debug_return_int(matched); @@ -119,7 +125,8 @@ userlist_matches(struct sudoers_parse_tr debug_decl(userlist_matches, SUDOERS_DEBUG_MATCH); TAILQ_FOREACH_REVERSE(m, list, member_list, entries) { - if ((matched = user_matches(parse_tree, pw, m)) != UNSPEC) + matched = user_matches(parse_tree, pw, m); + if (SPECIFIED(matched)) break; } debug_return_int(matched); @@ -164,48 +171,53 @@ runaslist_matches(struct sudoers_parse_t /* If no runas user or runas group listed in sudoers, use default. */ if (user_list == NULL && group_list == NULL) { debug_return_int(userpw_matches(def_runas_default, - runas_pw->pw_name, runas_pw)); + runas_pw->pw_name, runas_pw) ? ALLOW : DENY); } if (user_list != NULL) { TAILQ_FOREACH_REVERSE(m, user_list, member_list, entries) { switch (m->type) { case ALL: - user_matched = !m->negated; + user_matched = m->negated ? DENY : ALLOW; break; case NETGROUP: if (netgr_matches(m->name, def_netgroup_tuple ? lhost : NULL, def_netgroup_tuple ? shost : NULL, runas_pw->pw_name)) - user_matched = !m->negated; + user_matched = m->negated ? DENY : ALLOW; break; case USERGROUP: if (usergr_matches(m->name, runas_pw->pw_name, runas_pw)) - user_matched = !m->negated; + user_matched = m->negated ? DENY : ALLOW; break; case ALIAS: a = alias_get(parse_tree, m->name, RUNASALIAS); if (a != NULL) { rc = runaslist_matches(parse_tree, &a->members, &empty, matching_user, NULL); - if (rc != UNSPEC) - user_matched = m->negated ? !rc : rc; + if (SPECIFIED(rc)) { + if (m->negated) { + user_matched = rc == ALLOW ? DENY : ALLOW; + } else { + user_matched = rc; + } + } alias_put(a); break; } FALLTHROUGH; case WORD: if (userpw_matches(m->name, runas_pw->pw_name, runas_pw)) - user_matched = !m->negated; + user_matched = m->negated ? DENY : ALLOW; break; case MYSELF: if (!ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED) || strcmp(user_name, runas_pw->pw_name) == 0) - user_matched = !m->negated; + user_matched = m->negated ? DENY : ALLOW; break; } - if (user_matched != UNSPEC) { + if (SPECIFIED(user_matched)) { if (matching_user != NULL && m->type != ALIAS) *matching_user = m; break; @@ -226,34 +238,40 @@ runaslist_matches(struct sudoers_parse_t TAILQ_FOREACH_REVERSE(m, group_list, member_list, entries) { switch (m->type) { case ALL: - group_matched = !m->negated; + group_matched = m->negated ? DENY : ALLOW; break; case ALIAS: a = alias_get(parse_tree, m->name, RUNASALIAS); if (a != NULL) { rc = runaslist_matches(parse_tree, &empty, &a->members, NULL, matching_group); - if (rc != UNSPEC) - group_matched = m->negated ? !rc : rc; + if (SPECIFIED(rc)) { + if (m->negated) { + group_matched = rc == ALLOW ? DENY : ALLOW; + } else { + group_matched = rc; + } + } alias_put(a); break; } FALLTHROUGH; case WORD: if (group_matches(m->name, runas_gr)) - group_matched = !m->negated; + group_matched = m->negated ? DENY : ALLOW; break; } - if (group_matched != UNSPEC) { + if (SPECIFIED(group_matched)) { if (matching_group != NULL && m->type != ALIAS) *matching_group = m; break; } } } - if (group_matched == UNSPEC) { + if (!SPECIFIED(group_matched)) { struct gid_list *runas_groups; /* + * * The runas group was not explicitly allowed by sudoers. * Check whether it is one of the target user's groups. */ @@ -295,7 +313,7 @@ hostlist_matches_int(struct sudoers_pars TAILQ_FOREACH_REVERSE(m, list, member_list, entries) { matched = host_matches(parse_tree, pw, lhost, shost, m); - if (matched != UNSPEC) + if (SPECIFIED(matched)) break; } debug_return_int(matched); @@ -324,37 +342,42 @@ host_matches(struct sudoers_parse_tree * const char *lhost, const char *shost, const struct member *m) { struct alias *a; - int matched = UNSPEC; + int rc, matched = UNSPEC; debug_decl(host_matches, SUDOERS_DEBUG_MATCH); switch (m->type) { case ALL: - matched = !m->negated; + matched = m->negated ? DENY : ALLOW; break; case NETGROUP: if (netgr_matches(m->name, lhost, shost, def_netgroup_tuple ? pw->pw_name : NULL)) - matched = !m->negated; + matched = m->negated ? DENY : ALLOW; break; case NTWKADDR: if (addr_matches(m->name)) - matched = !m->negated; + matched = m->negated ? DENY : ALLOW; break; case ALIAS: a = alias_get(parse_tree, m->name, HOSTALIAS); if (a != NULL) { /* XXX */ - int rc = hostlist_matches_int(parse_tree, pw, lhost, shost, + rc = hostlist_matches_int(parse_tree, pw, lhost, shost, &a->members); - if (rc != UNSPEC) - matched = m->negated ? !rc : rc; + if (SPECIFIED(rc)) { + if (m->negated) { + matched = rc == ALLOW ? DENY : ALLOW; + } else { + matched = rc; + } + } alias_put(a); break; } FALLTHROUGH; case WORD: if (hostname_matches(shost, lhost, m->name)) - matched = !m->negated; + matched = m->negated ? DENY : ALLOW; break; } debug_return_int(matched); @@ -375,7 +398,7 @@ cmndlist_matches(struct sudoers_parse_tr TAILQ_FOREACH_REVERSE(m, list, member_list, entries) { matched = cmnd_matches(parse_tree, m, runchroot, info); - if (matched != UNSPEC) + if (SPECIFIED(matched)) break; } debug_return_int(matched); @@ -397,21 +420,26 @@ cmnd_matches(struct sudoers_parse_tree * switch (m->type) { case ALL: if (m->name == NULL) { - matched = !m->negated; + matched = m->negated ? DENY : ALLOW; break; } FALLTHROUGH; case COMMAND: c = (struct sudo_command *)m->name; if (command_matches(c->cmnd, c->args, runchroot, info, &c->digests)) - matched = !m->negated; + matched = m->negated ? DENY : ALLOW; break; case ALIAS: a = alias_get(parse_tree, m->name, CMNDALIAS); if (a != NULL) { rc = cmndlist_matches(parse_tree, &a->members, runchroot, info); - if (rc != UNSPEC) - matched = m->negated ? !rc : rc; + if (SPECIFIED(rc)) { + if (m->negated) { + matched = rc == ALLOW ? DENY : ALLOW; + } else { + matched = rc; + } + } alias_put(a); } break; diff -up ./plugins/sudoers/parse.c.rowhammer ./plugins/sudoers/parse.c --- ./plugins/sudoers/parse.c.rowhammer 2020-12-17 02:33:43.000000000 +0100 +++ ./plugins/sudoers/parse.c 2024-01-22 16:01:16.333874689 +0100 @@ -151,7 +151,7 @@ sudoers_lookup_check(struct sudo_nss *ns if (runas_match == ALLOW) { cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd, cs->runchroot, info); - if (cmnd_match != UNSPEC) { + if (SPECIFIED(cmnd_match)) { /* * If user is running command as himself, * set runas_pw = sudo_user.pw. @@ -365,7 +365,7 @@ sudoers_lookup(struct sudo_nss_list *snl } m = sudoers_lookup_check(nss, pw, &validated, &info, &cs, &defs, now); - if (m != UNSPEC) { + if (SPECIFIED(m)) { match = m; parse_tree = nss->parse_tree; } @@ -373,7 +373,7 @@ sudoers_lookup(struct sudo_nss_list *snl if (!sudo_nss_can_continue(nss, m)) break; } - if (match != UNSPEC) { + if (SPECIFIED(match)) { if (info.cmnd_path != NULL) { /* Update user_cmnd, user_stat, cmnd_status from matching entry. */ free(user_cmnd); diff -up ./plugins/sudoers/parse.h.rowhammer ./plugins/sudoers/parse.h --- ./plugins/sudoers/parse.h.rowhammer 2021-01-09 21:12:16.000000000 +0100 +++ ./plugins/sudoers/parse.h 2024-01-22 16:01:16.333874689 +0100 @@ -20,6 +20,9 @@ #ifndef SUDOERS_PARSE_H #define SUDOERS_PARSE_H +#include +#include +#include #include #include "sudo_queue.h" @@ -31,13 +34,26 @@ #undef UNSPEC #define UNSPEC -1 + +/* Denied by policy (rowhammer resistent). */ #undef DENY -#define DENY 0 +#define DENY 0xad5d6da /* 1010110101011101011011011010 */ + +/* Allowed by policy (rowhammer resistent). */ #undef ALLOW -#define ALLOW 1 +#define ALLOW 0x52a2925 /* 0101001010100010100100100101 */ + #undef IMPLIED #define IMPLIED 2 + +/* + * We must explicitly check against ALLOW and DENY instead testing + * that the value is not UNSPEC to avoid potential ROWHAMMER issues. + */ +#define SPECIFIED(_v) ((_v) == ALLOW || (_v) == DENY) + + /* * Initialize all tags to UNSPEC. */