From 4b95db9a699a6697c43fb983eefe73e5d081849e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Thu, 15 Aug 2024 16:36:50 +0200 Subject: [PATCH] Sync from SUSE:ALP:Source:Standard:1.0 cockpit revision ceae2a1bfb3a5166c79a2adc4ce3fc44 --- ...r-watching-lastlog2-and-wutmp-on-ove.patch | 137 +++++++++++++++ CVE-2024-6126.patch | 156 ++++++++++++++++++ cockpit.changes | 10 ++ cockpit.spec | 6 +- 4 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 0001-users-Support-for-watching-lastlog2-and-wutmp-on-ove.patch create mode 100644 CVE-2024-6126.patch diff --git a/0001-users-Support-for-watching-lastlog2-and-wutmp-on-ove.patch b/0001-users-Support-for-watching-lastlog2-and-wutmp-on-ove.patch new file mode 100644 index 0000000..b39485a --- /dev/null +++ b/0001-users-Support-for-watching-lastlog2-and-wutmp-on-ove.patch @@ -0,0 +1,137 @@ +From b95268ea4002fe7bcc7839d0bc7587c4e76306c5 Mon Sep 17 00:00:00 2001 +From: Luna +Date: Wed, 5 Jun 2024 10:34:15 +0530 +Subject: [PATCH] users: Support for watching lastlog2 and wutmp on overview + page + +--- + pkg/users/account-details.js | 27 ++++++++++++++++++--------- + pkg/users/users.js | 22 ++++++++++++++-------- + pkg/users/utils.js | 14 ++++++++++++++ + 3 files changed, 46 insertions(+), 17 deletions(-) + +diff --git a/pkg/users/account-details.js b/pkg/users/account-details.js +index 9edd70c41..94710912a 100644 +--- a/pkg/users/account-details.js ++++ b/pkg/users/account-details.js +@@ -48,7 +48,8 @@ import { account_shell_dialog } from "./shell-dialog.js"; + import { set_password_dialog, reset_password_dialog } from "./password-dialogs.js"; + import { AccountLogs } from "./account-logs-panel.jsx"; + import { AuthorizedKeys } from "./authorized-keys-panel.js"; +-import { get_locked } from "./utils.js"; ++import { get_locked, getUtmpPath } from "./utils.js"; ++import { useInit } from 'hooks.js'; + + const _ = cockpit.gettext; + +@@ -98,16 +99,24 @@ function get_expire(name) { + + export function AccountDetails({ accounts, groups, current_user, user, shells }) { + const [expiration, setExpiration] = useState(null); +- useEffect(() => { +- get_expire(user).then(setExpiration); ++ const [utmppath, setUtmpPath] = useState(null); ++ ++ useInit(async () => { ++ setUtmpPath(await getUtmpPath()); ++ }); + +- // Watch `/var/run/utmp` to register when user logs in or out +- const handle = cockpit.file("/var/run/utmp", { superuser: "try", binary: true }); +- handle.watch(() => { ++ useEffect(() => { ++ if (utmppath !== null) { + get_expire(user).then(setExpiration); +- }); +- return handle.close; +- }, [user, accounts]); ++ ++ // Watch `/var/run/utmp` to register when user logs in or out ++ const handle = cockpit.file("/var/run/utmp", { superuser: "try", binary: true }); ++ handle.watch(() => { ++ get_expire(user).then(setExpiration); ++ }); ++ return handle.close; ++ } ++ }, [user, accounts, utmppath]); + + const [edited_real_name, set_edited_real_name] = useState(null); + const [committing_real_name, set_committing_real_name] = useState(false); +diff --git a/pkg/users/users.js b/pkg/users/users.js +index 8f35be519..ecafc3472 100755 +--- a/pkg/users/users.js ++++ b/pkg/users/users.js +@@ -29,7 +29,7 @@ import { usePageLocation, useLoggedInUser, useFile, useInit } from "hooks.js"; + import { etc_passwd_syntax, etc_group_syntax, etc_shells_syntax } from "pam_user_parser.js"; + import { EmptyStatePanel } from "cockpit-components-empty-state.jsx"; + +-import { get_locked } from "./utils.js"; ++import { get_locked, getUtmpPath } from "./utils.js"; + import { AccountsMain } from "./accounts-list.js"; + import { AccountDetails } from "./account-details.js"; + +@@ -86,13 +86,17 @@ function AccountsPage() { + }, [logindef]); + + const [details, setDetails] = useState(null); +- useInit(() => { ++ useInit(async () => { ++ const utmppath = await getUtmpPath(); ++ + getLogins().then(setDetails); + + // Watch `/var/run/utmp` to register when user logs in or out +- const handleUtmp = cockpit.file("/var/run/utmp", { superuser: "try", binary: true }); +- handleUtmp.watch(() => getLogins().then(setDetails), { read: false }); +- ++ let handleUtmp; ++ if (utmppath !== null) { ++ handleUtmp = cockpit.file("/var/run/utmp", { superuser: "try", binary: true }); ++ handleUtmp.watch(() => getLogins().then(setDetails), { read: false }); ++ } + // Watch /etc/shadow to register lock/unlock/expire changes; but avoid reading it, it's sensitive data + const handleShadow = cockpit.file("/etc/shadow", { superuser: "try" }); + handleShadow.watch(() => getLogins().then(setDetails), { read: false }); +@@ -151,12 +155,14 @@ function AccountsPage() { + } + + async function getLogins() { +- let lastlog = ""; ++ let LastLogPath; + try { +- lastlog = await cockpit.spawn(["lastlog"], { environ: ["LC_ALL=C"] }); ++ await cockpit.spawn(["test", "-e", "/var/lib/lastlog/lastlog2.db"], { err: "ignore" }); ++ LastLogPath = "lastlog2"; + } catch (err) { +- console.warn("Unexpected error when getting last login information", err); ++ LastLogPath = "lastlog"; + } ++ const lastlog = await cockpit.spawn([LastLogPath], { environ: ["LC_ALL=C"] }); + + let currentLogins = []; + try { +diff --git a/pkg/users/utils.js b/pkg/users/utils.js +index 7b2efed05..3cbe295a5 100644 +--- a/pkg/users/utils.js ++++ b/pkg/users/utils.js +@@ -8,3 +8,17 @@ export const get_locked = name => + return status == "LK" || status == "L"; + }) + .catch(() => null); ++ ++export async function getUtmpPath() { ++ try { ++ await cockpit.spawn(["test", "-e", "/var/run/utmp"], { err: "ignore" }); ++ return "/var/run/utmp"; ++ } catch (err1) { ++ try { ++ await cockpit.spawn(["test", "-e", "/var/lib/wtmpdb/wtmp.db"], { err: "ignore" }); ++ return "/var/lib/wtmpdb/wtmp.db"; ++ } catch (err2) { ++ return null; ++ } ++ } ++} +-- +2.45.1 + diff --git a/CVE-2024-6126.patch b/CVE-2024-6126.patch new file mode 100644 index 0000000..566f37a --- /dev/null +++ b/CVE-2024-6126.patch @@ -0,0 +1,156 @@ +From 2274359df6feffc990831c7d7a32a56d9244d38a Mon Sep 17 00:00:00 2001 +From: Martin Pitt +Date: Mon, 10 Jun 2024 10:49:56 +0200 +Subject: [PATCH] pam-ssh-add: Fix insecure killing of session ssh-agent + [CVE-2024-6126] + +Some distributions like Debian 12, or possibly some administrators +enable pam_env's deprecated `user_readenv` option [1]. The user session +can change the `$SSH_AGENT_PID`, so that it can pass an arbitrary pid to +`pam_sm_close_session()`. This is a local authenticated DoS. + +Avoid this by storing the agent pid in a global variable. The +cockpit-session process stays around for the entire session time, so we +don't need to put the pid into the PAM data. + +It can also happen that the user session's ssh-agent gets killed, and +some other process later on recycles the PID. Temporarily drop +privileges to the target user so that we at least don't kill anyone +else's process. + +Add an integration test which checks that changing the env variable +works, pointing it to a different process doesn't kill that, and +ssh-agent (the original pid) is still cleaned up correctly. However, as +pam_so.env in Fedora crashes hard, skip the test there. + +Many thanks to Paolo Perego for discovering, +and Luna Dragon for reporting this issue! + +[1] https://man7.org/linux/man-pages/man8/pam_env.8.html + +CVE-2024-6126 +https://bugzilla.redhat.com/show_bug.cgi?id=2290859 +--- + src/pam-ssh-add/pam-ssh-add.c | 46 ++++++++++++++++++++++++++++------- + test/verify/check-session | 30 +++++++++++++++++++++++ + 2 files changed, 67 insertions(+), 9 deletions(-) + +diff --git a/src/pam-ssh-add/pam-ssh-add.c b/src/pam-ssh-add/pam-ssh-add.c +index a9159d710..839b797d2 100644 +--- a/src/pam-ssh-add/pam-ssh-add.c ++++ b/src/pam-ssh-add/pam-ssh-add.c +@@ -54,6 +54,9 @@ const char *pam_ssh_agent_arg = NULL; + const char *pam_ssh_add_program = PATH_SSH_ADD; + const char *pam_ssh_add_arg = NULL; + ++static unsigned long ssh_agent_pid; ++static uid_t ssh_agent_uid; ++ + /* Environment */ + #define ENVIRON_SIZE 5 + #define PATH "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +@@ -866,6 +869,25 @@ start_agent (pam_handle_t *pamh, + error ("couldn't set agent environment: %s", + pam_strerror (pamh, res)); + } ++ ++ /* parse and store the agent pid for later cleanup */ ++ if (strncmp (auth_pid, "SSH_AGENT_PID=", 14) == 0) ++ { ++ unsigned long pid = strtoul (auth_pid + 14, NULL, 10); ++ if (pid > 0 && pid != ULONG_MAX) ++ { ++ ssh_agent_pid = pid; ++ ssh_agent_uid = auth_pwd->pw_uid; ++ } ++ else ++ { ++ error ("invalid SSH_AGENT_PID value: %s", auth_pid); ++ } ++ } ++ else ++ { ++ error ("unexpected agent pid format: %s", auth_pid); ++ } + } + + free (auth_socket); +@@ -952,19 +974,25 @@ pam_sm_close_session (pam_handle_t *pamh, + int argc, + const char *argv[]) + { +- const char *s_pid; +- int pid = 0; + parse_args (argc, argv); + + /* Kill the ssh agent we started */ +- s_pid = pam_getenv (pamh, "SSH_AGENT_PID"); +- if (s_pid) +- pid = atoi (s_pid); +- +- if (pid > 0) ++ if (ssh_agent_pid > 0) + { +- debug ("Closing %d", pid); +- kill (pid, SIGTERM); ++ debug ("Closing %lu", ssh_agent_pid); ++ /* kill as user to guard against crashing ssh-agent and PID reuse */ ++ if (setresuid (ssh_agent_uid, ssh_agent_uid, -1) < 0) ++ { ++ error ("could not drop privileges for killing ssh agent: %m"); ++ return PAM_SESSION_ERR; ++ } ++ if (kill (ssh_agent_pid, SIGTERM) < 0 && errno != ESRCH) ++ message ("could not kill ssh agent %lu: %m", ssh_agent_pid); ++ if (setresuid (0, 0, -1) < 0) ++ { ++ error ("could not restore privileges after killing ssh agent: %m"); ++ return PAM_SESSION_ERR; ++ } + } + return PAM_SUCCESS; + } +diff --git a/test/verify/check-session b/test/verify/check-session +index f771b5f69..939d29428 100755 +--- a/test/verify/check-session ++++ b/test/verify/check-session +@@ -76,6 +76,36 @@ class TestSession(testlib.MachineCase): + b.logout() + wait_session(should_exist=False) + ++ # try to pwn $SSH_AGENT_PID via pam_env's user_readenv=1 ++ ++ if m.image in ["fedora-39", "fedora-40"]: ++ # pam_env user_readenv crashes in Fedora, skip the test ++ # https://bugzilla.redhat.com/show_bug.cgi?id=2293045 ++ return ++ ++ # this is enabled by default in tools/cockpit.debian.pam, as well as ++ # Debian/Ubuntu's /etc/pam.d/sshd; but not in Fedora/RHEL ++ if "debian" not in m.image and "ubuntu" not in m.image: ++ self.write_file("/etc/pam.d/cockpit", "session required pam_env.so user_readenv=1\n", append=True) ++ victim_pid = m.spawn("sleep infinity", "sleep.log") ++ self.addCleanup(m.execute, f"kill {victim_pid} || true") ++ self.write_file("/home/admin/.pam_environment", f"SSH_AGENT_PID={victim_pid}\n", owner="admin") ++ ++ b.login_and_go() ++ wait_session(should_exist=True) ++ # starts ssh-agent in session ++ m.execute("pgrep -u admin ssh-agent") ++ # but the session has the modified SSH_AGENT_PID ++ bridge = m.execute("pgrep -u admin cockpit-bridge").strip() ++ agent = m.execute(f"grep --null-data SSH_AGENT_PID /proc/{bridge}/environ | xargs -0 | sed 's/.*=//'").strip() ++ self.assertEqual(agent, str(victim_pid)) ++ ++ # logging out still kills the actual ssh-agent, not the victim pid ++ b.logout() ++ wait_session(should_exist=False) ++ m.execute("while pgrep -u admin ssh-agent; do sleep 1; done", timeout=10) ++ m.execute(f"test -e /proc/{victim_pid}") ++ + + if __name__ == '__main__': + testlib.test_main() +-- +2.45.2 + diff --git a/cockpit.changes b/cockpit.changes index 3f76895..a2971f8 100644 --- a/cockpit.changes +++ b/cockpit.changes @@ -1,3 +1,13 @@ +------------------------------------------------------------------- +Wed Jul 3 06:04:40 UTC 2024 - Luna D Dragon + +- add CVE-2024-6126.patch to resolve CVE-2024-6126 + +------------------------------------------------------------------- +Wed Jun 5 05:11:19 UTC 2024 - Luna D Dragon + +- add 0001-users-Support-for-watching-lastlog2-and-wutmp-on-ove.patch to fix bsc#1220551 + ------------------------------------------------------------------- Wed Mar 13 11:26:28 UTC 2024 - Miika Alikirri diff --git a/cockpit.spec b/cockpit.spec index 6546b0a..2cba874 100644 --- a/cockpit.spec +++ b/cockpit.spec @@ -66,11 +66,13 @@ Patch2: suse_docs.patch Patch3: suse-microos-branding.patch Patch4: css-overrides.patch Patch5: storage-btrfs.patch +Patch6: 0001-users-Support-for-watching-lastlog2-and-wutmp-on-ove.patch +Patch7: CVE-2024-6126.patch # SLE Micro specific patches Patch101: hide-pcp.patch Patch102: 0002-selinux-temporary-remove-setroubleshoot-section.patch # For anything based on SLES 15 codebase (including Leap, SLE Micro) -Patch103: 0004-leap-gnu18-removal.patch +#Patch103: 0004-leap-gnu18-removal.patch Patch104: selinux_libdir.patch %if 0%{?fedora} >= 38 || 0%{?rhel} >= 9 @@ -247,6 +249,8 @@ BuildRequires: python3-tox-current-env %patch3 -p1 %patch4 -p1 %patch5 -p1 +%patch6 -p1 +%patch7 -p1 # SLE Micro specific patches %if 0%{?is_smo}