sudo/sudo-1.9.15-CVE-2023-42465.patch
Radovan Sroka f8883a97a0 RHEL 8.9.0.Z ERRATUM
- Rebase to 1.9.5p2
- CVE-2023-28486 sudo: Sudo does not escape control characters in log messages
Resolves: RHEL-21825
- CVE-2023-28487 sudo: Sudo does not escape control characters in sudoreplay output
Resolves: RHEL-21831
- CVE-2023-42465 sudo: Targeted Corruption of Register and Stack Variables
Resolves: RHEL-21820

Signed-off-by: Radovan Sroka <rsroka@redhat.com>
2024-01-26 11:45:49 +01:00

599 lines
18 KiB
Diff

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 <config.h>
#include <sys/stat.h>
@@ -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 <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
#include <sys/stat.h>
#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.
*/