Additional hardening on emerg-shutdown

This commit is contained in:
Aaron Rainbolt 2025-09-25 23:55:03 -05:00
parent 590aaec73d
commit 58cc6731f2
No known key found for this signature in database
GPG key ID: A709160D73C79109
2 changed files with 77 additions and 34 deletions

View file

@ -19,15 +19,37 @@ binary_prefix='/run'
EMERG_SHUTDOWN_KEYS='' EMERG_SHUTDOWN_KEYS=''
root_devices[0]='' root_devices[0]=''
## Taken from kloak/Makefile, see it for more information
gcc_hardening_options=( gcc_hardening_options=(
"-Wall" "-Wformat" "-Wformat=2" "-Wconversion" "-O2" "-Wall" "-Wextra" "-Wformat" "-Wformat=2" "-Wconversion"
"-Wimplicit-fallthrough" "-Werror=format-security" "-Werror=implicit" "-Wimplicit-fallthrough" "-Werror=format-security" "-Werror=implicit"
"-Werror=int-conversion" "-Werror=incompatible-pointer-types" "-Werror=int-conversion" "-Werror=incompatible-pointer-types"
"-Wtrampolines" "-Wbidi-chars=any" "-U_FORTIFY_SOURCE" "-D_FORTIFY_SOURCE=3" "-Wformat-overflow" "-Wformat-signedness" "-Wnull-dereference" "-Winit-self"
"-fstack-clash-protection" "-fstack-protector-strong" "-Wmissing-include-dirs" "-Wshift-negative-value" "-Wshift-overflow"
"-fno-delete-null-pointer-checks" "-fno-strict-overflow" "-Wswitch-default" "-Wuninitialized" "-Walloca" "-Warray-bounds"
"-fno-strict-aliasing" "-fsanitize=undefined" "-fcf-protection=full" "-Wfloat-equal" "-Wshadow" "-Wpointer-arith" "-Wundef" "-Wunused-macros"
"-Wbad-function-cast" "-Wcast-qual" "-Wcast-align" "-Wwrite-strings"
"-Wdate-time" "-Wstrict-prototypes" "-Wold-style-definition"
"-Wredundant-decls" "-Winvalid-utf8" "-Wvla" "-Wdisabled-optimization"
"-Wstack-protector" "-Wdeclaration-after-statement" "-Wtrampolines"
"-Wbidi-chars=any,ucn" "-Wformat-overflow=2" "-Wformat-truncation=2"
"-Wshift-overflow=2" "-Wtrivial-auto-var-init" "-Wstringop-overflow=3"
"-Wstrict-flex-arrays" "-Walloc-zero" "-Warray-bounds=2"
"-Wattribute-alias=2" "-Wduplicated-branches" "-Wduplicated-cond"
"-Wcast-align=strict" "-Wjump-misses-init" "-Wlogical-op" "-U_FORTIFY_SOURCE"
"-D_FORTIFY_SOURCE=3" "-fstack-clash-protection" "-fstack-protector-all"
"-fno-delete-null-pointer-checks" "-fno-strict-aliasing"
"-fsanitize=address,undefined" "-fno-sanitize-recover=all"
"-fstrict-flex-arrays=3" "-ftrivial-auto-var-init=pattern" "-fPIE"
)
gcc_machine="$(gcc -dumpmachine)"
if [ "${gcc_machine}" = 'x86_64-linux-gnu' ]; then
gcc_hardening_options+=( '-fcf-protection=full' )
elif [ "${gcc_machine}" = 'aarch64-linux-gnu' ]; then
gcc_hardening_options+=( '-mbranch-protection=standard' )
fi
gcc_hardening_options+=(
"-Wl,-z,nodlopen" "-Wl,-z,noexecstack" "-Wl,-z,relro" "-Wl,-z,now" "-Wl,-z,nodlopen" "-Wl,-z,noexecstack" "-Wl,-z,relro" "-Wl,-z,now"
"-Wl,--as-needed" "-Wl,--no-copy-dt-needed-entries" "-pie" "-Wl,--as-needed" "-Wl,--no-copy-dt-needed-entries" "-pie"
) )
@ -56,11 +78,12 @@ else
## Build the actual emerg-shutdown executable ## Build the actual emerg-shutdown executable
if [ ! -f '/run/emerg-shutdown' ]; then if [ ! -f '/run/emerg-shutdown' ]; then
gcc \ gcc \
-g
/usr/src/security-misc/emerg-shutdown.c \
-o \ -o \
/run/emerg-shutdown \ /run/emerg-shutdown \
-static \ -static \
"${gcc_hardening_options[@]}" \ "${gcc_hardening_options[@]}" \
/usr/src/security-misc/emerg-shutdown.c \
|| { || {
printf "%s\n" 'Could not compile force-shutdown executable!' printf "%s\n" 'Could not compile force-shutdown executable!'
exit 1 exit 1
@ -74,6 +97,5 @@ fi
systemd-notify --ready systemd-notify --ready
## Launch emerg-shutdown ## Launch emerg-shutdown
OLDIFS="$IFS"
IFS=',' IFS=','
"${binary_prefix}/emerg-shutdown" "--devices=${root_devices[*]}" "--keys=${EMERG_SHUTDOWN_KEYS}" "${binary_prefix}/emerg-shutdown" "--devices=${root_devices[*]}" "--keys=${EMERG_SHUTDOWN_KEYS}"

View file

@ -95,9 +95,10 @@
#include <signal.h> #include <signal.h>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <assert.h>
#define fd_stdin 0 //#define fd_stdin 0
#define fd_stdout 1 //#define fd_stdout 1
#define fd_stderr 2 #define fd_stderr 2
#define max_inputs 255 #define max_inputs 255
@ -275,7 +276,7 @@ bool bitset_get(const uint64_t *bits, uint32_t i) {
return (bits[i / 64] >> (i % 64)) & 1UL; return (bits[i / 64] >> (i % 64)) & 1UL;
} }
void print(int fd, char *str) { void print(int fd, const char *str) {
size_t len = strlen(str) + 1; size_t len = strlen(str) + 1;
while (true) { while (true) {
ssize_t write_len = write(fd, str, len); ssize_t write_len = write(fd, str, len);
@ -291,7 +292,7 @@ void print(int fd, char *str) {
} }
} }
void print_usage() { void print_usage(void) {
print(fd_stderr, "Usage:\n"); print(fd_stderr, "Usage:\n");
print(fd_stderr, " emerg-shutdown [OPTIONS...]\n"); print(fd_stderr, " emerg-shutdown [OPTIONS...]\n");
print(fd_stderr, "Options:\n"); print(fd_stderr, "Options:\n");
@ -343,7 +344,7 @@ void *safe_reallocarray(void *ptr, size_t nmemb, size_t size) {
/* Inspired by https://www.strudel.org.uk/itoa/ */ /* Inspired by https://www.strudel.org.uk/itoa/ */
char *int_to_str(uint32_t val) { char *int_to_str(uint32_t val) {
static char buf[11]; static char buf[11];
uint8_t i; int8_t i;
char *rslt = NULL; char *rslt = NULL;
const char *digits = "0123456789"; const char *digits = "0123456789";
@ -356,9 +357,10 @@ char *int_to_str(uint32_t val) {
break; break;
} }
} }
assert(i >= 0);
rslt = safe_calloc(1, 11 - i); rslt = safe_calloc(1, 11 - (uint8_t)(i));
memcpy(rslt, buf + i, 11 - i); memcpy(rslt, buf + i, 11 - (uint8_t)(i));
return rslt; return rslt;
} }
@ -397,7 +399,7 @@ void load_list(const char *arg, size_t *result_list_len_ref, char ***result_list
free(arg_copy); free(arg_copy);
} }
long int kill_system() { long int kill_system(void) {
/* /*
* It isn't safe to simply call the reboot syscall here - there is a * 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 * graphics driver bug in the i915 driver on Bookworm that will throw a
@ -437,14 +439,22 @@ long int kill_system() {
/* Turn off panic_on_oops. */ /* Turn off panic_on_oops. */
sysctl_fd = open(panic_on_oops_path, O_WRONLY); sysctl_fd = open(panic_on_oops_path, O_WRONLY);
if (sysctl_fd != -1) { if (sysctl_fd != -1) {
/* We intentionally ignore the return from `write` here. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
write(sysctl_fd, "0", 1); write(sysctl_fd, "0", 1);
#pragma GCC diagnostic pop
close(sysctl_fd); close(sysctl_fd);
} }
/* Turn off panic_on_warn. */ /* Turn off panic_on_warn. */
sysctl_fd = open(panic_on_warn_path, O_WRONLY); sysctl_fd = open(panic_on_warn_path, O_WRONLY);
if (sysctl_fd != -1) { if (sysctl_fd != -1) {
/* We intentionally ignore the return from `write` here. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
write(sysctl_fd, "0", 1); write(sysctl_fd, "0", 1);
#pragma GCC diagnostic pop
close(sysctl_fd); close(sysctl_fd);
} }
@ -495,6 +505,11 @@ void hw_monitor(int argc, char **argv) {
int ie_idx = 0; int ie_idx = 0;
size_t kg_idx = 0; size_t kg_idx = 0;
/* Socket management */
struct sockaddr_nl sa = { 0 };
int ns = 0;
int ret = 0;
for (arg_idx = 1; arg_idx < argc; arg_idx++) { for (arg_idx = 1; arg_idx < argc; arg_idx++) {
if (strncmp(argv[arg_idx], "--devices=", strlen("--devices=")) == 0) { if (strncmp(argv[arg_idx], "--devices=", strlen("--devices=")) == 0) {
if (target_dev_name_raw_list != NULL) { if (target_dev_name_raw_list != NULL) {
@ -599,8 +614,10 @@ void hw_monitor(int argc, char **argv) {
for (pkl_idx = 0; pkl_idx < panic_key_list_len; pkl_idx++) { for (pkl_idx = 0; pkl_idx < panic_key_list_len; pkl_idx++) {
size_t keygroup_str_list_len = 0; size_t keygroup_str_list_len = 0;
char **keygroup_str_list = NULL; char **keygroup_str_list = NULL;
uint32_t *pkl_element = NULL;
load_list(panic_key_str_list[pkl_idx], &keygroup_str_list_len, &keygroup_str_list, "|", false); 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 = safe_calloc(keygroup_str_list_len + 1, sizeof(uint32_t));
pkl_element[keygroup_str_list_len] = 0; pkl_element[keygroup_str_list_len] = 0;
for (kg_idx = 0; kg_idx < keygroup_str_list_len; kg_idx++) { for (kg_idx = 0; kg_idx < keygroup_str_list_len; kg_idx++) {
@ -621,18 +638,16 @@ void hw_monitor(int argc, char **argv) {
} }
/* Device event listener setup */ /* Device event listener setup */
struct sockaddr_nl sa = { sa.nl_family = AF_NETLINK;
.nl_family = AF_NETLINK, sa.nl_pad = 0;
.nl_pad = 0, sa.nl_pid = (uint32_t)getpid();
.nl_pid = (uint32_t)getpid(), sa.nl_groups = NETLINK_KOBJECT_UEVENT;
.nl_groups = NETLINK_KOBJECT_UEVENT, ns = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
};
int ns = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (ns < 0) { if (ns < 0) {
print(fd_stderr, "Failed to create netlink socket!\n"); print(fd_stderr, "Failed to create netlink socket!\n");
exit(1); exit(1);
} }
int ret = bind(ns, (struct sockaddr *) &sa, sizeof(sa)); ret = bind(ns, (struct sockaddr *) &sa, sizeof(sa));
if (ret < 0) { if (ret < 0) {
print(fd_stderr, "Failed to bind netlink socket!\n"); print(fd_stderr, "Failed to bind netlink socket!\n");
exit(1); exit(1);
@ -711,13 +726,17 @@ void hw_monitor(int argc, char **argv) {
/* Event loop */ /* Event loop */
while (poll(pollfd_list, event_fd_list_len + 1, -1) != -1) { while (poll(pollfd_list, event_fd_list_len + 1, -1) != -1) {
size_t ie_max_idx = 0;
/* Panic key handler */ /* Panic key handler */
for (efl_idx = 0; efl_idx < event_fd_list_len; efl_idx++) { for (efl_idx = 0; efl_idx < event_fd_list_len; efl_idx++) {
ssize_t ieread_bytes = 0;
if (!(pollfd_list[efl_idx].revents & POLLIN)) { if (!(pollfd_list[efl_idx].revents & POLLIN)) {
continue; continue;
} }
ssize_t ieread_bytes = read(event_fd_list[efl_idx], ie_buf, sizeof(struct input_event) * 64); ieread_bytes = read(event_fd_list[efl_idx], ie_buf,
sizeof(struct input_event) * 64);
if (ieread_bytes <= 0 if (ieread_bytes <= 0
|| ((size_t)ieread_bytes % sizeof(struct input_event)) != 0) { || ((size_t)ieread_bytes % sizeof(struct input_event)) != 0) {
@ -729,8 +748,9 @@ void hw_monitor(int argc, char **argv) {
exit(1); exit(1);
} }
for (ie_idx = 0; ie_idx < (size_t)ieread_bytes / sizeof(struct input_event); ie_max_idx = (size_t)ieread_bytes / sizeof(struct input_event);
ie_idx++) { assert(ie_max_idx < INT_MAX);
for (ie_idx = 0; ie_idx < (int)(ie_max_idx); ie_idx++) {
if (ie_buf[ie_idx].type != EV_KEY) { if (ie_buf[ie_idx].type != EV_KEY) {
continue; continue;
} }
@ -783,6 +803,11 @@ void hw_monitor(int argc, char **argv) {
struct iovec iov = { buf, sizeof(buf) }; struct iovec iov = { buf, sizeof(buf) };
struct sockaddr_nl sa2; struct sockaddr_nl sa2;
struct msghdr msg = { 0 }; struct msghdr msg = { 0 };
char *tmpbuf = NULL;
bool device_removed = false;
bool device_changed = false;
bool disk_media_changed = false;
msg.msg_name = &sa2; msg.msg_name = &sa2;
msg.msg_namelen = sizeof(sa2); msg.msg_namelen = sizeof(sa2);
msg.msg_iov = &iov; msg.msg_iov = &iov;
@ -790,10 +815,6 @@ void hw_monitor(int argc, char **argv) {
msg.msg_control = NULL; msg.msg_control = NULL;
msg.msg_controllen = 0; msg.msg_controllen = 0;
msg.msg_flags = 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); len = recvmsg(ns, &msg, 0);
if (len == -1) { if (len == -1) {
@ -906,7 +927,7 @@ next_str:
* - 'd': Wait 15 seconds, then kill the system. This is used to keep systemd * - 'd': Wait 15 seconds, then kill the system. This is used to keep systemd
* from delaying shutdown excessively. * from delaying shutdown excessively.
*/ */
void fifo_monitor(int argc, char **argv) { void fifo_monitor(char **argv) {
long monitor_fifo_timeout = 0; long monitor_fifo_timeout = 0;
char *arg_copy = NULL; char *arg_copy = NULL;
char *arg_part = NULL; char *arg_part = NULL;
@ -1046,7 +1067,7 @@ int main(int argc, char **argv) {
hw_monitor(argc, argv); hw_monitor(argc, argv);
} else if (monitor_mode == fifo_monitor_val) { } else if (monitor_mode == fifo_monitor_val) {
/* fifo_monitor handles its own argument parsing */ /* fifo_monitor handles its own argument parsing */
fifo_monitor(argc, argv); fifo_monitor(argv);
} else { } else {
print(fd_stderr, "Unknown monitor mode chosen!\n"); print(fd_stderr, "Unknown monitor mode chosen!\n");
print_usage(); print_usage();