From d746f5a48cd2b48cde0dbec2642609a982d37643 Mon Sep 17 00:00:00 2001 From: qsw333 Date: Thu, 4 Jan 2024 21:25:21 +0800 Subject: [PATCH] fix CVE-2023-42465 Signed-off-by: qsW (cherry picked from commit 6bfa5229b5ebc9dbd3b56e6694a33997cf6e8273) --- backport-CVE-2023-42465.patch | 424 ++++++++ ...he-definition-of-ALLOW-DENY-being-tr.patch | 257 +++++ ...unctions-return-ALLOW-DENY-not-true-.patch | 989 ++++++++++++++++++ sudo.spec | 8 +- 4 files changed, 1677 insertions(+), 1 deletion(-) create mode 100644 backport-CVE-2023-42465.patch create mode 100644 backport-Do-not-rely-on-the-definition-of-ALLOW-DENY-being-tr.patch create mode 100644 backport-Make-all-match-functions-return-ALLOW-DENY-not-true-.patch diff --git a/backport-CVE-2023-42465.patch b/backport-CVE-2023-42465.patch new file mode 100644 index 0000000..1ec3071 --- /dev/null +++ b/backport-CVE-2023-42465.patch @@ -0,0 +1,424 @@ +From 7873f8334c8d31031f8cfa83bd97ac6029309e4f Mon Sep 17 00:00:00 2001 +From: "Todd C. Miller" +Date: Sat, 9 Sep 2023 14:07:04 -0600 +Subject: [PATCH] Try to make sudo less vulnerable to ROWHAMMER attacks. + +We now use ROWHAMMER-resistent values for ALLOW, DENY, AUTH_SUCCESS, +AUTH_FAILURE, AUTH_ERROR and AUTH_NONINTERACTIVE. In addition, we +explicitly test for expected values instead of using a negated test +against an error value. In the parser match functions this means +explicitly checking for ALLOW or DENY instead of accepting anything +that is not set to UNSPEC. + +Thanks to Andrew J. Adiletta, M. Caner Tol, Yarkin Doroz, and Berk +Sunar, all affiliated with the Vernam Applied Cryptography and +Cybersecurity Lab at Worcester Polytechnic Institute, for the report. +Paper preprint: https://arxiv.org/abs/2309.02545 + +Reference: https://github.com/sudo-project/sudo/commit/7873f8334c8d31031f8cfa83bd97ac6029309e4f +Conflict: passwd.c sudo_auth.h match.c parse.c + +--- + plugins/sudoers/auth/passwd.c | 18 ++++++----- + plugins/sudoers/auth/sudo_auth.c | 51 ++++++++++++++++++++++---------- + plugins/sudoers/auth/sudo_auth.h | 10 +++---- + plugins/sudoers/match.c | 25 ++++++++-------- + plugins/sudoers/parse.c | 6 ++-- + plugins/sudoers/parse.h | 23 ++++++++++---- + 6 files changed, 86 insertions(+), 47 deletions(-) + +diff --git a/plugins/sudoers/auth/passwd.c b/plugins/sudoers/auth/passwd.c +index 910a510..2a9766c 100644 +--- a/plugins/sudoers/auth/passwd.c ++++ b/plugins/sudoers/auth/passwd.c +@@ -61,7 +61,7 @@ sudo_passwd_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_c + char des_pass[9], *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. */ +@@ -73,7 +73,7 @@ sudo_passwd_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_c + */ + pw_len = strlen(pw_epasswd); + if (pw_len == DESLEN || HAS_AGEINFO(pw_epasswd, pw_len)) { +- strlcpy(des_pass, pass, sizeof(des_pass)); ++ (void)strlcpy(des_pass, pass, sizeof(des_pass)); + pass = des_pass; + } + +@@ -83,16 +83,20 @@ sudo_passwd_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_c + * only compare the first DESLEN characters in that case. + */ + epass = (char *) crypt(pass, pw_epasswd); ++ 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; ++ } + } + + explicit_bzero(des_pass, sizeof(des_pass)); + +- debug_return_int(matched ? AUTH_SUCCESS : AUTH_FAILURE); ++ debug_return_int(ret); + } + + int +diff --git a/plugins/sudoers/auth/sudo_auth.c b/plugins/sudoers/auth/sudo_auth.c +index 1bdacdb..767fe26 100644 +--- a/plugins/sudoers/auth/sudo_auth.c ++++ b/plugins/sudoers/auth/sudo_auth.c +@@ -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) ++ switch (status) { ++ case AUTH_SUCCESS: ++ break; ++ case AUTH_FAILURE: + SET(auth->flags, FLAG_DISABLED); +- else if (status == AUTH_FATAL) +- break; /* assume error msg already printed */ ++ 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, bool force) + 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 *prompt, int validated, + 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 */ + } + } +@@ -350,7 +356,6 @@ done: + log_auth_failure(validated, ntries); + ret = false; + break; +- case AUTH_FATAL: + default: + log_auth_failure(validated, 0); + ret = -1; +@@ -362,24 +367,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) { ++ switch (status) { ++ case AUTH_SUCCESS: ++ break; ++ case AUTH_FAILURE: ++ ret = false; ++ break; ++ default: + /* Assume error msg already printed. */ +- debug_return_int(-1); ++ ret = -1; ++ break; + } + } + } +- debug_return_int(1); ++ debug_return_int(ret); + } + + bool +@@ -400,25 +413,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) { ++ switch (status) { ++ case AUTH_SUCCESS: ++ break; ++ case AUTH_FAILURE: ++ ret = false; ++ break; ++ default: + /* Assume error msg already printed. */ +- debug_return_int(-1); ++ ret = -1; ++ break; + } + } + } +- debug_return_int(1); ++ debug_return_int(ret); + } + + /* +diff --git a/plugins/sudoers/auth/sudo_auth.h b/plugins/sudoers/auth/sudo_auth.h +index 9ee408d..585637c 100644 +--- a/plugins/sudoers/auth/sudo_auth.h ++++ b/plugins/sudoers/auth/sudo_auth.h +@@ -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 --git a/plugins/sudoers/match.c b/plugins/sudoers/match.c +index 052e988..edf8fa1 100644 +--- a/plugins/sudoers/match.c ++++ b/plugins/sudoers/match.c +@@ -93,7 +93,7 @@ user_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, + if ((a = alias_get(parse_tree, m->name, USERALIAS)) != NULL) { + /* XXX */ + const int rc = userlist_matches(parse_tree, pw, &a->members); +- if (rc != UNSPEC) { ++ if (SPECIFIED(rc)) { + if(m->negated) { + matched = rc == ALLOW ? DENY : ALLOW; + } else { +@@ -125,7 +125,8 @@ userlist_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, + 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); +@@ -195,7 +196,7 @@ runaslist_matches(struct sudoers_parse_tree *parse_tree, + if (a != NULL) { + rc = runaslist_matches(parse_tree, &a->members, + &empty, matching_user, NULL); +- if (rc != UNSPEC){ ++ if (SPECIFIED(rc)){ + if (m->negated) { + user_matched = rc == ALLOW ? DENY : ALLOW; + } else { +@@ -216,7 +217,7 @@ runaslist_matches(struct sudoers_parse_tree *parse_tree, + 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; +@@ -229,7 +230,7 @@ runaslist_matches(struct sudoers_parse_tree *parse_tree, + * Skip checking runas group if none was specified. + */ + if (ISSET(sudo_user.flags, RUNAS_GROUP_SPECIFIED)) { +- if (user_matched == UNSPEC) { ++ if (!SPECIFIED(user_matched)) { + if (strcmp(user_name, runas_pw->pw_name) == 0) + user_matched = ALLOW; /* only changing group */ + } +@@ -244,7 +245,7 @@ runaslist_matches(struct sudoers_parse_tree *parse_tree, + if (a != NULL) { + rc = runaslist_matches(parse_tree, &empty, + &a->members, NULL, matching_group); +- if (rc != UNSPEC){ ++ if (SPECIFIED(rc)){ + if (m->negated) { + group_matched = rc == ALLOW ? DENY : ALLOW; + } else { +@@ -260,14 +261,14 @@ runaslist_matches(struct sudoers_parse_tree *parse_tree, + 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. +@@ -311,7 +312,7 @@ hostlist_matches_int(struct sudoers_parse_tree *parse_tree, + + 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); +@@ -362,7 +363,7 @@ host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, + /* XXX */ + const int rc = hostlist_matches_int(parse_tree, pw, lhost, + shost,&a->members); +- if (rc != UNSPEC){ ++ if (SPECIFIED(rc)){ + if(m->negated){ + matched = rc == ALLOW ? DENY : ALLOW; + } else { +@@ -395,7 +396,7 @@ cmndlist_matches(struct sudoers_parse_tree *parse_tree, + + TAILQ_FOREACH_REVERSE(m, list, member_list, entries) { + matched = cmnd_matches(parse_tree, m); +- if (matched != UNSPEC) ++ if (SPECIFIED(matched)) + break; + } + debug_return_int(matched); +@@ -429,7 +430,7 @@ cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m) + a = alias_get(parse_tree, m->name, CMNDALIAS); + if (a != NULL) { + rc = cmndlist_matches(parse_tree, &a->members); +- if (rc != UNSPEC){ ++ if (SPECIFIED(rc)){ + if (m->negated) { + matched = rc == ALLOW ? DENY : ALLOW; + } else { +diff --git a/plugins/sudoers/parse.c b/plugins/sudoers/parse.c +index 6f29e0b..b5d37fb 100644 +--- a/plugins/sudoers/parse.c ++++ b/plugins/sudoers/parse.c +@@ -147,7 +147,7 @@ sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw, + NULL); + if (runas_match == ALLOW) { + cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd); +- if (cmnd_match != UNSPEC) { ++ if (SPECIFIED(cmnd_match)) { + /* + * If user is running command as himself, + * set runas_pw = sudo_user.pw. +@@ -301,7 +301,7 @@ sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int validated, + } + + m = sudoers_lookup_check(nss, pw, &validated, &cs, &defs, now); +- if (m != UNSPEC) { ++ if (SPECIFIED(m)) { + match = m; + parse_tree = nss->parse_tree; + } +@@ -309,7 +309,7 @@ sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int validated, + if (!sudo_nss_can_continue(nss, m)) + break; + } +- if (match != UNSPEC) { ++ if (SPECIFIED(match)) { + if (defs != NULL) + update_defaults(parse_tree, defs, SETDEF_GENERIC, false); + if (!apply_cmndspec(cs)) +diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h +index ecc81b8..e1ba680 100644 +--- a/plugins/sudoers/parse.h ++++ b/plugins/sudoers/parse.h +@@ -28,15 +28,28 @@ + /* Returns true if string 's' contains meta characters. */ + #define has_meta(s) (strpbrk(s, "\\?*[]") != NULL) + ++/* Allowed by policy (rowhammer resistent). */ ++#undef ALLOW ++#define ALLOW 0x52a2925 /* 0101001010100010100100100101 */ ++ ++/* Denied by policy (rowhammer resistent) */ ++#undef DENY ++#define DENY 0xad5d6da /* 1010110101011101011011011010 */ ++ ++/* Neither allowed, nor denied. */ + #undef UNSPEC + #define UNSPEC -1 +-#undef DENY +-#define DENY 0 +-#undef ALLOW +-#define ALLOW 1 ++ ++/* Tag implied by root access (SETENV only). */ + #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. + */ +@@ -83,7 +96,7 @@ + * Returns true if the specified tag is not UNSPEC or IMPLIED, else false. + */ + #define TAG_SET(tt) \ +- ((tt) != UNSPEC && (tt) != IMPLIED) ++ ((tt) == true || (tt) == false) + + /* + * Returns true if any tags set in nt differ between ot and nt, else false. +-- +2.33.0 + diff --git a/backport-Do-not-rely-on-the-definition-of-ALLOW-DENY-being-tr.patch b/backport-Do-not-rely-on-the-definition-of-ALLOW-DENY-being-tr.patch new file mode 100644 index 0000000..1bebc14 --- /dev/null +++ b/backport-Do-not-rely-on-the-definition-of-ALLOW-DENY-being-tr.patch @@ -0,0 +1,257 @@ +From cf00568d888c90a8c5d06a06283bc87a45992933 Mon Sep 17 00:00:00 2001 +From: "Todd C. Miller" +Date: Sat, 26 Aug 2023 10:32:37 -0600 +Subject: [PATCH] Do not rely on the definition of ALLOW/DENY being true/false. + +We now explicitly check for ALLOW and DENY when checking return +values and negating values. + +Reference: https://github.com/sudo-project/sudo/commit/cf00568d888c90a8c5d06a06283bc87a45992933 +Conflict: cvtsudoers.c match.c +--- + plugins/sudoers/cvtsudoers.c | 6 +-- + plugins/sudoers/match.c | 85 +++++++++++++++++++++++------------- + 2 files changed, 58 insertions(+), 33 deletions(-) + +diff --git a/plugins/sudoers/cvtsudoers.c b/plugins/sudoers/cvtsudoers.c +index 111b326..12fcfd4 100644 +--- a/plugins/sudoers/cvtsudoers.c ++++ b/plugins/sudoers/cvtsudoers.c +@@ -686,7 +686,7 @@ userlist_matches_filter(struct sudoers_parse_tree *parse_tree, + 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) { +@@ -712,7 +712,7 @@ userlist_matches_filter(struct sudoers_parse_tree *parse_tree, + 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); + +@@ -788,7 +788,7 @@ hostlist_matches_filter(struct sudoers_parse_tree *parse_tree, + + /* 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 --git a/plugins/sudoers/match.c b/plugins/sudoers/match.c +index 9051545..052e988 100644 +--- a/plugins/sudoers/match.c ++++ b/plugins/sudoers/match.c +@@ -77,31 +77,36 @@ user_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, + + 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; ++ const int rc = userlist_matches(parse_tree, pw, &a->members); ++ if (rc != UNSPEC) { ++ 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); +@@ -172,38 +177,43 @@ runaslist_matches(struct sudoers_parse_tree *parse_tree, + 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 (rc != UNSPEC){ ++ 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) { +@@ -227,22 +237,27 @@ runaslist_matches(struct sudoers_parse_tree *parse_tree, + 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 (rc != UNSPEC){ ++ 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) { +@@ -330,32 +345,37 @@ host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, + + 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, +- &a->members); +- if (rc != UNSPEC) +- matched = m->negated ? !rc : rc; ++ const int rc = hostlist_matches_int(parse_tree, pw, lhost, ++ shost,&a->members); ++ if (rc != UNSPEC){ ++ 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); +@@ -396,21 +416,26 @@ cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m) + 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, &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); +- if (rc != UNSPEC) +- matched = m->negated ? !rc : rc; ++ if (rc != UNSPEC){ ++ if (m->negated) { ++ matched = rc == ALLOW ? DENY : ALLOW; ++ } else { ++ matched = rc; ++ } ++ } + alias_put(a); + } + break; +-- +2.33.0 + diff --git a/backport-Make-all-match-functions-return-ALLOW-DENY-not-true-.patch b/backport-Make-all-match-functions-return-ALLOW-DENY-not-true-.patch new file mode 100644 index 0000000..71809a8 --- /dev/null +++ b/backport-Make-all-match-functions-return-ALLOW-DENY-not-true-.patch @@ -0,0 +1,989 @@ +From 2ef90231a132547fa4236ff05fc0fafcd3f3d7a4 Mon Sep 17 00:00:00 2001 +From: "Todd C. Miller" +Date: Sat, 9 Sep 2023 14:07:06 -0600 +Subject: [PATCH] Make all match functions return ALLOW/DENY not true/false. + +Reference: https://github.com/sudo-project/sudo/commit/2ef90231a132547fa4236ff05fc0fafcd3f3d7a4 +Conflict: match.c match_command.c lookup.c +--- + plugins/sudoers/ldap.c | 2 +- + plugins/sudoers/match.c | 146 +++++++++++--------- + plugins/sudoers/match_addr.c | 45 +++--- + plugins/sudoers/match_command.c | 103 +++++++------- + plugins/sudoers/match_digest.c | 10 +- + plugins/sudoers/parse.h | 16 +-- + plugins/sudoers/regress/parser/check_addr.c | 2 +- + plugins/sudoers/sssd.c | 6 +- + 8 files changed, 171 insertions(+), 159 deletions(-) + +diff --git a/plugins/sudoers/ldap.c b/plugins/sudoers/ldap.c +index 5bdd488..b64a5a8 100644 +--- a/plugins/sudoers/ldap.c ++++ b/plugins/sudoers/ldap.c +@@ -340,7 +340,7 @@ sudo_ldap_check_non_unix_group(LDAP *ld, LDAPMessage *entry, struct passwd *pw) + val = (*p)->bv_val; + if (*val == '+') { + if (netgr_matches(val, def_netgroup_tuple ? user_runhost : NULL, +- def_netgroup_tuple ? user_srunhost : NULL, pw->pw_name)) ++ def_netgroup_tuple ? user_srunhost : NULL, pw->pw_name) == ALLOW) + ret = true; + DPRINTF2("ldap sudoUser netgroup '%s' ... %s", val, + ret ? "MATCH!" : "not"); +diff --git a/plugins/sudoers/match.c b/plugins/sudoers/match.c +index edf8fa1..6fb7a2c 100644 +--- a/plugins/sudoers/match.c ++++ b/plugins/sudoers/match.c +@@ -82,11 +82,11 @@ user_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, + case NETGROUP: + if (netgr_matches(m->name, + def_netgroup_tuple ? lhost : NULL, +- def_netgroup_tuple ? shost : NULL, pw->pw_name)) ++ def_netgroup_tuple ? shost : NULL, pw->pw_name) == ALLOW) + matched = m->negated ? DENY : ALLOW; + break; + case USERGROUP: +- if (usergr_matches(m->name, pw->pw_name, pw)) ++ if (usergr_matches(m->name, pw->pw_name, pw) == ALLOW) + matched = m->negated ? DENY : ALLOW; + break; + case ALIAS: +@@ -105,7 +105,7 @@ user_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, + } + /* FALLTHROUGH */ + case WORD: +- if (userpw_matches(m->name, pw->pw_name, pw)) ++ if (userpw_matches(m->name, pw->pw_name, pw) == ALLOW) + matched = m->negated ? DENY : ALLOW; + break; + } +@@ -184,11 +184,11 @@ runaslist_matches(struct sudoers_parse_tree *parse_tree, + if (netgr_matches(m->name, + def_netgroup_tuple ? lhost : NULL, + def_netgroup_tuple ? shost : NULL, +- runas_pw->pw_name)) ++ runas_pw->pw_name) == ALLOW) + user_matched = m->negated ? DENY : ALLOW; + break; + case USERGROUP: +- if (usergr_matches(m->name, runas_pw->pw_name, runas_pw)) ++ if (usergr_matches(m->name, runas_pw->pw_name, runas_pw) == ALLOW) + user_matched = m->negated ? DENY : ALLOW; + break; + case ALIAS: +@@ -208,7 +208,7 @@ runaslist_matches(struct sudoers_parse_tree *parse_tree, + } + /* FALLTHROUGH */ + case WORD: +- if (userpw_matches(m->name, runas_pw->pw_name, runas_pw)) ++ if (userpw_matches(m->name, runas_pw->pw_name, runas_pw) == ALLOW) + user_matched = m->negated ? DENY : ALLOW; + break; + case MYSELF: +@@ -257,7 +257,7 @@ runaslist_matches(struct sudoers_parse_tree *parse_tree, + } + /* FALLTHROUGH */ + case WORD: +- if (group_matches(m->name, runas_gr)) ++ if (group_matches(m->name, runas_gr) == ALLOW) + group_matched = m->negated ? DENY : ALLOW; + break; + } +@@ -341,21 +341,21 @@ host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, + const char *lhost, const char *shost, const struct member *m) + { + struct alias *a; +- int matched = UNSPEC; ++ int ret = UNSPEC; + debug_decl(host_matches, SUDOERS_DEBUG_MATCH); + + switch (m->type) { + case ALL: +- matched = m->negated ? DENY : ALLOW; ++ ret = m->negated ? DENY : ALLOW; + break; + case NETGROUP: + if (netgr_matches(m->name, lhost, shost, +- def_netgroup_tuple ? pw->pw_name : NULL)) +- matched = m->negated ? DENY : ALLOW; ++ def_netgroup_tuple ? pw->pw_name : NULL) == ALLOW) ++ ret = m->negated ? DENY : ALLOW; + break; + case NTWKADDR: +- if (addr_matches(m->name)) +- matched = m->negated ? DENY : ALLOW; ++ if (addr_matches(m->name) == ALLOW) ++ ret = m->negated ? DENY : ALLOW; + break; + case ALIAS: + a = alias_get(parse_tree, m->name, HOSTALIAS); +@@ -365,9 +365,9 @@ host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, + shost,&a->members); + if (SPECIFIED(rc)){ + if(m->negated){ +- matched = rc == ALLOW ? DENY : ALLOW; ++ ret = rc == ALLOW ? DENY : ALLOW; + } else { +- matched = rc; ++ ret = rc; + } + } + alias_put(a); +@@ -375,11 +375,11 @@ host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, + } + /* FALLTHROUGH */ + case WORD: +- if (hostname_matches(shost, lhost, m->name)) +- matched = m->negated ? DENY : ALLOW; ++ if (hostname_matches(shost, lhost, m->name) == ALLOW) ++ ret = m->negated ? DENY : ALLOW; + break; + } +- debug_return_int(matched); ++ debug_return_int(ret); + } + + /* +@@ -391,15 +391,15 @@ cmndlist_matches(struct sudoers_parse_tree *parse_tree, + const struct member_list *list) + { + struct member *m; +- int matched = UNSPEC; ++ int matched; + debug_decl(cmndlist_matches, SUDOERS_DEBUG_MATCH); + + TAILQ_FOREACH_REVERSE(m, list, member_list, entries) { + matched = cmnd_matches(parse_tree, m); + if (SPECIFIED(matched)) +- break; ++ debug_return_int(matched); + } +- debug_return_int(matched); ++ debug_return_int(UNSPEC); + } + + /* +@@ -423,7 +423,7 @@ cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m) + /* FALLTHROUGH */ + case COMMAND: + c = (struct sudo_command *)m->name; +- if (command_matches(c->cmnd, c->args, &c->digests)) ++ if (command_matches(c->cmnd, c->args, &c->digests) == ALLOW) + matched = m->negated ? DENY : ALLOW; + break; + case ALIAS: +@@ -445,96 +445,105 @@ cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m) + } + + /* +- * Returns true if the hostname matches the pattern, else false ++ * Returns ALLOW if the hostname matches the pattern, else DENY + */ +-bool ++int + hostname_matches(const char *shost, const char *lhost, const char *pattern) + { + const char *host; +- bool rc; ++ int ret; + debug_decl(hostname_matches, SUDOERS_DEBUG_MATCH); + + host = strchr(pattern, '.') != NULL ? lhost : shost; ++ ret = DENY; + if (has_meta(pattern)) { +- rc = !fnmatch(pattern, host, FNM_CASEFOLD); ++ if (fnmatch(pattern, host, FNM_CASEFOLD) == 0) ++ ret = ALLOW; + } else { +- rc = !strcasecmp(host, pattern); ++ if (strcasecmp(host, pattern) == 0) ++ ret = ALLOW; + } + 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); ++ host, pattern, ret == ALLOW ? "ALLOW" : "DENY"); ++ debug_return_int(ret); + } + + /* +- * Returns true if the user/uid from sudoers matches the specified user/uid, +- * else returns false. ++ * Returns ALLOW if the user/uid from sudoers matches the specified user/uid, ++ * else returns DENY. + */ +-bool ++int + userpw_matches(const char *sudoers_user, const char *user, const struct passwd *pw) + { + const char *errstr; ++ int ret = DENY; + uid_t uid; +- bool rc; + debug_decl(userpw_matches, SUDOERS_DEBUG_MATCH); + + if (pw != NULL && *sudoers_user == '#') { + uid = (uid_t) sudo_strtoid(sudoers_user + 1, &errstr); + if (errstr == NULL && uid == pw->pw_uid) { +- rc = true; ++ ret = ALLOW; + goto done; + } + } +- if (def_case_insensitive_user) +- rc = strcasecmp(sudoers_user, user) == 0; +- else +- rc = strcmp(sudoers_user, user) == 0; ++ if (def_case_insensitive_user) { ++ if (strcasecmp(sudoers_user, user) == 0) ++ ret = ALLOW; ++ } else { ++ if (strcmp(sudoers_user, user) == 0) ++ ret = ALLOW; ++ } + 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); ++ user, sudoers_user, ret == ALLOW ? "ALLOW" : "DENY"); ++ debug_return_int(ret); + } + + /* +- * Returns true if the group/gid from sudoers matches the specified group/gid, +- * else returns false. ++ * Returns ALLOW if the group/gid from sudoers matches the specified group/gid, ++ * else returns DENY. + */ +-bool ++int + group_matches(const char *sudoers_group, const struct group *gr) + { + const char *errstr; ++ int ret = DENY; + gid_t gid; +- bool rc; + debug_decl(group_matches, SUDOERS_DEBUG_MATCH); + + if (*sudoers_group == '#') { + gid = (gid_t) sudo_strtoid(sudoers_group + 1, &errstr); + if (errstr == NULL && gid == gr->gr_gid) { +- rc = true; ++ ret = ALLOW; + goto done; + } + } +- if (def_case_insensitive_group) +- rc = strcasecmp(sudoers_group, gr->gr_name) == 0; +- else +- rc = strcmp(sudoers_group, gr->gr_name) == 0; ++ if (def_case_insensitive_group) { ++ if (strcasecmp(sudoers_group, gr->gr_name) == 0) ++ ret = ALLOW; ++ } else { ++ if (strcmp(sudoers_group, gr->gr_name) == 0) ++ ret = ALLOW; ++ } + 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); ++ gr->gr_name, sudoers_group, ret == ALLOW ? "ALLOW" : "DENY"); ++ debug_return_int(ret); + } + + /* + * Returns true if the given user belongs to the named group, + * else returns false. + */ +-bool ++int + usergr_matches(const char *group, const char *user, const struct passwd *pw) + { +- bool matched = false; + struct passwd *pw0 = NULL; ++ int ret = DENY; + debug_decl(usergr_matches, SUDOERS_DEBUG_MATCH); + + /* Make sure we have a valid usergroup, sudo style */ +@@ -547,7 +556,7 @@ usergr_matches(const char *group, const char *user, const struct passwd *pw) + /* Query group plugin for %:name groups. */ + if (*group == ':' && def_group_plugin) { + if (group_plugin_query(user, group + 1, pw) == true) +- matched = true; ++ ret = ALLOW; + goto done; + } + +@@ -562,14 +571,14 @@ usergr_matches(const char *group, const char *user, const struct passwd *pw) + } + + if (user_in_group(pw, group)) { +- matched = true; ++ ret = ALLOW; + goto done; + } + + /* Query the group plugin for Unix groups too? */ + if (def_group_plugin && def_always_query_group_plugin) { + if (group_plugin_query(user, group, pw) == true) { +- matched = true; ++ ret = ALLOW; + goto done; + } + } +@@ -579,8 +588,9 @@ done: + 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); ++ "user %s matches group %s: %s", user, group, ++ ret == ALLOW ? "ALLOW" : "DENY"); ++ debug_return_int(ret); + } + + #if defined(HAVE_GETDOMAINNAME) || defined(SI_SRPC_DOMAIN) +@@ -656,22 +666,22 @@ sudo_getdomainname(void) + #endif /* HAVE_GETDOMAINNAME || SI_SRPC_DOMAIN */ + + /* +- * Returns true if "host" and "user" belong to the netgroup "netgr", +- * else return false. Either of "lhost", "shost" or "user" may be NULL ++ * Returns ALLOW if "host" and "user" belong to the netgroup "netgr", ++ * else return DENY. Either of "lhost", "shost" or "user" may be NULL + * in which case that argument is not checked... + */ +-bool ++int + netgr_matches(const char *netgr, const char *lhost, const char *shost, const char *user) + { + #ifdef HAVE_INNETGR + const char *domain; + #endif +- bool rc = false; ++ int ret = DENY; + debug_decl(netgr_matches, SUDOERS_DEBUG_MATCH); + + if (!def_use_netgroups) { + sudo_debug_printf(SUDO_DEBUG_INFO, "netgroups are disabled"); +- debug_return_bool(false); ++ debug_return_int(DENY); + } + + #ifdef HAVE_INNETGR +@@ -679,22 +689,22 @@ netgr_matches(const char *netgr, const char *lhost, const char *shost, const cha + if (*netgr++ != '+') { + sudo_debug_printf(SUDO_DEBUG_DIAG, "netgroup %s has no leading '+'", + netgr); +- debug_return_bool(false); ++ debug_return_int(DENY); + } + + /* get the domain name (if any) */ + domain = sudo_getdomainname(); + + if (innetgr(netgr, lhost, user, domain)) +- rc = true; ++ ret = ALLOW; + else if (lhost != shost && innetgr(netgr, shost, user, domain)) +- rc = true; ++ ret = ALLOW; + + 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"); ++ ret == ALLOW ? "ALLOW" : "DENY"); + #endif /* HAVE_INNETGR */ + +- debug_return_bool(rc); ++ debug_return_int(ret); + } +diff --git a/plugins/sudoers/match_addr.c b/plugins/sudoers/match_addr.c +index 714c41c..3b701c9 100644 +--- a/plugins/sudoers/match_addr.c ++++ b/plugins/sudoers/match_addr.c +@@ -44,7 +44,7 @@ + #include "sudoers.h" + #include "interfaces.h" + +-static bool ++static int + addr_matches_if(const char *n) + { + union sudo_in_addr_un addr; +@@ -63,7 +63,7 @@ addr_matches_if(const char *n) + if (inet_pton(AF_INET, n, &addr.ip4) == 1) { + family = AF_INET; + } else { +- debug_return_bool(false); ++ debug_return_int(DENY); + } + + SLIST_FOREACH(ifp, get_interfaces(), entries) { +@@ -74,28 +74,28 @@ addr_matches_if(const char *n) + if (ifp->addr.ip4.s_addr == addr.ip4.s_addr || + (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr) + == addr.ip4.s_addr) +- debug_return_bool(true); ++ debug_return_int(ALLOW); + break; + #ifdef HAVE_STRUCT_IN6_ADDR + case AF_INET6: + if (memcmp(ifp->addr.ip6.s6_addr, addr.ip6.s6_addr, + sizeof(addr.ip6.s6_addr)) == 0) +- debug_return_bool(true); ++ debug_return_int(ALLOW); + for (j = 0; j < sizeof(addr.ip6.s6_addr); j++) { + if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr.ip6.s6_addr[j]) + break; + } + if (j == sizeof(addr.ip6.s6_addr)) +- debug_return_bool(true); ++ debug_return_int(ALLOW); + break; + #endif /* HAVE_STRUCT_IN6_ADDR */ + } + } + +- debug_return_bool(false); ++ debug_return_int(DENY); + } + +-static bool ++static int + addr_matches_if_netmask(const char *n, const char *m) + { + unsigned int i; +@@ -116,7 +116,7 @@ addr_matches_if_netmask(const char *n, const char *m) + if (inet_pton(AF_INET, n, &addr.ip4) == 1) { + family = AF_INET; + } else { +- debug_return_bool(false); ++ debug_return_int(DENY); + } + + if (family == AF_INET) { +@@ -124,14 +124,14 @@ addr_matches_if_netmask(const char *n, const char *m) + if (inet_pton(AF_INET, m, &mask.ip4) != 1) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "IPv4 netmask %s: %s", m, "invalid value"); +- debug_return_bool(false); ++ debug_return_int(DENY); + } + } else { + i = sudo_strtonum(m, 1, 32, &errstr); + if (errstr != NULL) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "IPv4 netmask %s: %s", m, errstr); +- debug_return_bool(false); ++ debug_return_int(DENY); + } + mask.ip4.s_addr = htonl(0xffffffffU << (32 - i)); + } +@@ -144,7 +144,7 @@ addr_matches_if_netmask(const char *n, const char *m) + if (errstr != NULL) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "IPv6 netmask %s: %s", m, errstr); +- debug_return_bool(false); ++ debug_return_int(DENY); + } + for (i = 0; i < sizeof(addr.ip6.s6_addr); i++) { + if (j < i * 8) +@@ -165,7 +165,7 @@ addr_matches_if_netmask(const char *n, const char *m) + switch (family) { + case AF_INET: + if ((ifp->addr.ip4.s_addr & mask.ip4.s_addr) == addr.ip4.s_addr) +- debug_return_bool(true); ++ debug_return_int(ALLOW); + break; + #ifdef HAVE_STRUCT_IN6_ADDR + case AF_INET6: +@@ -174,35 +174,36 @@ addr_matches_if_netmask(const char *n, const char *m) + break; + } + if (j == sizeof(addr.ip6.s6_addr)) +- debug_return_bool(true); ++ debug_return_int(ALLOW); + break; + #endif /* HAVE_STRUCT_IN6_ADDR */ + } + } + +- debug_return_bool(false); ++ debug_return_int(DENY); + } + + /* +- * Returns true if "n" is one of our ip addresses or if +- * "n" is a network that we are on, else returns false. ++ * Returns ALLOW if "n" is one of our ip addresses or if ++ * "n" is a network that we are on, else returns DENY. + */ +-bool ++int + addr_matches(char *n) + { + char *m; +- bool rc; ++ int ret; + debug_decl(addr_matches, SUDOERS_DEBUG_MATCH); + + /* If there's an explicit netmask, use it. */ + if ((m = strchr(n, '/'))) { + *m++ = '\0'; +- rc = addr_matches_if_netmask(n, m); ++ ret = addr_matches_if_netmask(n, m); + *(m - 1) = '/'; + } else +- rc = addr_matches_if(n); ++ ret = addr_matches_if(n); + + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, +- "IP address %s matches local host: %s", n, rc ? "true" : "false"); +- debug_return_bool(rc); ++ "IP address %s matches local host: %s", n, ++ ret == ALLOW ? "ALLOW" : "DENY"); ++ debug_return_int(ret); + } +diff --git a/plugins/sudoers/match_command.c b/plugins/sudoers/match_command.c +index ac942f4..d06634a 100644 +--- a/plugins/sudoers/match_command.c ++++ b/plugins/sudoers/match_command.c +@@ -55,7 +55,7 @@ + # define O_EXEC O_PATH + #endif + +-static bool ++static int + command_args_match(const char *sudoers_cmnd, const char *sudoers_args) + { + int flags = 0; +@@ -66,7 +66,7 @@ command_args_match(const char *sudoers_cmnd, const char *sudoers_args) + * If the empty string is specified in sudoers, no user args are allowed. + */ + if (!sudoers_args || (!user_args && !strcmp("\"\"", sudoers_args))) +- debug_return_bool(true); ++ debug_return_int(ALLOW); + + /* + * If args are specified in sudoers, they must match the user args. +@@ -75,9 +75,9 @@ command_args_match(const char *sudoers_cmnd, const char *sudoers_args) + if (strcmp(sudoers_cmnd, "sudoedit") == 0) + flags = FNM_PATHNAME; + if (fnmatch(sudoers_args, user_args ? user_args : "", flags) == 0) +- debug_return_bool(true); ++ debug_return_int(ALLOW); + +- debug_return_bool(false); ++ debug_return_int(DENY); + } + + /* +@@ -189,7 +189,7 @@ set_cmnd_fd(int fd) + /* + * Return true if user_cmnd names one of the inodes in dir, else false. + */ +-static bool ++static int + command_matches_dir(const char *sudoers_dir, size_t dlen, + const struct command_digest_list *digests) + { +@@ -205,11 +205,11 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, + */ + dirp = opendir(sudoers_dir); + if (dirp == NULL) +- debug_return_bool(false); ++ debug_return_int(DENY); + + if (strlcpy(buf, sudoers_dir, sizeof(buf)) >= sizeof(buf)) { + closedir(dirp); +- debug_return_bool(false); ++ debug_return_int(DENY); + } + while ((dent = readdir(dirp)) != NULL) { + if (fd != -1) { +@@ -250,14 +250,14 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, + + if (dent != NULL) { + set_cmnd_fd(fd); +- debug_return_bool(true); ++ debug_return_int(ALLOW); + } + if (fd != -1) + close(fd); +- debug_return_bool(false); ++ debug_return_int(DENY); + } + +-static bool ++static int + command_matches_all(const struct command_digest_list *digests) + { + struct stat sb; /* XXX - unused */ +@@ -273,21 +273,21 @@ command_matches_all(const struct command_digest_list *digests) + } + + /* Check digest of user_cmnd since we have no sudoers_cmnd for ALL. */ +- if (!digest_matches(fd, user_cmnd, digests)) ++ if (digest_matches(fd, user_cmnd, digests) != ALLOW) + goto bad; + set_cmnd_fd(fd); + + /* No need to set safe_cmnd for ALL. */ +- debug_return_bool(true); ++ debug_return_int(ALLOW); + bad: + if (fd != -1) { + close(fd); + fd = -1; + } +- debug_return_bool(false); ++ debug_return_int(DENY); + } + +-static bool ++static int + command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args, + const struct command_digest_list *digests) + { +@@ -303,31 +303,31 @@ command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args, + * else return false. + */ + if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0) +- debug_return_bool(false); +- if (command_args_match(sudoers_cmnd, sudoers_args)) { ++ debug_return_int(DENY); ++ if (command_args_match(sudoers_cmnd, sudoers_args) == ALLOW) { + /* Open the file for fdexec or for digest matching. */ + if (!open_cmnd(user_cmnd, digests, &fd)) + goto bad; + if (!do_stat(fd, user_cmnd, &sb)) + goto bad; + /* Check digest of user_cmnd since sudoers_cmnd is a pattern. */ +- if (!digest_matches(fd, user_cmnd, digests)) ++ if (digest_matches(fd, user_cmnd, digests) != ALLOW) + goto bad; + set_cmnd_fd(fd); + + /* No need to set safe_cmnd since user_cmnd matches sudoers_cmnd */ +- debug_return_bool(true); ++ debug_return_int(ALLOW); + bad: + if (fd != -1) { + close(fd); + fd = -1; + } +- debug_return_bool(false); ++ debug_return_int(DENY); + } +- debug_return_bool(false); ++ debug_return_int(DENY); + } + +-static bool ++static int + command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, + const struct command_digest_list *digests) + { +@@ -349,19 +349,19 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, + if ((base = strrchr(sudoers_cmnd, '/')) != NULL) { + base++; + if (!has_meta(base) && strcmp(user_base, base) != 0) +- debug_return_bool(false); ++ debug_return_int(DENY); + } + } + /* +- * Return true if we find a match in the glob(3) results AND ++ * Return ALLOW if we find a match in the glob(3) results AND + * 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 DENY. + */ + if (glob(sudoers_cmnd, GLOB_NOSORT, NULL, &gl) != 0 || gl.gl_pathc == 0) { + globfree(&gl); +- debug_return_bool(false); ++ debug_return_int(DENY); + } + /* If user_cmnd is fully-qualified, check for an exact match. */ + if (user_cmnd[0] == '/') { +@@ -381,7 +381,7 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, + (user_stat->st_dev == sudoers_stat.st_dev && + user_stat->st_ino == sudoers_stat.st_ino)) { + /* There could be multiple matches, check digest early. */ +- if (!digest_matches(fd, cp, digests)) { ++ if (digest_matches(fd, cp, digests) != ALLOW) { + bad_digest = true; + continue; + } +@@ -409,8 +409,9 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, + /* If it ends in '/' it is a directory spec. */ + dlen = strlen(cp); + if (cp[dlen - 1] == '/') { +- if (command_matches_dir(cp, dlen, digests)) +- debug_return_bool(true); ++ if (command_matches_dir(cp, dlen, ++ digests) == ALLOW) ++ debug_return_int(ALLOW); + continue; + } + +@@ -430,7 +431,7 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, + if (user_stat == NULL || + (user_stat->st_dev == sudoers_stat.st_dev && + user_stat->st_ino == sudoers_stat.st_ino)) { +- if (!digest_matches(fd, cp, digests)) ++ if (digest_matches(fd, cp, digests) != ALLOW) + continue; + free(safe_cmnd); + if ((safe_cmnd = strdup(cp)) == NULL) { +@@ -445,18 +446,18 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, + done: + globfree(&gl); + if (cp != NULL) { +- if (command_args_match(sudoers_cmnd, sudoers_args)) { ++ if (command_args_match(sudoers_cmnd, sudoers_args) == ALLOW) { + /* safe_cmnd was set above. */ + set_cmnd_fd(fd); +- debug_return_bool(true); ++ debug_return_int(ALLOW); + } + } + if (fd != -1) + close(fd); +- debug_return_bool(false); ++ debug_return_int(DENY); + } + +-static bool ++static int + command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests) + { + struct stat sudoers_stat; +@@ -468,7 +469,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const + /* If it ends in '/' it is a directory spec. */ + dlen = strlen(sudoers_cmnd); + if (sudoers_cmnd[dlen - 1] == '/') +- debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, digests)); ++ debug_return_int(command_matches_dir(sudoers_cmnd, dlen, digests)); + + /* Only proceed if user_base and basename(sudoers_cmnd) match */ + if ((base = strrchr(sudoers_cmnd, '/')) == NULL) +@@ -476,7 +477,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const + else + base++; + if (strcmp(user_base, base) != 0) +- debug_return_bool(false); ++ debug_return_int(DENY); + + /* Open the file for fdexec or for digest matching. */ + if (!open_cmnd(sudoers_cmnd, digests, &fd)) +@@ -498,9 +499,9 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const + if (strcmp(user_cmnd, sudoers_cmnd) != 0) + goto bad; + } +- if (!command_args_match(sudoers_cmnd, sudoers_args)) ++ if (command_args_match(sudoers_cmnd, sudoers_args) != ALLOW) + goto bad; +- if (!digest_matches(fd, sudoers_cmnd, digests)) { ++ if (digest_matches(fd, sudoers_cmnd, digests) != ALLOW) { + /* XXX - log functions not available but we should log very loudly */ + goto bad; + } +@@ -510,25 +511,25 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const + goto bad; + } + set_cmnd_fd(fd); +- debug_return_bool(true); ++ debug_return_int(ALLOW); + bad: + if (fd != -1) + close(fd); +- debug_return_bool(false); ++ debug_return_int(DENY); + } + + /* +- * If path doesn't end in /, return true iff cmnd & path name the same inode; +- * otherwise, return true if user_cmnd names one of the inodes in path. ++ * If path doesn't end in /, return ALLOW iff cmnd & path name the same inode; ++ * otherwise, return ALLOW if user_cmnd names one of the inodes in path. + */ +-bool ++int + command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests) + { +- bool rc = false; ++ int ret = DENY; + debug_decl(command_matches, SUDOERS_DEBUG_MATCH); + + if (sudoers_cmnd == NULL) { +- rc = command_matches_all(digests); ++ ret = command_matches_all(digests); + goto done; + } + +@@ -542,9 +543,9 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct + */ + if (strcmp(sudoers_cmnd, "sudoedit") == 0 && + strcmp(user_cmnd, "sudoedit") == 0 && +- command_args_match(sudoers_cmnd, sudoers_args)) { ++ command_args_match(sudoers_cmnd, sudoers_args) == ALLOW) { + /* No need to set safe_cmnd since user_cmnd matches sudoers_cmnd */ +- rc = true; ++ ret = ALLOW; + } + goto done; + } +@@ -555,17 +556,17 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct + * use glob(3) and/or fnmatch(3) to do the matching. + */ + if (def_fast_glob) +- rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, digests); ++ ret = command_matches_fnmatch(sudoers_cmnd, sudoers_args, digests); + else +- rc = command_matches_glob(sudoers_cmnd, sudoers_args, digests); ++ ret = command_matches_glob(sudoers_cmnd, sudoers_args, digests); + } else { +- rc = command_matches_normal(sudoers_cmnd, sudoers_args, digests); ++ ret = command_matches_normal(sudoers_cmnd, sudoers_args, digests); + } + 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); ++ ret == ALLOW ? "ALLOW" : "DENY"); ++ debug_return_int(ret); + } +diff --git a/plugins/sudoers/match_digest.c b/plugins/sudoers/match_digest.c +index b08ba58..aa47462 100644 +--- a/plugins/sudoers/match_digest.c ++++ b/plugins/sudoers/match_digest.c +@@ -37,7 +37,7 @@ + #include "sudo_digest.h" + #include + +-bool ++int + digest_matches(int fd, const char *file, const struct command_digest_list *digests) + { + unsigned int digest_type = SUDO_DIGEST_INVALID; +@@ -45,12 +45,12 @@ digest_matches(int fd, const char *file, const struct command_digest_list *diges + unsigned char *sudoers_digest = NULL; + struct command_digest *digest; + size_t digest_len = (size_t)-1; +- bool matched = false; ++ int matched = DENY; + debug_decl(digest_matches, SUDOERS_DEBUG_MATCH); + + if (TAILQ_EMPTY(digests)) { + /* No digest, no problem. */ +- debug_return_bool(true); ++ debug_return_int(ALLOW); + } + + if (fd == -1) { +@@ -100,7 +100,7 @@ digest_matches(int fd, const char *file, const struct command_digest_list *diges + } + } + if (memcmp(file_digest, sudoers_digest, digest_len) == 0) { +- matched = true; ++ matched = ALLOW; + break; + } + +@@ -118,5 +118,5 @@ bad_format: + done: + free(sudoers_digest); + free(file_digest); +- debug_return_bool(matched); ++ debug_return_int(matched); + } +diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h +index e1ba680..6734eb6 100644 +--- a/plugins/sudoers/parse.h ++++ b/plugins/sudoers/parse.h +@@ -318,22 +318,22 @@ void free_parse_tree(struct sudoers_parse_tree *parse_tree); + void reparent_parse_tree(struct sudoers_parse_tree *new_tree); + + /* match_addr.c */ +-bool addr_matches(char *n); ++int addr_matches(char *n); + + /* match_command.c */ +-bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests); ++int command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests); + + /* match_digest.c */ +-bool digest_matches(int fd, const char *file, const struct command_digest_list *digests); ++int digest_matches(int fd, const char *file, const struct command_digest_list *digests); + + /* match.c */ + struct group; + struct passwd; +-bool group_matches(const char *sudoers_group, const struct group *gr); +-bool hostname_matches(const char *shost, const char *lhost, const char *pattern); +-bool netgr_matches(const char *netgr, const char *lhost, const char *shost, const char *user); +-bool usergr_matches(const char *group, const char *user, const struct passwd *pw); +-bool userpw_matches(const char *sudoers_user, const char *user, const struct passwd *pw); ++int group_matches(const char *sudoers_group, const struct group *gr); ++int hostname_matches(const char *shost, const char *lhost, const char *pattern); ++int netgr_matches(const char *netgr, const char *lhost, const char *shost, const char *user); ++int usergr_matches(const char *group, const char *user, const struct passwd *pw); ++int userpw_matches(const char *sudoers_user, const char *user, const struct passwd *pw); + int cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m); + int cmndlist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *list); + int host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const char *host, const char *shost, const struct member *m); +diff --git a/plugins/sudoers/regress/parser/check_addr.c b/plugins/sudoers/regress/parser/check_addr.c +index fd0515f..135235d 100644 +--- a/plugins/sudoers/regress/parser/check_addr.c ++++ b/plugins/sudoers/regress/parser/check_addr.c +@@ -57,7 +57,7 @@ check_addr(char *input) + sudo_fatalx("expecting 0 or 1, got %s", cp); + input[len] = '\0'; + +- matched = addr_matches(input); ++ matched = addr_matches(input) == ALLOW; + if (matched != expected) { + sudo_warnx("%s %smatched: FAIL", input, matched ? "" : "not "); + return 1; +diff --git a/plugins/sudoers/sssd.c b/plugins/sudoers/sssd.c +index 6fe8337..e993099 100644 +--- a/plugins/sudoers/sssd.c ++++ b/plugins/sudoers/sssd.c +@@ -196,20 +196,20 @@ sudo_sss_check_user(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule) + case '+': + /* Netgroup spec found, check membership. */ + if (netgr_matches(val, def_netgroup_tuple ? host : NULL, +- def_netgroup_tuple ? shost : NULL, handle->pw->pw_name)) { ++ def_netgroup_tuple ? shost : NULL, handle->pw->pw_name) == ALLOW) { + ret = true; + } + break; + case '%': + /* User group found, check membership. */ +- if (usergr_matches(val, handle->pw->pw_name, handle->pw)) { ++ if (usergr_matches(val, handle->pw->pw_name, handle->pw) == ALLOW) { + ret = true; + } + break; + default: + /* Not a netgroup or user group. */ + if (strcmp(val, "ALL") == 0 || +- userpw_matches(val, handle->pw->pw_name, handle->pw)) { ++ userpw_matches(val, handle->pw->pw_name, handle->pw) == ALLOW) { + ret = true; + } + break; +-- +2.33.0 + diff --git a/sudo.spec b/sudo.spec index 7522828..0b86674 100644 --- a/sudo.spec +++ b/sudo.spec @@ -1,6 +1,6 @@ Name: sudo Version: 1.9.2 -Release: 14 +Release: 15 Summary: Allows restricted root access for specified users License: ISC URL: http://www.courtesan.com/sudo/ @@ -37,6 +37,9 @@ Patch23: backport-CVE-2023-22809.patch Patch24: backport-CVE-2023-28486_CVE-2023-28487.patch Patch25: Fix-compilation-error-on-sw64-arch.patch Patch26: backport-don-t-report-a-usage-error-for-sudo-V.patch +Patch27: backport-Do-not-rely-on-the-definition-of-ALLOW-DENY-being-tr.patch +Patch28: backport-CVE-2023-42465.patch +Patch29: backport-Make-all-match-functions-return-ALLOW-DENY-not-true-.patch Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Requires: pam @@ -177,6 +180,9 @@ install -p -c -m 0644 %{SOURCE3} $RPM_BUILD_ROOT/etc/pam.d/sudo-i %exclude %{_pkgdocdir}/ChangeLog %changelog +* Mon Jan 8 2024 wangqingsan - 1.9.2-15 +- fix CVE-2023-42465. + * Mon Nov 27 2023 zhangruifang - 1.9.2-14 - Don't report a usage error for "sudo -V". -- Gitee