diff --git a/README.md b/README.md index cf3ea62..6483423 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,15 @@ 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. +- 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. -- 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 @@ -221,12 +220,10 @@ 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 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. +- 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. @@ -280,23 +277,15 @@ Completely disables `ptrace()`. Can be enabled easily if needed. * [security-misc pull request #242](https://github.com/Kicksecure/security-misc/pull/242) -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) - **Non-compliance:** -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. @@ -712,6 +701,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 @@ -723,20 +725,14 @@ 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 - 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 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/changelog.upstream b/changelog.upstream index 6a483f4..95ddcfd 100644 --- a/changelog.upstream +++ b/changelog.upstream @@ -1,3 +1,167 @@ +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 + + `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 +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 +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 +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 + + 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 +178,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 +668,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..796dfa1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,39 @@ +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). + + -- Patrick Schleizer Sat, 09 Aug 2025 09:45:34 +0000 + +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). + + -- Patrick Schleizer Wed, 06 Aug 2025 08:27:15 +0000 + +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). + + -- Patrick Schleizer Sun, 03 Aug 2025 11:33:03 +0000 + security-misc (3:46.4-1) unstable; urgency=medium * New upstream version (local package). diff --git a/debian/control b/debian/control index fd56b5f..13143e4 100644 --- a/debian/control +++ b/debian/control @@ -13,13 +13,14 @@ 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 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/etc/default/grub.d/40_kernel_hardening.cfg b/etc/default/grub.d/40_kernel_hardening.cfg index 671c28b..513b5c8 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,12 +204,9 @@ 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. +## 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 +214,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. 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" 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/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..e8b4b48 --- /dev/null +++ b/etc/security-misc/emerg-shutdown/30_security_misc.conf @@ -0,0 +1,34 @@ +## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC +## See the file COPYING for copying conditions. + +## 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 +## 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_END" + +## 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 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/etc/security/limits.d/30_security-misc.conf b/etc/security/limits.d/30_security-misc.conf index c1270f9..639bbac 100644 --- a/etc/security/limits.d/30_security-misc.conf +++ b/etc/security/limits.d/30_security-misc.conf @@ -2,4 +2,6 @@ ## See the file COPYING for copying conditions. ## Disable coredumps. -* - core 0 +## `-` in the second field sets both hard and soft limits at the same time. +## See `man 5 limits.conf`. +* - core 0 \ No newline at end of file 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..b582dd1 --- /dev/null +++ b/etc/ssh/ssh_config.d/30_security-misc.conf @@ -0,0 +1,22 @@ +## 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. + +## 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 + 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..45496fd --- /dev/null +++ b/etc/ssh/sshd_config.d/30_security-misc.conf @@ -0,0 +1,78 @@ +## 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'. + +## See also: +## https://www.kicksecure.com/wiki/SSH#Server_Configuration_File + +## 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 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); 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..9b63314 --- /dev/null +++ b/etc/usbguard/rules.d/30_security-misc.conf @@ -0,0 +1,55 @@ +## 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 { 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. + +## 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.** + +## 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:*:* 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:*:* } + 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/permission-hardener.d/25_default_whitelist_ssh.conf b/usr/lib/permission-hardener.d/25_default_whitelist_ssh.conf index 2b55bd2..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/bookworm/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. diff --git a/usr/lib/sysctl.d/990-security-misc.conf b/usr/lib/sysctl.d/990-security-misc.conf index eaa671e..0c44a05 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,36 +180,40 @@ 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. +## 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=yes +## 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. +## 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 +222,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: diff --git a/usr/lib/systemd/system-preset/50-security-misc.preset b/usr/lib/systemd/system-preset/50-security-misc.preset index 1895526..fda8e4d 100644 --- a/usr/lib/systemd/system-preset/50-security-misc.preset +++ b/usr/lib/systemd/system-preset/50-security-misc.preset @@ -17,3 +17,12 @@ disable proc-hidepid.service ## Disable due to issues. See: ## 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 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 new file mode 100644 index 0000000..fa738ab --- /dev/null +++ b/usr/lib/systemd/system/emerg-shutdown.service @@ -0,0 +1,20 @@ +## 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 +DefaultDependencies=no +Before=sysinit.target +Requires=systemd-udevd.service +After=systemd-udevd.service +Requires=local-fs.target +After=local-fs.target + +[Service] +Type=notify +ExecStart=/usr/libexec/security-misc/emerg-shutdown +NotifyAccess=main + +[Install] +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 new file mode 100644 index 0000000..8e4c5b6 --- /dev/null +++ b/usr/lib/systemd/system/ensure-shutdown.service @@ -0,0 +1,25 @@ +## 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=systemd-udevd.service +After=systemd-udevd.service +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=sysinit.target 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 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/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}' 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" diff --git a/usr/libexec/security-misc/emerg-shutdown b/usr/libexec/security-misc/emerg-shutdown new file mode 100755 index 0000000..3016088 --- /dev/null +++ b/usr/libexec/security-misc/emerg-shutdown @@ -0,0 +1,79 @@ +#!/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 + +## 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]='' + +## 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 + bash -n "${config_file}" + source "${config_file}" + fi +done + +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 \ + "${gcc_hardening_options[@]}" \ + /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 + +## Launch emerg-shutdown +OLDIFS="$IFS" +IFS=',' +"${binary_prefix}/emerg-shutdown" "--devices=${root_devices[*]}" "--keys=${EMERG_SHUTDOWN_KEYS}" diff --git a/usr/libexec/security-misc/ensure-shutdown b/usr/libexec/security-misc/ensure-shutdown new file mode 100755 index 0000000..85ab31d --- /dev/null +++ b/usr/libexec/security-misc/ensure-shutdown @@ -0,0 +1,31 @@ +#!/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 /usr/local/etc/security-misc/emerg-shutdown/*.conf; do + 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 + ENSURE_SHUTDOWN_TIMEOUT=30; +fi + +/run/emerg-shutdown --monitor-fifo "--timeout=${ENSURE_SHUTDOWN_TIMEOUT}" & +sleep 1 +disown +exit 0 diff --git a/usr/libexec/security-misc/panic-on-oops b/usr/libexec/security-misc/panic-on-oops index 749eb3c..b0aaf51 100755 --- a/usr/libexec/security-misc/panic-on-oops +++ b/usr/libexec/security-misc/panic-on-oops @@ -12,12 +12,19 @@ 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. -#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 +## Makes the kernel immediately panic on both oopses and warnings. +## 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. +## +## 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 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] 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 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/emerg-shutdown.c b/usr/src/security-misc/emerg-shutdown.c new file mode 100644 index 0000000..1c89a10 --- /dev/null +++ b/usr/src/security-misc/emerg-shutdown.c @@ -0,0 +1,1055 @@ +/* + * 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 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 + * 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 +#include +#include +#include +#include +#include +#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 + +#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 */ +struct name_value { + const char *name; + const uint32_t 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} +}; +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 0; +} + +/* 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) { + ssize_t write_len = write(fd, str, len); + if (write_len < 0) { + /* File descriptor was closed, continue regardless */ + return; + } + len -= (size_t)write_len; + if (len == 0) { + return; + } + str += write_len; + } +} + +void print_usage() { + print(fd_stderr, "Usage:\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, " --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"); + 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) { + 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]; + uint8_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, const char *sep, bool parse_opt) { + char **result_list = NULL; + size_t result_list_len = 0; + size_t 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); + 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, sep); + 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); +} + +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 + * 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); +} + +/* 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; + size_t panic_key_list_len = 0; + char **panic_key_str_list = NULL; + char **target_dev_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; + 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; + size_t tdl_idx = 0; + size_t tdp_char_idx = 0; + size_t pkl_idx = 0; + uint32_t input_idx = 0; + size_t efl_idx = 0; + int ie_idx = 0; + size_t kg_idx = 0; + + 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, ",", 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"); + print_usage(); + 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); + } + } + 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) { + 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(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++) { + 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, "The device '"); + print(fd_stderr, target_dev_path); + print(fd_stderr, "' 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, "The device '"); + print(fd_stderr, target_dev_path); + print(fd_stderr, "' is not supported!\n"); + print_usage(); + exit(1); + } + + 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, "The device '"); + print(fd_stderr, target_dev_path); + print(fd_stderr, "' is not supported!\n"); + print_usage(); + exit(1); + } + + memcpy(target_dev_parse, target_dev_path, strlen(target_dev_path) + 1); + + /* returns "dev" */ + 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, "The device '"); + print(fd_stderr, target_dev_path); + print(fd_stderr, "' is not supported!\n"); + print_usage(); + exit(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++) { + 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); + 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++) { + 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"); + print_usage(); + exit(1); + } + pkl_element[kg_idx] = keycode; + free(keygroup_str_list[kg_idx]); + } + + free(keygroup_str_list); + panic_key_list[pkl_idx] = pkl_element; + } + + /* Device event listener setup */ + struct sockaddr_nl sa = { + .nl_family = AF_NETLINK, + .nl_pad = 0, + .nl_pid = (uint32_t)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); + } + + /* 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; + + strcpy(input_path_buf, "/dev/input/event"); + loop_str = int_to_str(input_idx); + strcat(input_path_buf, loop_str); + free(loop_str); + + tmp_fd = open(input_path_buf, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); + if (tmp_fd < 0) { + continue; + } + + 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++) { + 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; + } + } + 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; + } + + ssize_t ieread_bytes = read(event_fd_list[efl_idx], ie_buf, sizeof(struct input_event) * 64); + + 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 + * recipe for bugs. */ + print(fd_stderr, "Error reading from input device!\n"); + exit(1); + } + + 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; + } + + for (pkl_idx = 0; pkl_idx < panic_key_list_len; pkl_idx++) { + 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 */ + } + } + } + + for (pkl_idx = 0; pkl_idx < panic_key_list_len; pkl_idx++) { + if (!panic_key_active_list[pkl_idx]) { + break; + } + if (pkl_idx == (panic_key_list_len - 1)) { + kill_system(); + /*print(fd_stderr, "SHUTDOWN!!!\n");*/ + exit(0); + } + } + } + } + + /* 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. + */ + + ssize_t len; + char buf[16384]; + struct iovec iov = { buf, sizeof(buf) }; + struct sockaddr_nl sa2; + 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; + bool disk_media_changed = false; + + len = recvmsg(ns, &msg, 0); + if (len == -1) { + kill_system(); + /*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 (strcmp(tmpbuf, "ACTION=change") == 0) { + 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) { + 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); + goto next_str; + } + + if (device_changed && strncmp(rem_dev_name, "sr", 2) != 0) { + free(rem_devname_line); + 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(); + } + + 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(); + /*print(fd_stderr, "SHUTDOWN!!!\n");*/ + exit(0); + } + } + + free(rem_devname_line); + } + } + +next_str: + len = len - (ssize_t)(strlen(tmpbuf) + 1); + tmpbuf += strlen(tmpbuf) + 1; + } + } + } + + 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, ""); + errno = 0; + monitor_fifo_timeout = strtol(arg_part, &arg_num_end, 10); + if (errno == ERANGE || monitor_fifo_timeout > UINT_MAX) { + 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((unsigned int)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); + } +}