From 7d31e17fc5a8fe3055568c1a0f541dea064f30a0 Mon Sep 17 00:00:00 2001 From: monsieuremre <130907164+monsieuremre@users.noreply.github.com> Date: Fri, 17 Nov 2023 17:02:41 +0000 Subject: [PATCH 01/57] usbguard --- debian/control | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/control b/debian/control index 9a57d45..4c40b96 100644 --- a/debian/control +++ b/debian/control @@ -30,6 +30,7 @@ Depends: adduser, python3, secure-delete, sudo, + usbguard, ${misc:Depends} Replaces: anon-gpg-tweaks, swappiness-lowest, tcp-timestamps-disable Description: Enhances Miscellaneous Security Settings From 7c8b9b294678056d684fd3dc22f012d75da40426 Mon Sep 17 00:00:00 2001 From: monsieuremre <130907164+monsieuremre@users.noreply.github.com> Date: Fri, 17 Nov 2023 17:14:01 +0000 Subject: [PATCH 02/57] 30_security-misc.conf --- etc/usbguard/rules.d/30_security-misc.conf | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 etc/usbguard/rules.d/30_security-misc.conf diff --git a/etc/usbguard/rules.d/30_security-misc.conf b/etc/usbguard/rules.d/30_security-misc.conf new file mode 100644 index 0000000..a93e29e --- /dev/null +++ b/etc/usbguard/rules.d/30_security-misc.conf @@ -0,0 +1,18 @@ +## Blacklisting is not the optimal approach to security. Normally all USB devices should be rejected (default) and only the devices you personally know and trust be whitelisted. +## If you can do this, it is recommended to do. For convenience for the majority, we do the following: + +## Allow all USB devices with mass storage interface +allow with-interface equals { 08:*:* } + +## Reject storage devices that also have extra suspicuous interfaces. +## Like a usb storage device that also tries to behave like a keyboard. +## This is a well known type of cyber attack. + +reject with-interface all-of { 08:*:* 03:00:* } +reject with-interface all-of { 08:*:* 03:01:* } +reject with-interface all-of { 08:*:* e0:*:* } +reject with-interface all-of { 08:*:* 0a:*:* } +reject with-interface all-of { 08:*:* 02:*:* } + +## We do not allow anything else. Keyboards, mice, and everything else, they will be rejected. The only exception is, if they were plugged in when the daemon starts. +## If you have your keyboard plugged in before booting, it will be allowed. If you plug after the fact, you have to manually allow the device or do a restart. From 4cae74d610ad37066e8a334019cfa5c82f088a2e Mon Sep 17 00:00:00 2001 From: monsieuremre Date: Sun, 21 Jul 2024 11:19:32 +0200 Subject: [PATCH 03/57] Update 30_security-misc.conf --- etc/usbguard/rules.d/30_security-misc.conf | 58 ++++++++++++++++------ 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/etc/usbguard/rules.d/30_security-misc.conf b/etc/usbguard/rules.d/30_security-misc.conf index a93e29e..fd2bce9 100644 --- a/etc/usbguard/rules.d/30_security-misc.conf +++ b/etc/usbguard/rules.d/30_security-misc.conf @@ -1,18 +1,46 @@ -## Blacklisting is not the optimal approach to security. Normally all USB devices should be rejected (default) and only the devices you personally know and trust be whitelisted. -## If you can do this, it is recommended to do. For convenience for the majority, we do the following: +# We allow those that were plugged in before the daemon starts. Everything is blocked as the default. Following rules apply on top of this. -## Allow all USB devices with mass storage interface +# Explicitly reject any interface that is not documented and/or defined by USB.org +# Note: Most probably superfluous +reject with-interface none-of { 01:*:* 02:*:* 03:*:* 04:*:* 05:*:* 06:*:* 07:*:* 08:*:* 09:*:* 0a:*:* 0b:*:* 0d:*:* 0e:*:* 0f:*:* 10:*:* 11:*:* 12:*:* 13:*:* 14:*:* 3c:*:* dc:*:* e0:*:* ef:*:* fe:*:* ff:*:*} + +### Allow all mouses and keyboards, in a sense, so the user can conveniently change them without restrating the daemon. +### Take extra measures to ensure security + +# Allow only one keyboard to be connected +allow with-interface one-of { 03:00:01 03:01:01 } if !allowed-matches(with-interface one-of { 03:00:01 03:01:01 }) + +# Allow only one mouse to be connected +allow with-interface one-of { 03:00:02 03:01:02 } if !allowed-matches(with-interface one-of { 03:00:02 03:01:02 }) + +# Explicitly reject any device with a mouse/keyboard interface in combination with some other interface +# Mouses and keyboards should only have one interface for all legitimate use cases +reject with-interface all-of { 03:*:* 02:*:* } +reject with-interface all-of { 03:*:* 04:*:* } +reject with-interface all-of { 03:*:* 05:*:* } +reject with-interface all-of { 03:*:* 06:*:* } +reject with-interface all-of { 03:*:* 07:*:* } +reject with-interface all-of { 03:*:* 08:*:* } +reject with-interface all-of { 03:*:* 09:*:* } +reject with-interface all-of { 03:*:* 0a:*:* } +reject with-interface all-of { 03:*:* 0b:*:* } +reject with-interface all-of { 03:*:* 0d:*:* } +reject with-interface all-of { 03:*:* 0e:*:* } +reject with-interface all-of { 03:*:* 0f:*:* } +reject with-interface all-of { 03:*:* 10:*:* } +reject with-interface all-of { 03:*:* 11:*:* } +reject with-interface all-of { 03:*:* 12:*:* } +reject with-interface all-of { 03:*:* 13:*:* } +reject with-interface all-of { 03:*:* 14:*:* } +reject with-interface all-of { 03:*:* 3c:*:* } +reject with-interface all-of { 03:*:* dc:*:* } +reject with-interface all-of { 03:*:* e0:*:* } +reject with-interface all-of { 03:*:* ef:*:* } +reject with-interface all-of { 03:*:* fe:*:* } +reject with-interface all-of { 03:*:* ff:*:* } + +# Allow USB mass storage +# If and only if the USB device only has the mass storage interface and nothing extra +# Suspicious interface combinations with mass storage are blocked allow with-interface equals { 08:*:* } -## Reject storage devices that also have extra suspicuous interfaces. -## Like a usb storage device that also tries to behave like a keyboard. -## This is a well known type of cyber attack. - -reject with-interface all-of { 08:*:* 03:00:* } -reject with-interface all-of { 08:*:* 03:01:* } -reject with-interface all-of { 08:*:* e0:*:* } -reject with-interface all-of { 08:*:* 0a:*:* } -reject with-interface all-of { 08:*:* 02:*:* } - -## We do not allow anything else. Keyboards, mice, and everything else, they will be rejected. The only exception is, if they were plugged in when the daemon starts. -## If you have your keyboard plugged in before booting, it will be allowed. If you plug after the fact, you have to manually allow the device or do a restart. From f3d46ee56233c4ef0552c20304413d137e90acfe Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Fri, 9 May 2025 18:46:41 -0500 Subject: [PATCH 04/57] Add emergency shutdown feature, triggered by root device removal --- debian/control | 2 + ...rce-poweroff-on-boot-media-removal.service | 14 + .../force-poweroff-on-boot-media-removal | 24 ++ .../security-misc/security-misc-memlockd.cfg | 2 + .../force-shutdown-when-device-removed.c | 295 ++++++++++++++++++ 5 files changed, 337 insertions(+) create mode 100644 usr/lib/systemd/system/force-poweroff-on-boot-media-removal.service create mode 100755 usr/libexec/security-misc/force-poweroff-on-boot-media-removal create mode 100644 usr/share/security-misc/security-misc-memlockd.cfg create mode 100644 usr/src/security-misc/force-shutdown-when-device-removed.c diff --git a/debian/control b/debian/control index fd56b5f..6235dad 100644 --- a/debian/control +++ b/debian/control @@ -20,6 +20,7 @@ Package: security-misc Architecture: all Depends: adduser, apparmor-profile-dist, + build-essential, dmsetup, helper-scripts, libcap2-bin, @@ -27,6 +28,7 @@ Depends: adduser, libpam-modules-bin, libpam-runtime, libpam-umask, + memlockd, python3, secure-delete, sudo, diff --git a/usr/lib/systemd/system/force-poweroff-on-boot-media-removal.service b/usr/lib/systemd/system/force-poweroff-on-boot-media-removal.service new file mode 100644 index 0000000..b99cf64 --- /dev/null +++ b/usr/lib/systemd/system/force-poweroff-on-boot-media-removal.service @@ -0,0 +1,14 @@ +## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC +## See the file COPYING for copying conditions. + +[Unit] +Description=Emergency shutdown when boot media is removed +Documentation=https://github.com/Kicksecure/security-misc + +[Service] +Type=oneshot +ExecStart=/usr/libexec/security-misc/force-poweroff-on-boot-media-removal +RemainAfterExit=true + +[Install] +WantedBy=multi-user.target diff --git a/usr/libexec/security-misc/force-poweroff-on-boot-media-removal b/usr/libexec/security-misc/force-poweroff-on-boot-media-removal new file mode 100755 index 0000000..0760ae1 --- /dev/null +++ b/usr/libexec/security-misc/force-poweroff-on-boot-media-removal @@ -0,0 +1,24 @@ +#!/bin/bash + +# Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC +# See the file COPYING for copying conditions. + +gcc \ + -o \ + /run/force-shutdown-when-device-removed \ + -static \ + /usr/src/security-misc/force-shutdown-when-device-removed.c \ + || { + printf "%s\n" 'Could not compile force-shutdown executable!' + exit 1; + } + +readarray -t root_devices < <(/usr/libexec/helper-scripts/get-backing-devices-for-mountpoint '/'); + +## memlockd daemonizes itself, so no need to background it +memlockd -c /usr/share/security-misc/security-misc-memlockd.cfg + +/run/force-shutdown-when-device-removed "${root_devices}" & +sleep 1 +disown +exit 0 diff --git a/usr/share/security-misc/security-misc-memlockd.cfg b/usr/share/security-misc/security-misc-memlockd.cfg new file mode 100644 index 0000000..ebdc4c6 --- /dev/null +++ b/usr/share/security-misc/security-misc-memlockd.cfg @@ -0,0 +1,2 @@ +# Lock systemd and all of its library dependencies into memory ++/usr/bin/systemd diff --git a/usr/src/security-misc/force-shutdown-when-device-removed.c b/usr/src/security-misc/force-shutdown-when-device-removed.c new file mode 100644 index 0000000..c7ddd52 --- /dev/null +++ b/usr/src/security-misc/force-shutdown-when-device-removed.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC + * See the file COPYING for copying conditions. + */ + +/* + * This program is designed specifically to immediately and forcibly power off + * the system in the event the device providing the root filesystem is + * abruptly removed from the system. The idea is that a user can shut down + * a portable installation of Kicksecure by simply yanking the USB drive + * containing the installation from the computer. Tails provides essentially + * the same feature, however it is known for occasionally failing to do its + * job properly. + * + * The fact that we're triggering a shutdown when the device containing the + * root filesystem vanishes presents a number of significant challenges: + * + * - The device providing the entire operating system is gone. The only things + * we will still have left are the kernel, files loaded into RAM (for + * instance under /run), and anything that happens to still be in the + * system's disk cache. + * - Virtually any process on the system may abruptly crash at any time. This + * isn't just because applications may be unable to access files. The Linux + * kernel's virtual memory subsystem doesn't just page out RAM contents to a + * swap file, it will sometimes simply erase pages containing executable + * code from memory if it can reload that code from disk later when needed. + * If part of a program isn't present in memory, and then the root device + * vanishes, any attempt to use code in the absent part of the application + * will result in the application crashing. (Attempts to access data in RAM + * that happened to be paged out will result in a similar crash.) + * - We have no control over what is and isn't in the disk cache, which makes + * it unsafe to launch any dynamically linked executable. What happens if we + * need to load a missing part of libc? What if the dynamic linker itself + * needs loaded from disk? + * - Systemd could lock up at any time, since the init process isn't immune to + * having bits of it erased from RAM to free up memory. If systemd receives + * a SIGSEGV, rather than crashing (which would panic the kernel), it goes + * into an "emergency mode" that tries to keep the system as operational as + * possible even though PID 1 is now out of service. + * + * Circumventing this set of difficulties is not easy, and it might not even + * be entirely possible. To give our feature the highest chance of success: + * + * - We use memlockd to lock systemd and all libraries it depends on into + * memory. It can holds its own pretty well in the event of a segfault, but + * if its crash handler ends up re-segfaulting, that could get ugly. + * - We compile the utility at boot time, statically link it against all of + * its dependencies (really only one, glibc), and load it into /run. This + * allows for decent architecture independence while removing any dependency + * on anything that isn't in RAM, thus (hopefully!) making the process + * crash-immune. + * - Because we're static-linking against glibc, we cannot call anything + * defined in stdio.h. This is because glibc uses dlopen() to load iconv + * modules, which are used internally by glibc for locale support. Things + * defined in stdio.h may use iconv, so calling anything there will + * basically make our static-linked executable become dynamically linked, + * which could segfault it since the root filesystem is gone. We can't call + * anything that could touch Name Service Switch (NSS) either, but we have + * no need to do so, so we should be safe there. See + * https://stackoverflow.com/questions/57476533/why-is-statically-linking-glibc-discouraged + * - We can't use udev either because libudev is only available as a dynamic + * library. That means we have to listen to kernel uevents directly to + * determine when the root device vanishes. Thankfully this isn't as much of + * a pain as it might sound like. + * - We don't call out to any external process, since those external processes + * could segfault. + * + * This is likely superior to Tails' implementation, which uses udev (and thus + * dynamic linking), uses an interpreter-driven script to shut down the system + * when the root device vanishes, and calls out to external executables to + * actually shut the system down. These issues are likely why Tails' + * implementation of emergency shutdown occasionally fails. See + * https://www.reddit.com/r/tails/comments/xh8njn/tails_wont_shutdown_when_i_pull_usb_stick/ + * (there are other similar posts as well). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define fd_stdin 0 +#define fd_stdout 1 +#define fd_stderr 2 + +void print(int fd, char *str) { + size_t len = strlen(str) + 1; + while (true) { + ssize_t write_len = write(fd, str, len); + len -= write_len; + if (len == 0) { + return; + } + str += write_len; + } +} + +void print_usage() { + print(fd_stderr, "Usage:\n"); + print(fd_stderr, " force-shutdown-when-device-removed DEVICE1 [DEVICE2...]\n"); + print(fd_stderr, "Example:\n"); + print(fd_stderr, " force-shutdown-when-device-removed /dev/sda3\n"); +} + +int main(int argc, char **argv) { + if (getuid() != 0) { + print(fd_stderr, "This program must be run as root!\n"); + exit(1); + } + + if (argc < 2) { + print(fd_stderr, "Invalid number of arguments!\n"); + print_usage(); + exit(1); + } + + size_t target_dev_name_list_len = argc - 1; + char **target_dev_name_list = calloc(target_dev_name_list_len, + sizeof(char *)); + if (target_dev_name_list == NULL) { + print(fd_stderr, "Out of memory during early setup!\n"); + exit(1); + } + + for (int i = 1; i < argc; i++) { + char *target_dev_path = argv[i]; + if (access(target_dev_path, F_OK) != 0) { + print(fd_stderr, "One of the specified devices does not exist!\n"); + print_usage(); + exit(1); + } + + if (strncmp(target_dev_path, "/dev/sr", strlen("/dev/sr")) != 0 + && strncmp(target_dev_path, "/dev/nvme", strlen("/dev/nvme")) != 0 + && strncmp(target_dev_path, "/dev/sd", strlen("/dev/sd")) != 0 + && strncmp(target_dev_path, "/dev/mmc", strlen("/dev/mmc")) != 0 + && strncmp(target_dev_path, "/dev/vd", strlen("/dev/vd")) != 0 + && strncmp(target_dev_path, "/dev/xvd", strlen("/dev/xvd")) != 0 + && strncmp(target_dev_path, "/dev/hd", strlen("/dev/hd")) != 0) { + print(fd_stderr, "One of the specified devices is not supported!\n"); + print_usage(); + exit(1); + } + + size_t device_path_slash_count = 0; + for (size_t j = 0; j < strlen(target_dev_path); j++) { + if (target_dev_path[j] == '/') { + device_path_slash_count++; + } + } + if (device_path_slash_count != 2) { + print(fd_stderr, "One of the specified devices is not supported!\n"); + print_usage(); + exit(1); + } + + char *target_dev_parse = calloc(1, strlen(target_dev_path) + 1); + if (target_dev_parse == NULL) { + print(fd_stderr, "Out of memory during early setup!\n"); + exit(1); + } + memcpy(target_dev_parse, target_dev_path, strlen(target_dev_path) + 1); + + /* returns "dev" */ + char *target_dev_name = strtok(target_dev_parse, "/"); + /* returns the actual device name we want */ + target_dev_name = strtok(NULL, "/"); + if (target_dev_name == NULL) { + print(fd_stderr, "One of the specified devices is not supported!\n"); + print_usage(); + exit(1); + } + + target_dev_name_list[i - 1] = calloc(1, strlen(target_dev_name) + 1); + memcpy(target_dev_name_list[i - 1], target_dev_name, + strlen(target_dev_name) + 1); + free(target_dev_parse); + } + + struct sockaddr_nl sa = { + .nl_family = AF_NETLINK, + .nl_pad = 0, + .nl_pid = getpid(), + .nl_groups = NETLINK_KOBJECT_UEVENT, + }; + int ns = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if (ns < 0) { + print(fd_stderr, "Failed to create netlink socket!\n"); + exit(1); + } + int ret = bind(ns, (struct sockaddr *) &sa, sizeof(sa)); + if (ret < 0) { + print(fd_stderr, "Failed to bind netlink socket!\n"); + exit(1); + } + + while (true) { + /* + * So, you looked at `man 7 netlink`, then looked at this code, and can't + * figure out how on earth any of this makes sense? Well guess what, turns + * out NETLINK_KOBJECT_UEVENT messages break all of the rules about how + * netlink messages work specified in that manpage. What you actually + * get... well, depends. + * + * - The messages we actually want are just NUL-separated string lists. + * These are the actual kernel uevents. + * - Mixed in with those will be uevents generated by systemd-udevd, which + * use a different format and are unsuitable for our purposes. We have + * to ignore those. Thankfully those messages start with the + * NUL-terminated string "libudev" so they're easy to filter out. + */ + + int len; + char buf[16384]; + struct iovec iov = { buf, sizeof(buf) }; + struct sockaddr_nl sa2; + struct msghdr msg = { &sa2, sizeof(sa2), &iov, 1, NULL, 0, 0 }; + len = recvmsg(ns, &msg, 0); + if (len == -1) { + reboot(RB_POWER_OFF); + //print(fd_stderr, "SHUTDOWN!!!\n"); + exit(0); + } + + if (len < 8) { + /* There aren't any super-short messages we're interested in, discard + * them */ + continue; + } + if (memcmp(buf, "libudev", 8) == 0) { + /* udevd message, ignore */ + continue; + } + + char *tmpbuf = buf; + bool device_removed = false; + while (len > 0) { + if (strcmp(tmpbuf, "ACTION=remove") == 0) { + device_removed = true; + goto next_str; + } + + if (strncmp(tmpbuf, "DEVNAME=", strlen("DEVNAME=")) == 0) { + if (device_removed) { + char *rem_devname_line; + /* + * Try to allocate the memory needed to check DEVNAME in a loop. We + * really do not want to simply abort here due to an out of memory + * condition, because that would result in the shutdown never + * occurring. We also don't want to force a shutdown when memory + * runs out, as that could result in the user losing work because + * they opened too many browser tabs. + */ + while(true) { + rem_devname_line = calloc(1, strlen(tmpbuf) + 1); + if (rem_devname_line == NULL) { + print(fd_stderr, "Out of memory while parsing devname, retrying in one second\n"); + sleep(1); + continue; + } else { + break; + } + } + + memcpy(rem_devname_line, tmpbuf, strlen(tmpbuf) + 1); + /* returns DEVNAME */ + char *rem_dev_name = strtok(rem_devname_line, "="); + /* returns the actual device name */ + rem_dev_name = strtok(NULL, "="); + if (rem_dev_name == NULL) { + continue; + } + + for (int i = 0; i < target_dev_name_list_len; i++) { + if (strcmp(rem_dev_name, target_dev_name_list[i]) == 0) { + reboot(RB_POWER_OFF); + //print(fd_stderr, "SHUTDOWN!!!\n"); + exit(0); + } + } + free(rem_devname_line); + } + } + +next_str: + len -= strlen(tmpbuf) + 1; + tmpbuf += strlen(tmpbuf) + 1; + } + } +} From dfb6f143f0324d0903ae2dd106bc0fb6907c1cb0 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Sun, 13 Jul 2025 20:53:29 -0500 Subject: [PATCH 05/57] Add panic key handling to emergency shutdown utility --- .../force-shutdown-when-device-removed.c | 630 +++++++++++++++--- 1 file changed, 529 insertions(+), 101 deletions(-) diff --git a/usr/src/security-misc/force-shutdown-when-device-removed.c b/usr/src/security-misc/force-shutdown-when-device-removed.c index c7ddd52..e7b887f 100644 --- a/usr/src/security-misc/force-shutdown-when-device-removed.c +++ b/usr/src/security-misc/force-shutdown-when-device-removed.c @@ -74,6 +74,13 @@ * (there are other similar posts as well). */ +/* + * TODO: Consider handling signals more gracefully (perhaps use ppoll instead + * of poll, handle things like EINTR, etc.). Right now the plan is to simply + * terminate when a signal is received and let systemd restart the process, + * but it might be better to just be signal-resilient. + */ + #include #include #include @@ -83,11 +90,183 @@ #include #include #include +#include +#include +#include +#include #define fd_stdin 0 #define fd_stdout 1 #define fd_stderr 2 +#define max_inputs 255 +#define input_path_size 20 +#define key_flags_len 12 + +/* Adapted from kloak/src/keycodes.c */ +struct name_value { + const char *name; + const int value; +}; +static struct name_value key_table[] = { + {"KEY_ESC", KEY_ESC}, + {"KEY_1", KEY_1}, + {"KEY_2", KEY_2}, + {"KEY_3", KEY_3}, + {"KEY_4", KEY_4}, + {"KEY_5", KEY_5}, + {"KEY_6", KEY_6}, + {"KEY_7", KEY_7}, + {"KEY_8", KEY_8}, + {"KEY_9", KEY_9}, + {"KEY_0", KEY_0}, + {"KEY_MINUS", KEY_MINUS}, + {"KEY_EQUAL", KEY_EQUAL}, + {"KEY_BACKSPACE", KEY_BACKSPACE}, + {"KEY_TAB", KEY_TAB}, + {"KEY_Q", KEY_Q}, + {"KEY_W", KEY_W}, + {"KEY_E", KEY_E}, + {"KEY_R", KEY_R}, + {"KEY_T", KEY_T}, + {"KEY_Y", KEY_Y}, + {"KEY_U", KEY_U}, + {"KEY_I", KEY_I}, + {"KEY_O", KEY_O}, + {"KEY_P", KEY_P}, + {"KEY_LEFTBRACE", KEY_LEFTBRACE}, + {"KEY_RIGHTBRACE", KEY_RIGHTBRACE}, + {"KEY_ENTER", KEY_ENTER}, + {"KEY_LEFTCTRL", KEY_LEFTCTRL}, + {"KEY_A", KEY_A}, + {"KEY_S", KEY_S}, + {"KEY_D", KEY_D}, + {"KEY_F", KEY_F}, + {"KEY_G", KEY_G}, + {"KEY_H", KEY_H}, + {"KEY_J", KEY_J}, + {"KEY_K", KEY_K}, + {"KEY_L", KEY_L}, + {"KEY_SEMICOLON", KEY_SEMICOLON}, + {"KEY_APOSTROPHE", KEY_APOSTROPHE}, + {"KEY_GRAVE", KEY_GRAVE}, + {"KEY_LEFTSHIFT", KEY_LEFTSHIFT}, + {"KEY_BACKSLASH", KEY_BACKSLASH}, + {"KEY_Z", KEY_Z}, + {"KEY_X", KEY_X}, + {"KEY_C", KEY_C}, + {"KEY_V", KEY_V}, + {"KEY_B", KEY_B}, + {"KEY_N", KEY_N}, + {"KEY_M", KEY_M}, + {"KEY_COMMA", KEY_COMMA}, + {"KEY_DOT", KEY_DOT}, + {"KEY_SLASH", KEY_SLASH}, + {"KEY_RIGHTSHIFT", KEY_RIGHTSHIFT}, + {"KEY_KPASTERISK", KEY_KPASTERISK}, + {"KEY_LEFTALT", KEY_LEFTALT}, + {"KEY_SPACE", KEY_SPACE}, + {"KEY_CAPSLOCK", KEY_CAPSLOCK}, + {"KEY_F1", KEY_F1}, + {"KEY_F2", KEY_F2}, + {"KEY_F3", KEY_F3}, + {"KEY_F4", KEY_F4}, + {"KEY_F5", KEY_F5}, + {"KEY_F6", KEY_F6}, + {"KEY_F7", KEY_F7}, + {"KEY_F8", KEY_F8}, + {"KEY_F9", KEY_F9}, + {"KEY_F10", KEY_F10}, + {"KEY_NUMLOCK", KEY_NUMLOCK}, + {"KEY_SCROLLLOCK", KEY_SCROLLLOCK}, + {"KEY_KP7", KEY_KP7}, + {"KEY_KP8", KEY_KP8}, + {"KEY_KP9", KEY_KP9}, + {"KEY_KPMINUS", KEY_KPMINUS}, + {"KEY_KP4", KEY_KP4}, + {"KEY_KP5", KEY_KP5}, + {"KEY_KP6", KEY_KP6}, + {"KEY_KPPLUS", KEY_KPPLUS}, + {"KEY_KP1", KEY_KP1}, + {"KEY_KP2", KEY_KP2}, + {"KEY_KP3", KEY_KP3}, + {"KEY_KP0", KEY_KP0}, + {"KEY_KPDOT", KEY_KPDOT}, + {"KEY_ZENKAKUHANKAKU", KEY_ZENKAKUHANKAKU}, + {"KEY_102ND", KEY_102ND}, + {"KEY_F11", KEY_F11}, + {"KEY_F12", KEY_F12}, + {"KEY_RO", KEY_RO}, + {"KEY_KATAKANA", KEY_KATAKANA}, + {"KEY_HIRAGANA", KEY_HIRAGANA}, + {"KEY_HENKAN", KEY_HENKAN}, + {"KEY_KATAKANAHIRAGANA", KEY_KATAKANAHIRAGANA}, + {"KEY_MUHENKAN", KEY_MUHENKAN}, + {"KEY_KPJPCOMMA", KEY_KPJPCOMMA}, + {"KEY_KPENTER", KEY_KPENTER}, + {"KEY_RIGHTCTRL", KEY_RIGHTCTRL}, + {"KEY_KPSLASH", KEY_KPSLASH}, + {"KEY_SYSRQ", KEY_SYSRQ}, + {"KEY_RIGHTALT", KEY_RIGHTALT}, + {"KEY_LINEFEED", KEY_LINEFEED}, + {"KEY_HOME", KEY_HOME}, + {"KEY_UP", KEY_UP}, + {"KEY_PAGEUP", KEY_PAGEUP}, + {"KEY_LEFT", KEY_LEFT}, + {"KEY_RIGHT", KEY_RIGHT}, + {"KEY_END", KEY_END}, + {"KEY_DOWN", KEY_DOWN}, + {"KEY_PAGEDOWN", KEY_PAGEDOWN}, + {"KEY_INSERT", KEY_INSERT}, + {"KEY_DELETE", KEY_DELETE}, + {"KEY_MACRO", KEY_MACRO}, + {"KEY_MUTE", KEY_MUTE}, + {"KEY_VOLUMEDOWN", KEY_VOLUMEDOWN}, + {"KEY_VOLUMEUP", KEY_VOLUMEUP}, + {"KEY_POWER", KEY_POWER}, + {"KEY_POWER2", KEY_POWER2}, + {"KEY_KPEQUAL", KEY_KPEQUAL}, + {"KEY_KPPLUSMINUS", KEY_KPPLUSMINUS}, + {"KEY_PAUSE", KEY_PAUSE}, + {"KEY_SCALE", KEY_SCALE}, + {"KEY_KPCOMMA", KEY_KPCOMMA}, + {"KEY_HANGEUL", KEY_HANGEUL}, + {"KEY_HANGUEL", KEY_HANGUEL}, + {"KEY_HANJA", KEY_HANJA}, + {"KEY_YEN", KEY_YEN}, + {"KEY_LEFTMETA", KEY_LEFTMETA}, + {"KEY_RIGHTMETA", KEY_RIGHTMETA}, + {"KEY_COMPOSE", KEY_COMPOSE}, + {"KEY_F13", KEY_F13}, + {"KEY_F14", KEY_F14}, + {"KEY_F15", KEY_F15}, + {"KEY_F16", KEY_F16}, + {"KEY_F17", KEY_F17}, + {"KEY_F18", KEY_F18}, + {"KEY_F19", KEY_F19}, + {"KEY_F20", KEY_F20}, + {"KEY_F21", KEY_F21}, + {"KEY_F22", KEY_F22}, + {"KEY_F23", KEY_F23}, + {"KEY_F24", KEY_F24}, + {"KEY_UNKNOWN", KEY_UNKNOWN}, + {NULL, 0} +}; +int lookup_keycode(const char *name) { + struct name_value *p; + for (p = key_table; p->name != NULL; ++p) { + if (strcmp(p->name, name) == 0) { + return p->value; + } + } + return -1; +} + +/* Adapted from systemd/src/login/logind-button.c */ +bool bitset_get(const uint64_t *bits, uint32_t i) { + return (bits[i / 64] >> (i % 64)) & 1UL; +} + void print(int fd, char *str) { size_t len = strlen(str) + 1; while (true) { @@ -102,35 +281,151 @@ void print(int fd, char *str) { void print_usage() { print(fd_stderr, "Usage:\n"); - print(fd_stderr, " force-shutdown-when-device-removed DEVICE1 [DEVICE2...]\n"); + print(fd_stderr, " emerg-shutdown --devices=DEVICE1[,DEVICE2...] --keys=KEY_1[,KEY_2...]\n"); print(fd_stderr, "Example:\n"); - print(fd_stderr, " force-shutdown-when-device-removed /dev/sda3\n"); + print(fd_stderr, " emerg-shutdown --devices=/dev/sda3 --keys=KEY_POWER\n"); +} + +void *safe_calloc(size_t nmemb, size_t size) { + void *ret_buf = calloc(nmemb, size); + if (ret_buf == NULL) { + print(fd_stderr, "Out of memory!\n"); + exit(1); + } + return ret_buf; +} + +void *safe_reallocarray(void *ptr, size_t nmemb, size_t size) { + void *ret_buf = reallocarray(ptr, nmemb, size); + if (ret_buf == NULL) { + print(fd_stderr, "Out of memory!\n"); + exit(1); + } + return ret_buf; +} + +/* Inspired by https://www.strudel.org.uk/itoa/ */ +char *int_to_str(uint32_t val) { + static char buf[11]; + int8_t i; + char *rslt = NULL; + const char *digits = "0123456789"; + + buf[10] = '\0'; + + for (i = 9; i >= 0; i--) { + buf[i] = digits[val % 10]; + val /= 10; + if (val == 0) { + break; + } + } + + rslt = safe_calloc(1, 11 - i); + memcpy(rslt, buf + i, 11 - i); + return rslt; +} + +void load_list(const char *arg, size_t *result_list_len_ref, char ***result_list_ref) { + char **result_list = NULL; + size_t result_list_len = 0; + int arg_copy_len = strlen(arg) + 1; + char *arg_copy = safe_calloc(1, arg_copy_len); + char *arg_val; + char *arg_part; + + memcpy(arg_copy, arg, arg_copy_len); + /* returns "--whatever" */ + arg_val = strtok(arg_copy, "="); + /* returns everything after the = sign */ + arg_val = strtok(NULL, ""); + + arg_part = strtok(arg_val, ","); + if (arg_part == NULL) { + return; + } + + do { + result_list_len++; + result_list = safe_reallocarray(result_list, result_list_len, sizeof(char *)); + result_list[result_list_len - 1] = safe_calloc(1, strlen(arg_part) + 1); + strcpy(result_list[result_list_len - 1], arg_part); + } while ((arg_part = strtok(NULL, ",")) != NULL); + + *result_list_len_ref = result_list_len; + *result_list_ref = result_list; + free(arg_copy); } int main(int argc, char **argv) { + /* Working variables */ + size_t target_dev_list_len = 0; + char **target_dev_name_raw_list = NULL; + size_t panic_key_list_len = 0; + char **panic_key_str_list = NULL; + char **target_dev_list = NULL; + int *panic_key_list = NULL; + bool *panic_key_active_list = NULL; + size_t event_fd_list_len = 0; + int *event_fd_list = NULL; + char input_path_buf[input_path_size]; + struct pollfd *pollfd_list = NULL; + struct input_event ie_buf[64]; + + /* Index variables */ + int arg_idx = 0; + size_t tdl_idx = 0; + size_t tdp_char_idx = 0; + size_t pkl_idx = 0; + int input_idx = 0; + size_t efl_idx = 0; + int ie_idx = 0; + + /* Prerequisite check */ if (getuid() != 0) { print(fd_stderr, "This program must be run as root!\n"); exit(1); } + /* Argument parsing */ if (argc < 2) { print(fd_stderr, "Invalid number of arguments!\n"); print_usage(); exit(1); } - size_t target_dev_name_list_len = argc - 1; - char **target_dev_name_list = calloc(target_dev_name_list_len, - sizeof(char *)); - if (target_dev_name_list == NULL) { - print(fd_stderr, "Out of memory during early setup!\n"); - exit(1); + for (arg_idx = 1; arg_idx < argc; arg_idx++) { + if (strncmp(argv[arg_idx], "--devices=", strlen("--devices=")) == 0) { + if (target_dev_name_raw_list != NULL) { + print(fd_stderr, "--devices cannot be passed more than once!\n"); + print_usage(); + exit(1); + } + load_list(argv[arg_idx], &target_dev_list_len, &target_dev_name_raw_list); + } else if (strncmp(argv[arg_idx], "--keys=", strlen("--keys=")) == 0) { + if (panic_key_str_list != NULL) { + print(fd_stderr, "--keys cannot be passed more than once!\n"); + print_usage(); + exit(1); + } + load_list(argv[arg_idx], &panic_key_list_len, &panic_key_str_list); + } } - for (int i = 1; i < argc; i++) { - char *target_dev_path = argv[i]; + target_dev_list = safe_calloc(target_dev_list_len, sizeof(char *)); + panic_key_list = safe_calloc(panic_key_list_len, sizeof(int)); + panic_key_active_list = safe_calloc(panic_key_list_len, sizeof(bool)); + + for (tdl_idx = 0; tdl_idx < target_dev_list_len; tdl_idx++) { + char *target_dev_path = target_dev_name_raw_list[tdl_idx]; + size_t device_path_slash_count = 0; + char *target_dev_parse = safe_calloc(1, strlen(target_dev_path) + 1); + char *target_dev_name = NULL; + if (access(target_dev_path, F_OK) != 0) { - print(fd_stderr, "One of the specified devices does not exist!\n"); + print(fd_stderr, "The device '"); + print(fd_stderr, target_dev_path); + print(fd_stderr, "' does not exist!\n"); print_usage(); exit(1); } @@ -142,46 +437,58 @@ int main(int argc, char **argv) { && strncmp(target_dev_path, "/dev/vd", strlen("/dev/vd")) != 0 && strncmp(target_dev_path, "/dev/xvd", strlen("/dev/xvd")) != 0 && strncmp(target_dev_path, "/dev/hd", strlen("/dev/hd")) != 0) { - print(fd_stderr, "One of the specified devices is not supported!\n"); + print(fd_stderr, "The device '"); + print(fd_stderr, target_dev_path); + print(fd_stderr, "' is not supported!\n"); print_usage(); exit(1); } - size_t device_path_slash_count = 0; - for (size_t j = 0; j < strlen(target_dev_path); j++) { - if (target_dev_path[j] == '/') { + for (tdp_char_idx = 0; tdp_char_idx < strlen(target_dev_path); tdp_char_idx++) { + if (target_dev_path[tdp_char_idx] == '/') { device_path_slash_count++; } } if (device_path_slash_count != 2) { - print(fd_stderr, "One of the specified devices is not supported!\n"); + print(fd_stderr, "The device '"); + print(fd_stderr, target_dev_path); + print(fd_stderr, "' is not supported!\n"); print_usage(); exit(1); } - char *target_dev_parse = calloc(1, strlen(target_dev_path) + 1); - if (target_dev_parse == NULL) { - print(fd_stderr, "Out of memory during early setup!\n"); - exit(1); - } memcpy(target_dev_parse, target_dev_path, strlen(target_dev_path) + 1); /* returns "dev" */ - char *target_dev_name = strtok(target_dev_parse, "/"); + target_dev_name = strtok(target_dev_parse, "/"); /* returns the actual device name we want */ target_dev_name = strtok(NULL, "/"); if (target_dev_name == NULL) { - print(fd_stderr, "One of the specified devices is not supported!\n"); + print(fd_stderr, "The device '"); + print(fd_stderr, target_dev_path); + print(fd_stderr, "' is not supported!\n"); print_usage(); exit(1); } - target_dev_name_list[i - 1] = calloc(1, strlen(target_dev_name) + 1); - memcpy(target_dev_name_list[i - 1], target_dev_name, - strlen(target_dev_name) + 1); + target_dev_list[tdl_idx] = calloc(1, strlen(target_dev_name) + 1); + memcpy(target_dev_list[tdl_idx], target_dev_name, strlen(target_dev_name) + 1); free(target_dev_parse); } + for (pkl_idx = 0; pkl_idx < panic_key_list_len; pkl_idx++) { + int keycode = lookup_keycode(panic_key_str_list[pkl_idx]); + if (keycode < 0) { + print(fd_stderr, "Invalid key code '"); + print(fd_stderr, panic_key_str_list[pkl_idx]); + print(fd_stderr, "'!\n"); + print_usage(); + exit(1); + } + panic_key_list[pkl_idx] = keycode; + } + + /* Device event listener setup */ struct sockaddr_nl sa = { .nl_family = AF_NETLINK, .nl_pad = 0, @@ -199,97 +506,218 @@ int main(int argc, char **argv) { exit(1); } - while (true) { - /* - * So, you looked at `man 7 netlink`, then looked at this code, and can't - * figure out how on earth any of this makes sense? Well guess what, turns - * out NETLINK_KOBJECT_UEVENT messages break all of the rules about how - * netlink messages work specified in that manpage. What you actually - * get... well, depends. - * - * - The messages we actually want are just NUL-separated string lists. - * These are the actual kernel uevents. - * - Mixed in with those will be uevents generated by systemd-udevd, which - * use a different format and are unsuitable for our purposes. We have - * to ignore those. Thankfully those messages start with the - * NUL-terminated string "libudev" so they're easy to filter out. - */ + /* Keyboard event listener setup + * Heavily inspired by systemd/src/login/logind-button.c and + * kloak/src/main.c */ + for (input_idx = 0; input_idx <= max_inputs; input_idx++) { + int tmp_fd = 0; + uint64_t key_flags[key_flags_len]; + bool supports_panic = true; + char *loop_str = NULL; - int len; - char buf[16384]; - struct iovec iov = { buf, sizeof(buf) }; - struct sockaddr_nl sa2; - struct msghdr msg = { &sa2, sizeof(sa2), &iov, 1, NULL, 0, 0 }; - len = recvmsg(ns, &msg, 0); - if (len == -1) { - reboot(RB_POWER_OFF); - //print(fd_stderr, "SHUTDOWN!!!\n"); - exit(0); - } + strcpy(input_path_buf, "/dev/input/event"); + loop_str = int_to_str(input_idx); + strcat(input_path_buf, loop_str); + free(loop_str); - if (len < 8) { - /* There aren't any super-short messages we're interested in, discard - * them */ - continue; - } - if (memcmp(buf, "libudev", 8) == 0) { - /* udevd message, ignore */ + tmp_fd = open(input_path_buf, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); + if (tmp_fd < 0) { continue; } - char *tmpbuf = buf; - bool device_removed = false; - while (len > 0) { - if (strcmp(tmpbuf, "ACTION=remove") == 0) { - device_removed = true; - goto next_str; + if (ioctl(tmp_fd, EVIOCGBIT(EV_SYN, sizeof(key_flags)), key_flags) < 0) { + print(fd_stderr, "Failed to query properties of input device '"); + print(fd_stderr, input_path_buf); + print(fd_stderr, "'!\n"); + exit(1); + } + + if (!bitset_get(key_flags, EV_KEY)) { + continue; + } + + if (ioctl(tmp_fd, EVIOCGBIT(EV_KEY, sizeof(key_flags)), key_flags) < 0) { + print(fd_stderr, "Failed to query keys available on input device '"); + print(fd_stderr, input_path_buf); + print(fd_stderr, "'!\n"); + exit(1); + } + + for (pkl_idx = 0; pkl_idx < panic_key_list_len; pkl_idx++) { + if (!bitset_get(key_flags, panic_key_list[pkl_idx])) { + supports_panic = false; + break; + } + } + if (!supports_panic) { + continue; + } + + event_fd_list_len++; + event_fd_list = safe_reallocarray(event_fd_list, event_fd_list_len, sizeof(int)); + event_fd_list[event_fd_list_len - 1] = tmp_fd; + } + + if (event_fd_list_len == 0) { + print(fd_stderr, "Failed to find any input device supporting panic keys!\n"); + exit(1); + } + + /* Poll setup */ + pollfd_list = safe_calloc(event_fd_list_len + 1, sizeof(struct pollfd)); + for (efl_idx = 0; efl_idx < event_fd_list_len; efl_idx++) { + pollfd_list[efl_idx].fd = event_fd_list[efl_idx]; + pollfd_list[efl_idx].events = POLLIN; + } + pollfd_list[event_fd_list_len].fd = ns; + pollfd_list[event_fd_list_len].events = POLLIN; + + /* Event loop */ + while (poll(pollfd_list, event_fd_list_len + 1, -1) != -1) { + /* Panic key handler */ + for (efl_idx = 0; efl_idx < event_fd_list_len; efl_idx++) { + if (!(pollfd_list[efl_idx].revents & POLLIN)) { + continue; } - if (strncmp(tmpbuf, "DEVNAME=", strlen("DEVNAME=")) == 0) { - if (device_removed) { - char *rem_devname_line; - /* - * Try to allocate the memory needed to check DEVNAME in a loop. We - * really do not want to simply abort here due to an out of memory - * condition, because that would result in the shutdown never - * occurring. We also don't want to force a shutdown when memory - * runs out, as that could result in the user losing work because - * they opened too many browser tabs. - */ - while(true) { - rem_devname_line = calloc(1, strlen(tmpbuf) + 1); - if (rem_devname_line == NULL) { - print(fd_stderr, "Out of memory while parsing devname, retrying in one second\n"); - sleep(1); - continue; + size_t ieread_bytes = read(event_fd_list[efl_idx], ie_buf, sizeof(struct input_event) * 64); + + if (ieread_bytes == -1 + || ieread_bytes == 0 + || (ieread_bytes % sizeof(struct input_event)) != 0) { + /* This will probably terminate the service if the user unplugs a + * keyboard or similar, however systemd can start it again. The + * alternative is to handle device hotplug, which sounds like a + * recipe for bugs. */ + print(fd_stderr, "Error reading from input device!\n"); + exit(1); + } + + for (ie_idx = 0; ie_idx < ieread_bytes / sizeof(struct input_event); ie_idx++) { + if (ie_buf[ie_idx].type != EV_KEY) { + continue; + } + + for (pkl_idx = 0; pkl_idx < panic_key_list_len; pkl_idx++) { + if (ie_buf[ie_idx].code == panic_key_list[pkl_idx]) { + if (ie_buf[ie_idx].value == 0) { + panic_key_active_list[pkl_idx] = false; } else { - break; + panic_key_active_list[pkl_idx] = true; } } + } - memcpy(rem_devname_line, tmpbuf, strlen(tmpbuf) + 1); - /* returns DEVNAME */ - char *rem_dev_name = strtok(rem_devname_line, "="); - /* returns the actual device name */ - rem_dev_name = strtok(NULL, "="); - if (rem_dev_name == NULL) { - continue; + for (pkl_idx = 0; pkl_idx < panic_key_list_len; pkl_idx++) { + if (!panic_key_active_list[pkl_idx]) { + break; } - - for (int i = 0; i < target_dev_name_list_len; i++) { - if (strcmp(rem_dev_name, target_dev_name_list[i]) == 0) { - reboot(RB_POWER_OFF); - //print(fd_stderr, "SHUTDOWN!!!\n"); - exit(0); - } + if (pkl_idx == (panic_key_list_len - 1)) { + print(fd_stderr, "SHUTDOWN!!!\n"); + exit(0); } - free(rem_devname_line); } } + } -next_str: - len -= strlen(tmpbuf) + 1; - tmpbuf += strlen(tmpbuf) + 1; + /* Netlink socket handler */ + if (pollfd_list[event_fd_list_len].revents & POLLIN) { + /* + * So, you looked at `man 7 netlink`, then looked at this code, and can't + * figure out how on earth any of this makes sense? Well guess what, turns + * out NETLINK_KOBJECT_UEVENT messages break all of the rules about how + * netlink messages work specified in that manpage. What you actually + * get... well, depends. + * + * - The messages we actually want are just NUL-separated string lists. + * These are the actual kernel uevents. + * - Mixed in with those will be uevents generated by systemd-udevd, which + * use a different format and are unsuitable for our purposes. We have + * to ignore those. Thankfully those messages start with the + * NUL-terminated string "libudev" so they're easy to filter out. + */ + + int len; + char buf[16384]; + struct iovec iov = { buf, sizeof(buf) }; + struct sockaddr_nl sa2; + struct msghdr msg = { &sa2, sizeof(sa2), &iov, 1, NULL, 0, 0 }; + char *tmpbuf = NULL; + bool device_removed = false; + + len = recvmsg(ns, &msg, 0); + if (len == -1) { + //reboot(RB_POWER_OFF); + print(fd_stderr, "SHUTDOWN!!!\n"); + exit(0); + } + + if (len < 8) { + /* There aren't any super-short messages we're interested in, discard + * them */ + continue; + } + if (memcmp(buf, "libudev", 8) == 0) { + /* udevd message, ignore */ + continue; + } + + tmpbuf = buf; + while (len > 0) { + if (strcmp(tmpbuf, "ACTION=remove") == 0) { + device_removed = true; + goto next_str; + } + + if (strncmp(tmpbuf, "DEVNAME=", strlen("DEVNAME=")) == 0) { + if (device_removed) { + char *rem_devname_line = NULL; + char *rem_dev_name = NULL; + + /* + * Try to allocate the memory needed to check DEVNAME in a loop. We + * really do not want to simply abort here due to an out of memory + * condition, because that would result in the shutdown never + * occurring. We also don't want to force a shutdown when memory + * runs out, as that could result in the user losing work because + * they opened too many browser tabs. + */ + while(true) { + rem_devname_line = calloc(1, strlen(tmpbuf) + 1); + if (rem_devname_line == NULL) { + print(fd_stderr, "Out of memory while parsing devname, retrying in one second\n"); + sleep(1); + continue; + } else { + break; + } + } + + memcpy(rem_devname_line, tmpbuf, strlen(tmpbuf) + 1); + /* returns DEVNAME */ + rem_dev_name = strtok(rem_devname_line, "="); + /* returns the actual device name */ + rem_dev_name = strtok(NULL, "="); + if (rem_dev_name == NULL) { + free(rem_devname_line); + continue; + } + + for (tdl_idx = 0; tdl_idx < target_dev_list_len; tdl_idx++) { + if (strcmp(rem_dev_name, target_dev_list[tdl_idx]) == 0) { + //reboot(RB_POWER_OFF); + print(fd_stderr, "SHUTDOWN!!!\n"); + exit(0); + } + } + free(rem_devname_line); + } + } + + next_str: + len -= strlen(tmpbuf) + 1; + tmpbuf += strlen(tmpbuf) + 1; + } } } } From e387086de4b6e6b90b23d4c32ddf8a566beb858c Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Mon, 14 Jul 2025 21:05:16 -0500 Subject: [PATCH 06/57] Allow specifying alternative keys in panic key combo, fix optical disk eject handling --- ...removal.service => emerg-shutdown.service} | 2 +- ...f-on-boot-media-removal => emerg-shutdown} | 9 +- ...when-device-removed.c => emerg-shutdown.c} | 90 +++++++++++++------ 3 files changed, 70 insertions(+), 31 deletions(-) rename usr/lib/systemd/system/{force-poweroff-on-boot-media-removal.service => emerg-shutdown.service} (81%) rename usr/libexec/security-misc/{force-poweroff-on-boot-media-removal => emerg-shutdown} (68%) rename usr/src/security-misc/{force-shutdown-when-device-removed.c => emerg-shutdown.c} (91%) diff --git a/usr/lib/systemd/system/force-poweroff-on-boot-media-removal.service b/usr/lib/systemd/system/emerg-shutdown.service similarity index 81% rename from usr/lib/systemd/system/force-poweroff-on-boot-media-removal.service rename to usr/lib/systemd/system/emerg-shutdown.service index b99cf64..af887f4 100644 --- a/usr/lib/systemd/system/force-poweroff-on-boot-media-removal.service +++ b/usr/lib/systemd/system/emerg-shutdown.service @@ -7,7 +7,7 @@ Documentation=https://github.com/Kicksecure/security-misc [Service] Type=oneshot -ExecStart=/usr/libexec/security-misc/force-poweroff-on-boot-media-removal +ExecStart=/usr/libexec/security-misc/emerg-shutdown RemainAfterExit=true [Install] diff --git a/usr/libexec/security-misc/force-poweroff-on-boot-media-removal b/usr/libexec/security-misc/emerg-shutdown similarity index 68% rename from usr/libexec/security-misc/force-poweroff-on-boot-media-removal rename to usr/libexec/security-misc/emerg-shutdown index 0760ae1..b60d3f9 100755 --- a/usr/libexec/security-misc/force-poweroff-on-boot-media-removal +++ b/usr/libexec/security-misc/emerg-shutdown @@ -5,9 +5,9 @@ gcc \ -o \ - /run/force-shutdown-when-device-removed \ + /run/emerg-shutdown \ -static \ - /usr/src/security-misc/force-shutdown-when-device-removed.c \ + /usr/src/security-misc/emerg-shutdown.c \ || { printf "%s\n" 'Could not compile force-shutdown executable!' exit 1; @@ -18,7 +18,10 @@ readarray -t root_devices < <(/usr/libexec/helper-scripts/get-backing-devices-fo ## memlockd daemonizes itself, so no need to background it memlockd -c /usr/share/security-misc/security-misc-memlockd.cfg -/run/force-shutdown-when-device-removed "${root_devices}" & +OLDIFS="$IFS" +IFS=',' +/run/emerg-shutdown "--devices=${root_devices[*]}" '--keys=KEY_LEFTCTRL|KEY_RIGHTCTRL,KEY_LEFTALT|KEY_RIGHTALT,KEY_DELETE' & +IFS="$OLDIFS" sleep 1 disown exit 0 diff --git a/usr/src/security-misc/force-shutdown-when-device-removed.c b/usr/src/security-misc/emerg-shutdown.c similarity index 91% rename from usr/src/security-misc/force-shutdown-when-device-removed.c rename to usr/src/security-misc/emerg-shutdown.c index e7b887f..bc5f95d 100644 --- a/usr/src/security-misc/force-shutdown-when-device-removed.c +++ b/usr/src/security-misc/emerg-shutdown.c @@ -281,7 +281,7 @@ void print(int fd, char *str) { void print_usage() { print(fd_stderr, "Usage:\n"); - print(fd_stderr, " emerg-shutdown --devices=DEVICE1[,DEVICE2...] --keys=KEY_1[,KEY_2...]\n"); + print(fd_stderr, " emerg-shutdown --devices=DEVICE1[,DEVICE2...] --keys=KEY_1[,KEY_2|KEY_3...]\n"); print(fd_stderr, "Example:\n"); print(fd_stderr, " emerg-shutdown --devices=/dev/sda3 --keys=KEY_POWER\n"); } @@ -326,7 +326,7 @@ char *int_to_str(uint32_t val) { return rslt; } -void load_list(const char *arg, size_t *result_list_len_ref, char ***result_list_ref) { +void load_list(const char *arg, size_t *result_list_len_ref, char ***result_list_ref, const char *sep, bool parse_opt) { char **result_list = NULL; size_t result_list_len = 0; int arg_copy_len = strlen(arg) + 1; @@ -335,12 +335,16 @@ void load_list(const char *arg, size_t *result_list_len_ref, char ***result_list char *arg_part; memcpy(arg_copy, arg, arg_copy_len); - /* returns "--whatever" */ - arg_val = strtok(arg_copy, "="); - /* returns everything after the = sign */ - arg_val = strtok(NULL, ""); + if (parse_opt) { + /* returns "--whatever" */ + arg_val = strtok(arg_copy, "="); + /* returns everything after the = sign */ + arg_val = strtok(NULL, ""); + } else { + arg_val = arg_copy; + } - arg_part = strtok(arg_val, ","); + arg_part = strtok(arg_val, sep); if (arg_part == NULL) { return; } @@ -364,7 +368,7 @@ int main(int argc, char **argv) { size_t panic_key_list_len = 0; char **panic_key_str_list = NULL; char **target_dev_list = NULL; - int *panic_key_list = NULL; + int **panic_key_list = NULL; bool *panic_key_active_list = NULL; size_t event_fd_list_len = 0; int *event_fd_list = NULL; @@ -380,6 +384,7 @@ int main(int argc, char **argv) { int input_idx = 0; size_t efl_idx = 0; int ie_idx = 0; + size_t kg_idx = 0; /* Prerequisite check */ if (getuid() != 0) { @@ -401,19 +406,19 @@ int main(int argc, char **argv) { print_usage(); exit(1); } - load_list(argv[arg_idx], &target_dev_list_len, &target_dev_name_raw_list); + load_list(argv[arg_idx], &target_dev_list_len, &target_dev_name_raw_list, ",", true); } else if (strncmp(argv[arg_idx], "--keys=", strlen("--keys=")) == 0) { if (panic_key_str_list != NULL) { print(fd_stderr, "--keys cannot be passed more than once!\n"); print_usage(); exit(1); } - load_list(argv[arg_idx], &panic_key_list_len, &panic_key_str_list); + load_list(argv[arg_idx], &panic_key_list_len, &panic_key_str_list, ",", true); } } target_dev_list = safe_calloc(target_dev_list_len, sizeof(char *)); - panic_key_list = safe_calloc(panic_key_list_len, sizeof(int)); + panic_key_list = safe_calloc(panic_key_list_len, sizeof(int *)); panic_key_active_list = safe_calloc(panic_key_list_len, sizeof(bool)); for (tdl_idx = 0; tdl_idx < target_dev_list_len; tdl_idx++) { @@ -477,15 +482,27 @@ int main(int argc, char **argv) { } for (pkl_idx = 0; pkl_idx < panic_key_list_len; pkl_idx++) { - int keycode = lookup_keycode(panic_key_str_list[pkl_idx]); - if (keycode < 0) { - print(fd_stderr, "Invalid key code '"); - print(fd_stderr, panic_key_str_list[pkl_idx]); - print(fd_stderr, "'!\n"); - print_usage(); - exit(1); + size_t keygroup_str_list_len = 0; + char **keygroup_str_list = NULL; + load_list(panic_key_str_list[pkl_idx], &keygroup_str_list_len, &keygroup_str_list, "|", false); + int *pkl_element = safe_calloc(keygroup_str_list_len + 1, sizeof(int)); + + pkl_element[keygroup_str_list_len] = 0; + for (kg_idx = 0; kg_idx < keygroup_str_list_len; kg_idx++) { + int keycode = lookup_keycode(keygroup_str_list[kg_idx]); + if (keycode < 0) { + print(fd_stderr, "Invalid key code '"); + print(fd_stderr, keygroup_str_list[kg_idx]); + print(fd_stderr, "'!\n"); + print_usage(); + exit(1); + } + pkl_element[kg_idx] = keycode; + free(keygroup_str_list[kg_idx]); } - panic_key_list[pkl_idx] = keycode; + + free(keygroup_str_list); + panic_key_list[pkl_idx] = pkl_element; } /* Device event listener setup */ @@ -544,8 +561,13 @@ int main(int argc, char **argv) { } for (pkl_idx = 0; pkl_idx < panic_key_list_len; pkl_idx++) { - if (!bitset_get(key_flags, panic_key_list[pkl_idx])) { - supports_panic = false; + for (kg_idx = 0; panic_key_list[pkl_idx][kg_idx] != 0; kg_idx++) { + if (!bitset_get(key_flags, panic_key_list[pkl_idx][kg_idx])) { + supports_panic = false; + break; + } + } + if (!supports_panic) { break; } } @@ -599,11 +621,14 @@ int main(int argc, char **argv) { } for (pkl_idx = 0; pkl_idx < panic_key_list_len; pkl_idx++) { - if (ie_buf[ie_idx].code == panic_key_list[pkl_idx]) { - if (ie_buf[ie_idx].value == 0) { - panic_key_active_list[pkl_idx] = false; - } else { - panic_key_active_list[pkl_idx] = true; + for (kg_idx = 0; panic_key_list[pkl_idx][kg_idx] != 0; kg_idx++) { + if (ie_buf[ie_idx].code == panic_key_list[pkl_idx][kg_idx]) { + if (ie_buf[ie_idx].value == 0) { + panic_key_active_list[pkl_idx] = false; + } else { + panic_key_active_list[pkl_idx] = true; + } + break; /* only breaks inner loop */ } } } @@ -644,6 +669,7 @@ int main(int argc, char **argv) { struct msghdr msg = { &sa2, sizeof(sa2), &iov, 1, NULL, 0, 0 }; char *tmpbuf = NULL; bool device_removed = false; + bool device_changed = false; len = recvmsg(ns, &msg, 0); if (len == -1) { @@ -668,9 +694,13 @@ int main(int argc, char **argv) { device_removed = true; goto next_str; } + if (strcmp(tmpbuf, "ACTION=change") == 0) { + device_changed = true; + goto next_str; + } if (strncmp(tmpbuf, "DEVNAME=", strlen("DEVNAME=")) == 0) { - if (device_removed) { + if (device_removed || device_changed) { char *rem_devname_line = NULL; char *rem_dev_name = NULL; @@ -703,6 +733,11 @@ int main(int argc, char **argv) { continue; } + if (device_changed && strncmp(rem_dev_name, "sr", 2) != 0) { + free(rem_devname_line); + continue; + } + for (tdl_idx = 0; tdl_idx < target_dev_list_len; tdl_idx++) { if (strcmp(rem_dev_name, target_dev_list[tdl_idx]) == 0) { //reboot(RB_POWER_OFF); @@ -710,6 +745,7 @@ int main(int argc, char **argv) { exit(0); } } + free(rem_devname_line); } } From b745c8ddae74d5e1684919442fa74d64e95263b8 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Mon, 14 Jul 2025 21:51:52 -0500 Subject: [PATCH 07/57] emerg-shutdown: Enable actual shutdown code, fix infinite loop when started too early --- usr/src/security-misc/emerg-shutdown.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/usr/src/security-misc/emerg-shutdown.c b/usr/src/security-misc/emerg-shutdown.c index bc5f95d..41f3d7c 100644 --- a/usr/src/security-misc/emerg-shutdown.c +++ b/usr/src/security-misc/emerg-shutdown.c @@ -638,7 +638,8 @@ int main(int argc, char **argv) { break; } if (pkl_idx == (panic_key_list_len - 1)) { - print(fd_stderr, "SHUTDOWN!!!\n"); + reboot(RB_POWER_OFF); + /*print(fd_stderr, "SHUTDOWN!!!\n");*/ exit(0); } } @@ -673,8 +674,8 @@ int main(int argc, char **argv) { len = recvmsg(ns, &msg, 0); if (len == -1) { - //reboot(RB_POWER_OFF); - print(fd_stderr, "SHUTDOWN!!!\n"); + reboot(RB_POWER_OFF); + /*print(fd_stderr, "SHUTDOWN!!!\n");*/ exit(0); } @@ -730,18 +731,18 @@ int main(int argc, char **argv) { rem_dev_name = strtok(NULL, "="); if (rem_dev_name == NULL) { free(rem_devname_line); - continue; + goto next_str; } if (device_changed && strncmp(rem_dev_name, "sr", 2) != 0) { free(rem_devname_line); - continue; + goto next_str; } for (tdl_idx = 0; tdl_idx < target_dev_list_len; tdl_idx++) { if (strcmp(rem_dev_name, target_dev_list[tdl_idx]) == 0) { - //reboot(RB_POWER_OFF); - print(fd_stderr, "SHUTDOWN!!!\n"); + reboot(RB_POWER_OFF); + /*print(fd_stderr, "SHUTDOWN!!!\n");*/ exit(0); } } @@ -750,7 +751,7 @@ int main(int argc, char **argv) { } } - next_str: +next_str: len -= strlen(tmpbuf) + 1; tmpbuf += strlen(tmpbuf) + 1; } From 5889d134a23b3d4f8db5d81171ea12907bb10d4d Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Sun, 20 Jul 2025 14:14:09 -0500 Subject: [PATCH 08/57] emerg-shutdow: Improve recvmsg handling, call reboot syscall directly --- usr/src/security-misc/emerg-shutdown.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/usr/src/security-misc/emerg-shutdown.c b/usr/src/security-misc/emerg-shutdown.c index 41f3d7c..8d2e1cd 100644 --- a/usr/src/security-misc/emerg-shutdown.c +++ b/usr/src/security-misc/emerg-shutdown.c @@ -82,7 +82,7 @@ */ #include -#include +#include #include #include #include @@ -94,6 +94,7 @@ #include #include #include +#include #define fd_stdin 0 #define fd_stdout 1 @@ -361,6 +362,10 @@ void load_list(const char *arg, size_t *result_list_len_ref, char ***result_list free(arg_copy); } +int kill_system() { + return syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL); +} + int main(int argc, char **argv) { /* Working variables */ size_t target_dev_list_len = 0; @@ -638,7 +643,7 @@ int main(int argc, char **argv) { break; } if (pkl_idx == (panic_key_list_len - 1)) { - reboot(RB_POWER_OFF); + kill_system(); /*print(fd_stderr, "SHUTDOWN!!!\n");*/ exit(0); } @@ -667,14 +672,21 @@ int main(int argc, char **argv) { char buf[16384]; struct iovec iov = { buf, sizeof(buf) }; struct sockaddr_nl sa2; - struct msghdr msg = { &sa2, sizeof(sa2), &iov, 1, NULL, 0, 0 }; + struct msghdr msg = { 0 }; + msg.msg_name = &sa2; + msg.msg_namelen = sizeof(sa2); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; char *tmpbuf = NULL; bool device_removed = false; bool device_changed = false; len = recvmsg(ns, &msg, 0); if (len == -1) { - reboot(RB_POWER_OFF); + kill_system(); /*print(fd_stderr, "SHUTDOWN!!!\n");*/ exit(0); } @@ -741,7 +753,7 @@ int main(int argc, char **argv) { for (tdl_idx = 0; tdl_idx < target_dev_list_len; tdl_idx++) { if (strcmp(rem_dev_name, target_dev_list[tdl_idx]) == 0) { - reboot(RB_POWER_OFF); + kill_system(); /*print(fd_stderr, "SHUTDOWN!!!\n");*/ exit(0); } From e42078e90d7d7e5339a7c4682eb93c844fd38580 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Mon, 28 Jul 2025 20:42:14 -0500 Subject: [PATCH 09/57] emerg-shutdown: fix the hang-on-shutdown bug, add autodetection of new keyboards, shutdown key configuration, and instant shutdown option --- .../emerg-shutdown/30_security_misc.conf | 19 +++++ usr/lib/systemd/system/emerg-shutdown.service | 3 +- usr/lib/udev/rules.d/95-emerg-shutdown.rules | 9 ++ usr/libexec/security-misc/emerg-shutdown | 58 +++++++++---- usr/src/security-misc/emerg-shutdown.c | 84 ++++++++++++++++++- 5 files changed, 153 insertions(+), 20 deletions(-) create mode 100644 etc/security-misc/emerg-shutdown/30_security_misc.conf create mode 100644 usr/lib/udev/rules.d/95-emerg-shutdown.rules diff --git a/etc/security-misc/emerg-shutdown/30_security_misc.conf b/etc/security-misc/emerg-shutdown/30_security_misc.conf new file mode 100644 index 0000000..a4bb394 --- /dev/null +++ b/etc/security-misc/emerg-shutdown/30_security_misc.conf @@ -0,0 +1,19 @@ +## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC +## See the file COPYING for copying conditions. + +## Please use "/etc/security-misc/emerg-shutdown/50_user.conf" for your custom +## configuration, which will override the defaults found here. +## When Kicksecure is updated, this file may be overwritten. + +## Set the key combo for forcing immediate shutdown. See the "Keys and +## buttons" section of "/usr/include/linux/input-event-codes.h" for possibly +## supported values. Not all keys are supported. +## +## All specified keys must be depressed at the same time to trigger a +## shutdown. Use a comma (",") to separate keys. If you want to alias certain +## keys to each other from emerg-shutdown's standpoint, use a pipe +## character("|"). +## +## The default key sequence triggers a shutdown when Ctrl+Alt+Delete is +## pressed, allowing the use of either the left or right Ctrl and Alt keys. +EMERG_SHUTDOWN_KEYS="KEY_LEFTCTRL|KEY_RIGHTCTRL,KEY_LEFTALT|KEY_RIGHTALT,KEY_DELETE" diff --git a/usr/lib/systemd/system/emerg-shutdown.service b/usr/lib/systemd/system/emerg-shutdown.service index af887f4..c1fca25 100644 --- a/usr/lib/systemd/system/emerg-shutdown.service +++ b/usr/lib/systemd/system/emerg-shutdown.service @@ -6,9 +6,8 @@ Description=Emergency shutdown when boot media is removed Documentation=https://github.com/Kicksecure/security-misc [Service] -Type=oneshot +Type=exec ExecStart=/usr/libexec/security-misc/emerg-shutdown -RemainAfterExit=true [Install] WantedBy=multi-user.target diff --git a/usr/lib/udev/rules.d/95-emerg-shutdown.rules b/usr/lib/udev/rules.d/95-emerg-shutdown.rules new file mode 100644 index 0000000..051af9f --- /dev/null +++ b/usr/lib/udev/rules.d/95-emerg-shutdown.rules @@ -0,0 +1,9 @@ +SUBSYSTEM!="input", GOTO="end" + +# new keyboard or mouse attached or removed, restart emerg-shutdown +KERNEL=="event*", ACTION=="add", ENV{ID_INPUT_KEYBOARD}=="1", RUN+="/usr/bin/systemctl restart emerg-shutdown.service" +KERNEL=="event*", ACTION=="add", ENV{ID_INPUT_KEYBOARD}=="1", GOTO="end" +KERNEL=="event*", ACTION=="remove", ENV{ID_INPUT_KEYBOARD}=="1", RUN+="/usr/bin/systemctl restart emerg-shutdown.service" +KERNEL=="event*", ACTION=="remove", ENV{ID_INPUT_KEYBOARD}=="1", GOTO="end" + +LABEL="end" diff --git a/usr/libexec/security-misc/emerg-shutdown b/usr/libexec/security-misc/emerg-shutdown index b60d3f9..d8dc7f9 100755 --- a/usr/libexec/security-misc/emerg-shutdown +++ b/usr/libexec/security-misc/emerg-shutdown @@ -3,25 +3,49 @@ # Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC # See the file COPYING for copying conditions. -gcc \ - -o \ - /run/emerg-shutdown \ - -static \ - /usr/src/security-misc/emerg-shutdown.c \ - || { - printf "%s\n" 'Could not compile force-shutdown executable!' - exit 1; - } +set -o errexit +set -o nounset +set -o errtrace +set -o pipefail -readarray -t root_devices < <(/usr/libexec/helper-scripts/get-backing-devices-for-mountpoint '/'); +## Make sure globs sort in a predictable, reproducible fashion +export LC_ALL=C -## memlockd daemonizes itself, so no need to background it -memlockd -c /usr/share/security-misc/security-misc-memlockd.cfg +## Read emergency shutdown key configuration +for config_file in /etc/security-misc/emerg-shutdown/*.conf; do + source "${config_file}" +done +if [ -z "${EMERG_SHUTDOWN_KEYS}" ]; then + ## Default to Ctrl+Alt+Delete if nothing else is set + EMERG_SHUTDOWN_KEYS="KEY_LEFTCTRL|KEY_RIGHTCTRL,KEY_LEFTALT|KEY_RIGHTALT,KEY_DELETE" +fi +## Find the devices that make up the root device +readarray -t root_devices < <(/usr/libexec/helper-scripts/get-backing-devices-for-mountpoint '/') || true; +if [ "${#root_devices[@]}" = '0' ] \ + || [ "${root_devices[0]}" == '' ]; then + ## /dev/sda1 might be the right one... + root_devices[0]='/dev/sda1' +fi + +## Build the actual emerg-shutdown executable +if [ ! -f '/run/emerg-shutdown' ]; then + gcc \ + -o \ + /run/emerg-shutdown \ + -static \ + /usr/src/security-misc/emerg-shutdown.c \ + || { + printf "%s\n" 'Could not compile force-shutdown executable!' + exit 1; + } + +fi + +## memlockd daemonizes itself, so no need to background it. +memlockd -c /usr/share/security-misc/security-misc-memlockd.cfg || true + +## Launch emerg-shutdown OLDIFS="$IFS" IFS=',' -/run/emerg-shutdown "--devices=${root_devices[*]}" '--keys=KEY_LEFTCTRL|KEY_RIGHTCTRL,KEY_LEFTALT|KEY_RIGHTALT,KEY_DELETE' & -IFS="$OLDIFS" -sleep 1 -disown -exit 0 +/run/emerg-shutdown "--devices=${root_devices[*]}" "--keys=${EMERG_SHUTDOWN_KEYS}" diff --git a/usr/src/security-misc/emerg-shutdown.c b/usr/src/security-misc/emerg-shutdown.c index 8d2e1cd..5a01e17 100644 --- a/usr/src/security-misc/emerg-shutdown.c +++ b/usr/src/security-misc/emerg-shutdown.c @@ -94,6 +94,8 @@ #include #include #include +#include +#include #include #define fd_stdin 0 @@ -104,6 +106,8 @@ #define input_path_size 20 #define key_flags_len 12 +int console_fd = 0; + /* Adapted from kloak/src/keycodes.c */ struct name_value { const char *name; @@ -283,6 +287,8 @@ void print(int fd, char *str) { void print_usage() { print(fd_stderr, "Usage:\n"); print(fd_stderr, " emerg-shutdown --devices=DEVICE1[,DEVICE2...] --keys=KEY_1[,KEY_2|KEY_3...]\n"); + print(fd_stderr, "Or:\n"); + print(fd_stderr, " emerg-shutdown --instant-shutdown\n"); print(fd_stderr, "Example:\n"); print(fd_stderr, " emerg-shutdown --devices=/dev/sda3 --keys=KEY_POWER\n"); } @@ -363,7 +369,74 @@ void load_list(const char *arg, size_t *result_list_len_ref, char ***result_list } int kill_system() { - return syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL); + /* + * It isn't safe to simply call the reboot syscall here - there is a + * graphics driver bug in the i915 driver on Bookworm that will throw a + * kernel warning during shutdown. Kicksecure sets panic_on_oops and + * panic_on_warn to 1 during bootup, which means this bug will cause a + * kernel panic and thus hang the system rather than shutting down. + * + * To mitigate this, we do two things: + * + * - We disable the panic_on_oops and panic_on_warn kernel settings before + * calling the reboot syscall. This way if a warn or oops does occur, at + * least it isn't as likely to block shutdown. + * - We switch virtual terminals before initiating the shutdown. This should + * hopefully keep whatever is going wrong from going wrong in the first + * place. + * + * This is probably a good idea for any system, because switching TTYs is a + * rather basic operation that is likely to work, while forcibly shutting + * down while X11 or Wayland still has control of the display is probably + * not as well tested (if it's been tested at all). + * + * Above all else though, we want to at least *try* to shutdown. Even if all + * our attempts to switch VTs fail and /proc isn't available for us to tweak + * kernel settings, we still need to try. Therefore we absolutely do not + * crash or block, except when waiting for a VT to become activated. (If VT + * activation blocks forever, the kernel is probably horribly broken and + * would probably panic imminently anyway.) + */ + + const char *panic_on_oops_path = "/proc/sys/kernel/panic_on_oops"; + const char *panic_on_warn_path = "/proc/sys/kernel/panic_on_warn"; + + int ret = 0; + int tgt_vt = 0; + int sysctl_fd = 0; + + /* Turn off panic_on_oops. */ + sysctl_fd = open(panic_on_oops_path, O_WRONLY); + if (sysctl_fd != -1) { + write(sysctl_fd, "0", 1); + close(sysctl_fd); + } + + /* Turn off panic_on_warn. */ + sysctl_fd = open(panic_on_warn_path, O_WRONLY); + if (sysctl_fd != -1) { + write(sysctl_fd, "0", 1); + close(sysctl_fd); + } + + /* Determine which VT to switch to. Anything that isn't open yet will do. */ + ret = ioctl(console_fd, VT_OPENQRY, &tgt_vt); + if (ret == -1) { + goto trykill; + } + + /* Try to switch to it. */ + ret = ioctl(console_fd, VT_ACTIVATE, tgt_vt); + if (ret == -1) { + goto trykill; + } + + /* Wait for it to become active. */ + ioctl(console_fd, VT_WAITACTIVE, tgt_vt); + +trykill: + return syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, + LINUX_REBOOT_CMD_POWER_OFF, NULL); } int main(int argc, char **argv) { @@ -405,6 +478,9 @@ int main(int argc, char **argv) { } for (arg_idx = 1; arg_idx < argc; arg_idx++) { + if (strncmp(argv[arg_idx], "--instant-shutdown", strlen("--instant-shutdown")) == 0) { + kill_system(); + } if (strncmp(argv[arg_idx], "--devices=", strlen("--devices=")) == 0) { if (target_dev_name_raw_list != NULL) { print(fd_stderr, "--devices cannot be passed more than once!\n"); @@ -422,6 +498,12 @@ int main(int argc, char **argv) { } } + console_fd = open("/dev/console", O_RDWR); + if (console_fd == -1) { + print(fd_stderr, "Could not open /dev/console!\n"); + exit(1); + } + target_dev_list = safe_calloc(target_dev_list_len, sizeof(char *)); panic_key_list = safe_calloc(panic_key_list_len, sizeof(int *)); panic_key_active_list = safe_calloc(panic_key_list_len, sizeof(bool)); From 1a60da71eddfcc6fb72a34596c770cd754146887 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Tue, 29 Jul 2025 21:16:51 -0500 Subject: [PATCH 10/57] emerg-shutdown: Add shutdown timeout for preventing stuck shutdowns, briefly document feature set and usage --- README.md | 13 ++ .../emerg-shutdown/30_security_misc.conf | 14 ++ .../system-preset/50-security-misc.preset | 4 + usr/lib/systemd/system/emerg-shutdown.service | 3 +- .../systemd/system/ensure-shutdown.service | 18 ++ usr/libexec/security-misc/emerg-shutdown | 3 +- usr/libexec/security-misc/ensure-shutdown | 28 +++ usr/src/security-misc/emerg-shutdown.c | 200 +++++++++++++++--- 8 files changed, 257 insertions(+), 26 deletions(-) create mode 100644 usr/lib/systemd/system/ensure-shutdown.service create mode 100755 usr/libexec/security-misc/ensure-shutdown diff --git a/README.md b/README.md index cf3ea62..ac12886 100644 --- a/README.md +++ b/README.md @@ -712,6 +712,19 @@ See: * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=860040 * https://forums.whonix.org/t/cannot-use-pkexec/8129 +## Emergency shutdown + +- Forcibly powers off the system if the drive the system booted from is + removed from the system. +- Forcibly powers off the system if a user-configurable "panic key sequence" + is pressed (Ctrl+Alt+Delete by default). +- Forcibly powers off the system if + `sudo /run/emerg-shutdown --instant-shutdown` is called. +- Optional - Forcibly powers off the system if shutdown gets stuck for longer + than a user-configurable number of seconds (30 by default). Requires tuning + by the user to function properly, see notes in + `/etc/security-misc/emerg-shutdown/30_security_misc.conf`. + ## Application-specific hardening - Enables "`apt-get --error-on=any`" which makes apt exit non-zero for diff --git a/etc/security-misc/emerg-shutdown/30_security_misc.conf b/etc/security-misc/emerg-shutdown/30_security_misc.conf index a4bb394..e844374 100644 --- a/etc/security-misc/emerg-shutdown/30_security_misc.conf +++ b/etc/security-misc/emerg-shutdown/30_security_misc.conf @@ -17,3 +17,17 @@ ## The default key sequence triggers a shutdown when Ctrl+Alt+Delete is ## pressed, allowing the use of either the left or right Ctrl and Alt keys. EMERG_SHUTDOWN_KEYS="KEY_LEFTCTRL|KEY_RIGHTCTRL,KEY_LEFTALT|KEY_RIGHTALT,KEY_DELETE" + +## Set the maximum number of seconds shutdown can take. If shutdown gets stuck +## for longer than this, the system will forcibly power down. +## +## NOTE: This requires ensure-shutdown.service to be enabled, which is not +## done by default. Enabling ensure-shutdown.service will cause shutdown to +## always take at least as long as systemd's DefaultTimeoutStopSec (which by +## default is 90 seconds). If you are going to enable ensure-shutdown.service, +## it is highly recommended to set DefaultTimeoutStopSec to a much smaller +## value, such as 5 seconds. The maximum shutdown time set here should be at +## least 10 seconds *longer* than DefaultTimeoutStopSec, to give normal +## shutdown a chance to actually succeed before forcibly shutting down the +## system. +ENSURE_SHUTDOWN_TIMEOUT=30 diff --git a/usr/lib/systemd/system-preset/50-security-misc.preset b/usr/lib/systemd/system-preset/50-security-misc.preset index 1895526..004563c 100644 --- a/usr/lib/systemd/system-preset/50-security-misc.preset +++ b/usr/lib/systemd/system-preset/50-security-misc.preset @@ -17,3 +17,7 @@ disable proc-hidepid.service ## Disable due to issues. See: ## https://github.com/Kicksecure/security-misc/issues/159 disable harden-module-loading.service + +## Disable due to timing difficulties. See: +## https://github.com/systemd/systemd/issues/38261#issuecomment-3134580852 +disable ensure-shutdown.service diff --git a/usr/lib/systemd/system/emerg-shutdown.service b/usr/lib/systemd/system/emerg-shutdown.service index c1fca25..0eb4258 100644 --- a/usr/lib/systemd/system/emerg-shutdown.service +++ b/usr/lib/systemd/system/emerg-shutdown.service @@ -6,8 +6,9 @@ Description=Emergency shutdown when boot media is removed Documentation=https://github.com/Kicksecure/security-misc [Service] -Type=exec +Type=notify ExecStart=/usr/libexec/security-misc/emerg-shutdown +NotifyAccess=main [Install] WantedBy=multi-user.target diff --git a/usr/lib/systemd/system/ensure-shutdown.service b/usr/lib/systemd/system/ensure-shutdown.service new file mode 100644 index 0000000..30bcc23 --- /dev/null +++ b/usr/lib/systemd/system/ensure-shutdown.service @@ -0,0 +1,18 @@ +## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC +## See the file COPYING for copying conditions. + +[Unit] +Description=Forcibly shut down the system if normal shutdown gets stuck +Documentation=https://github.com/Kicksecure/security-misc +Wants=emerg-shutdown.service +After=emerg-shutdown.service + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/libexec/security-misc/ensure-shutdown +ExecStop=bash -c -- 'echo "d" > /run/emerg-shutdown-trigger' +KillMode=process + +[Install] +WantedBy=multi-user.target diff --git a/usr/libexec/security-misc/emerg-shutdown b/usr/libexec/security-misc/emerg-shutdown index d8dc7f9..81dc9c1 100755 --- a/usr/libexec/security-misc/emerg-shutdown +++ b/usr/libexec/security-misc/emerg-shutdown @@ -39,9 +39,10 @@ if [ ! -f '/run/emerg-shutdown' ]; then printf "%s\n" 'Could not compile force-shutdown executable!' exit 1; } - fi +systemd-notify --ready + ## memlockd daemonizes itself, so no need to background it. memlockd -c /usr/share/security-misc/security-misc-memlockd.cfg || true diff --git a/usr/libexec/security-misc/ensure-shutdown b/usr/libexec/security-misc/ensure-shutdown new file mode 100755 index 0000000..8eb663f --- /dev/null +++ b/usr/libexec/security-misc/ensure-shutdown @@ -0,0 +1,28 @@ +#!/bin/bash + +# Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC +# See the file COPYING for copying conditions. + +set -o errexit +set -o nounset +set -o errtrace +set -o pipefail + +source /usr/libexec/helper-scripts/strings.bsh + +## Make sure globs sort in a predictable, reproducible fashion +export LC_ALL=C + +## Read emergency shutdown key configuration +for config_file in /etc/security-misc/emerg-shutdown/*.conf; do + source "${config_file}" +done +if [ -z "${ENSURE_SHUTDOWN_TIMEOUT}" ] \ + || ! is_whole_number "${ENSURE_SHUTDOWN_TIMEOUT}"; then + ENSURE_SHUTDOWN_TIMEOUT=30; +fi + +/run/emerg-shutdown --monitor-fifo "--timeout=${ENSURE_SHUTDOWN_TIMEOUT}" & +sleep 1 +disown +exit 0 diff --git a/usr/src/security-misc/emerg-shutdown.c b/usr/src/security-misc/emerg-shutdown.c index 5a01e17..83cb6de 100644 --- a/usr/src/security-misc/emerg-shutdown.c +++ b/usr/src/security-misc/emerg-shutdown.c @@ -74,13 +74,6 @@ * (there are other similar posts as well). */ -/* - * TODO: Consider handling signals more gracefully (perhaps use ppoll instead - * of poll, handle things like EINTR, etc.). Right now the plan is to simply - * terminate when a signal is received and let systemd restart the process, - * but it might be better to just be signal-resilient. - */ - #include #include #include @@ -97,6 +90,10 @@ #include #include #include +#include +#include +#include +#include #define fd_stdin 0 #define fd_stdout 1 @@ -106,6 +103,11 @@ #define input_path_size 20 #define key_flags_len 12 +#define hw_monitor_val 1 +#define fifo_monitor_val 2 + +#define max_sig_num 31 + int console_fd = 0; /* Adapted from kloak/src/keycodes.c */ @@ -289,6 +291,8 @@ void print_usage() { print(fd_stderr, " emerg-shutdown --devices=DEVICE1[,DEVICE2...] --keys=KEY_1[,KEY_2|KEY_3...]\n"); print(fd_stderr, "Or:\n"); print(fd_stderr, " emerg-shutdown --instant-shutdown\n"); + print(fd_stderr, "Or:\n"); + print(fd_stderr, " emerg-shutdown --monitor-fifo --timeout=TIMEOUT\n"); print(fd_stderr, "Example:\n"); print(fd_stderr, " emerg-shutdown --devices=/dev/sda3 --keys=KEY_POWER\n"); } @@ -439,7 +443,8 @@ trykill: LINUX_REBOOT_CMD_POWER_OFF, NULL); } -int main(int argc, char **argv) { +/* Monitor for device removal and emergency shutdown key combos. */ +void hw_monitor(int argc, char **argv) { /* Working variables */ size_t target_dev_list_len = 0; char **target_dev_name_raw_list = NULL; @@ -464,23 +469,7 @@ int main(int argc, char **argv) { int ie_idx = 0; size_t kg_idx = 0; - /* Prerequisite check */ - if (getuid() != 0) { - print(fd_stderr, "This program must be run as root!\n"); - exit(1); - } - - /* Argument parsing */ - if (argc < 2) { - print(fd_stderr, "Invalid number of arguments!\n"); - print_usage(); - exit(1); - } - for (arg_idx = 1; arg_idx < argc; arg_idx++) { - if (strncmp(argv[arg_idx], "--instant-shutdown", strlen("--instant-shutdown")) == 0) { - kill_system(); - } if (strncmp(argv[arg_idx], "--devices=", strlen("--devices=")) == 0) { if (target_dev_name_raw_list != NULL) { print(fd_stderr, "--devices cannot be passed more than once!\n"); @@ -495,6 +484,12 @@ int main(int argc, char **argv) { exit(1); } load_list(argv[arg_idx], &panic_key_list_len, &panic_key_str_list, ",", true); + } else { + print(fd_stderr, "Unrecognized argument '"); + print(fd_stderr, argv[arg_idx]); + print(fd_stderr, "' passed!\n"); + print_usage(); + exit(1); } } @@ -851,4 +846,161 @@ next_str: } } } + + print(fd_stderr, "Hardware monitor poll gave up!\n"); + exit(1); +} + +/* + * Monitor for a kill command on a fifo. Two commands are recognized: + * + * - 'k': Instantly kill the system. + * - 'd': Wait 15 seconds, then kill the system. This is used to keep systemd + * from delaying shutdown excessively. + */ +void fifo_monitor(int argc, char **argv) { + long monitor_fifo_timeout = 0; + char *arg_copy = NULL; + char *arg_part = NULL; + char *arg_num_end = NULL; + const char *trigger_fifo_path = "/run/emerg-shutdown-trigger"; + int trigger_fifo_fd = 0; + struct pollfd trigger_fifo_poll = { 0 }; + char trigger_fifo_charbuf = '\0'; + ssize_t trigger_fifo_readlen = 0; + int sig_idx = 0; + struct sigaction sigact_swallow = { 0 }; + + if (strncmp(argv[2], "--timeout=", strlen("--timeout=")) != 0) { + print(fd_stderr, "Timeout not passed for --monitor-fifo!\n"); + print_usage(); + exit(1); + } + + arg_copy = safe_calloc(1, strlen(argv[2]) + 1); + memcpy(arg_copy, argv[2], strlen(argv[2]) + 1); + /* returns "--timeout" */ + arg_part = strtok(arg_copy, "="); + /* returns everything after the = sign */ + arg_part = strtok(NULL, ""); + monitor_fifo_timeout = strtol(arg_part, &arg_num_end, 10); + if (errno == ERANGE) { + print(fd_stderr, "Timeout out of range!\n"); + print_usage(); + exit(1); + } + if (*arg_num_end != '\0') { + print(fd_stderr, "Timeout is not purely numeric!\n"); + print_usage(); + exit(1); + } + if (monitor_fifo_timeout < 1) { + print(fd_stderr, "Timeout is less than one!\n"); + print_usage(); + exit(1); + } + + free(arg_copy); + arg_copy = NULL; + arg_part = NULL; + arg_num_end = NULL; + + if (mkfifo(trigger_fifo_path, 0777) == -1) { + print(fd_stderr, "Cannot create trigger fifo!\n"); + exit(1); + } + + trigger_fifo_fd = open(trigger_fifo_path, O_RDONLY | O_NONBLOCK); + if (trigger_fifo_fd == -1) { + print(fd_stderr, "Cannot open trigger fifo for reading!\n"); + exit(1); + } + + trigger_fifo_poll.fd = trigger_fifo_fd; + trigger_fifo_poll.events = POLLIN; + + /* Swallow all signals that we can. */ + sigact_swallow.sa_handler = SIG_IGN; + for (sig_idx = 1; sig_idx < max_sig_num; sig_idx++) { + if (sig_idx == SIGSTOP) { + continue; + } + if (sig_idx == SIGKILL) { + continue; + } + if (sigaction(sig_idx, &sigact_swallow, NULL) == -1) { + print(fd_stderr, "Failed to set up signal ignores!\n"); + exit(1); + } + } + for (sig_idx = SIGRTMIN; sig_idx <= SIGRTMAX; sig_idx++) { + if (sigaction(sig_idx, &sigact_swallow, NULL) == -1) { + print(fd_stderr, "Failed to set up real-time signal ignores!\n"); + exit(1); + } + } + + while (poll(&trigger_fifo_poll, 1, -1) != -1) { + trigger_fifo_readlen = read(trigger_fifo_fd, &trigger_fifo_charbuf, 1); + if (trigger_fifo_readlen != 1) { + print(fd_stderr, "Error reading from trigger fifo!\n"); + exit(1); + } + if (trigger_fifo_charbuf == 'k') { + kill_system(); + } else if (trigger_fifo_charbuf == 'd') { + sleep(monitor_fifo_timeout); + kill_system(); + } + } + + print(fd_stderr, "Trigger fifo poll gave up!\n"); + exit(1); +} + +int main(int argc, char **argv) { + int monitor_mode = hw_monitor_val; + + /* Prerequisite check */ + if (getuid() != 0) { + print(fd_stderr, "This program must be run as root!\n"); + exit(1); + } + + if (argc < 2) { + print(fd_stderr, "Not enough arguments!\n"); + print_usage(); + exit(1); + } + + if (strcmp(argv[1], "--instant-shutdown") == 0) { + if (argc != 2) { + print(fd_stderr, "Too many arguments, --instant-shutdown must be passed alone!\n"); + print_usage(); + exit(1); + } + + kill_system(); + } + if (strcmp(argv[1], "--monitor-fifo") == 0) { + if (argc != 3) { + print(fd_stderr, "Wrong number of arguments for --monitor-fifo!\n"); + print_usage(); + exit(1); + } + + monitor_mode = fifo_monitor_val; + } + + if (monitor_mode == hw_monitor_val) { + /* hw_monitor handles its own argument parsing */ + hw_monitor(argc, argv); + } else if (monitor_mode == fifo_monitor_val) { + /* fifo_monitor handles its own argument parsing */ + fifo_monitor(argc, argv); + } else { + print(fd_stderr, "Unknown monitor mode chosen!\n"); + print_usage(); + exit(1); + } } From b9416fa77a1e8850c5f579314875671799a55c60 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Sun, 3 Aug 2025 07:15:41 -0400 Subject: [PATCH 11/57] validate configuration file --- usr/libexec/security-misc/emerg-shutdown | 1 + usr/libexec/security-misc/ensure-shutdown | 1 + 2 files changed, 2 insertions(+) diff --git a/usr/libexec/security-misc/emerg-shutdown b/usr/libexec/security-misc/emerg-shutdown index 81dc9c1..189afe6 100755 --- a/usr/libexec/security-misc/emerg-shutdown +++ b/usr/libexec/security-misc/emerg-shutdown @@ -13,6 +13,7 @@ export LC_ALL=C ## Read emergency shutdown key configuration for config_file in /etc/security-misc/emerg-shutdown/*.conf; do + bash -n "${config_file}" source "${config_file}" done if [ -z "${EMERG_SHUTDOWN_KEYS}" ]; then diff --git a/usr/libexec/security-misc/ensure-shutdown b/usr/libexec/security-misc/ensure-shutdown index 8eb663f..3aac905 100755 --- a/usr/libexec/security-misc/ensure-shutdown +++ b/usr/libexec/security-misc/ensure-shutdown @@ -15,6 +15,7 @@ export LC_ALL=C ## Read emergency shutdown key configuration for config_file in /etc/security-misc/emerg-shutdown/*.conf; do + bash -n "${config_file}" source "${config_file}" done if [ -z "${ENSURE_SHUTDOWN_TIMEOUT}" ] \ From 4da810c8fa4fd40b8701e7dfe217125d965ee03e Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Sun, 3 Aug 2025 07:16:00 -0400 Subject: [PATCH 12/57] comment --- etc/security-misc/emerg-shutdown/30_security_misc.conf | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/etc/security-misc/emerg-shutdown/30_security_misc.conf b/etc/security-misc/emerg-shutdown/30_security_misc.conf index e844374..904610b 100644 --- a/etc/security-misc/emerg-shutdown/30_security_misc.conf +++ b/etc/security-misc/emerg-shutdown/30_security_misc.conf @@ -1,8 +1,9 @@ ## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC ## See the file COPYING for copying conditions. -## Please use "/etc/security-misc/emerg-shutdown/50_user.conf" for your custom -## configuration, which will override the defaults found here. +## Please use "/etc/security-misc/emerg-shutdown/50_user.conf" or +## "/usr/local/etc/security-misc/emerg-shutdown/50_user.conf" +## for your custom configuration, which will override the defaults found here. ## When Kicksecure is updated, this file may be overwritten. ## Set the key combo for forcing immediate shutdown. See the "Keys and From 92bcd824e4af8a90a18a7726d4a5715c0b20e2ca Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Sun, 3 Aug 2025 07:17:25 -0400 Subject: [PATCH 13/57] also parse /usr/local/etc --- usr/libexec/security-misc/emerg-shutdown | 2 +- usr/libexec/security-misc/ensure-shutdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/libexec/security-misc/emerg-shutdown b/usr/libexec/security-misc/emerg-shutdown index 189afe6..fe982ca 100755 --- a/usr/libexec/security-misc/emerg-shutdown +++ b/usr/libexec/security-misc/emerg-shutdown @@ -12,7 +12,7 @@ set -o pipefail export LC_ALL=C ## Read emergency shutdown key configuration -for config_file in /etc/security-misc/emerg-shutdown/*.conf; do +for config_file in /etc/security-misc/emerg-shutdown/*.conf /usr/local/etc/security-misc/emerg-shutdown/*.conf; do bash -n "${config_file}" source "${config_file}" done diff --git a/usr/libexec/security-misc/ensure-shutdown b/usr/libexec/security-misc/ensure-shutdown index 3aac905..48643ea 100755 --- a/usr/libexec/security-misc/ensure-shutdown +++ b/usr/libexec/security-misc/ensure-shutdown @@ -14,7 +14,7 @@ source /usr/libexec/helper-scripts/strings.bsh export LC_ALL=C ## Read emergency shutdown key configuration -for config_file in /etc/security-misc/emerg-shutdown/*.conf; do +for config_file in /etc/security-misc/emerg-shutdown/*.conf /usr/local/etc/security-misc/emerg-shutdown/*.conf; do bash -n "${config_file}" source "${config_file}" done From d1bca0204fa1dac9ec3fb6e9b121af9526778181 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Sun, 3 Aug 2025 11:33:03 +0000 Subject: [PATCH 14/57] bumped changelog version --- changelog.upstream | 87 ++++++++++++++++++++++++++++++++++++++++++++++ debian/changelog | 6 ++++ 2 files changed, 93 insertions(+) diff --git a/changelog.upstream b/changelog.upstream index 6a483f4..1ecfbea 100644 --- a/changelog.upstream +++ b/changelog.upstream @@ -1,3 +1,53 @@ +commit 92bcd824e4af8a90a18a7726d4a5715c0b20e2ca +Author: Patrick Schleizer +Date: Sun Aug 3 07:17:25 2025 -0400 + + also parse /usr/local/etc + +commit 4da810c8fa4fd40b8701e7dfe217125d965ee03e +Author: Patrick Schleizer +Date: Sun Aug 3 07:16:00 2025 -0400 + + comment + +commit b9416fa77a1e8850c5f579314875671799a55c60 +Author: Patrick Schleizer +Date: Sun Aug 3 07:15:41 2025 -0400 + + validate configuration file + +commit 4ba029471e8c12d5691f7ee94897137fb3cbe15f +Merge: c1e76aa 1a60da7 +Author: Patrick Schleizer +Date: Sun Aug 3 07:04:20 2025 -0400 + + Merge remote-tracking branch 'ArrayBolt3/arraybolt3/emerg-shutdown' + +commit 1a60da71eddfcc6fb72a34596c770cd754146887 +Author: Aaron Rainbolt +Date: Tue Jul 29 21:16:51 2025 -0500 + + emerg-shutdown: Add shutdown timeout for preventing stuck shutdowns, briefly document feature set and usage + +commit e42078e90d7d7e5339a7c4682eb93c844fd38580 +Author: Aaron Rainbolt +Date: Mon Jul 28 20:42:14 2025 -0500 + + emerg-shutdown: fix the hang-on-shutdown bug, add autodetection of new keyboards, shutdown key configuration, and instant shutdown option + +commit a1d1c5603300106f06c1a798088521b77430ff95 +Merge: 5889d13 c1e76aa +Author: Aaron Rainbolt +Date: Sun Jul 27 21:43:43 2025 -0500 + + Merge branch 'master' into arraybolt3/emerg-shutdown + +commit c1e76aa52cd28f38c1ab6550e0f4de0010a9ea14 +Author: Patrick Schleizer +Date: Mon Jul 21 10:00:25 2025 +0000 + + bumped changelog version + commit 36114e29a2ce1045b5f5d82372fcf0463efc5ca7 Merge: e3ce9c3 f851886 Author: Patrick Schleizer @@ -14,12 +64,43 @@ Date: Mon Jul 21 05:58:44 2025 -0400 Enable `indirect_target_selection=force` +commit 5889d134a23b3d4f8db5d81171ea12907bb10d4d +Author: Aaron Rainbolt +Date: Sun Jul 20 14:14:09 2025 -0500 + + emerg-shutdow: Improve recvmsg handling, call reboot syscall directly + commit 6f9763f525097b8f8ad5f9864c1694a2642e1bd6 Author: raja-grewal Date: Sat Jul 19 05:19:27 2025 +0000 Enable `indirect_target_selection=force` +commit b745c8ddae74d5e1684919442fa74d64e95263b8 +Author: Aaron Rainbolt +Date: Mon Jul 14 21:51:52 2025 -0500 + + emerg-shutdown: Enable actual shutdown code, fix infinite loop when started too early + +commit e387086de4b6e6b90b23d4c32ddf8a566beb858c +Author: Aaron Rainbolt +Date: Mon Jul 14 21:05:16 2025 -0500 + + Allow specifying alternative keys in panic key combo, fix optical disk eject handling + +commit dfb6f143f0324d0903ae2dd106bc0fb6907c1cb0 +Author: Aaron Rainbolt +Date: Sun Jul 13 20:53:29 2025 -0500 + + Add panic key handling to emergency shutdown utility + +commit 2a7071055f94f984398fe2ec49c32b206913bea2 +Merge: f3d46ee e3ce9c3 +Author: Aaron Rainbolt +Date: Sun Jul 13 15:21:34 2025 -0500 + + Merge branch 'master' into arraybolt3/emerg-shutdown + commit e3ce9c38c5b241f789945de7229c0ee15fa0a266 Author: Patrick Schleizer Date: Wed Jul 2 20:52:17 2025 +0000 @@ -473,6 +554,12 @@ Date: Thu May 15 15:06:10 2025 -0400 * Only rudimentary tests were conducted +commit f3d46ee56233c4ef0552c20304413d137e90acfe +Author: Aaron Rainbolt +Date: Fri May 9 18:46:41 2025 -0500 + + Add emergency shutdown feature, triggered by root device removal + commit 341dce33fb806ab03822470e6af91604662c22dd Author: Patrick Schleizer Date: Fri Apr 25 09:54:23 2025 +0000 diff --git a/debian/changelog b/debian/changelog index e108966..907f1b8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +security-misc (3:46.5-1) unstable; urgency=medium + + * New upstream version (local package). + + -- Patrick Schleizer Sun, 03 Aug 2025 11:33:03 +0000 + security-misc (3:46.4-1) unstable; urgency=medium * New upstream version (local package). From 63f29093416a5f21ae14b398cf805c864b5541d7 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Sun, 3 Aug 2025 15:00:14 -0500 Subject: [PATCH 15/57] Fix emerg-shutdown and ensure-shutdown libexec scripts, start emerg-shutdown and ensure-shutdown earlier --- .../emerg-shutdown/30_security_misc.conf | 18 ++++++------ .../system-preset/50-security-misc.preset | 1 + usr/lib/systemd/system/block-shutdown.service | 29 +++++++++++++++++++ usr/lib/systemd/system/emerg-shutdown.service | 8 ++++- .../system/ensure-shutdown-trigger.service | 18 ++++++++++++ .../systemd/system/ensure-shutdown.service | 9 +++++- usr/libexec/security-misc/emerg-shutdown | 6 ++-- usr/libexec/security-misc/ensure-shutdown | 6 ++-- usr/share/lintian/overrides/security-misc | 3 ++ 9 files changed, 83 insertions(+), 15 deletions(-) create mode 100644 usr/lib/systemd/system/block-shutdown.service create mode 100644 usr/lib/systemd/system/ensure-shutdown-trigger.service diff --git a/etc/security-misc/emerg-shutdown/30_security_misc.conf b/etc/security-misc/emerg-shutdown/30_security_misc.conf index 904610b..4ec6cbd 100644 --- a/etc/security-misc/emerg-shutdown/30_security_misc.conf +++ b/etc/security-misc/emerg-shutdown/30_security_misc.conf @@ -22,13 +22,13 @@ EMERG_SHUTDOWN_KEYS="KEY_LEFTCTRL|KEY_RIGHTCTRL,KEY_LEFTALT|KEY_RIGHTALT,KEY_DEL ## Set the maximum number of seconds shutdown can take. If shutdown gets stuck ## for longer than this, the system will forcibly power down. ## -## NOTE: This requires ensure-shutdown.service to be enabled, which is not -## done by default. Enabling ensure-shutdown.service will cause shutdown to -## always take at least as long as systemd's DefaultTimeoutStopSec (which by -## default is 90 seconds). If you are going to enable ensure-shutdown.service, -## it is highly recommended to set DefaultTimeoutStopSec to a much smaller -## value, such as 5 seconds. The maximum shutdown time set here should be at -## least 10 seconds *longer* than DefaultTimeoutStopSec, to give normal -## shutdown a chance to actually succeed before forcibly shutting down the -## system. +## NOTE: This requires ensure-shutdown.service and +## ensure-shutdown-trigger.service to be enabled, which is not done by +## default. Enabling ensure-shutdown.service will cause shutdown to always +## take at least as long as systemd's DefaultTimeoutStopSec (which by default +## is 90 seconds). If you are going to enable ensure-shutdown.service, it is +## highly recommended to set DefaultTimeoutStopSec to a much smaller value, +## such as 5 seconds. The maximum shutdown time set here should be at least 10 +## seconds *longer* than DefaultTimeoutStopSec, to give normal shutdown a +## chance to actually succeed before forcibly shutting down the system. ENSURE_SHUTDOWN_TIMEOUT=30 diff --git a/usr/lib/systemd/system-preset/50-security-misc.preset b/usr/lib/systemd/system-preset/50-security-misc.preset index 004563c..4b44458 100644 --- a/usr/lib/systemd/system-preset/50-security-misc.preset +++ b/usr/lib/systemd/system-preset/50-security-misc.preset @@ -21,3 +21,4 @@ disable harden-module-loading.service ## Disable due to timing difficulties. See: ## https://github.com/systemd/systemd/issues/38261#issuecomment-3134580852 disable ensure-shutdown.service +disable ensure-shutdown-trigger.service diff --git a/usr/lib/systemd/system/block-shutdown.service b/usr/lib/systemd/system/block-shutdown.service new file mode 100644 index 0000000..3d4be21 --- /dev/null +++ b/usr/lib/systemd/system/block-shutdown.service @@ -0,0 +1,29 @@ +## Copyright (C) 2019 - 2025 ENCRYPTED SUPPORT LLC +## See the file COPYING for copying conditions. + +## This unit, if uncommented and started, will prevent the system from ever +## shutting down unless ensure-shutdown.service is enabled and correctly +## configured. If you have enabled ensure-shutdown.service and tuned the +## ENSURE_SHUTDOWN_TIMEOUT and DefaultTimeoutStopSec variables (in +## /etc/security-misc/emerg-shutdown/30_security_misc.conf and +## /etc/systemd/system.conf respectively) and want to make sure +## ensure-shutdown.service actually works, you can uncomment this unit and +## start it with `sudo systemctl start block-shutdown.service`. If the systems +## successfully powers down even with this unit started, +## ensure-shutdown.service is working. + +# [Unit] +# Description=Blocks shutdown indefinitely unless ensure-shutdown.service is enabled +# +# [Service] +# Type=exec +# ExecStart=bash -c -- "trap '' SIGTERM; sleep infinity" +# KillSignal=SIGTERM +# FinalKillSignal=SIGTERM +# RestartKillSignal=SIGTERM +# WatchdogSignal=SIGTERM +# SendSIGHUP=no +# TimeoutStopSec=infinity +# +# [Install] +# WantedBy=multi-user.target diff --git a/usr/lib/systemd/system/emerg-shutdown.service b/usr/lib/systemd/system/emerg-shutdown.service index 0eb4258..fa0dd62 100644 --- a/usr/lib/systemd/system/emerg-shutdown.service +++ b/usr/lib/systemd/system/emerg-shutdown.service @@ -4,6 +4,12 @@ [Unit] Description=Emergency shutdown when boot media is removed Documentation=https://github.com/Kicksecure/security-misc +DefaultDependencies=no +Before=sysinit.target +Requires=udev.service +After=udev.service +Requires=local-fs.service +After=local-fs.service [Service] Type=notify @@ -11,4 +17,4 @@ ExecStart=/usr/libexec/security-misc/emerg-shutdown NotifyAccess=main [Install] -WantedBy=multi-user.target +WantedBy=sysinit.target diff --git a/usr/lib/systemd/system/ensure-shutdown-trigger.service b/usr/lib/systemd/system/ensure-shutdown-trigger.service new file mode 100644 index 0000000..0945e43 --- /dev/null +++ b/usr/lib/systemd/system/ensure-shutdown-trigger.service @@ -0,0 +1,18 @@ +## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC +## See the file COPYING for copying conditions. + +## NOTE: If enabling this unit, also enable ensure-shutdown.service, otherwise +## this will do nothing. + +[Unit] +Description=Forcibly shut down the system if normal shutdown gets stuck (alternate trigger unit) +Documentation=https://github.com/Kicksecure/security-misc + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=true +ExecStop=bash -c -- 'echo "d" > /run/emerg-shutdown-trigger' + +[Install] +WantedBy=multi-user.target diff --git a/usr/lib/systemd/system/ensure-shutdown.service b/usr/lib/systemd/system/ensure-shutdown.service index 30bcc23..52eb487 100644 --- a/usr/lib/systemd/system/ensure-shutdown.service +++ b/usr/lib/systemd/system/ensure-shutdown.service @@ -1,9 +1,16 @@ ## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC ## See the file COPYING for copying conditions. +## NOTE: If enabling this unit, also enable ensure-shutdown-trigger.service, +## otherwise this will likely be unable to unstick a stuck shutdown. + [Unit] Description=Forcibly shut down the system if normal shutdown gets stuck Documentation=https://github.com/Kicksecure/security-misc +DefaultDependencies=no +Before=sysinit.target +Requires=udev.service +After=udev.service Wants=emerg-shutdown.service After=emerg-shutdown.service @@ -15,4 +22,4 @@ ExecStop=bash -c -- 'echo "d" > /run/emerg-shutdown-trigger' KillMode=process [Install] -WantedBy=multi-user.target +WantedBy=sysinit.target diff --git a/usr/libexec/security-misc/emerg-shutdown b/usr/libexec/security-misc/emerg-shutdown index fe982ca..1afebcb 100755 --- a/usr/libexec/security-misc/emerg-shutdown +++ b/usr/libexec/security-misc/emerg-shutdown @@ -13,8 +13,10 @@ export LC_ALL=C ## Read emergency shutdown key configuration for config_file in /etc/security-misc/emerg-shutdown/*.conf /usr/local/etc/security-misc/emerg-shutdown/*.conf; do - bash -n "${config_file}" - source "${config_file}" + if [ -f "${config_file}" ]; then + bash -n "${config_file}" + source "${config_file}" + fi done if [ -z "${EMERG_SHUTDOWN_KEYS}" ]; then ## Default to Ctrl+Alt+Delete if nothing else is set diff --git a/usr/libexec/security-misc/ensure-shutdown b/usr/libexec/security-misc/ensure-shutdown index 48643ea..85ab31d 100755 --- a/usr/libexec/security-misc/ensure-shutdown +++ b/usr/libexec/security-misc/ensure-shutdown @@ -15,8 +15,10 @@ export LC_ALL=C ## Read emergency shutdown key configuration for config_file in /etc/security-misc/emerg-shutdown/*.conf /usr/local/etc/security-misc/emerg-shutdown/*.conf; do - bash -n "${config_file}" - source "${config_file}" + if [ -f "${config_file}" ]; then + bash -n "${config_file}" + source "${config_file}" + fi done if [ -z "${ENSURE_SHUTDOWN_TIMEOUT}" ] \ || ! is_whole_number "${ENSURE_SHUTDOWN_TIMEOUT}"; then diff --git a/usr/share/lintian/overrides/security-misc b/usr/share/lintian/overrides/security-misc index 26c3c70..8351249 100644 --- a/usr/share/lintian/overrides/security-misc +++ b/usr/share/lintian/overrides/security-misc @@ -15,3 +15,6 @@ security-misc: uses-dpkg-database-directly [usr/bin/remount-secure] ## Special target to make sure this runs as non-parallelized as possible to avoid race conditions. security-misc: systemd-service-file-refers-to-unusual-wantedby-target sysinit-post.target [usr/lib/systemd/system/remount-secure.service] + +## False-positive. Unit is commented out by default. +security-misc: systemd-service-file-missing-install-key [usr/lib/systemd/system/block-shutdown.service] From c2690efcacbf7be7c57751ba1cee7f910d350cfc Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Mon, 4 Aug 2025 09:27:11 +0000 Subject: [PATCH 16/57] bumped changelog version --- changelog.upstream | 19 +++++++++++++++++++ debian/changelog | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/changelog.upstream b/changelog.upstream index 1ecfbea..0381ecf 100644 --- a/changelog.upstream +++ b/changelog.upstream @@ -1,3 +1,22 @@ +commit 166bc257b0b2eea87d684cc847bf6da1fba7c4b4 +Merge: d1bca02 63f2909 +Author: Patrick Schleizer +Date: Mon Aug 4 05:26:55 2025 -0400 + + Merge remote-tracking branch 'ArrayBolt3/arraybolt3/emerg-shutdown' + +commit 63f29093416a5f21ae14b398cf805c864b5541d7 +Author: Aaron Rainbolt +Date: Sun Aug 3 15:00:14 2025 -0500 + + Fix emerg-shutdown and ensure-shutdown libexec scripts, start emerg-shutdown and ensure-shutdown earlier + +commit d1bca0204fa1dac9ec3fb6e9b121af9526778181 +Author: Patrick Schleizer +Date: Sun Aug 3 11:33:03 2025 +0000 + + bumped changelog version + commit 92bcd824e4af8a90a18a7726d4a5715c0b20e2ca Author: Patrick Schleizer Date: Sun Aug 3 07:17:25 2025 -0400 diff --git a/debian/changelog b/debian/changelog index 907f1b8..460f1a2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +security-misc (3:46.6-1) unstable; urgency=medium + + * New upstream version (local package). + + -- Patrick Schleizer Mon, 04 Aug 2025 09:27:11 +0000 + security-misc (3:46.5-1) unstable; urgency=medium * New upstream version (local package). From 5a17e67c0a7678300f6342d5c90ded5494ebc838 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Tue, 5 Aug 2025 20:14:07 -0500 Subject: [PATCH 17/57] Fix local-fs.target dependency in emerg-shutdown.service --- usr/lib/systemd/system/emerg-shutdown.service | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/lib/systemd/system/emerg-shutdown.service b/usr/lib/systemd/system/emerg-shutdown.service index fa0dd62..f2dc2e8 100644 --- a/usr/lib/systemd/system/emerg-shutdown.service +++ b/usr/lib/systemd/system/emerg-shutdown.service @@ -8,8 +8,8 @@ DefaultDependencies=no Before=sysinit.target Requires=udev.service After=udev.service -Requires=local-fs.service -After=local-fs.service +Requires=local-fs.target +After=local-fs.target [Service] Type=notify From 1f7525722e7027b5c3379460eee5f62669631dee Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Wed, 6 Aug 2025 01:48:47 +0000 Subject: [PATCH 18/57] Enable `cfi=kcfi` --- README.md | 5 ++--- etc/default/grub.d/40_kernel_hardening.cfg | 14 +++++--------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ac12886..ed9c740 100644 --- a/README.md +++ b/README.md @@ -221,9 +221,8 @@ Kernel space: - Disable 32-bit vDSO mappings as they are a legacy compatibility feature. -- Optional - Use kCFI as the default CFI implementation (when using Linux kernel >= 6.2) - since it may be slightly more resilient to attacks that are able to write - arbitrary executables in memory. +- Use kCFI as the default CFI implementation as it is more resilient to attacks that able + to write arbitrary executables into memory omitting the necessary hash validation. - Optional - Disable support for all x86 processes and syscalls (when using Linux kernel >= 6.7) to reduce attack surface. diff --git a/etc/default/grub.d/40_kernel_hardening.cfg b/etc/default/grub.d/40_kernel_hardening.cfg index 671c28b..685f673 100644 --- a/etc/default/grub.d/40_kernel_hardening.cfg +++ b/etc/default/grub.d/40_kernel_hardening.cfg @@ -186,15 +186,14 @@ GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX vdso32=0" ## The default implementation is FineIBT as of Linux kernel 6.2. ## The Intel-developed IBT (Indirect Branch Tracking) is only used if supported by the CPU. ## kCFI is software-only while FineIBT is a hybrid software/hardware implementation. -## FineIBT may result in some performance benefits as it only performs checking at destinations. +## FineIBT may result in some performance benefits as it only performs hash checks at the destinations. +## kCFI mandates hash validation at the source (which is randomized), making it more difficult to bypass. ## FineIBT is considered weaker against attacks that can write arbitrary executables into memory. -## Upstream hardening work has provided users the ability to disable FineIBT based on requests. -## Choice of CFI implementation is highly dependent on user threat model as there are pros/cons to both. -## Do not modify from the default setting if unsure of implications. ## -## https://lore.kernel.org/all/20221027092842.699804264@infradead.org/ +## https://lwn.net/Articles/891976/ ## https://lore.kernel.org/lkml/202210010918.4918F847C4@keescook/T/#u ## https://lore.kernel.org/lkml/202210182217.486CBA50@keescook/T/ +## https://lore.kernel.org/all/20221027092842.699804264@infradead.org/ ## https://lore.kernel.org/lkml/202407150933.E1871BE@keescook/ ## https://isopenbsdsecu.re/mitigations/forward_edge_cfi/ ## https://docs.kernel.org/next/x86/shstk.html @@ -205,10 +204,7 @@ GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX vdso32=0" ## KSPP=yes ## KSPP sets the kernel parameter. ## -## TODO: Debian 13 Trixie -## Applicable when using Linux kernel >= 6.2 (retained here for future-proofing and completeness). -## -#GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX cfi=kcfi" +GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX cfi=kcfi" ## Disable support for x86 processes and syscalls. ## Unconditionally disables IA32 emulation to substantially reduce attack surface. From 45d20dd972e6501237d35d1605c81d4e3fde43b1 Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Wed, 6 Aug 2025 02:35:15 +0000 Subject: [PATCH 19/57] Upgrade sysctls and docs on kernel panics --- README.md | 11 +++++------ usr/lib/sysctl.d/990-security-misc.conf | 25 +++++++++++++++---------- usr/libexec/security-misc/panic-on-oops | 19 +++++++++++-------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index ac12886..b124a7a 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,10 @@ configuration file and significant hardening is applied to a myriad of component - Restrict kernel profiling and the performance events system to `CAP_PERFMON`. -- Force the kernel to panic on both "oopses", which can potentially indicate and thwart - certain kernel exploitation attempts, and also kernel warnings in the `WARN()` path. +- Force the kernel to immediately panic on both "oopses" (which can potentially indicate + and thwart certain kernel exploitation attempts) and kernel warnings in the `WARN()` path. -- Optional - Force immediate reboot on the occurrence of a single kernel panic and also - (when using Linux kernel >= 6.2) limit the number of allowed panics to one. +- Optional - Force immediate reboot on the occurrence of a single kernel panic. - Disable the use of legacy TIOCSTI operations which can be used to inject keypresses. @@ -280,6 +279,8 @@ Completely disables `ptrace()`. Can be enabled easily if needed. * [security-misc pull request #242](https://github.com/Kicksecure/security-misc/pull/242) +**Non-compliance:** + 2. `sysctl kernel.panic=-1` Forces an immediate reboot on kernel panic. This can be enabled, but it may lead to unexpected @@ -288,8 +289,6 @@ system crashes. * [security-misc pull request #264](https://github.com/Kicksecure/security-misc/pull/264) * [security-misc pull request #268](https://github.com/Kicksecure/security-misc/pull/268) -**Non-compliance:** - 3. `sysctl user.max_user_namespaces=0` Disables user namespaces entirely. Not recommended due to the potential for widespread breakages. diff --git a/usr/lib/sysctl.d/990-security-misc.conf b/usr/lib/sysctl.d/990-security-misc.conf index eaa671e..139e2e6 100644 --- a/usr/lib/sysctl.d/990-security-misc.conf +++ b/usr/lib/sysctl.d/990-security-misc.conf @@ -164,14 +164,14 @@ kernel.sysrq=0 ## kernel.perf_event_paranoid=3 -## Force the kernel to panic on "oopses" and kernel warnings in the WARN() path. -## Can sometimes potentially indicate and thwart certain kernel exploitation attempts. +## Force the kernel to immediately panic on "oopses" and kernel warnings in the WARN() path. ## Panics may be due to false-positives such as bad drivers. +## Both allowed limits are set to one so that panics occur on the single first instance of either scenario. ## Oopses are serious but non-fatal errors. +## Certain "oopses" can sometimes indicate and thwart potential kernel exploitation attempts. ## Warnings are messages generated by the kernel to indicate unexpected conditions or errors. ## By default, code execution continues regardless of warnings emitted by macros like WARN() and WARN_ON(). ## Note that by forcing kernel panics on oopses and warnings, this exposes the system to targeted denial of service attacks. -## Forcing immediate system reboots on any single kernel panic is an extreme option. ## ## https://en.wikipedia.org/wiki/Kernel_panic#Linux ## https://en.wikipedia.org/wiki/Linux_kernel_oops @@ -180,20 +180,25 @@ kernel.perf_event_paranoid=3 ## https://git.sr.ht/~gregkh/presentation-security/tree/3fdaf81a2f8b2c8d64cdb2f529cc714624868aa8/item/security-stuff.pdf ## https://forums.whonix.org/t/set-oops-panic-kernel-parameter-or-kernel-panisc-on-oops-1-sysctl-for-better-security/7713 ## -## KSPP=partial -## KSPP sets the sysctls, CONFIG_PANIC_ON_OOPS=y, but also requires CONFIG_PANIC_TIMEOUT=-1. +## KSPP=yes +## KSPP sets the sysctls and CONFIG_PANIC_ON_OOPS=y ## ## See /usr/libexec/security-misc/panic-on-oops for implementation. ## -## TODO: Debian 13 Trixie -## The limits are applicable when using Linux kernel >= 6.2 (retained here for future-proofing and completeness). -## #kernel.panic=-1 -#kernel.panic_on_oops=1 -#kernel.panic_on_warn=1 #kernel.oops_limit=1 #kernel.warn_limit=1 +## Force immediate system reboots on the occurrence of a single kernel panic. +## This is an extreme safety option which also creates a large opening for targeted denial of service attacks. +## +## KSPP=no +## KSPP sets CONFIG_PANIC_TIMEOUT=-1. +## +## See /usr/libexec/security-misc/panic-on-oops for implementation. +## +#kernel.panic=-1 + ## Disable the use of legacy TIOCSTI operations which can be used to inject keypresses. ## Can lead to privilege escalation by pushing characters into a controlling TTY. ## Will break out-dated screen readers that continue to rely on this legacy functionality. diff --git a/usr/libexec/security-misc/panic-on-oops b/usr/libexec/security-misc/panic-on-oops index 749eb3c..3c45a68 100755 --- a/usr/libexec/security-misc/panic-on-oops +++ b/usr/libexec/security-misc/panic-on-oops @@ -12,12 +12,15 @@ if [ -f /usr/libexec/helper-scripts/pre.bsh ]; then source /usr/libexec/helper-scripts/pre.bsh fi -## Makes the kernel panic on oopses and warnings. This prevents the -## kernel from continuing to run a flawed processes. Many kernel -## exploits will also cause an oops, these settings will make the -## kernel kill the offending processes. +## Makes the kernel immediately panic on both oopses and warnings. +## This prevents the kernel from continuing to run a potentially +## flawed processes. Many kernel exploits will also cause an oops, +## these settings will make the kernel kill the offending processes. +sysctl kernel.oops_limit=1 +sysctl kernel.warn_limit=1 + +## Makes the system immediately reboot on the occur occurrence of +## a single kernel panic. This is an extreme safety option which also +## creates a large opening for targeted denial of service attacks. #sysctl kernel.panic=-1 -sysctl kernel.panic_on_oops=1 -sysctl kernel.panic_on_warn=1 -#sysctl kernel.oops_limit=1 -#sysctl kernel.warn_limit=1 + From 498551536c71f1c1ac33f3c1992e18c9277e6618 Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Wed, 6 Aug 2025 03:12:06 +0000 Subject: [PATCH 20/57] Update docs --- README.md | 6 ++---- etc/default/grub.d/40_kernel_hardening.cfg | 3 --- etc/modprobe.d/30_security-misc_blacklist.conf | 2 +- usr/lib/sysctl.d/990-security-misc.conf | 9 ++------- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ac12886..e853332 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,7 @@ configuration file and significant hardening is applied to a myriad of component - Disable the use of legacy TIOCSTI operations which can be used to inject keypresses. -- Disable asynchronous I/O (when using Linux kernel >= 6.6) as `io_uring` has been - the source of numerous kernel exploits. +- Disable asynchronous I/O as `io_uring` has been the source of numerous kernel exploits. #### User space @@ -225,8 +224,7 @@ Kernel space: since it may be slightly more resilient to attacks that are able to write arbitrary executables in memory. -- Optional - Disable support for all x86 processes and syscalls (when using Linux kernel >= 6.7) - to reduce attack surface. +- Optional - Disable support for all x86 processes and syscalls to reduce attack surface. - Disable the EFI persistent storage feature which prevents the kernel from writing crash logs and other persistent data to either the UEFI variable storage or ACPI ERST backends. diff --git a/etc/default/grub.d/40_kernel_hardening.cfg b/etc/default/grub.d/40_kernel_hardening.cfg index 671c28b..1f3cc7c 100644 --- a/etc/default/grub.d/40_kernel_hardening.cfg +++ b/etc/default/grub.d/40_kernel_hardening.cfg @@ -218,9 +218,6 @@ GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX vdso32=0" ## KSPP=yes ## KSPP does not set CONFIG_COMPAT, CONFIG_IA32_EMULATION, CONFIG_X86_X32, CONFIG_X86_X32_ABI, and CONFIG_MODIFY_LDT_SYSCALL. ## -## TODO: Debian 13 Trixie -## Applicable when using Linux kernel >= 6.7 (retained here for future-proofing and completeness). -## #GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX ia32_emulation=0" ## Disable EFI persistent storage feature. diff --git a/etc/modprobe.d/30_security-misc_blacklist.conf b/etc/modprobe.d/30_security-misc_blacklist.conf index 5ce1edc..936e26a 100644 --- a/etc/modprobe.d/30_security-misc_blacklist.conf +++ b/etc/modprobe.d/30_security-misc_blacklist.conf @@ -27,7 +27,7 @@ blacklist sr_mod ## Partial selection of their infrastructure blacklist. ## Duplicate and already disabled modules have been omitted. ## -## https://github.com/GrapheneOS/infrastructure/blob/main/modprobe.d/local.conf +## https://github.com/GrapheneOS/infrastructure/blob/main/etc/modprobe.d/local.conf ## #blacklist cfg80211 #blacklist intel_agp diff --git a/usr/lib/sysctl.d/990-security-misc.conf b/usr/lib/sysctl.d/990-security-misc.conf index eaa671e..23dc6ef 100644 --- a/usr/lib/sysctl.d/990-security-misc.conf +++ b/usr/lib/sysctl.d/990-security-misc.conf @@ -197,19 +197,17 @@ kernel.perf_event_paranoid=3 ## Disable the use of legacy TIOCSTI operations which can be used to inject keypresses. ## Can lead to privilege escalation by pushing characters into a controlling TTY. ## Will break out-dated screen readers that continue to rely on this legacy functionality. +## Note this was already disabled by default as of Linux kernel 6.2. ## ## https://lore.kernel.org/lkml/20221228205726.rfevry7ud6gmttg5@begin/T/ ## ## KSPP=yes ## KSPP sets the sysctl and does not set CONFIG_LEGACY_TIOCSTI. ## -## TODO: Debian 13 Trixie -## This is disabled by default when using Linux kernel >= 6.2. -## dev.tty.legacy_tiocsti=0 ## Disable asynchronous I/O for all processes. -## Leading cause of numerous kernel exploits. +## Use of io_uring has been the leading cause of numerous kernel exploits. ## Disabling will reduce the read/write performance of storage devices. ## ## https://en.wikipedia.org/wiki/Io_uring#Security @@ -218,9 +216,6 @@ dev.tty.legacy_tiocsti=0 ## https://github.com/moby/moby/pull/46762 ## https://forums.whonix.org/t/io-uring-security-vulnerabilties/16890 ## -## TODO: Debian 13 Trixie -## Applicable when using Linux kernel >= 6.6 (retained here for future-proofing and completeness). -## kernel.io_uring_disabled=2 ## 2. User Space: From 86f44063eb753fe1bbdd754ce104670d26aed6ca Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Tue, 5 Aug 2025 22:58:06 -0500 Subject: [PATCH 21/57] Port to Trixie. --- README_generic.md | 2 +- debian/control | 2 +- usr/lib/permission-hardener.d/25_default_whitelist_ssh.conf | 2 +- usr/libexec/security-misc/askpass | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README_generic.md b/README_generic.md index 787af72..4c05521 100644 --- a/README_generic.md +++ b/README_generic.md @@ -28,7 +28,7 @@ sudo cp ~/derivative.asc /usr/share/keyrings/derivative.asc 3\. Add the derivative repository. ``` -echo "deb [signed-by=/usr/share/keyrings/derivative.asc] https://deb.kicksecure.com bookworm main contrib non-free" | sudo tee /etc/apt/sources.list.d/derivative.list +echo "deb [signed-by=/usr/share/keyrings/derivative.asc] https://deb.kicksecure.com trixie main contrib non-free" | sudo tee /etc/apt/sources.list.d/derivative.list ``` 4\. Update your package lists. diff --git a/debian/control b/debian/control index 6235dad..13143e4 100644 --- a/debian/control +++ b/debian/control @@ -13,7 +13,7 @@ Build-Depends: config-package-dev, Homepage: https://www.kicksecure.com/wiki/Security-misc Vcs-Browser: https://github.com/Kicksecure/security-misc Vcs-Git: https://github.com/Kicksecure/security-misc.git -Standards-Version: 4.6.2 +Standards-Version: 4.7.2 Rules-Requires-Root: no Package: security-misc diff --git a/usr/lib/permission-hardener.d/25_default_whitelist_ssh.conf b/usr/lib/permission-hardener.d/25_default_whitelist_ssh.conf index 2b55bd2..7c825e7 100644 --- a/usr/lib/permission-hardener.d/25_default_whitelist_ssh.conf +++ b/usr/lib/permission-hardener.d/25_default_whitelist_ssh.conf @@ -6,7 +6,7 @@ ## configuration. When security-misc is updated, this file may be overwritten. ## Used for SSH client key management -## https://manpages.debian.org/bookworm/openssh-client/ssh-agent.1.en.html +## https://manpages.debian.org/trixie/openssh-client/ssh-agent.1.en.html ## Debian installs ssh-agent with setgid permissions (2755) and with ## _ssh as the group to help mitigate ptrace attacks that could extract ## private keys from the agent's memory. diff --git a/usr/libexec/security-misc/askpass b/usr/libexec/security-misc/askpass index 56ecffc..d428975 100755 --- a/usr/libexec/security-misc/askpass +++ b/usr/libexec/security-misc/askpass @@ -7,4 +7,4 @@ set -e title="$0: password required for $(whoami) to perform action as superuser" -zenity --password --title="$title" +yad --password --title="$title" From 4166d6d1e60d564be4c3fb2ad530e7a180638e6a Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Wed, 6 Aug 2025 15:53:49 +1000 Subject: [PATCH 22/57] Update docs on recovery restrictions --- etc/default/grub.d/41_recovery_restrict.cfg | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/etc/default/grub.d/41_recovery_restrict.cfg b/etc/default/grub.d/41_recovery_restrict.cfg index f54247b..5da19e7 100644 --- a/etc/default/grub.d/41_recovery_restrict.cfg +++ b/etc/default/grub.d/41_recovery_restrict.cfg @@ -7,14 +7,17 @@ ## KSPP=no: not (currently) compliant with recommendations by the KSPP ## If there is no explicit KSPP compliance notice, the setting is not mentioned by the KSPP. -## Disable access to single-user (recovery) mode. +## Disable access to the GRUB single-user (recovery) mode menu entries. ## ## https://forums.kicksecure.com/t/remove-linux-recovery-mode-boot-option-from-default-grub-boot-menu/727 ## GRUB_DISABLE_RECOVERY="true" ## Disable access to Dracut's recovery console. +## Prevents the emergency shell from starting automatically during boot failures. ## +## https://insinuator.net/2025/07/insecure-boot-injecting-initramfs-from-a-debug-shell/ +## https://serverfault.com/questions/554853/how-can-i-secure-the-dracut-shell ## https://forums.kicksecure.com/t/harden-dracut-initramfs-generator-by-disabling-recovery-console/724 ## GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT rd.emergency=halt" From 42941653621311187650f12e8d7aa39c45cb6984 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Wed, 6 Aug 2025 08:27:15 +0000 Subject: [PATCH 23/57] bumped changelog version --- changelog.upstream | 19 +++++++++++++++++++ debian/changelog | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/changelog.upstream b/changelog.upstream index 0381ecf..ea0f4a2 100644 --- a/changelog.upstream +++ b/changelog.upstream @@ -1,3 +1,22 @@ +commit 784ff8af3616765a9c22febf66b522376ecedf12 +Merge: c2690ef 5a17e67 +Author: Patrick Schleizer +Date: Wed Aug 6 04:26:37 2025 -0400 + + Merge remote-tracking branch 'ArrayBolt3/arraybolt3/emerg-shutdown' + +commit 5a17e67c0a7678300f6342d5c90ded5494ebc838 +Author: Aaron Rainbolt +Date: Tue Aug 5 20:14:07 2025 -0500 + + Fix local-fs.target dependency in emerg-shutdown.service + +commit c2690efcacbf7be7c57751ba1cee7f910d350cfc +Author: Patrick Schleizer +Date: Mon Aug 4 09:27:11 2025 +0000 + + bumped changelog version + commit 166bc257b0b2eea87d684cc847bf6da1fba7c4b4 Merge: d1bca02 63f2909 Author: Patrick Schleizer diff --git a/debian/changelog b/debian/changelog index 460f1a2..14ebd05 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +security-misc (3:46.7-1) unstable; urgency=medium + + * New upstream version (local package). + + -- Patrick Schleizer Wed, 06 Aug 2025 08:27:15 +0000 + security-misc (3:46.6-1) unstable; urgency=medium * New upstream version (local package). From 44e7d3059a5618991a1408f77707132bfea86fef Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Wed, 6 Aug 2025 19:10:14 -0500 Subject: [PATCH 24/57] Integrate emerg-shutdown into the initramfs --- .../99emerg-shutdown/module-setup.sh | 48 ++++++++++++++ usr/lib/systemd/system/emerg-shutdown.service | 4 +- .../systemd/system/ensure-shutdown.service | 4 +- usr/libexec/security-misc/emerg-shutdown | 66 +++++++++++-------- .../emerg-shutdown-initramfs.service | 21 ++++++ 5 files changed, 111 insertions(+), 32 deletions(-) create mode 100755 usr/lib/dracut/modules.d/99emerg-shutdown/module-setup.sh create mode 100644 usr/share/security-misc/emerg-shutdown-initramfs.service diff --git a/usr/lib/dracut/modules.d/99emerg-shutdown/module-setup.sh b/usr/lib/dracut/modules.d/99emerg-shutdown/module-setup.sh new file mode 100755 index 0000000..98d6be9 --- /dev/null +++ b/usr/lib/dracut/modules.d/99emerg-shutdown/module-setup.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC +## See the file COPYING for copying conditions. + +## called by dracut +check() { + require_binaries /run/emerg-shutdown || return 1 + return 255 +} + +## called by dracut +depends() { + echo 'systemd bash' + return 0 +} + +## called by dracut +install() { + local config_file + + inst systemd-notify + + inst_simple /usr/libexec/security-misc/emerg-shutdown + inst_simple /usr/share/security-misc/emerg-shutdown-initramfs.service /usr/lib/systemd/system/emerg-shutdown-initramfs.service + inst_simple /run/emerg-shutdown /emerg-shutdown + + for config_file in /etc/security-misc/emerg-shutdown/*.conf; do + if [ -f "${config_file}" ]; then + inst_multiple /etc/security-misc/emerg-shutdown/*.conf + break + fi + done + for config_file in /usr/local/etc/security-misc/emerg-shutdown/*.conf; do + if [ -f "${config_file}" ]; then + inst_multiple /usr/local/etc/security-misc/emerg-shutdown/*.conf + break + fi + done + + mkdir -p "${initdir}/usr/lib/systemd/system/initrd.target.wants" + ln -s '../emerg-shutdown-initramfs.service' "${initdir}/usr/lib/systemd/system/initrd.target.wants/emerg-shutdown-initramfs.service" +} + +## called by dracut +installkernel () { + hostonly='' instmods evdev +} diff --git a/usr/lib/systemd/system/emerg-shutdown.service b/usr/lib/systemd/system/emerg-shutdown.service index f2dc2e8..fa738ab 100644 --- a/usr/lib/systemd/system/emerg-shutdown.service +++ b/usr/lib/systemd/system/emerg-shutdown.service @@ -6,8 +6,8 @@ Description=Emergency shutdown when boot media is removed Documentation=https://github.com/Kicksecure/security-misc DefaultDependencies=no Before=sysinit.target -Requires=udev.service -After=udev.service +Requires=systemd-udevd.service +After=systemd-udevd.service Requires=local-fs.target After=local-fs.target diff --git a/usr/lib/systemd/system/ensure-shutdown.service b/usr/lib/systemd/system/ensure-shutdown.service index 52eb487..8e4c5b6 100644 --- a/usr/lib/systemd/system/ensure-shutdown.service +++ b/usr/lib/systemd/system/ensure-shutdown.service @@ -9,8 +9,8 @@ Description=Forcibly shut down the system if normal shutdown gets stuck Documentation=https://github.com/Kicksecure/security-misc DefaultDependencies=no Before=sysinit.target -Requires=udev.service -After=udev.service +Requires=systemd-udevd.service +After=systemd-udevd.service Wants=emerg-shutdown.service After=emerg-shutdown.service diff --git a/usr/libexec/security-misc/emerg-shutdown b/usr/libexec/security-misc/emerg-shutdown index 1afebcb..f3496fc 100755 --- a/usr/libexec/security-misc/emerg-shutdown +++ b/usr/libexec/security-misc/emerg-shutdown @@ -1,7 +1,7 @@ #!/bin/bash -# Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC -# See the file COPYING for copying conditions. +## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC +## See the file COPYING for copying conditions. set -o errexit set -o nounset @@ -11,6 +11,14 @@ set -o pipefail ## Make sure globs sort in a predictable, reproducible fashion export LC_ALL=C +in_dracut='false' +if [ -f '/dracut-state.sh' ]; then + in_dracut='true' +fi +binary_prefix='/run' +EMERG_SHUTDOWN_KEYS='' +root_devices[0]='' + ## Read emergency shutdown key configuration for config_file in /etc/security-misc/emerg-shutdown/*.conf /usr/local/etc/security-misc/emerg-shutdown/*.conf; do if [ -f "${config_file}" ]; then @@ -18,38 +26,40 @@ for config_file in /etc/security-misc/emerg-shutdown/*.conf /usr/local/etc/secur source "${config_file}" fi done -if [ -z "${EMERG_SHUTDOWN_KEYS}" ]; then - ## Default to Ctrl+Alt+Delete if nothing else is set - EMERG_SHUTDOWN_KEYS="KEY_LEFTCTRL|KEY_RIGHTCTRL,KEY_LEFTALT|KEY_RIGHTALT,KEY_DELETE" -fi -## Find the devices that make up the root device -readarray -t root_devices < <(/usr/libexec/helper-scripts/get-backing-devices-for-mountpoint '/') || true; -if [ "${#root_devices[@]}" = '0' ] \ - || [ "${root_devices[0]}" == '' ]; then - ## /dev/sda1 might be the right one... - root_devices[0]='/dev/sda1' -fi +if [ "${in_dracut}" = 'true' ]; then + binary_prefix='' + modprobe evdev || { + printf '%s\n' 'Failed to load evdev driver!' + exit 1 + } + ## modules may not work immediately after loaded, give them time to + ## initialize + sleep 0.1 +else + ## Find the devices that make up the root device + readarray -t root_devices < <(/usr/libexec/helper-scripts/get-backing-devices-for-mountpoint '/') || true; -## Build the actual emerg-shutdown executable -if [ ! -f '/run/emerg-shutdown' ]; then - gcc \ - -o \ - /run/emerg-shutdown \ - -static \ - /usr/src/security-misc/emerg-shutdown.c \ - || { - printf "%s\n" 'Could not compile force-shutdown executable!' - exit 1; - } + ## Build the actual emerg-shutdown executable + if [ ! -f '/run/emerg-shutdown' ]; then + gcc \ + -o \ + /run/emerg-shutdown \ + -static \ + /usr/src/security-misc/emerg-shutdown.c \ + || { + printf "%s\n" 'Could not compile force-shutdown executable!' + exit 1 + } + fi + + ## memlockd daemonizes itself, so no need to background it. + memlockd -c /usr/share/security-misc/security-misc-memlockd.cfg || true fi systemd-notify --ready -## memlockd daemonizes itself, so no need to background it. -memlockd -c /usr/share/security-misc/security-misc-memlockd.cfg || true - ## Launch emerg-shutdown OLDIFS="$IFS" IFS=',' -/run/emerg-shutdown "--devices=${root_devices[*]}" "--keys=${EMERG_SHUTDOWN_KEYS}" +"${binary_prefix}/emerg-shutdown" "--devices=${root_devices[*]}" "--keys=${EMERG_SHUTDOWN_KEYS}" diff --git a/usr/share/security-misc/emerg-shutdown-initramfs.service b/usr/share/security-misc/emerg-shutdown-initramfs.service new file mode 100644 index 0000000..8de5412 --- /dev/null +++ b/usr/share/security-misc/emerg-shutdown-initramfs.service @@ -0,0 +1,21 @@ +## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC +## See the file COPYING for copying conditions. + +## This file should not be installed on the host system, it is intended for +## inclusion in a dracut initramfs only. + +[Unit] +Description=Emergency shutdown when boot media is removed +Documentation=https://github.com/Kicksecure/security-misc +DefaultDependencies=no +Before=sysinit.target +Requires=systemd-udevd.service +After=systemd-udevd.service + +[Service] +Type=notify +ExecStart=/usr/libexec/security-misc/emerg-shutdown +NotifyAccess=main + +[Install] +WantedBy=sysinit.target From 2a3bc39eba317d5f9b0e710dd3663c82d92add94 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Wed, 6 Aug 2025 19:10:37 -0500 Subject: [PATCH 25/57] Use Ctrl+Alt+End as the default panic key rather than Ctrl+Alt+Delete --- etc/security-misc/emerg-shutdown/30_security_misc.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/security-misc/emerg-shutdown/30_security_misc.conf b/etc/security-misc/emerg-shutdown/30_security_misc.conf index 4ec6cbd..e8b4b48 100644 --- a/etc/security-misc/emerg-shutdown/30_security_misc.conf +++ b/etc/security-misc/emerg-shutdown/30_security_misc.conf @@ -17,7 +17,7 @@ ## ## The default key sequence triggers a shutdown when Ctrl+Alt+Delete is ## pressed, allowing the use of either the left or right Ctrl and Alt keys. -EMERG_SHUTDOWN_KEYS="KEY_LEFTCTRL|KEY_RIGHTCTRL,KEY_LEFTALT|KEY_RIGHTALT,KEY_DELETE" +EMERG_SHUTDOWN_KEYS="KEY_LEFTCTRL|KEY_RIGHTCTRL,KEY_LEFTALT|KEY_RIGHTALT,KEY_END" ## Set the maximum number of seconds shutdown can take. If shutdown gets stuck ## for longer than this, the system will forcibly power down. From 29480df770047c8ada3e993cf28f87ffbfd71dec Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Wed, 6 Aug 2025 19:24:34 -0500 Subject: [PATCH 26/57] Improve emerg-shutdown usage documentation --- usr/src/security-misc/emerg-shutdown.c | 28 ++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/usr/src/security-misc/emerg-shutdown.c b/usr/src/security-misc/emerg-shutdown.c index 83cb6de..287c953 100644 --- a/usr/src/security-misc/emerg-shutdown.c +++ b/usr/src/security-misc/emerg-shutdown.c @@ -42,7 +42,7 @@ * be entirely possible. To give our feature the highest chance of success: * * - We use memlockd to lock systemd and all libraries it depends on into - * memory. It can holds its own pretty well in the event of a segfault, but + * memory. It can hold its own pretty well in the event of a segfault, but * if its crash handler ends up re-segfaulting, that could get ugly. * - We compile the utility at boot time, statically link it against all of * its dependencies (really only one, glibc), and load it into /run. This @@ -288,13 +288,29 @@ void print(int fd, char *str) { void print_usage() { print(fd_stderr, "Usage:\n"); - print(fd_stderr, " emerg-shutdown --devices=DEVICE1[,DEVICE2...] --keys=KEY_1[,KEY_2|KEY_3...]\n"); - print(fd_stderr, "Or:\n"); - print(fd_stderr, " emerg-shutdown --instant-shutdown\n"); - print(fd_stderr, "Or:\n"); - print(fd_stderr, " emerg-shutdown --monitor-fifo --timeout=TIMEOUT\n"); + print(fd_stderr, " emerg-shutdown [OPTIONS...]\n"); + print(fd_stderr, "Options:\n"); + print(fd_stderr, " --devices=DEVICE1[,DEVICE2...]\n"); + print(fd_stderr, " A comma-separated list of devices. If any of these devices are\n"); + print(fd_stderr, " removed from the system, an emergency shutdown will occur.\n"); + print(fd_stderr, " --keys=KEY_1[,KEY_2|KEY_3...]\n"); + print(fd_stderr, " A comma-separated list of keys. If all of the specified keys are\n"); + print(fd_stderr, " pressed at the same time, an emergency shutdown will occur.\n"); + print(fd_stderr, " Keys separated with a pipe will be treated as aliases of each\n"); + print(fd_stderr, " other.\n"); + print(fd_stderr, " --instant-shutdown\n"); + print(fd_stderr, " Immediately triggers an emergency shutdown. Cannot be combined\n"); + print(fd_stderr, " with other options.\n"); + print(fd_stderr, " --monitor-fifo\n"); + print(fd_stderr, " Used internally to implement the ensure-shutdown service. Do\n"); + print(fd_stderr, " not use.\n"); + print(fd_stderr, " --timeout=TIMEOUT\n"); + print(fd_stderr, " Used internally to implement the ensure-shutdown service. Do\n"); + print(fd_stderr, " not use.\n"); print(fd_stderr, "Example:\n"); print(fd_stderr, " emerg-shutdown --devices=/dev/sda3 --keys=KEY_POWER\n"); + print(fd_stderr, "See /etc/security-misc/emerg-shutdown/30_security-misc.cofn to\n"); + print(fd_stderr, "configure the emerg-shutdown service.\n"); } void *safe_calloc(size_t nmemb, size_t size) { From 0c1af00aae50dba2983c3736744e0da320bb9330 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Wed, 6 Aug 2025 19:33:38 -0500 Subject: [PATCH 27/57] Implement paranoid mode in emerg-shutdown --- usr/src/security-misc/emerg-shutdown.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/usr/src/security-misc/emerg-shutdown.c b/usr/src/security-misc/emerg-shutdown.c index 287c953..89c4400 100644 --- a/usr/src/security-misc/emerg-shutdown.c +++ b/usr/src/security-misc/emerg-shutdown.c @@ -298,6 +298,10 @@ void print_usage() { print(fd_stderr, " pressed at the same time, an emergency shutdown will occur.\n"); print(fd_stderr, " Keys separated with a pipe will be treated as aliases of each\n"); print(fd_stderr, " other.\n"); + print(fd_stderr, " --paranoid\n"); + print(fd_stderr, " Watches for the removal of any removable device whatsoever. An\n"); + print(fd_stderr, " emergency shutdown will be triggered if any device is removed.\n"); + print(fd_stderr, " Cannot be combined with --devices.\n"); print(fd_stderr, " --instant-shutdown\n"); print(fd_stderr, " Immediately triggers an emergency shutdown. Cannot be combined\n"); print(fd_stderr, " with other options.\n"); @@ -474,6 +478,7 @@ void hw_monitor(int argc, char **argv) { char input_path_buf[input_path_size]; struct pollfd *pollfd_list = NULL; struct input_event ie_buf[64]; + bool paranoid_mode = false; /* Index variables */ int arg_idx = 0; @@ -493,6 +498,8 @@ void hw_monitor(int argc, char **argv) { exit(1); } load_list(argv[arg_idx], &target_dev_list_len, &target_dev_name_raw_list, ",", true); + } else if (strcmp(argv[arg_idx], "--paranoid") == 0) { + paranoid_mode = true; } else if (strncmp(argv[arg_idx], "--keys=", strlen("--keys=")) == 0) { if (panic_key_str_list != NULL) { print(fd_stderr, "--keys cannot be passed more than once!\n"); @@ -508,6 +515,11 @@ void hw_monitor(int argc, char **argv) { exit(1); } } + if (target_dev_name_raw_list != NULL && paranoid_mode) { + print(fd_stderr, "--devices and --paranoid are mutually exclusive!\n"); + print_usage(); + exit(1); + } console_fd = open("/dev/console", O_RDWR); if (console_fd == -1) { @@ -844,6 +856,11 @@ void hw_monitor(int argc, char **argv) { goto next_str; } + if (paranoid_mode) { + /* Something was removed, we don't care what, shut down now */ + kill_system(); + } + for (tdl_idx = 0; tdl_idx < target_dev_list_len; tdl_idx++) { if (strcmp(rem_dev_name, target_dev_list[tdl_idx]) == 0) { kill_system(); From 3a77abe5c9807caec530e69c41d5cf803b625e70 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Wed, 6 Aug 2025 20:05:57 -0500 Subject: [PATCH 28/57] Port hardening options from kloak to emerg-shutdown, fix new compiler warnings --- usr/libexec/security-misc/emerg-shutdown | 14 +++++++ usr/src/security-misc/emerg-shutdown.c | 52 +++++++++++++----------- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/usr/libexec/security-misc/emerg-shutdown b/usr/libexec/security-misc/emerg-shutdown index f3496fc..3016088 100755 --- a/usr/libexec/security-misc/emerg-shutdown +++ b/usr/libexec/security-misc/emerg-shutdown @@ -19,6 +19,19 @@ binary_prefix='/run' EMERG_SHUTDOWN_KEYS='' root_devices[0]='' +## Taken from kloak/Makefile, see it for more information +gcc_hardening_options=( + "-Wall" "-Wformat" "-Wformat=2" "-Wconversion" + "-Wimplicit-fallthrough" "-Werror=format-security" "-Werror=implicit" + "-Werror=int-conversion" "-Werror=incompatible-pointer-types" + "-Wtrampolines" "-Wbidi-chars=any" "-U_FORTIFY_SOURCE" "-D_FORTIFY_SOURCE=3" + "-fstack-clash-protection" "-fstack-protector-strong" + "-fno-delete-null-pointer-checks" "-fno-strict-overflow" + "-fno-strict-aliasing" "-fsanitize=undefined" "-fcf-protection=full" + "-Wl,-z,nodlopen" "-Wl,-z,noexecstack" "-Wl,-z,relro" "-Wl,-z,now" + "-Wl,--as-needed" "-Wl,--no-copy-dt-needed-entries" "-pie" +) + ## Read emergency shutdown key configuration for config_file in /etc/security-misc/emerg-shutdown/*.conf /usr/local/etc/security-misc/emerg-shutdown/*.conf; do if [ -f "${config_file}" ]; then @@ -46,6 +59,7 @@ else -o \ /run/emerg-shutdown \ -static \ + "${gcc_hardening_options[@]}" \ /usr/src/security-misc/emerg-shutdown.c \ || { printf "%s\n" 'Could not compile force-shutdown executable!' diff --git a/usr/src/security-misc/emerg-shutdown.c b/usr/src/security-misc/emerg-shutdown.c index 89c4400..cd310e8 100644 --- a/usr/src/security-misc/emerg-shutdown.c +++ b/usr/src/security-misc/emerg-shutdown.c @@ -94,6 +94,7 @@ #include #include #include +#include #define fd_stdin 0 #define fd_stdout 1 @@ -113,7 +114,7 @@ int console_fd = 0; /* Adapted from kloak/src/keycodes.c */ struct name_value { const char *name; - const int value; + const uint32_t value; }; static struct name_value key_table[] = { {"KEY_ESC", KEY_ESC}, @@ -259,14 +260,14 @@ static struct name_value key_table[] = { {"KEY_UNKNOWN", KEY_UNKNOWN}, {NULL, 0} }; -int lookup_keycode(const char *name) { +uint32_t lookup_keycode(const char *name) { struct name_value *p; for (p = key_table; p->name != NULL; ++p) { if (strcmp(p->name, name) == 0) { return p->value; } } - return -1; + return 0; } /* Adapted from systemd/src/login/logind-button.c */ @@ -278,7 +279,11 @@ void print(int fd, char *str) { size_t len = strlen(str) + 1; while (true) { ssize_t write_len = write(fd, str, len); - len -= write_len; + if (write_len < 0) { + /* File descriptor was closed, continue regardless */ + return; + } + len -= (size_t)write_len; if (len == 0) { return; } @@ -338,7 +343,7 @@ void *safe_reallocarray(void *ptr, size_t nmemb, size_t size) { /* Inspired by https://www.strudel.org.uk/itoa/ */ char *int_to_str(uint32_t val) { static char buf[11]; - int8_t i; + uint8_t i; char *rslt = NULL; const char *digits = "0123456789"; @@ -360,7 +365,7 @@ char *int_to_str(uint32_t val) { void load_list(const char *arg, size_t *result_list_len_ref, char ***result_list_ref, const char *sep, bool parse_opt) { char **result_list = NULL; size_t result_list_len = 0; - int arg_copy_len = strlen(arg) + 1; + size_t arg_copy_len = strlen(arg) + 1; char *arg_copy = safe_calloc(1, arg_copy_len); char *arg_val; char *arg_part; @@ -392,7 +397,7 @@ void load_list(const char *arg, size_t *result_list_len_ref, char ***result_list free(arg_copy); } -int kill_system() { +long int kill_system() { /* * It isn't safe to simply call the reboot syscall here - there is a * graphics driver bug in the i915 driver on Bookworm that will throw a @@ -471,7 +476,7 @@ void hw_monitor(int argc, char **argv) { size_t panic_key_list_len = 0; char **panic_key_str_list = NULL; char **target_dev_list = NULL; - int **panic_key_list = NULL; + uint32_t **panic_key_list = NULL; bool *panic_key_active_list = NULL; size_t event_fd_list_len = 0; int *event_fd_list = NULL; @@ -485,7 +490,7 @@ void hw_monitor(int argc, char **argv) { size_t tdl_idx = 0; size_t tdp_char_idx = 0; size_t pkl_idx = 0; - int input_idx = 0; + uint32_t input_idx = 0; size_t efl_idx = 0; int ie_idx = 0; size_t kg_idx = 0; @@ -528,7 +533,7 @@ void hw_monitor(int argc, char **argv) { } target_dev_list = safe_calloc(target_dev_list_len, sizeof(char *)); - panic_key_list = safe_calloc(panic_key_list_len, sizeof(int *)); + panic_key_list = safe_calloc(panic_key_list_len, sizeof(uint32_t *)); panic_key_active_list = safe_calloc(panic_key_list_len, sizeof(bool)); for (tdl_idx = 0; tdl_idx < target_dev_list_len; tdl_idx++) { @@ -595,12 +600,12 @@ void hw_monitor(int argc, char **argv) { size_t keygroup_str_list_len = 0; char **keygroup_str_list = NULL; load_list(panic_key_str_list[pkl_idx], &keygroup_str_list_len, &keygroup_str_list, "|", false); - int *pkl_element = safe_calloc(keygroup_str_list_len + 1, sizeof(int)); + uint32_t *pkl_element = safe_calloc(keygroup_str_list_len + 1, sizeof(uint32_t)); pkl_element[keygroup_str_list_len] = 0; for (kg_idx = 0; kg_idx < keygroup_str_list_len; kg_idx++) { - int keycode = lookup_keycode(keygroup_str_list[kg_idx]); - if (keycode < 0) { + uint32_t keycode = lookup_keycode(keygroup_str_list[kg_idx]); + if (keycode == 0) { print(fd_stderr, "Invalid key code '"); print(fd_stderr, keygroup_str_list[kg_idx]); print(fd_stderr, "'!\n"); @@ -619,7 +624,7 @@ void hw_monitor(int argc, char **argv) { struct sockaddr_nl sa = { .nl_family = AF_NETLINK, .nl_pad = 0, - .nl_pid = getpid(), + .nl_pid = (uint32_t)getpid(), .nl_groups = NETLINK_KOBJECT_UEVENT, }; int ns = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); @@ -712,11 +717,10 @@ void hw_monitor(int argc, char **argv) { continue; } - size_t ieread_bytes = read(event_fd_list[efl_idx], ie_buf, sizeof(struct input_event) * 64); + ssize_t ieread_bytes = read(event_fd_list[efl_idx], ie_buf, sizeof(struct input_event) * 64); - if (ieread_bytes == -1 - || ieread_bytes == 0 - || (ieread_bytes % sizeof(struct input_event)) != 0) { + if (ieread_bytes <= 0 + || ((size_t)ieread_bytes % sizeof(struct input_event)) != 0) { /* This will probably terminate the service if the user unplugs a * keyboard or similar, however systemd can start it again. The * alternative is to handle device hotplug, which sounds like a @@ -725,7 +729,8 @@ void hw_monitor(int argc, char **argv) { exit(1); } - for (ie_idx = 0; ie_idx < ieread_bytes / sizeof(struct input_event); ie_idx++) { + for (ie_idx = 0; ie_idx < (size_t)ieread_bytes / sizeof(struct input_event); + ie_idx++) { if (ie_buf[ie_idx].type != EV_KEY) { continue; } @@ -773,7 +778,7 @@ void hw_monitor(int argc, char **argv) { * NUL-terminated string "libudev" so they're easy to filter out. */ - int len; + ssize_t len; char buf[16384]; struct iovec iov = { buf, sizeof(buf) }; struct sockaddr_nl sa2; @@ -874,7 +879,7 @@ void hw_monitor(int argc, char **argv) { } next_str: - len -= strlen(tmpbuf) + 1; + len = len - (ssize_t)(strlen(tmpbuf) + 1); tmpbuf += strlen(tmpbuf) + 1; } } @@ -916,8 +921,9 @@ void fifo_monitor(int argc, char **argv) { arg_part = strtok(arg_copy, "="); /* returns everything after the = sign */ arg_part = strtok(NULL, ""); + errno = 0; monitor_fifo_timeout = strtol(arg_part, &arg_num_end, 10); - if (errno == ERANGE) { + if (errno == ERANGE || monitor_fifo_timeout > UINT_MAX) { print(fd_stderr, "Timeout out of range!\n"); print_usage(); exit(1); @@ -982,7 +988,7 @@ void fifo_monitor(int argc, char **argv) { if (trigger_fifo_charbuf == 'k') { kill_system(); } else if (trigger_fifo_charbuf == 'd') { - sleep(monitor_fifo_timeout); + sleep((unsigned int)monitor_fifo_timeout); kill_system(); } } From 0cc0a8310020afc10de6512095336e55559a84d9 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Thu, 7 Aug 2025 07:08:19 +0000 Subject: [PATCH 29/57] bumped changelog version --- changelog.upstream | 43 +++++++++++++++++++++++++++++++++++++++++++ debian/changelog | 6 ++++++ 2 files changed, 49 insertions(+) diff --git a/changelog.upstream b/changelog.upstream index ea0f4a2..2c9ba0d 100644 --- a/changelog.upstream +++ b/changelog.upstream @@ -1,3 +1,46 @@ +commit 505a2b7d7995ad48a17add86513ced3499f64ee9 +Merge: 4294165 3a77abe +Author: Patrick Schleizer +Date: Thu Aug 7 03:08:02 2025 -0400 + + Merge remote-tracking branch 'ArrayBolt3/arraybolt3/emerg-shutdown' + +commit 3a77abe5c9807caec530e69c41d5cf803b625e70 +Author: Aaron Rainbolt +Date: Wed Aug 6 20:05:57 2025 -0500 + + Port hardening options from kloak to emerg-shutdown, fix new compiler warnings + +commit 0c1af00aae50dba2983c3736744e0da320bb9330 +Author: Aaron Rainbolt +Date: Wed Aug 6 19:33:38 2025 -0500 + + Implement paranoid mode in emerg-shutdown + +commit 29480df770047c8ada3e993cf28f87ffbfd71dec +Author: Aaron Rainbolt +Date: Wed Aug 6 19:24:34 2025 -0500 + + Improve emerg-shutdown usage documentation + +commit 2a3bc39eba317d5f9b0e710dd3663c82d92add94 +Author: Aaron Rainbolt +Date: Wed Aug 6 19:10:37 2025 -0500 + + Use Ctrl+Alt+End as the default panic key rather than Ctrl+Alt+Delete + +commit 44e7d3059a5618991a1408f77707132bfea86fef +Author: Aaron Rainbolt +Date: Wed Aug 6 19:10:14 2025 -0500 + + Integrate emerg-shutdown into the initramfs + +commit 42941653621311187650f12e8d7aa39c45cb6984 +Author: Patrick Schleizer +Date: Wed Aug 6 08:27:15 2025 +0000 + + bumped changelog version + commit 784ff8af3616765a9c22febf66b522376ecedf12 Merge: c2690ef 5a17e67 Author: Patrick Schleizer diff --git a/debian/changelog b/debian/changelog index 14ebd05..2494b88 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +security-misc (3:46.8-1) unstable; urgency=medium + + * New upstream version (local package). + + -- Patrick Schleizer Thu, 07 Aug 2025 07:08:19 +0000 + security-misc (3:46.7-1) unstable; urgency=medium * New upstream version (local package). From 2ada07cf66727ea66283c55c0ba078489b3db94e Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Thu, 7 Aug 2025 22:23:03 -0500 Subject: [PATCH 30/57] Add SSH hardening config --- etc/ssh/ssh_config.d/30_security-misc.conf | 19 +++++ etc/ssh/sshd_config.d/30_security-misc.conf | 79 +++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 etc/ssh/ssh_config.d/30_security-misc.conf create mode 100644 etc/ssh/sshd_config.d/30_security-misc.conf diff --git a/etc/ssh/ssh_config.d/30_security-misc.conf b/etc/ssh/ssh_config.d/30_security-misc.conf new file mode 100644 index 0000000..6693f72 --- /dev/null +++ b/etc/ssh/ssh_config.d/30_security-misc.conf @@ -0,0 +1,19 @@ +## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC +## See the file COPYING for copying conditions. + +## Don't edit this file, to overwrite any options, edit a file with a higher +## number that is read later by SSH, such as +## '/etc/ssh/ssh_config.d/50_user.conf'. If your configuration changes do not +## need to be system-wide, you may also consider placing overrides in +## ~/.ssh/config. + +Host * + VisualHostKey yes + Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr + MACs umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com + KexAlgorithms sntrup761x25519-sha512,sntrup761x25519-sha512@openssh.com,mlkem768x25519-sha256,curve25519-sha256,curve25519-sha256@libssh.org + ## To force the use of quantum-resistant key exchange algorithms, override + ## the above with + # KexAlgorithms sntrup761x25519-sha512,sntrup761x25519-sha512@openssh.com,mlkem768x25519-sha256 + HostKeyAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519 + PubkeyAcceptedAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519 diff --git a/etc/ssh/sshd_config.d/30_security-misc.conf b/etc/ssh/sshd_config.d/30_security-misc.conf new file mode 100644 index 0000000..5abf9cc --- /dev/null +++ b/etc/ssh/sshd_config.d/30_security-misc.conf @@ -0,0 +1,79 @@ +## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC +## See the file COPYING for copying conditions. + +## Don't edit this file, to overwrite any options, edit a file with a higher +## number that is read later by SSHD, such as +## '/etc/ssh/sshd_config.d/50_user.conf'. + +## This is okay because of strict firewall. For an onion-only server, listen +## on 127.0.0.1. +ListenAddress 0.0.0.0 + +## Number of allowed login attempts per connection. +MaxAuthTries 3 + +## Require strong ciphers and algorithms. +HostKey /etc/ssh/ssh_host_ed25519_key +HostKeyAlgorithms ssh-ed25519 +PubkeyAcceptedAlgorithms ssh-ed25519,sk-ssh-ed25519@openssh.com +Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com +MACs umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com +KexAlgorithms sntrup761x25519-sha512,sntrup761x25519-sha512@openssh.com,mlkem768x25519-sha256,curve25519-sha256,curve25519-sha256@libssh.org +## To force the use of quantum-resistant key exchange algorithms, override the +## above with +# KexAlgorithms sntrup761x25519-sha512,sntrup761x25519-sha512@openssh.com,mlkem768x25519-sha256 + +## Override with 'no' to fully deny root login, or leave this as +## 'prohibit-password' for denying root password login but still allowing +## other authentication methods such as public key. +PermitRootLogin prohibit-password + +## Public key authentication is transparent, non-interactive and more secure. +PasswordAuthentication no + +## Change to 'yes' to enable challenge-response passwords (beware issues with +## some PAM modules and threads) +KbdInteractiveAuthentication no + +## PAM can be used for account and session processing when using +## ChallengeResponseAuthentication or PasswordAuthentication. +## +## Depending on your PAM configuration, PAM authentication via +## ChallengeResponseAuthentication may bypass the setting of "PermitRootLogin +## without-password". +## +## If you want PAM account and session checks to run without PAM +## authentication, then enable this but set PasswordAuthentication and +## ChallengeResponseAuthentication to 'no'. +## +## The default upstream is 'no', Debian sets this to 'yes'. If using a locked +## account, read: +## https://www.kicksecure.com/wiki/SSH#SSH_Login_Comparison_Table +## We set it to 'yes' to work with libpam-tmpdir. +## https://www.kicksecure.com/wiki/Dev/Strong_Linux_User_Account_Isolation@libpam-tmpdir +## Also folders such as '/run/user/1000' will exist thanks to PAM. +## The absence of that folder can lead to issues (such as with msgcollector). +UsePAM yes + +## Block dangerous forwarding. +AllowAgentForwarding no +AllowTcpForwarding no +X11Forwarding no + +## Hide unnecessary login banners. +PrintMotd no +#Banner /etc/issue.net +#Hiding Debian version from SSH banner (obscurity) +DebianBanner no + +## Some options are dangerous but may be required in certain circumstances. As +## an example, if forwarding is required, selectively allow it with a 'Match' +## block. Consider a new separate user named 'tunnel' which wants to forward +## its local port to be available on the server on port 443. Note that a +## tunnel user doesn't even require a TTY nor a shell, so don't forget to +## change the 'tunnel' shell to something that prevents login such as +## '/usr/sbin/nologin'. +#Match User tunnel +# AllowTcpForwarding yes +# PermitListen localhost:443 +# PermitTTY no From 046c932898290d250a7900e3c59973a698e5c55f Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Sat, 9 Aug 2025 05:40:11 -0400 Subject: [PATCH 31/57] `disable emerg-shutdown.service`: Disabled due to bug: breaks ISO Live Mode Calamares installer --- usr/lib/systemd/system-preset/50-security-misc.preset | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/usr/lib/systemd/system-preset/50-security-misc.preset b/usr/lib/systemd/system-preset/50-security-misc.preset index 4b44458..fda8e4d 100644 --- a/usr/lib/systemd/system-preset/50-security-misc.preset +++ b/usr/lib/systemd/system-preset/50-security-misc.preset @@ -18,7 +18,11 @@ disable proc-hidepid.service ## https://github.com/Kicksecure/security-misc/issues/159 disable harden-module-loading.service +## TODO: polish, test ## Disable due to timing difficulties. See: ## https://github.com/systemd/systemd/issues/38261#issuecomment-3134580852 disable ensure-shutdown.service disable ensure-shutdown-trigger.service + +## TODO: Disabled due to bug: breaks ISO Live Mode Calamares installer +disable emerg-shutdown.service From 5dc251c5da724092d264481740e4f6ed347aa0a7 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Sat, 9 Aug 2025 09:45:35 +0000 Subject: [PATCH 32/57] bumped changelog version --- changelog.upstream | 14 ++++++++++++++ debian/changelog | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/changelog.upstream b/changelog.upstream index 2c9ba0d..7cc05ee 100644 --- a/changelog.upstream +++ b/changelog.upstream @@ -1,3 +1,17 @@ +commit 046c932898290d250a7900e3c59973a698e5c55f +Author: Patrick Schleizer +Date: Sat Aug 9 05:40:11 2025 -0400 + + `disable emerg-shutdown.service`: + + Disabled due to bug: breaks ISO Live Mode Calamares installer + +commit 0cc0a8310020afc10de6512095336e55559a84d9 +Author: Patrick Schleizer +Date: Thu Aug 7 07:08:19 2025 +0000 + + bumped changelog version + commit 505a2b7d7995ad48a17add86513ced3499f64ee9 Merge: 4294165 3a77abe Author: Patrick Schleizer diff --git a/debian/changelog b/debian/changelog index 2494b88..5efc19b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +security-misc (3:46.9-1) unstable; urgency=medium + + * New upstream version (local package). + + -- Patrick Schleizer Sat, 09 Aug 2025 09:45:34 +0000 + security-misc (3:46.8-1) unstable; urgency=medium * New upstream version (local package). From c59a3b233bd8893d466c020a2e2695ab545c6e60 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Sat, 9 Aug 2025 21:55:03 -0500 Subject: [PATCH 33/57] Fix unexpected shutdowns when booting Kicksecure from optical media --- usr/src/security-misc/emerg-shutdown.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/usr/src/security-misc/emerg-shutdown.c b/usr/src/security-misc/emerg-shutdown.c index cd310e8..1c89a10 100644 --- a/usr/src/security-misc/emerg-shutdown.c +++ b/usr/src/security-misc/emerg-shutdown.c @@ -793,6 +793,7 @@ void hw_monitor(int argc, char **argv) { char *tmpbuf = NULL; bool device_removed = false; bool device_changed = false; + bool disk_media_changed = false; len = recvmsg(ns, &msg, 0); if (len == -1) { @@ -821,6 +822,10 @@ void hw_monitor(int argc, char **argv) { device_changed = true; goto next_str; } + if (strcmp(tmpbuf, "DISK_MEDIA_CHANGE=1") == 0) { + disk_media_changed = true; + goto next_str; + } if (strncmp(tmpbuf, "DEVNAME=", strlen("DEVNAME=")) == 0) { if (device_removed || device_changed) { @@ -861,6 +866,11 @@ void hw_monitor(int argc, char **argv) { goto next_str; } + if (device_changed && !disk_media_changed) { + free(rem_devname_line); + goto next_str; + } + if (paranoid_mode) { /* Something was removed, we don't care what, shut down now */ kill_system(); From c7bdca32c05ddc70b785a28ebe5be13614407dc8 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Sun, 10 Aug 2025 06:34:30 +0000 Subject: [PATCH 34/57] bumped changelog version --- changelog.upstream | 19 +++++++++++++++++++ debian/changelog | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/changelog.upstream b/changelog.upstream index 7cc05ee..95ddcfd 100644 --- a/changelog.upstream +++ b/changelog.upstream @@ -1,3 +1,22 @@ +commit 3629f2c3a59d44e265f0c66389435de1b2414998 +Merge: 5dc251c c59a3b2 +Author: Patrick Schleizer +Date: Sun Aug 10 02:25:48 2025 -0400 + + Merge remote-tracking branch 'ArrayBolt3/arraybolt3/emerg-shutdown' + +commit c59a3b233bd8893d466c020a2e2695ab545c6e60 +Author: Aaron Rainbolt +Date: Sat Aug 9 21:55:03 2025 -0500 + + Fix unexpected shutdowns when booting Kicksecure from optical media + +commit 5dc251c5da724092d264481740e4f6ed347aa0a7 +Author: Patrick Schleizer +Date: Sat Aug 9 09:45:35 2025 +0000 + + bumped changelog version + commit 046c932898290d250a7900e3c59973a698e5c55f Author: Patrick Schleizer Date: Sat Aug 9 05:40:11 2025 -0400 diff --git a/debian/changelog b/debian/changelog index 5efc19b..796dfa1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +security-misc (3:47.0-1) unstable; urgency=medium + + * New upstream version (local package). + + -- Patrick Schleizer Sun, 10 Aug 2025 06:34:30 +0000 + security-misc (3:46.9-1) unstable; urgency=medium * New upstream version (local package). From c33ea7be6d2a82462042cf482a32bf259bf51bd5 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Sun, 10 Aug 2025 15:23:48 -0500 Subject: [PATCH 35/57] Move security-misc/apt-get-update* to helper-scripts --- usr/libexec/security-misc/apt-get-update | 46 ------------------- .../security-misc/apt-get-update-sanity-test | 21 --------- 2 files changed, 67 deletions(-) delete mode 100755 usr/libexec/security-misc/apt-get-update delete mode 100755 usr/libexec/security-misc/apt-get-update-sanity-test diff --git a/usr/libexec/security-misc/apt-get-update b/usr/libexec/security-misc/apt-get-update deleted file mode 100755 index 9cbfd8e..0000000 --- a/usr/libexec/security-misc/apt-get-update +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -## Copyright (C) 2012 - 2025 ENCRYPTED SUPPORT LLC -## See the file COPYING for copying conditions. - -## TODO: Move this to helper-scripts. - -set -o errexit -set -o nounset -set -o errtrace -set -o pipefail - -command -v start-stop-daemon >/dev/null -command -v timeout >/dev/null -command -v apt-get >/dev/null - -export LC_ALL=C -pidfile="/run/helper-scripts/security-misc-apt-get-update-pid" - -sigterm_trap() { - /usr/libexec/helper-scripts/apt-get-update-kill-helper &>/dev/null - exit 143 -} - -## terminate potential previous invocations. -/usr/libexec/helper-scripts/apt-get-update-kill-helper &>/dev/null - -trap "sigterm_trap" SIGTERM SIGINT - -[[ -v timeout_after ]] || timeout_after="600" -[[ -v kill_after ]] || kill_after="10" - -start-stop-daemon \ - --make-pidfile \ - --pidfile "$pidfile" \ - --exec /usr/bin/timeout \ - --start \ - -- \ - --kill-after="$kill_after" \ - "$timeout_after" \ - apt-get update --error-on=any "$@" & - -lastpid="$!" -wait "$lastpid" - -exit "$?" diff --git a/usr/libexec/security-misc/apt-get-update-sanity-test b/usr/libexec/security-misc/apt-get-update-sanity-test deleted file mode 100755 index 7efac72..0000000 --- a/usr/libexec/security-misc/apt-get-update-sanity-test +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -## Copyright (C) 2012 - 2025 ENCRYPTED SUPPORT LLC -## See the file COPYING for copying conditions. - -set -x -set -e -set -o pipefail - -if ! printf '%s\n' "" | wc -l >/dev/null ; then - printf '%s\n' "\ -$0: ERROR: command 'wc' test failed! Do not ignore this! - -'wc' can core dump. Example: -zsh: illegal hardware instruction (core dumped) wc -l -https://github.com/rspamd/rspamd/issues/5137" >&2 - exit 1 -fi - -wc -L "/var/lib/apt/lists/"*InRelease -wc -L "/var/lib/apt/lists/"*InRelease | awk '$1 > 1024 {print; exit 1}' From fce86dccb67db0a37601899bf3115bd9f4fa714a Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Wed, 13 Aug 2025 10:44:40 +1000 Subject: [PATCH 36/57] Typo --- usr/libexec/security-misc/panic-on-oops | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/usr/libexec/security-misc/panic-on-oops b/usr/libexec/security-misc/panic-on-oops index 3c45a68..b5cb2e8 100755 --- a/usr/libexec/security-misc/panic-on-oops +++ b/usr/libexec/security-misc/panic-on-oops @@ -19,8 +19,7 @@ fi sysctl kernel.oops_limit=1 sysctl kernel.warn_limit=1 -## Makes the system immediately reboot on the occur occurrence of -## a single kernel panic. This is an extreme safety option which also -## creates a large opening for targeted denial of service attacks. +## Makes the system immediately reboot on the occurrence of a single +## kernel panic. This is an extreme safety option which also creates +## a large opening for targeted denial of service attacks. #sysctl kernel.panic=-1 - From 00c660d40dce06d979fc7b9dbf7a6e952a9e51cc Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Fri, 15 Aug 2025 11:29:27 +1000 Subject: [PATCH 37/57] Typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ed9c740..ca0966d 100644 --- a/README.md +++ b/README.md @@ -221,8 +221,8 @@ Kernel space: - Disable 32-bit vDSO mappings as they are a legacy compatibility feature. -- Use kCFI as the default CFI implementation as it is more resilient to attacks that able - to write arbitrary executables into memory omitting the necessary hash validation. +- Use kCFI as the default CFI implementation as it is more resilient to attacks that are + able to write arbitrary executables into memory omitting the necessary hash validation. - Optional - Disable support for all x86 processes and syscalls (when using Linux kernel >= 6.7) to reduce attack surface. From 94668b2e93c37a3879968f455575c631f9f8bcac Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Fri, 15 Aug 2025 16:46:35 -0500 Subject: [PATCH 38/57] Set hard and soft limits on core file size at the same time --- etc/security/limits.d/30_security-misc.conf | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/etc/security/limits.d/30_security-misc.conf b/etc/security/limits.d/30_security-misc.conf index 0f0108f..52e9d78 100644 --- a/etc/security/limits.d/30_security-misc.conf +++ b/etc/security/limits.d/30_security-misc.conf @@ -2,5 +2,6 @@ ## See the file COPYING for copying conditions. ## Disable coredumps. -* hard core 0 -* soft core 0 +## `-` in the second field sets both hard and soft limits at the same time. +## See `man 5 limits.conf`. +* - core 0 From 66ec5bda5872bbf51eb480836d3ffb3bb2f934cf Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Fri, 15 Aug 2025 16:51:07 -0500 Subject: [PATCH 39/57] Remove obsolete Thunderbird configuration hardening --- etc/thunderbird/pref/40_security-misc.js | 59 ------------------------ 1 file changed, 59 deletions(-) delete mode 100644 etc/thunderbird/pref/40_security-misc.js diff --git a/etc/thunderbird/pref/40_security-misc.js b/etc/thunderbird/pref/40_security-misc.js deleted file mode 100644 index 931f9d2..0000000 --- a/etc/thunderbird/pref/40_security-misc.js +++ /dev/null @@ -1,59 +0,0 @@ -//#### Copyright (C) 2019 - 2025 ENCRYPTED SUPPORT LLC -//#### See the file COPYING for copying conditions. - -//#### meta start -//#### project Whonix and Kicksecure -//#### category security and apps -//#### description https://forums.whonix.org/t/enable-network-idn-show-punycode-by-default-in-thunderbird-to-fix-url-not-showing-real-domain-name-homograph-attack-punycode/8415 -//#### meta end - -// https://forums.whonix.org/t/enable-network-idn-show-punycode-by-default-in-thunderbird-to-fix-url-not-showing-real-domain-name-homograph-attack-punycode/8415 -pref("network.IDN_show_punycode", true); - -// Disable all and any kind of telemetry by default -pref("toolkit.telemetry.enabled", false); -pref("toolkit.telemetry.unified", false); -pref("toolkit.telemetry.shutdownPingSender.enabled", false); -pref("toolkit.telemetry.updatePing.enabled", false); -pref("toolkit.telemetry.archive.enabled", false); -pref("toolkit.telemetry.bhrPing.enabled", false); -pref("toolkit.telemetry.firstShutdownPing.enabled", false); -pref("toolkit.telemetry.newProfilePing.enabled", false); -pref("toolkit.telemetry.server", ""); // Defense in depth -pref("toolkit.telemetry.server_owner", ""); // Defense in depth -pref("datareporting.healthreport.uploadEnabled", false); -pref("datareporting.policy.dataSubmissionEnabled", false); -pref("toolkit.telemetry.coverage.opt-out", true); // from Firefox -pref("toolkit.coverage.opt-out", true); // from Firefox - -// Disable implicit outbound traffic -pref("network.connectivity-service.enabled", false); -pref("network.prefetch-next", false); -pref("network.dns.disablePrefetch", true); -pref("network.predictor.enabled", false); - -// No need to explain the problems with javascript -// If you want javascript, use your browser -// Thunderbird needs no javascript -// pref("javascript.enabled", false); // Will break setting up services that require redirecting to their javascripted webpage for login, like gmail etc. So commented out for now. - -// Disable scripting when viewing pdf files -user_pref("pdfjs.enableScripting", false); - -// If you want cookies, use your browser -pref("network.cookie.cookieBehavior", 2); - -// Do not send user agent information -// For email clients, this is more like a relic of the past -// Completely not necessary and just exposes a lot of information about the client -// Since v115.0 Thunderbird already minimizes the user agent -// But we want it gone for good for no information leak at all -// https://hg.mozilla.org/comm-central/rev/cbbbc8d93cd7 -pref("mailnews.headers.sendUserAgent", false); - -// Normally we send emails after marking them with a time stamp -// That includes our local time zone -// This option makes our local time zone appear as UTC -// And rounds the time stamp to the closes minute -// https://hg.mozilla.org/comm-central/rev/98aa0bf2e719 -pref("mail.sanitize_date_header", true); From cba16879eff9d3d998c127e41c38d2067cdf04cc Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Fri, 15 Aug 2025 17:16:42 -0500 Subject: [PATCH 40/57] Polish USBGuard configuration --- debian/control | 1 - etc/usbguard/rules.d/30_security-misc.conf | 43 +++++++++++++--------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/debian/control b/debian/control index 0dd839e..13143e4 100644 --- a/debian/control +++ b/debian/control @@ -32,7 +32,6 @@ Depends: adduser, python3, secure-delete, sudo, - usbguard, ${misc:Depends} Replaces: anon-gpg-tweaks, swappiness-lowest, tcp-timestamps-disable Description: Enhances Miscellaneous Security Settings diff --git a/etc/usbguard/rules.d/30_security-misc.conf b/etc/usbguard/rules.d/30_security-misc.conf index fd2bce9..9b63314 100644 --- a/etc/usbguard/rules.d/30_security-misc.conf +++ b/etc/usbguard/rules.d/30_security-misc.conf @@ -1,22 +1,31 @@ -# We allow those that were plugged in before the daemon starts. Everything is blocked as the default. Following rules apply on top of this. +## We allow devices that were plugged in before the daemon starts. Everything +## is blocked as the default. Following rules apply on top of this. -# Explicitly reject any interface that is not documented and/or defined by USB.org -# Note: Most probably superfluous -reject with-interface none-of { 01:*:* 02:*:* 03:*:* 04:*:* 05:*:* 06:*:* 07:*:* 08:*:* 09:*:* 0a:*:* 0b:*:* 0d:*:* 0e:*:* 0f:*:* 10:*:* 11:*:* 12:*:* 13:*:* 14:*:* 3c:*:* dc:*:* e0:*:* ef:*:* fe:*:* ff:*:*} +## Explicitly reject any interface that is not documented and/or defined by +## USB.org. +## Note: Most probably superfluous. +reject with-interface none-of { 00:*:* 01:*:* 02:*:* 03:*:* 05:*:* 06:*:* 07:*:* 08:*:* 09:*:* 0a:*:* 0b:*:* 0d:*:* 0e:*:* 0f:*:* 10:*:* 11:*:* 12:*:* 13:*:* 14:*:* 3c:*:* dc:*:* e0:*:* ef:*:* fe:*:* ff:*:* } -### Allow all mouses and keyboards, in a sense, so the user can conveniently change them without restrating the daemon. -### Take extra measures to ensure security +## Allow all mouses and keyboards, in a sense, so the user can conveniently +## change them without restrating the daemon. -# Allow only one keyboard to be connected -allow with-interface one-of { 03:00:01 03:01:01 } if !allowed-matches(with-interface one-of { 03:00:01 03:01:01 }) +## Allow only one keyboard to be connected +allow with-interface equals { 03:01:01 } if !allowed-matches(with-interface equals { 03:01:01 }) +## Allow only one mouse to be connected +allow with-interface equals { 03:01:02 } if !allowed-matches(with-interface equals { 03:01:02 }) +## NOTE: Some HID devices will have an interface of 03:00:00 - these are HID +## devices that do not support a "boot interface". **These are blocked +## entirely.** It is very likely that this will cause issues with some mice +## and keyboards. Also note, all HID devices other than mice and keyboards +## will be blocked, **including touchscreens.** -# Allow only one mouse to be connected -allow with-interface one-of { 03:00:02 03:01:02 } if !allowed-matches(with-interface one-of { 03:00:02 03:01:02 }) - -# Explicitly reject any device with a mouse/keyboard interface in combination with some other interface -# Mouses and keyboards should only have one interface for all legitimate use cases +## Explicitly reject any device with a mouse/keyboard interface in +## combination with some other interface. +## Mice and keyboards should likely never have non-HID interfaces provided +## alongside them. +reject with-interface all-of { 03:*:* 00:*:* } +reject with-interface all-of { 03:*:* 01:*:* } reject with-interface all-of { 03:*:* 02:*:* } -reject with-interface all-of { 03:*:* 04:*:* } reject with-interface all-of { 03:*:* 05:*:* } reject with-interface all-of { 03:*:* 06:*:* } reject with-interface all-of { 03:*:* 07:*:* } @@ -39,8 +48,8 @@ reject with-interface all-of { 03:*:* ef:*:* } reject with-interface all-of { 03:*:* fe:*:* } reject with-interface all-of { 03:*:* ff:*:* } -# Allow USB mass storage -# If and only if the USB device only has the mass storage interface and nothing extra -# Suspicious interface combinations with mass storage are blocked +## Allow USB mass storage, if and only if the USB device only has the mass +## storage interface and nothing extra. +## Suspicious interface combinations with mass storage are blocked. allow with-interface equals { 08:*:* } From 1f75426f079d6e0aecd8fac22088ad36a7c16398 Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Sat, 16 Aug 2025 02:20:00 +0000 Subject: [PATCH 41/57] Clarify docs for disabling 32-bit x86 support --- README.md | 2 +- etc/default/grub.d/40_kernel_hardening.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e853332..2239824 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ Kernel space: since it may be slightly more resilient to attacks that are able to write arbitrary executables in memory. -- Optional - Disable support for all x86 processes and syscalls to reduce attack surface. +- Optional - Disable support for all 32-bit x86 processes and syscalls to reduce attack surface. - Disable the EFI persistent storage feature which prevents the kernel from writing crash logs and other persistent data to either the UEFI variable storage or ACPI ERST backends. diff --git a/etc/default/grub.d/40_kernel_hardening.cfg b/etc/default/grub.d/40_kernel_hardening.cfg index 1f3cc7c..0e6cbfe 100644 --- a/etc/default/grub.d/40_kernel_hardening.cfg +++ b/etc/default/grub.d/40_kernel_hardening.cfg @@ -210,7 +210,7 @@ GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX vdso32=0" ## #GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX cfi=kcfi" -## Disable support for x86 processes and syscalls. +## Disable support for all 32-bit x86 processes and syscalls. ## Unconditionally disables IA32 emulation to substantially reduce attack surface. ## ## https://lore.kernel.org/all/20230623111409.3047467-7-nik.borisov@suse.com/ From c33f7d04e2cef477b675fbf6c2a91583ba3bf808 Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Sat, 16 Aug 2025 03:32:48 +0000 Subject: [PATCH 42/57] Remove duplicate comment --- usr/lib/sysctl.d/990-security-misc.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/usr/lib/sysctl.d/990-security-misc.conf b/usr/lib/sysctl.d/990-security-misc.conf index 139e2e6..be306df 100644 --- a/usr/lib/sysctl.d/990-security-misc.conf +++ b/usr/lib/sysctl.d/990-security-misc.conf @@ -185,7 +185,6 @@ kernel.perf_event_paranoid=3 ## ## See /usr/libexec/security-misc/panic-on-oops for implementation. ## -#kernel.panic=-1 #kernel.oops_limit=1 #kernel.warn_limit=1 From f1de0da69b46f91ea7fd34db601393d23599b3bb Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Sat, 16 Aug 2025 04:01:12 +0000 Subject: [PATCH 43/57] Clarify description on panics on oopses and warns --- usr/libexec/security-misc/panic-on-oops | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/usr/libexec/security-misc/panic-on-oops b/usr/libexec/security-misc/panic-on-oops index b5cb2e8..1add1ca 100755 --- a/usr/libexec/security-misc/panic-on-oops +++ b/usr/libexec/security-misc/panic-on-oops @@ -13,9 +13,10 @@ if [ -f /usr/libexec/helper-scripts/pre.bsh ]; then fi ## Makes the kernel immediately panic on both oopses and warnings. -## This prevents the kernel from continuing to run a potentially -## flawed processes. Many kernel exploits will also cause an oops, -## these settings will make the kernel kill the offending processes. +## These settings force a full system crash rather than continuing +## to run after an inconsistent state is triggered by a potentially +## flawed processes. The reasons for the errors could be kernel +## exploit attempts but may also simply be general software bugs. sysctl kernel.oops_limit=1 sysctl kernel.warn_limit=1 From 247015bcc6e924e24874d16ed7ad78165ace58a3 Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Sun, 17 Aug 2025 06:27:44 +0000 Subject: [PATCH 44/57] Set `sysctl kernel.panic=-1` --- README.md | 15 ++++----------- usr/lib/sysctl.d/990-security-misc.conf | 6 ++++-- usr/libexec/security-misc/panic-on-oops | 6 +++--- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index b124a7a..425bd0d 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ configuration file and significant hardening is applied to a myriad of component - Force the kernel to immediately panic on both "oopses" (which can potentially indicate and thwart certain kernel exploitation attempts) and kernel warnings in the `WARN()` path. -- Optional - Force immediate reboot on the occurrence of a single kernel panic. +- Force immediate system reboot on the occurrence of a single kernel panic, reducing the + risk and impact of both denial of service and cold boot attacks. - Disable the use of legacy TIOCSTI operations which can be used to inject keypresses. @@ -281,21 +282,13 @@ Completely disables `ptrace()`. Can be enabled easily if needed. **Non-compliance:** -2. `sysctl kernel.panic=-1` - -Forces an immediate reboot on kernel panic. This can be enabled, but it may lead to unexpected -system crashes. - -* [security-misc pull request #264](https://github.com/Kicksecure/security-misc/pull/264) -* [security-misc pull request #268](https://github.com/Kicksecure/security-misc/pull/268) - -3. `sysctl user.max_user_namespaces=0` +2. `sysctl user.max_user_namespaces=0` Disables user namespaces entirely. Not recommended due to the potential for widespread breakages. * [security-misc pull request #263](https://github.com/Kicksecure/security-misc/pull/263) -4. `sysctl fs.binfmt_misc.status=0` +3. `sysctl fs.binfmt_misc.status=0` Disables the registration of interpreters for miscellaneous binary formats. Currently not feasible due to compatibility issues with Firefox. diff --git a/usr/lib/sysctl.d/990-security-misc.conf b/usr/lib/sysctl.d/990-security-misc.conf index be306df..3c43d8d 100644 --- a/usr/lib/sysctl.d/990-security-misc.conf +++ b/usr/lib/sysctl.d/990-security-misc.conf @@ -189,9 +189,11 @@ kernel.perf_event_paranoid=3 #kernel.warn_limit=1 ## Force immediate system reboots on the occurrence of a single kernel panic. -## This is an extreme safety option which also creates a large opening for targeted denial of service attacks. +## Ensures the system does not hang forever if a panic occurs, reducing susceptibility to cold boot attacks. +## Increases resilience and limits impact of denial of service attacks as system automatically restarts. +## Immediate rebooting also prevents persistent information disclosure on panic details that were dumped to screen. ## -## KSPP=no +## KSPP=yes ## KSPP sets CONFIG_PANIC_TIMEOUT=-1. ## ## See /usr/libexec/security-misc/panic-on-oops for implementation. diff --git a/usr/libexec/security-misc/panic-on-oops b/usr/libexec/security-misc/panic-on-oops index 1add1ca..83ceeac 100755 --- a/usr/libexec/security-misc/panic-on-oops +++ b/usr/libexec/security-misc/panic-on-oops @@ -21,6 +21,6 @@ sysctl kernel.oops_limit=1 sysctl kernel.warn_limit=1 ## Makes the system immediately reboot on the occurrence of a single -## kernel panic. This is an extreme safety option which also creates -## a large opening for targeted denial of service attacks. -#sysctl kernel.panic=-1 +## kernel panic. This reduces the risk and impact of both denial of +## service and cold boot attacks. +sysctl kernel.panic=-1 From 6df3e3cde8053d6b2771f510457da774336546bf Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Sun, 17 Aug 2025 06:32:11 +0000 Subject: [PATCH 45/57] Update kernel panic service description --- usr/lib/systemd/system/panic-on-oops.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/systemd/system/panic-on-oops.service b/usr/lib/systemd/system/panic-on-oops.service index 6b10ddc..429af3d 100644 --- a/usr/lib/systemd/system/panic-on-oops.service +++ b/usr/lib/systemd/system/panic-on-oops.service @@ -2,7 +2,7 @@ ## See the file COPYING for copying conditions. [Unit] -Description=Sets 'sysctl kernel.panic_on_oops=1' late during the boot process. +Description=Sets 'sysctl' settings relating to kernel panics on both oopses and warnings late during the boot process. Documentation=https://github.com/Kicksecure/security-misc ConditionKernelCommandLine=!panic-on-oops=0 From e06b78a52225db02415aeafb833160c9ea0280d9 Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Sun, 17 Aug 2025 07:05:32 +0000 Subject: [PATCH 46/57] Temporarily revert IA32 doc updates --- README.md | 5 +++-- etc/default/grub.d/40_kernel_hardening.cfg | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2239824..7f3eead 100644 --- a/README.md +++ b/README.md @@ -224,8 +224,9 @@ Kernel space: since it may be slightly more resilient to attacks that are able to write arbitrary executables in memory. -- Optional - Disable support for all 32-bit x86 processes and syscalls to reduce attack surface. - +- Optional - Disable support for all x86 processes and syscalls (when using Linux kernel >= 6.7) + to reduce attack surface. + - Disable the EFI persistent storage feature which prevents the kernel from writing crash logs and other persistent data to either the UEFI variable storage or ACPI ERST backends. diff --git a/etc/default/grub.d/40_kernel_hardening.cfg b/etc/default/grub.d/40_kernel_hardening.cfg index 0e6cbfe..671c28b 100644 --- a/etc/default/grub.d/40_kernel_hardening.cfg +++ b/etc/default/grub.d/40_kernel_hardening.cfg @@ -210,7 +210,7 @@ GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX vdso32=0" ## #GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX cfi=kcfi" -## Disable support for all 32-bit x86 processes and syscalls. +## Disable support for x86 processes and syscalls. ## Unconditionally disables IA32 emulation to substantially reduce attack surface. ## ## https://lore.kernel.org/all/20230623111409.3047467-7-nik.borisov@suse.com/ @@ -218,6 +218,9 @@ GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX vdso32=0" ## KSPP=yes ## KSPP does not set CONFIG_COMPAT, CONFIG_IA32_EMULATION, CONFIG_X86_X32, CONFIG_X86_X32_ABI, and CONFIG_MODIFY_LDT_SYSCALL. ## +## TODO: Debian 13 Trixie +## Applicable when using Linux kernel >= 6.7 (retained here for future-proofing and completeness). +## #GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX ia32_emulation=0" ## Disable EFI persistent storage feature. From 3de9cd5646ad45fe745711b83f79f4d469fc8473 Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Sun, 17 Aug 2025 07:06:55 +0000 Subject: [PATCH 47/57] Remove whitespace --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f3eead..180dfa7 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,7 @@ Kernel space: - Optional - Disable support for all x86 processes and syscalls (when using Linux kernel >= 6.7) to reduce attack surface. - + - Disable the EFI persistent storage feature which prevents the kernel from writing crash logs and other persistent data to either the UEFI variable storage or ACPI ERST backends. From f175d1961e4e028539f5a90c0db1fcd1f760cdba Mon Sep 17 00:00:00 2001 From: raja-grewal Date: Sun, 17 Aug 2025 07:08:08 +0000 Subject: [PATCH 48/57] Enable `ia32_emulation=0` --- README.md | 3 +-- etc/default/grub.d/40_kernel_hardening.cfg | 7 ++----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ac12886..6a98c52 100644 --- a/README.md +++ b/README.md @@ -225,8 +225,7 @@ Kernel space: since it may be slightly more resilient to attacks that are able to write arbitrary executables in memory. -- Optional - Disable support for all x86 processes and syscalls (when using Linux kernel >= 6.7) - to reduce attack surface. +- Disable support for all 32-bit x86 processes and syscalls to reduce attack surface. - Disable the EFI persistent storage feature which prevents the kernel from writing crash logs and other persistent data to either the UEFI variable storage or ACPI ERST backends. diff --git a/etc/default/grub.d/40_kernel_hardening.cfg b/etc/default/grub.d/40_kernel_hardening.cfg index 671c28b..5ac2c8c 100644 --- a/etc/default/grub.d/40_kernel_hardening.cfg +++ b/etc/default/grub.d/40_kernel_hardening.cfg @@ -210,7 +210,7 @@ GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX vdso32=0" ## #GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX cfi=kcfi" -## Disable support for x86 processes and syscalls. +## Disable support for all 32-bit x86 processes and syscalls. ## Unconditionally disables IA32 emulation to substantially reduce attack surface. ## ## https://lore.kernel.org/all/20230623111409.3047467-7-nik.borisov@suse.com/ @@ -218,10 +218,7 @@ GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX vdso32=0" ## KSPP=yes ## KSPP does not set CONFIG_COMPAT, CONFIG_IA32_EMULATION, CONFIG_X86_X32, CONFIG_X86_X32_ABI, and CONFIG_MODIFY_LDT_SYSCALL. ## -## TODO: Debian 13 Trixie -## Applicable when using Linux kernel >= 6.7 (retained here for future-proofing and completeness). -## -#GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX ia32_emulation=0" +GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX ia32_emulation=0" ## Disable EFI persistent storage feature. ## Disable Error Record Serialization Table (ERST) support as a form of defense-in-depth. From 312586307b2999d46e4673079fb1538f76ff329e Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Wed, 20 Aug 2025 06:25:35 -0400 Subject: [PATCH 49/57] readme --- README_generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_generic.md b/README_generic.md index 787af72..4c05521 100644 --- a/README_generic.md +++ b/README_generic.md @@ -28,7 +28,7 @@ sudo cp ~/derivative.asc /usr/share/keyrings/derivative.asc 3\. Add the derivative repository. ``` -echo "deb [signed-by=/usr/share/keyrings/derivative.asc] https://deb.kicksecure.com bookworm main contrib non-free" | sudo tee /etc/apt/sources.list.d/derivative.list +echo "deb [signed-by=/usr/share/keyrings/derivative.asc] https://deb.kicksecure.com trixie main contrib non-free" | sudo tee /etc/apt/sources.list.d/derivative.list ``` 4\. Update your package lists. From e15bdd2de513fbdcdb3b0fed4c1c1c5f74652291 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Wed, 20 Aug 2025 06:57:29 -0400 Subject: [PATCH 50/57] bumped Standards-Version --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 6235dad..13143e4 100644 --- a/debian/control +++ b/debian/control @@ -13,7 +13,7 @@ Build-Depends: config-package-dev, Homepage: https://www.kicksecure.com/wiki/Security-misc Vcs-Browser: https://github.com/Kicksecure/security-misc Vcs-Git: https://github.com/Kicksecure/security-misc.git -Standards-Version: 4.6.2 +Standards-Version: 4.7.2 Rules-Requires-Root: no Package: security-misc From 5d67277c9f27a54d373e683a1d4e1ddf8c16ac25 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Wed, 20 Aug 2025 09:46:43 -0400 Subject: [PATCH 51/57] comments --- usr/lib/permission-hardener.d/25_default_whitelist_ssh.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/lib/permission-hardener.d/25_default_whitelist_ssh.conf b/usr/lib/permission-hardener.d/25_default_whitelist_ssh.conf index 7c825e7..3b84548 100644 --- a/usr/lib/permission-hardener.d/25_default_whitelist_ssh.conf +++ b/usr/lib/permission-hardener.d/25_default_whitelist_ssh.conf @@ -6,14 +6,14 @@ ## configuration. When security-misc is updated, this file may be overwritten. ## Used for SSH client key management -## https://manpages.debian.org/trixie/openssh-client/ssh-agent.1.en.html +## https://manpages.debian.org/ssh-agent ## Debian installs ssh-agent with setgid permissions (2755) and with ## _ssh as the group to help mitigate ptrace attacks that could extract ## private keys from the agent's memory. ssh-agent matchwhitelist ## Used only for SSH host-based authentication -## https://linux.die.net/man/8/ssh-keysign +## https://manpages.debian.org/ssh-keysign ## Needed to allow access to the machine's host key for use in the ## authentication process. This is a non-default method of authenticating to ## SSH, and is likely rarely used, thus this should be safe to disable. From 31fd316e72a87323f83e7e63d6a3440bf77cb350 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Wed, 20 Aug 2025 09:48:20 -0400 Subject: [PATCH 52/57] comments --- usr/libexec/security-misc/panic-on-oops | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/usr/libexec/security-misc/panic-on-oops b/usr/libexec/security-misc/panic-on-oops index 83ceeac..b0aaf51 100755 --- a/usr/libexec/security-misc/panic-on-oops +++ b/usr/libexec/security-misc/panic-on-oops @@ -17,10 +17,14 @@ fi ## to run after an inconsistent state is triggered by a potentially ## flawed processes. The reasons for the errors could be kernel ## exploit attempts but may also simply be general software bugs. +## +## https://docs.kernel.org/admin-guide/sysctl/kernel.html#oops-limit sysctl kernel.oops_limit=1 +## https://docs.kernel.org/admin-guide/sysctl/kernel.html#warn-limit sysctl kernel.warn_limit=1 ## Makes the system immediately reboot on the occurrence of a single ## kernel panic. This reduces the risk and impact of both denial of ## service and cold boot attacks. +## https://docs.kernel.org/admin-guide/sysctl/kernel.html#panic sysctl kernel.panic=-1 From 0e4664daa0267fe62888a9559db2b14d5e1ae8ea Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Wed, 20 Aug 2025 10:07:58 -0400 Subject: [PATCH 53/57] cleanup --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index 67ce5de..c47fddf 100644 --- a/README.md +++ b/README.md @@ -725,16 +725,6 @@ See: - Deactivates thumbnails in Thunar. - Rationale: lower attack surface when using the file manager - https://forums.whonix.org/t/disable-preview-in-file-manager-by-default/18904 -- Thunderbird is hardened with the following options: - - Displays domain names in punycode to prevent IDN homograph attacks (a - form of phishing). - - Strips email client information from sent email headers. - - Strips user time information from sent email headers by replacing the - originating time zone with UTC and rounding the timestamp to the nearest - minute. - - Disables scripting when viewing PDF files. - - Disables implicit outgoing connections. - - Disables all and any kind of telemetry. - Security and privacy enhancements for gnupg's config file `/etc/skel/.gnupg/gpg.conf`. See also: - https://raw.github.com/ioerror/torbirdy/master/gpg.conf From 2b876c74a3ed9ec91954c8d4f776dc803d212fd0 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Wed, 20 Aug 2025 10:09:10 -0400 Subject: [PATCH 54/57] readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c47fddf..6483423 100644 --- a/README.md +++ b/README.md @@ -729,6 +729,10 @@ See: `/etc/skel/.gnupg/gpg.conf`. See also: - https://raw.github.com/ioerror/torbirdy/master/gpg.conf - https://github.com/ioerror/torbirdy/pull/11 +- Hardens SSH client + `/etc/ssh/ssh_config.d/30_security-misc.conf` +- Hardens SSH server + `/etc/ssh/sshd_config.d/30_security-misc.conf` ### Project scope of application-specific hardening From 812f05f847de0f1cb5586ccfd4c5b684d507dd46 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Wed, 20 Aug 2025 10:11:49 -0400 Subject: [PATCH 55/57] comments --- etc/ssh/ssh_config.d/30_security-misc.conf | 3 +++ etc/ssh/sshd_config.d/30_security-misc.conf | 3 +++ 2 files changed, 6 insertions(+) diff --git a/etc/ssh/ssh_config.d/30_security-misc.conf b/etc/ssh/ssh_config.d/30_security-misc.conf index 6693f72..b582dd1 100644 --- a/etc/ssh/ssh_config.d/30_security-misc.conf +++ b/etc/ssh/ssh_config.d/30_security-misc.conf @@ -7,6 +7,9 @@ ## need to be system-wide, you may also consider placing overrides in ## ~/.ssh/config. +## See also: +## https://www.kicksecure.com/wiki/SSH#Client_Configuration_File + Host * VisualHostKey yes Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr diff --git a/etc/ssh/sshd_config.d/30_security-misc.conf b/etc/ssh/sshd_config.d/30_security-misc.conf index 5abf9cc..f279fee 100644 --- a/etc/ssh/sshd_config.d/30_security-misc.conf +++ b/etc/ssh/sshd_config.d/30_security-misc.conf @@ -5,6 +5,9 @@ ## number that is read later by SSHD, such as ## '/etc/ssh/sshd_config.d/50_user.conf'. +## See also: +## https://www.kicksecure.com/wiki/SSH#Server_Configuration_File + ## This is okay because of strict firewall. For an onion-only server, listen ## on 127.0.0.1. ListenAddress 0.0.0.0 From c2d5bf38f539703c4858818f29d73b21d5776c80 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Wed, 20 Aug 2025 10:44:10 -0400 Subject: [PATCH 56/57] comment --- etc/ssh/sshd_config.d/30_security-misc.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/ssh/sshd_config.d/30_security-misc.conf b/etc/ssh/sshd_config.d/30_security-misc.conf index f279fee..93f93e4 100644 --- a/etc/ssh/sshd_config.d/30_security-misc.conf +++ b/etc/ssh/sshd_config.d/30_security-misc.conf @@ -53,7 +53,7 @@ KbdInteractiveAuthentication no ## account, read: ## https://www.kicksecure.com/wiki/SSH#SSH_Login_Comparison_Table ## We set it to 'yes' to work with libpam-tmpdir. -## https://www.kicksecure.com/wiki/Dev/Strong_Linux_User_Account_Isolation@libpam-tmpdir +## https://www.kicksecure.com/wiki/Dev/Strong_Linux_User_Account_Isolation#libpam-tmpdir ## Also folders such as '/run/user/1000' will exist thanks to PAM. ## The absence of that folder can lead to issues (such as with msgcollector). UsePAM yes From 81d437fe3efca99b0d1a29b2a1a3e486e91716c1 Mon Sep 17 00:00:00 2001 From: Patrick Schleizer Date: Wed, 20 Aug 2025 21:40:39 -0400 Subject: [PATCH 57/57] fix --- etc/ssh/sshd_config.d/30_security-misc.conf | 4 ---- 1 file changed, 4 deletions(-) diff --git a/etc/ssh/sshd_config.d/30_security-misc.conf b/etc/ssh/sshd_config.d/30_security-misc.conf index 93f93e4..45496fd 100644 --- a/etc/ssh/sshd_config.d/30_security-misc.conf +++ b/etc/ssh/sshd_config.d/30_security-misc.conf @@ -8,10 +8,6 @@ ## See also: ## https://www.kicksecure.com/wiki/SSH#Server_Configuration_File -## This is okay because of strict firewall. For an onion-only server, listen -## on 127.0.0.1. -ListenAddress 0.0.0.0 - ## Number of allowed login attempts per connection. MaxAuthTries 3