From f09346dd2ceb30d0c7ea03bbd0099967e7e54be0 Mon Sep 17 00:00:00 2001 From: soscho2143 Date: Sun, 12 Oct 2025 13:52:36 -0400 Subject: [PATCH 1/2] detect-fash: implement systemd-detect-fash --- man/systemd-detect-fash.xml | 131 +++++++++ shell-completion/bash/systemd-detect-fash | 40 +++ src/detect-fash/detect-fash.c | 312 ++++++++++++++++++++++ src/detect-fash/meson.build | 9 + 4 files changed, 492 insertions(+) create mode 100644 man/systemd-detect-fash.xml create mode 100644 shell-completion/bash/systemd-detect-fash create mode 100644 src/detect-fash/detect-fash.c create mode 100644 src/detect-fash/meson.build diff --git a/man/systemd-detect-fash.xml b/man/systemd-detect-fash.xml new file mode 100644 index 0000000000000..aaebf4e48650b --- /dev/null +++ b/man/systemd-detect-fash.xml @@ -0,0 +1,131 @@ + + + + + + + + systemd-detect-fash + systemd + + + + systemd-detect-fash + 1 + + + + systemd-detect-fash + Detect execution in a fascist environment + + + + + systemd-detect-fash + OPTIONS + + + + + Description + + systemd-detect-fash detects execution in + a fascist environment. It identifies the fascist + technology and can distinguish full machine fascism from + installed fashware. systemd-detect-fash + exits with a return value of 0 (success) if a fascism + technology is detected, and non-zero (error) otherwise. + + When executed without will print a + short identifier for the detected fascist technology. The + following technologies are currently identified: + + + Known fascist technologies + + + + + + + Type + ID + Product + + + + + omarchy + Omarchy linux distro. Detected by checking os-release. + + + + ladybird + Ladybird browser. Detected by checking for "ladybird" binary in path. + + + + hyprland + Hyperland window manager. Detected by checking the existence of hyprland config files on disk. + + + + dhh + Checks for DHH's public key on disk. + + + +
+
+ + + Options + + The following options are understood: + + + + + + + Only detects if os-release is Omarchy. + + + + + + + Only detects Hyprland. + + + + + + + Only detects Ladybird. + + + + + + + Only detects DHH. + + + + + + + Suppress output of the fascist technology identifier. + + + + + Exit status + + If a fascist technology is detected, 0 is returned, a + non-zero code otherwise. + +
diff --git a/shell-completion/bash/systemd-detect-fash b/shell-completion/bash/systemd-detect-fash new file mode 100644 index 0000000000000..dc2a7f5f4774a --- /dev/null +++ b/shell-completion/bash/systemd-detect-fash @@ -0,0 +1,40 @@ +# shellcheck shell=bash +# systemd-detect-fash(1) completion -*- shell-script -*- +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word() { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +_systemd_detect_fash() { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword + local i verb comps + + local -A OPTS=( + [STANDALONE]='-h --help --version -q --quiet -o --omarchy -l --ladybird -y --hyprland -d --dhh' + ) + + _init_completion || return + + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) +} + +complete -F _systemd_detect_fash systemd-detect-fash diff --git a/src/detect-fash/detect-fash.c b/src/detect-fash/detect-fash.c new file mode 100644 index 0000000000000..311547ec6a619 --- /dev/null +++ b/src/detect-fash/detect-fash.c @@ -0,0 +1,312 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include +#include +#include +#include + +#include "alloc-util.h" +#include "build.h" +#include "log.h" +#include "main-func.h" +#include "pretty-print.h" +#include "string-table.h" + +static bool arg_quiet = false; +static enum { + ANY_FASCISM, + ONLY_LADYBIRD, + ONLY_OMARCHY, + ONLY_HYPRLAND, + ONLY_DHH +} arg_mode = ANY_FASCISM; + +/* detects if os-release is omarchy */ +static int detect_omarchy(void) { + const char *term = "omarchy"; + const int len = 256; + + /* if we cannot access os-release we cannot check */ + if (access("/etc/os-release", F_OK) != 0) + return -1; + + FILE *osfile = fopen("/etc/os-release", "r"); + char os[len]; + fgets(os, len, osfile); + if (strcasestr(os, term) != NULL) + return 1; + + return 0; +} + +/* + detects if the LadyBird browser + has been built on this machine + or if the binary exists in $PATH +*/ +static unsigned detect_ladybird(void) { + + /* name of the ladybird binary */ + const char* ladybird_bin = "/ladybird"; + + /* check if build variable is available */ + char* LADYBIRD_SOURCE_DIR = getenv("LADYBIRD_SOURCE_DIR"); + if (LADYBIRD_SOURCE_DIR != NULL) + return 1; + + char* PATH = getenv("PATH"); + if (PATH == NULL) + return 0; + + /* this value will get mutated so we need to duplicate it */ + char* path = strdup(PATH); + /* loop through PATH until we find a file named "ladybird" */ + char* path_iter = strtok(path, ":"); + char* abs_path = malloc(256); + while (path_iter != NULL) { + strncat(abs_path, path_iter, 128); + strncat(abs_path, ladybird_bin, 128); + /* if we do NOT find the binary at current path, keep going */ + if (access(abs_path, F_OK) != 0){ + path_iter = strtok(NULL, ":"); + abs_path[0] = 0; + continue; + } + free(abs_path); + free(path); + return 1; + } + free(abs_path); + free(path); + return 0; +} + +/* detects if hyprland is installed */ +static unsigned detect_hyprland(void) { + const char* hyprland_config = "/hypr/hyprland.conf"; + const char* XDG_CONFIG_HOME = getenv("XDG_CONFIG_HOME"); + const char* HOME = getenv("HOME"); + int maxlen = 128; + + char *hyprland_abs_path = malloc(maxlen); + + if (XDG_CONFIG_HOME != NULL) { + strncat(hyprland_abs_path, XDG_CONFIG_HOME, maxlen - strlen(hyprland_config)); + } else if (HOME != NULL) { + strncat(hyprland_abs_path, HOME, maxlen - strlen(hyprland_config)); + strcat(hyprland_abs_path, "/.config"); + } else { + return 0; + } + strcat(hyprland_abs_path, hyprland_config); + if (access(hyprland_abs_path, F_OK) == 0){ + free(hyprland_abs_path); + return 1; + } + free(hyprland_abs_path); + return 0; +} + +/* detects if this is dhh's computer using his ssh pubkey */ +static int detect_dhh(void) { + /* fingerprint of dhh's ssh public key */ + const char *dhh_fingerprint = "SHA256:YCKX7xo5Hkihy/NVH5ang8Oty9q8Vvqu4sxI7EbDxPg"; + /* path to ssh pubkey */ + const char *ssh_pubkey = "/.ssh/id_ed25519.pub"; + /* command to generate fingerprint */ + const char *ssh_fingerpint_cmd = "ssh-keygen -E sha256 -lf "; + + /* get the home directory */ + char *HOME = getenv("HOME"); + + if (HOME == NULL) + return -1; + /* check if we have read access to the public key on disk */ + char *ssh_pubkey_abs_path = (char *)malloc(strlen(HOME) + strlen(ssh_pubkey) + 1); + ssh_pubkey_abs_path[0] = 0; + strcat(ssh_pubkey_abs_path, HOME); + strcat(ssh_pubkey_abs_path, ssh_pubkey); + if (access(ssh_pubkey_abs_path, F_OK) != 0) + return 0; + + /* generate a fingerprint of it */ + char *get_fingerprint_cmd = (char *)malloc(strlen(ssh_fingerpint_cmd) + strlen(ssh_pubkey_abs_path) + 1); + get_fingerprint_cmd[0] = 0; + strcat(get_fingerprint_cmd, ssh_fingerpint_cmd); + strcat(get_fingerprint_cmd, ssh_pubkey_abs_path); + + char fingerprint[70]; + FILE *fingerprint_cmd_output = popen(get_fingerprint_cmd, "r"); + + if (fingerprint_cmd_output == NULL) + return -1; + fgets(fingerprint, 70, fingerprint_cmd_output); + + /* free memory */ + pclose(fingerprint_cmd_output); + free(ssh_pubkey_abs_path); + free(get_fingerprint_cmd); + + /* comare it to DHH's fingerprint */ + if (strstr(fingerprint, dhh_fingerprint) != NULL) + return 1; + return 0; +} + +static int help(void) { + _cleanup_free_ char *link = NULL; + int r; + + r = terminal_urlify_man("systemd-detect-fash", "1", &link); + if (r < 0) + return log_oom(); + + printf("%s [OPTIONS...]\n\n" + "Detect execution in a fascist environment.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -q --quiet Quiet mode\n" + " -o --omarchy Only detect omarchy\n" + " -l --ladybird Only detect ladybird\n" + " -y --hyprland Only detect hyprland\n" + " -d --dhh Only detect dhh\n" + "\nSee the %s for details.\n", + program_invocation_short_name, + link); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_OMARCHY, + ARG_LADYBIRD, + ARG_HYPRLAND, + ARG_DHH + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "omarchy", no_argument, NULL, 'o' }, + { "ladybird", no_argument, NULL, 'l' }, + { "hyprland", no_argument, NULL, 'y' }, + { "dhh", no_argument, NULL, 'd' }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hqolyd", options, NULL)) >= 0) + + switch (c) { + + case 'h': + return help(); + + case ARG_VERSION: + return version(); + + case 'q': + arg_quiet = true; + break; + + case 'l': + arg_mode = ONLY_LADYBIRD; + break; + + case 'o': + arg_mode = ONLY_OMARCHY; + break; + + case 'y': + arg_mode = ONLY_HYPRLAND; + break; + + case 'd': + arg_mode = ONLY_DHH; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached(); + } + return 1; +} + +static int run(int argc, char *argv[]) { + int dhh = 0; + int hyprland = 0; + int ladybird = 0; + int omarchy = 0; + int fascism = 0; + int r; + + /* This is mostly intended to be used for scripts which want + * to detect whether we are being run in a fascist + * environment or not */ + + log_setup(); + + r = parse_argv(argc, argv); + if (r <= 0) + return r; + + switch (arg_mode) { + case ONLY_OMARCHY: + omarchy = detect_omarchy(); + fascism = omarchy; + if (omarchy < 0) + return log_error_errno(fascism, "Failed to check for omarchy: %m"); + break; + + case ONLY_LADYBIRD: + ladybird = detect_ladybird(); + fascism = ladybird; + if (ladybird < 0) + return log_error_errno(fascism, "Failed to check for ladybird: %m"); + break; + + case ONLY_HYPRLAND: + hyprland = detect_hyprland(); + fascism = hyprland; + if (hyprland < 0) + return log_error_errno(fascism, "Failed to check for hyprland: %m"); + break; + + case ONLY_DHH: + dhh = detect_dhh(); + fascism = dhh; + if (dhh < 0) + return log_error_errno(fascism, "Failed to check for dhh: %m"); + break; + + case ANY_FASCISM: + default: + ladybird = detect_ladybird(); + omarchy = detect_omarchy(); + hyprland = detect_hyprland(); + dhh = detect_dhh(); + fascism = (ladybird | omarchy | hyprland | dhh); + if (fascism < 0) + return log_error_errno(fascism, "Failed to check for fascism: %m"); + } + + if (!arg_quiet) { + if (ladybird) puts("ladybird"); + if (omarchy) puts("omarchy"); + if (dhh) puts("dhh"); + if (hyprland) puts("hyprland"); + } + return fascism; +} + +DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); diff --git a/src/detect-fash/meson.build b/src/detect-fash/meson.build new file mode 100644 index 0000000000000..f4cca34117e7b --- /dev/null +++ b/src/detect-fash/meson.build @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +executables += [ + executable_template + { + 'name' : 'systemd-detect-fash', + 'public' : true, + 'sources' : files('detect-fash.c'), + }, +] From 825072a331cb6d7464eb4479c4998ab0d020e32f Mon Sep 17 00:00:00 2001 From: soscho2143 Date: Sun, 12 Oct 2025 14:18:49 -0400 Subject: [PATCH 2/2] detect-fash: added to meson.build --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index c67e7b6c30de4..76c625d22080d 100644 --- a/meson.build +++ b/meson.build @@ -2353,6 +2353,7 @@ subdir('src/cryptenroll') subdir('src/cryptsetup') subdir('src/debug-generator') subdir('src/delta') +subdir('src/detect-fash') subdir('src/detect-virt') subdir('src/dissect') subdir('src/environment-d-generator')