Phase 27
User Accounts
The OS supports multiple user accounts with login authentication, file ownership, and permission enforcement. A `login` program prompts for username and password before granting shell access. This is the foundation for multi-user operation and secure remote access.
Learning goal
Understand how Unix user/group identity works at the kernel level (UID/GID).
Category
userspace
Components touched
Builds on
Unlocked by this phase
Why this phase matters
The OS supports multiple user accounts with login authentication, file ownership, and permission enforcement. A login program prompts for username and password before granting shell access. This is the foundation for multi-user operation and secure remote access.
How it works in m3OS
Phase 27 adds multi-user support to m3OS: per-process UID/GID identity, file ownership and Unix permission enforcement across all filesystem backends, SHA-256 password hashing, and a login-based boot flow. Six new userspace programs (login, su, passwd, adduser, id, whoami) provide authentication and account management.
After Phase 27, the system boots to a login: prompt instead of dropping directly into a shell. Two default accounts exist: root (UID 0) and user (UID 1000).
How it builds on earlier phases
This phase builds on Text Editor and turns that foundation into User Accounts.
Implementation outline
- Add UID/GID fields to the kernel process structure; default to 0 (root) for init.
- Implement
getuid,setuid, and related syscalls. - Add permission mode and owner fields to VFS inodes.
- Implement permission checking in VFS operations.
- Implement
chmodandchownsyscalls. - Write the
loginprogram: read/etc/passwd, verify password hash from/etc/shadow. - Modify init to spawn
logininstead of directly spawning the shell. - Write
su,passwd,id,whoami,adduserutilities. - Create default
/etc/passwd,/etc/shadow,/etc/groupin the disk image.
Task progress snapshot
86 of 86 tracked tasks are complete.
Documentation targets
- Documentation deliverables have not been listed yet.
Deferred follow-on work
- ext2 filesystem (Phase 28)
- PAM or pluggable authentication
- Supplementary groups
- ACLs (Access Control Lists)
- Mandatory access control (SELinux-style)
- Home directory creation on first login
- User quotas
- Account lockout after failed attempts
Source materials
Important code
Code spotlight cards
Code spotlight
Add UID/GID fields to Process struct
Add UID/GID fields to Process struct. Why it matters: Every permission check and identity syscall reads these fields from the current process. Focus on `uid` and `gid` in `kernel/src/process/mod.rs` while you trace the snippet.
/// Descriptor for a single userspace process.
pub struct Process {
/// This process's unique identifier.
pub pid: Pid,
/// Parent PID. 0 means no parent (init or an orphan).
pub ppid: Pid,
/// Current lifecycle state.
pub state: ProcessState,
/// Root physical address of this process's page table.
///
/// `None` means the process has not yet been assigned a dedicated
/// address space and shares the kernel's mappings (pre-Phase 12).
pub page_table_root: Option<x86_64::PhysAddr>,
/// Top of this process's kernel-mode stack (virtual address).
pub kernel_stack_top: u64,
/// Userspace entry-point virtual address.
pub entry_point: u64,
/// Initial userspace RSP: the virtual address of `argc` on the ABI stack,
/// as returned by `setup_abi_stack`. This is **not** the raw top of the
/// stack allocation — it points into the stack, below the argv/envp data.
pub user_stack_top: u64,
/// Exit code written when the process transitions to [`ProcessState::Zombie`].
pub exit_code: Option<i32>,
/// Signal that caused the process to stop (set when transitioning to Stopped). Code spotlight
Update identity syscalls to return real fields
Update identity syscalls to return real fields. Why it matters: Programs like id and whoami rely on these syscalls to report the current user. Focus on `sys_linux_getuid` and `sys_linux_getgid` in `kernel/src/arch/x86_64/syscall.rs` while you trace the snippet.
/// `getuid()` — return real user ID (syscall 102).
fn sys_linux_getuid() -> u64 {
current_process_ids().0 as u64
}
/// `getgid()` — return real group ID (syscall 104).
fn sys_linux_getgid() -> u64 { Code spotlight
Extend ramdisk nodes with metadata
Extend ramdisk nodes with metadata. Why it matters: Ramdisk files (initrd binaries) need permission bits so exec permission checks work. Focus on `uid` and `gid` in `kernel/src/fs/ramdisk.rs` while you trace the snippet.
Key files
- kernel/src/process/mod.rs — The Process struct (kernel/src/process/mod.rs) carries four
- kernel/src/arch/x86_64/syscall.rs — kernel/src/arch/x86_64/syscall.rs:
- kernel-core/src/fs/tmpfs.rs — FileData and DirData in kernel-core/src/fs/tmpfs.rs each hold
- kernel/src/fs/ext2.rs — ext2 driver (kernel/src/fs/ext2.rs) reads and writes these fields
- userspace/syscall-lib/src/sha256.rs — userspace/syscall-lib/src/sha256.rs (FIPS 180-4, no external
- xtask/src/main.rs — populate_ext2_files() in xtask/src/main.rs, which uses debugfs
- userspace/init/src/main.rs — Init (userspace/init/src/main.rs) was modified to:
- userspace/login/ — Rust no_std binary. Implements the full authentication cycle: prompt,
- userspace/su/ — Rust no_std binary. su <username> (defaults to root). Any user can
- userspace/passwd/ — setuid-on-exec support exists.
- userspace/adduser/ — with SHA-256, and rewrites /etc/shadow with the updated entry.
- userspace/id/ — group files. Creates the home directory and sets ownership.
- userspace/whoami/ — /etc/passwd and /etc/group, prints uid=N(name) gid=N(name).
- userspace/syscall-lib/ — Phase 27 added to userspace/syscall-lib/:
How real OSes differ
- Real Unix systems use PAM (Pluggable Authentication Modules) for flexible auth backends.
- NSS (Name Service Switch) enables looking up users from LDAP, NIS, etc.
- Supplementary groups allow a user to belong to multiple groups.
- setuid/setgid binaries provide controlled privilege escalation.
- Fine-grained capabilities (Linux capabilities) replace the binary root vs. non-root model.
- SELinux/AppArmor provide mandatory access control beyond discretionary Unix permissions.
- Our implementation uses the classic Unix permission model (user/group/other with rwx bits), which is simple to understand and sufficient for learning.
Success criteria
- Boot reaches a `login:` prompt instead of an immediate shell.
- Entering correct credentials for `root` drops into a root shell.
- Entering wrong credentials is rejected with an error message.
- `id` shows the correct UID/GID after login.
- Files created by `user` are owned by UID 1000.
- `user` cannot read `/etc/shadow` (permission denied).
- `user` cannot delete files owned by `root` in protected directories.
- `su root` from `user` prompts for root's password and grants a root shell.
- `passwd` changes the current user's password, and the new password works on next login.
Related phases
Keep exploring the roadmap
Phase 26
CompleteText Editor
A usable text editor runs inside the OS. Users can create, edit, and save files from the shell. This is the foundational tool that makes all subsequent "do real work inside the OS" phases possible — you cannot write code, configuration files, or documents without an editor.
Phase 28
CompleteExt2 Filesystem
Replace FAT32 as the primary persistent filesystem with ext2, which natively stores Unix ownership (uid/gid), permission modes, and timestamps in every inode. After this phase, file permissions set via `chmod`/`chown` survive reboots and the FAT32 permissions index file workaround from Phase 27 is no longer needed.