m³OS docs GitHub

Phase 48

Security Foundation

m3OS closes the trust-floor gaps that currently block any honest claim of "safe enough for serious headless use." When this phase lands, identity transitions, password storage, entropy, and default remote-exposure behavior all match the project's intended multi-user and network-facing story.

Complete

Learning goal

Understand the difference between interesting security mechanisms and a credible threat model.

Category

infrastructure

Why this phase matters

m3OS closes the trust-floor gaps that currently block any honest claim of “safe enough for serious headless use.” When this phase lands, identity transitions, password storage, entropy, and default remote-exposure behavior all match the project’s intended multi-user and network-facing story.

How it works in m3OS

Phase 48 closes the trust-floor gaps in the m3OS multi-user and network-facing security model. It replaces advisory, demo-quality defaults with real enforcement: kernel-checked credential transitions, hardware-backed entropy, iterated password hashing, and safe boot defaults.

How it builds on earlier phases

This phase builds on Doom and turns that foundation into Security Foundation.

Implementation outline

  • Audit the current trust-floor failures and map each one to a concrete kernel, userspace, or image-default fix.
  • Implement kernel-side credential transition enforcement and remove any remaining trust shortcuts.
  • Replace the current weak entropy path with a documented stronger CSPRNG/seed pipeline.
  • Remove baked-in default credentials and define the replacement provisioning or first-boot flow.
  • Make telnet opt-in only and align init/service defaults with the new exposure policy.
  • Upgrade password hashing and account-file update semantics.
  • Add or extend smoke/regression coverage for login, SSH, password changes, and boot defaults.
  • Update documentation, release framing, and version references for the new trust floor.

Task progress snapshot

0 of 115 tracked tasks are complete.

Documentation targets

  • Documentation deliverables have not been listed yet.

Deferred follow-on work

  • Full privilege separation across all network services
  • Advanced key-management, rotation, and audit infrastructure
  • Rich multi-factor or hardware-backed authentication flows
  • General sandboxing beyond the repaired trust floor

Source materials

Important code

Code spotlight cards

Code spotlight

Audit current setuid/setgid enforcement

kernel/src/arch/x86_64/syscall.rs L2657-L2680

credentials unconditionally with no privilege check, meaning any unprivileged. Why it matters: sys_linux_setuid and sys_linux_setgid currently set. Focus on `sys_linux_setuid` and `sys_linux_setgid` in `kernel/src/arch/x86_64/syscall.rs` while you trace the snippet.

/// `setuid(uid)` — set user ID (syscall 105).
///
/// Enforces POSIX-style privilege checks:
/// - If euid == 0 (root): sets both real uid and effective uid.
/// - If euid != 0: only allows setting effective uid back to the real uid.
/// - Otherwise returns -EPERM.
///
/// Login and su work because they run as root (euid 0) when transitioning.
fn sys_linux_setuid(uid_arg: u64) -> u64 {
    let new_uid = uid_arg as u32;
    let pid = crate::process::current_pid();
    let mut table = crate::process::PROCESS_TABLE.lock();
    let proc = match table.find_mut(pid) {
        Some(p) => p,
        None => return NEG_EPERM,
    };
    let mut cred = kernel_core::cred::Credentials {
        uid: proc.uid,
        gid: proc.gid,
        euid: proc.euid,
        egid: proc.egid,
    };
    if cred.set_uid(new_uid).is_err() {
        return NEG_EPERM;

Code spotlight

Audit password hashing and default credentials

userspace/syscall-lib/src/sha256.rs L125-L137

iteration using the username as salt. The image ships with hardcoded root and. Why it matters: Passwords are currently hashed with a single SHA-256. Focus on `hash_password` and `populate_ext2_files` in `userspace/syscall-lib/src/sha256.rs` while you trace the snippet.

/// Hash a password with salt: SHA-256(salt || password).
pub fn hash_password(password: &[u8], salt: &[u8]) -> [u8; 32] {
    // Concatenate salt + password into a buffer.
    let total = salt.len() + password.len();
    let mut buf = [0u8; 512];
    if total > buf.len() {
        // Truncate if too long (shouldn't happen in practice).
        return sha256(password);
    }
    buf[..salt.len()].copy_from_slice(salt);
    buf[salt.len()..total].copy_from_slice(password);
    sha256(&buf[..total])
}

Code spotlight

Audit service defaults and telnet exposure

userspace/init/src/main.rs L46-L56

auto-starts on every boot with restart=always. A plaintext remote shell in. Why it matters: Telnet is currently in the default service list and. Focus on `KNOWN_CONFIGS` and `restart=always` in `userspace/init/src/main.rs` while you trace the snippet.

/// Known service config files to try opening (no readdir available).
const KNOWN_CONFIGS: &[&[u8]] = &[
    b"/etc/services.d/sshd.conf\0",
    b"/etc/services.d/telnetd.conf\0",
    b"/etc/services.d/syslogd.conf\0",
    b"/etc/services.d/crond.conf\0",
    b"/etc/services.d/httpd.conf\0",
    b"/etc/services.d/dhcpd.conf\0",
    b"/etc/services.d/ntpd.conf\0",
    b"/etc/services.d/ftpd.conf\0",
];

Key files

  • kernel/Cargo.toml — When the phase lands, bump kernel/Cargo.toml and any release/version references to 0.48.0.
  • kernel-core/src/cred.rs — Credential transition logic with POSIX privilege checks
  • kernel-core/src/prng.rs — Xorshift64-multiply PRNG, extracted for host testing
  • kernel/src/arch/x86_64/syscall.rs — Kernel syscall handlers for setuid/setgid/getrandom
  • userspace/syscall-lib/src/sha256.rs — Password hashing with iterated SHA-256
  • userspace/login/src/main.rs — First-boot password setup for locked accounts
  • userspace/passwd/src/main.rs — Password change with random salts
  • userspace/adduser/src/main.rs — Account creation with single-write pattern and fsync
  • xtask/src/main.rs — Image build with locked accounts and telnet opt-in

How real OSes differ

  • Linux and BSD systems have much deeper credential, entropy, and hardening stacks than m3OS needs initially.
  • Production systems also layer service users, secret rotation, audit trails, and more advanced account policy on top of the basics.
  • m3OS should copy the discipline of safe defaults and explicit trust boundaries, not the full operational complexity of a mature distribution.

Success criteria

  • Unprivileged processes can no longer escalate via unconditional `setuid` or `setgid`.
  • `getrandom()` is backed by a documented stronger entropy source than the current time-seeded fallback.
  • The default boot path does not expose telnet unless explicitly enabled.
  • Images no longer ship with baked-in default credentials.
  • Password hashes use a slow salted scheme, or a clearly documented transitional replacement that materially improves the current state.
  • Boot/login/SSH smoke coverage exists for the hardened defaults and passes in the normal validation path.

Related phases

Keep exploring the roadmap