Skip to main content

Phase 1: Authentication

Motivation

Stock xv6 calls exec("sh", ...) in init.c and the shell runs immediately with no identity. Any process is implicitly trusted equally. For a medical device this is catastrophic: a patient application could call any syscall, read any file, modify any device configuration.

Phase 1 imposes an identity boundary at the earliest possible moment, before the first shell command executes.

Original xv6 vs Phase 1 Code Delta

Stock xv6 has no login step and no kernel-backed user identity. This phase introduces both.

ComponentStock xv6-riscvModified xv6-security
Boot programuser/init.c starts sh directlyuser/init.c starts login first
Process credentialsstruct proc has no user fieldsAdded uid, gid, role, username, authenticated
Auth backendNo credential database/etc/passwd seeded at boot via auth_init()
Auth syscallsNonesys_login, sys_whoami, sys_useradd, sys_userdel, sys_passwd
Fork behaviorChild inherits memory/files onlyChild also inherits identity fields (uid/gid/role/username/authenticated)

Files touched for this phase: kernel/proc.h, kernel/proc.c, kernel/auth.h, kernel/auth.c, kernel/sysfile.c, kernel/syscall.c, kernel/syscall.h, user/init.c, user/login.c.

What Changed in struct proc

Before Phase 1, struct proc had no user-identity fields at all. After Phase 1:

/* kernel/proc.h: new fields */
struct proc {
// ... existing fields ...
int uid; // numeric user ID
int gid; // numeric group ID
int role; // ROLE_ADMIN / ROLE_DOCTOR / ROLE_PATIENT
char username[16]; // for audit and whoami
int authenticated; // 0 until login() syscall succeeds
};

Forked children inherit all five fields, so every descendant of a logged-in shell carries the same identity.

Compared to stock xv6, this is the foundational kernel ABI change for user identity. Later phases (permissions and audit) rely on these fields rather than on user-space trust.

The /etc/passwd Format

username|uid|gid|role|hash
FieldExampleNotes
usernameadminMax 15 chars
uid0numeric user ID
gid0group ID, mirrors the uid here
role00=admin, 1=patient, 2=doctor
hasha3f8...SHA-256 hash, 64 hex chars

The Three Roles

The project has exactly three roles. The uid and the role number are the same value.

RoleuidWhat they represent
ADMIN0Full access. Bypasses every file permission check.
PATIENT1Reads their own records. Blocked from device config.
DOCTOR2Writes the insulin log. Cannot read device config.

Stock xv6 does not ship /etc/passwd or account management syscalls. Here, auth_init() creates /etc and /etc/passwd on first boot and seeds three demo users so the secure boot path is immediately testable.

Demo accounts baked into the image:

UsernamePasswordRole
adminadmin123Administrator
doctor1doctor123Doctor
patient1patient123Patient

Login Sequence

Syscall Surface

QEMU terminal showing login failure then successful root login

SyscallWho can callDescription
login(user, pass)AnyoneAuthenticate. Sets the kernel identity on success.
whoami(buf, len)Logged-in userCopy current username, uid, gid, role into the buffer.
useradd(user, pass, role)Admin onlyAdd an account entry to /etc/passwd.
userdel(user)Admin onlyRemove an account entry. The admin account cannot be deleted.
passwd(user, old, new)Owner or adminChange a password. A normal user can only change their own and must give the old password. Admin can change anyone's.

The four account syscalls map to these user commands. Run them from the xv6 shell after logging in:

whoami # print who you are: name, uid, gid, role
useradd nurse nurse123 1 # admin adds a patient-role account (role 1)
passwd doctor1 doctor123 newpw # doctor1 changes its own password
userdel nurse # admin removes the account

Compared with original xv6, these syscall numbers are newly assigned in the syscall table and dispatched through kernel/syscall.c.

The admin-only restriction is enforced inside the kernel (kernel/auth.c), not just in the user tool. A patient cannot call useradd by constructing a raw ECALL.

login.c Walk-through

// user/login.c (simplified)
int main(void) {
char user[16], pass[64];
int failures = 0;
for (;;) {
printf("Username: "); read_line(user, sizeof user);
printf("Password: "); read_line(pass, sizeof pass);

if (login(user, pass) == 0) { // ECALL -> sys_login()
char *argv[] = { "sh", 0 };
exec("sh", argv); // only runs on success
}
failures++;
printf("Login failed.\n");
if (failures >= 3) { // lock the device after 3 tries
printf("Device locked after 3 failed attempts.\n");
for (;;) pause(1000);
}
}
}

After three failed attempts the login program stops trying and pauses forever. The device is locked until reboot. This models a wearable that should not let an attacker brute-force the PIN.

exec overwrites the login process image with the shell. The kernel's proc entry keeps uid, gid, role, and authenticated = 1 across the exec because credentials are stored in struct proc, not in the user-space image.

In stock xv6, the equivalent control flow is init -> sh with no identity gate. The modified flow is init -> login -> sh and only transitions to shell after sys_login succeeds.

Compliance Coverage

TestWhat it checks
T01Valid admin login succeeds
T02Valid patient login succeeds
T03Valid doctor login succeeds
T04Wrong password is rejected
T05useradd by a non-admin returns -EPERM
T06whoami returns the correct username

Security Notes

  • Passwords are hashed with SHA-256 before storage in /etc/passwd. The implementation is self-contained (~80 lines of C) and needs no external crypto library. This is not yet production-quality. The hash has no salt and is fast to brute-force. A real system would use Argon2id or bcrypt with a per-account salt and a tunable work factor.
  • Forked children inherit uid, gid, role, username, and authenticated from the parent in kfork(). This is intentional for a Unix-style session model where child processes run under the same logged-in identity.

Remember to copy credentials on fork. If the child shell loses identity, later permission checks and audit entries become misleading.

Avoid storing plaintext passwords in the filesystem. Even in xv6, the project should model safer habits.