From 12bacf271db414e1f94de8e9dcda8b497df3f9dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lars=20Sj=C3=B6strom?= <lars@radicore.se>
Date: Tue, 4 Mar 2025 21:47:19 +0100
Subject: [PATCH] feat: generate passwd/group with systemd-sysusers

---
 flake.nix                      |   1 +
 pkgs/busybox/clang-cross.patch |  37 ++++++
 pkgs/busybox/default.nix       | 219 +++++++++++++++++++++++++++++++++
 pkgs/rootfs/default.nix        |   2 +-
 pkgs/rootfs/mkrootfs.sh        |  65 +---------
 5 files changed, 262 insertions(+), 62 deletions(-)
 create mode 100644 pkgs/busybox/clang-cross.patch
 create mode 100644 pkgs/busybox/default.nix

diff --git a/flake.nix b/flake.nix
index 5c76b2f..97a1f97 100644
--- a/flake.nix
+++ b/flake.nix
@@ -36,6 +36,7 @@
           };
           kernel = pkgs.callPackage ./pkgs/kernel { };
           glibc = pkgs.callPackage ./pkgs/glibc { };
+          busybox = pkgs.callPackage ./pkgs/busybox { };
           kexec = pkgs.callPackage ./pkgs/kexec-tools { };
           lvm2 = pkgs.callPackage ./pkgs/lvm2 { };
           tpm2-tools = pkgs.callPackage ./pkgs/tpm2-tools { inherit patosPkgs; };
diff --git a/pkgs/busybox/clang-cross.patch b/pkgs/busybox/clang-cross.patch
new file mode 100644
index 0000000..b2d696b
--- /dev/null
+++ b/pkgs/busybox/clang-cross.patch
@@ -0,0 +1,37 @@
+diff --git a/Makefile b/Makefile
+index 6fedcffba..3385836c4 100644
+--- a/Makefile
++++ b/Makefile
+@@ -271,8 +271,8 @@ export quiet Q KBUILD_VERBOSE
+ # Look for make include files relative to root of kernel src
+ MAKEFLAGS += --include-dir=$(srctree)
+ 
+-HOSTCC  	= gcc
+-HOSTCXX  	= g++
++HOSTCC		= cc
++HOSTCXX	= c++
+ HOSTCFLAGS	:=
+ HOSTCXXFLAGS	:=
+ # We need some generic definitions
+@@ -289,7 +289,7 @@ MAKEFLAGS += -rR
+ # Make variables (CC, etc...)
+ 
+ AS		= $(CROSS_COMPILE)as
+-CC		= $(CROSS_COMPILE)gcc
++CC		= $(CROSS_COMPILE)cc
+ LD		= $(CC) -nostdlib
+ CPP		= $(CC) -E
+ AR		= $(CROSS_COMPILE)ar
+diff --git a/scripts/Makefile.IMA b/scripts/Makefile.IMA
+index f155108d7..185257064 100644
+--- a/scripts/Makefile.IMA
++++ b/scripts/Makefile.IMA
+@@ -39,7 +39,7 @@ ifndef HOSTCC
+ HOSTCC = cc
+ endif
+ AS              = $(CROSS_COMPILE)as
+-CC              = $(CROSS_COMPILE)gcc
++CC              = $(CROSS_COMPILE)cc
+ LD              = $(CC) -nostdlib
+ CPP             = $(CC) -E
+ AR              = $(CROSS_COMPILE)ar
diff --git a/pkgs/busybox/default.nix b/pkgs/busybox/default.nix
new file mode 100644
index 0000000..571d0cf
--- /dev/null
+++ b/pkgs/busybox/default.nix
@@ -0,0 +1,219 @@
+{
+  stdenv,
+  lib,
+  buildPackages,
+  fetchurl,
+  fetchpatch,
+  fetchFromGitLab,
+  enableStatic ? stdenv.hostPlatform.isStatic,
+  enableMinimal ? false,
+  enableAppletSymlinks ? true,
+  # Allow forcing musl without switching stdenv itself, e.g. for our bootstrapping:
+  # nix build -f pkgs/top-level/release.nix stdenvBootstrapTools.x86_64-linux.dist
+  useMusl ? stdenv.hostPlatform.libc == "musl",
+  musl,
+  extraConfig ? "",
+}:
+
+assert stdenv.hostPlatform.libc == "musl" -> useMusl;
+
+let
+  configParser = ''
+    function parseconfig {
+        while read LINE; do
+            NAME=`echo "$LINE" | cut -d \  -f 1`
+            OPTION=`echo "$LINE" | cut -d \  -f 2`
+
+            if ! [[ "$NAME" =~ ^CONFIG_ ]]; then continue; fi
+
+            echo "parseconfig: removing $NAME"
+            sed -i /$NAME'\(=\| \)'/d .config
+
+            echo "parseconfig: setting $NAME=$OPTION"
+            echo "$NAME=$OPTION" >> .config
+        done
+    }
+  '';
+
+  libcConfig = lib.optionalString useMusl ''
+    CONFIG_FEATURE_UTMP n
+    CONFIG_FEATURE_WTMP n
+  '';
+
+  # The debian version lags behind the upstream version and also contains
+  # a debian-specific suffix. We only fetch the debian repository to get the
+  # default.script
+  debianVersion = "1.30.1-6";
+  debianSource = fetchFromGitLab {
+    domain = "salsa.debian.org";
+    owner = "installer-team";
+    repo = "busybox";
+    rev = "debian/1%${debianVersion}";
+    sha256 = "sha256-6r0RXtmqGXtJbvLSD1Ma1xpqR8oXL2bBKaUE/cSENL8=";
+  };
+  debianDispatcherScript = "${debianSource}/debian/tree/udhcpc/etc/udhcpc/default.script";
+  outDispatchPath = "$out/default.script";
+in
+
+stdenv.mkDerivation rec {
+  pname = "busybox";
+  version = "1.36.1";
+
+  # Note to whoever is updating busybox: please verify that:
+  # nix-build pkgs/stdenv/linux/make-bootstrap-tools.nix -A test
+  # still builds after the update.
+  src = fetchurl {
+    url = "https://busybox.net/downloads/${pname}-${version}.tar.bz2";
+    sha256 = "sha256-uMwkyVdNgJ5yecO+NJeVxdXOtv3xnKcJ+AzeUOR94xQ=";
+  };
+
+  hardeningDisable = [
+    "format"
+    "pie"
+  ] ++ lib.optionals enableStatic [ "fortify" ];
+
+  patches = [
+    (fetchurl {
+      name = "CVE-2022-28391.patch";
+      url = "https://git.alpinelinux.org/aports/plain/main/busybox/0001-libbb-sockaddr2str-ensure-only-printable-characters-.patch?id=ed92963eb55bbc8d938097b9ccb3e221a94653f4";
+      sha256 = "sha256-yviw1GV+t9tbHbY7YNxEqPi7xEreiXVqbeRyf8c6Awo=";
+    })
+    (fetchurl {
+      name = "CVE-2022-28391.patch";
+      url = "https://git.alpinelinux.org/aports/plain/main/busybox/0002-nslookup-sanitize-all-printed-strings-with-printable.patch?id=ed92963eb55bbc8d938097b9ccb3e221a94653f4";
+      sha256 = "sha256-vl1wPbsHtXY9naajjnTicQ7Uj3N+EQ8pRNnrdsiow+w=";
+    })
+    (fetchpatch {
+      name = "CVE-2022-48174.patch"; # https://bugs.busybox.net/show_bug.cgi?id=15216
+      url = "https://git.busybox.net/busybox/patch/?id=d417193cf37ca1005830d7e16f5fa7e1d8a44209";
+      hash = "sha256-mpDEwYncpU6X6tmtj9xM2KCrB/v2ys5bYxmPPrhm6es=";
+    })
+    (fetchpatch {
+      name = "CVE-2023-42366.patch"; # https://bugs.busybox.net/show_bug.cgi?id=15874
+      # This patch is also used by Alpine, see https://git.alpinelinux.org/aports/tree/main/busybox/0037-awk.c-fix-CVE-2023-42366-bug-15874.patch
+      url = "https://bugs.busybox.net/attachment.cgi?id=9697";
+      hash = "sha256-2eYfLZLjStea9apKXogff6sCAdG9yHx0ZsgUBaGfQIA=";
+    })
+    (fetchpatch {
+      name = "CVE-2023-42363.patch"; # https://bugs.busybox.net/show_bug.cgi?id=15865
+      url = "https://git.launchpad.net/ubuntu/+source/busybox/plain/debian/patches/CVE-2023-42363.patch?id=c9d8a323b337d58e302717d41796aa0242963d5a";
+      hash = "sha256-1W9Q8+yFkYQKzNTrvndie8QuaEbyAFL1ZASG2fPF+Z4=";
+    })
+    (fetchpatch {
+      name = "CVE-2023-42364_CVE-2023-42365.patch"; # https://bugs.busybox.net/show_bug.cgi?id=15871 https://bugs.busybox.net/show_bug.cgi?id=15868
+      url = "https://git.alpinelinux.org/aports/plain/main/busybox/CVE-2023-42364-CVE-2023-42365.patch?id=8a4bf5971168bf48201c05afda7bee0fbb188e13";
+      hash = "sha256-nQPgT9eA1asCo38Z9X7LR9My0+Vz5YBPba3ARV3fWcc=";
+    })
+  ] ++ lib.optional (stdenv.hostPlatform != stdenv.buildPlatform) ./clang-cross.patch;
+
+  separateDebugInfo = true;
+
+  # postPatch = "patchShebangs .";
+
+  configurePhase = ''
+    export KCONFIG_NOTIMESTAMP=1
+    make ${if enableMinimal then "allnoconfig" else "defconfig"}
+
+    ${configParser}
+
+    cat << EOF | parseconfig
+
+    CONFIG_PREFIX "$out"
+    CONFIG_INSTALL_NO_USR y
+
+    CONFIG_LFS y
+
+    # More features for modprobe.
+    ${lib.optionalString (!enableMinimal) ''
+      CONFIG_FEATURE_MODPROBE_BLACKLIST y
+      CONFIG_FEATURE_MODUTILS_ALIAS y
+      CONFIG_FEATURE_MODUTILS_SYMBOLS y
+      CONFIG_MODPROBE_SMALL n
+    ''}
+
+    ${lib.optionalString enableStatic ''
+      CONFIG_STATIC y
+    ''}
+
+    ${lib.optionalString (!enableAppletSymlinks) ''
+      CONFIG_INSTALL_APPLET_DONT y
+      CONFIG_INSTALL_APPLET_SYMLINKS n
+    ''}
+
+    # Use the external mount.cifs program.
+    CONFIG_FEATURE_MOUNT_CIFS n
+    CONFIG_FEATURE_MOUNT_HELPERS y
+
+    # BB_SHADOW
+    FEATURE_SHADOWPASSWDS y
+    CONFIG_USE_BB_PWD_GRP y
+    CONFIG_USE_BB_SHADOW y
+    CONFIG_USE_BB_CRYPT y
+    USE_BB_CRYPT_SHA y
+    CONFIG_FEATURE_DEFAULT_PASSWD_ALGO "sha512"
+
+    # Set paths for console fonts.
+    CONFIG_DEFAULT_SETFONT_DIR "/etc/kbd"
+
+    # Bump from 4KB, much faster I/O
+    CONFIG_FEATURE_COPYBUF_KB 64
+
+    # Doesn't build with current kernel headers.
+    # https://bugs.busybox.net/show_bug.cgi?id=15934
+    CONFIG_TC n
+
+    # Set the path for the udhcpc script
+    CONFIG_UDHCPC_DEFAULT_SCRIPT "${outDispatchPath}"
+
+    ${extraConfig}
+    CONFIG_CROSS_COMPILER_PREFIX "${stdenv.cc.targetPrefix}"
+    ${libcConfig}
+    EOF
+
+    make oldconfig
+
+    runHook postConfigure
+  '';
+
+  postConfigure = lib.optionalString (useMusl && stdenv.hostPlatform.libc != "musl") ''
+    makeFlagsArray+=("CC=${stdenv.cc.targetPrefix}cc -isystem ${musl.dev}/include -B${musl}/lib -L${musl}/lib")
+  '';
+
+  makeFlags = [ "SKIP_STRIP=y" ];
+
+  postInstall = ''
+    sed -e '
+    1 a busybox() { '$out'/bin/busybox "$@"; }\
+    logger() { '$out'/bin/logger "$@"; }\
+    ' ${debianDispatcherScript} > ${outDispatchPath}
+    chmod 555 ${outDispatchPath}
+    HOST_PATH=$out/bin patchShebangs --host ${outDispatchPath}
+  '';
+
+  strictDeps = true;
+
+  depsBuildBuild = [ buildPackages.stdenv.cc ];
+
+  buildInputs = lib.optionals (enableStatic && !useMusl && stdenv.cc.libc ? static) [
+    stdenv.cc.libc
+    stdenv.cc.libc.static
+  ];
+
+  enableParallelBuilding = true;
+
+  doCheck = false; # tries to access the net
+
+  passthru.shellPath = "/bin/ash";
+
+  meta = with lib; {
+    description = "Tiny versions of common UNIX utilities in a single small executable";
+    homepage = "https://busybox.net/";
+    license = licenses.gpl2Only;
+    maintainers = with maintainers; [
+      TethysSvensson
+      qyliss
+    ];
+    platforms = platforms.linux;
+    priority = 15; # below systemd (halt, init, poweroff, reboot) and coreutils
+  };
+}
diff --git a/pkgs/rootfs/default.nix b/pkgs/rootfs/default.nix
index 20fe642..dd0e2a7 100644
--- a/pkgs/rootfs/default.nix
+++ b/pkgs/rootfs/default.nix
@@ -21,7 +21,7 @@ stdenvNoCC.mkDerivation (finalAttrs: {
   systemd = patosPkgs.systemd.out;
   dbusBroker = patosPkgs.dbus-broker.out;
   kernel = patosPkgs.kernel.kernel;
-  busybox = pkgs.busybox.out;
+  busybox = patosPkgs.busybox.out;
   kmodLibs = pkgs.kmod.lib;
   kmodBin = pkgs.kmod.out;
   libbpf = pkgs.libbpf.out;
diff --git a/pkgs/rootfs/mkrootfs.sh b/pkgs/rootfs/mkrootfs.sh
index 31fc347..d86ae78 100644
--- a/pkgs/rootfs/mkrootfs.sh
+++ b/pkgs/rootfs/mkrootfs.sh
@@ -139,67 +139,10 @@ cp -P $libbpf/lib/libbpf* $out/usr/lib
 # remove pkgconfig
 rm -rf $out/usr/lib/pkgconfig
 
-cat <<EOF > $out/etc/passwd
-root::0:0:root:/root:/bin/sh
-bin:x:1:1:bin:/bin:/usr/bin/nologin
-daemon:x:2:2:daemon:/:/usr/bin/nologin
-mail:x:8:12:mail:/var/spool/mail:/usr/bin/nologin
-ftp:x:14:11:ftp:/srv/ftp:/usr/bin/nologin
-http:x:33:33:http:/srv/http:/usr/bin/nologin
-uuidd:x:68:68:uuidd:/:/usr/bin/nologin
-messagebus:x:81:81:messagebus:/:/usr/bin/nologin
-nobody:x:99:99:nobody:/:/usr/bin/nologin
-systemd-coredump:x:151:992::/var/empty:/usr/bin/nologin
-systemd-network:x:152:152::/var/empty:/usr/bin/nologin
-systemd-resolve:x:153:153::/var/empty:/usr/bin/nologin
-systemd-timesync:x:154:154::/var/empty:/usr/bin/nologin
-EOF
-chmod 644 $out/etc/passwd
-
-cat <<EOF > $out/etc/group
-root:x:0:root
-bin:x:1:root,bin,daemon
-daemon:x:2:root,bin,daemon
-sys:x:3:root,bin
-adm:x:4:root,daemon
-tty:x:5:
-disk:x:6:root
-lp:x:7:daemon
-mem:x:8:
-kmem:x:9:
-wheel:x:10:root
-ftp:x:11:
-mail:x:12:
-uucp:x:14:
-log:x:19:root
-utmp:x:20:
-locate:x:21:
-rfkill:x:24:
-smmsp:x:25:
-proc:x:26:
-http:x:33:
-games:x:50:
-lock:x:54:
-uuidd:x:68:
-messagebus:x:81:
-systemd-journal:x:62:
-systemd-network:x:152:
-systemd-resolve:x:153:
-systemd-timesync:x:154:
-systemd-oom:x:991:
-systemd-coredump:x:992:
-network:x:90:
-video:x:91:
-audio:x:92:
-optical:x:93:
-floppy:x:94:
-storage:x:95:
-scanner:x:96:
-input:x:97:
-power:x:98:
-nobody:x:99:
-EOF
-chmod 644 $out/etc/group
+### install sys users (default password is patos)
+mkdir creds
+echo -n patos > creds/passwd.plaintext-password.root
+CREDENTIALS_DIRECTORY=$PWD/creds SYSTEMD_CRYPT_PREFIX='$6$' $systemd/usr/bin/systemd-sysusers --root=$out $out/usr/lib/sysusers.d/*.conf
 
 ### Find and install all shared libs
 find $out -type f -executable -exec ldd {} \; | awk '{print $3}' | grep -v systemd | grep -v glibc | grep -v tpm2 | grep -v devmapper | sort -u | xargs -I {} cp {} $out/usr/lib/