Security
Max's requirement: NULLos should become more secure from the ground up than Linux, macOS, Windows, Android and iOS.
And stay dead simple while doing it.
The idea: every program has an ID card
Next to spec there's a small file, the ID card (ausweis). It says what the program may do –
in German, one line per right. For example dl (download):
datei lesen argumente
datei schreiben argumente
geräte zufall
netz verbinden alles
netz lauschen nein
prozesse nein
The kernel enforces this. The ID card sits inside the program itself (its own section of the ELF file) and
is read on every start. It's like pledge/unveil on OpenBSD, only mandatory instead of
voluntary. Almost every tool already has its ID card – 306 of them, all checked without errors (in the cards under “more”).
If there's an unknown word in the ID card, the program doesn't start at all. Programs without an ID card get a transitional ID card without admin rights for now – later they'll only be allowed
input and output.
Authority instead of mandatory rules
Max decided how rights get passed on (variant “intersection”): A program never gets more than its
ID card allows, never more than whoever starts it has – and it gets the concrete things because the human names them.
- Naming = allowing: If you type
c urlaub.jpg /usb/, c may read exactly that file
and write exactly there – without asking. It can't get at anything else.
- Ask only where needed – and then it's not the program asking, but a guard of the system that no
program can fake: “
web wants to read /heim/max/fotos. [e] once [i] always [n] no”.
- No SELinux rule jungle, no clicking “Yes” on every question.
Already in the kernel: users with rwx rights, the enforced ID card, rights only flow
downward (if the kernel can't build the chain, the program doesn't start – instead of running without limits), and einschr – a program restricts itself, never back, and its children inherit that.
/bin, /sys and /boot are write-protected even for the admin.
Since this afternoon also “naming = allowing”: the shell s is a broker – what you type becomes the authority for the program it starts. A narrow program stays narrow. With scripts the arguments come from the script, not from you – that's why a script carries its own ID card in a header line # ausweis: …, and the shell restricts itself accordingly while running it. Honest limit: a cracked shell could start anything – that's why it's protected by fuzzing. Not yet: the guard that asks. coming soon
What's coming
- Signed updates only: only the update tool changes system folders, with a signature.
- Every block explains itself – instead of “Permission denied”, a sentence saying what's missing and how to allow it.
All coming soon – the plan has 40 numbered measures (S1–S40).
What already exists
- Its own TLS 1.3: X25519, AES-GCM, ChaCha20-Poly1305, certificate chains with RSA,
ECDSA and Ed25519, also for IPv6 addresses.
dl uses it to fetch HTTPS – on NULLos itself too. Since Oct 30 also as a server (Ed25519 or ECDSA P-256, tested against openssl s_client), and the math runs without secret-dependent branches or tables – AES without a table, against timing attacks.
- Its own SSH, client and server – with re-keying in the middle of a session. Plus RDP (
rdp, server with NLA login, tested against FreeRDP) and Kerberos (krb).
- Firewall on by default: all incoming closed, outgoing and replies allowed, IPv4 and IPv6 the same. Rules
as short sentences,
fw warum explains every decision, profiles zuhause/unterwegs/server.
Fuzzed with 2 million runs, nothing found.
- Fuzz tests for TLS, SSH, PPP and the compiler: random data and forgeries thrown at the parsers, with libFuzzer, AFL++ and memory checkers.
- Protection against cold boot attacks: the kernel zeroes memory right when it's freed, protects its free list with a check word and wipes RAM on power off, reboot and on a panic. Passwords are read without echo, type-ahead input is discarded, secrets are wiped in a way the compiler can't optimize away.
- USB devices only with approval: against BadUSB and “Rubber Ducky” – after startup a new device only gets a driver once you approve it.
- Physical attacks and side channels are collected (over 100 entries, with owners); crypto like AES and Ed25519 computes without secret-dependent branches, tables or timing differences.
- Crypto by the standard only, tested against the official test vectors.
- Secure randomness in the kernel: ChaCha20 with a BLAKE2s pool, checked against the RFC test vectors at startup.
- SMEP/SMAP: the kernel can neither execute program memory nor read it by accident (if the CPU supports it).
- The kernel touches program memory at only one checkpoint – tested with broken pointers and lengths for about 40 calls.
- W^X: nothing is writable and executable at the same time. Stack protection in the kernel with a guard page – and
in programs: the kernel hands every program start 16 bytes of fresh randomness, and the libc turns that into the “canary”.
- Hardened
malloc: free wipes, the free list is encrypted and checked, a double free aborts, large blocks sit in front of a guard page (mprotect, never writable and executable at once). All tools are built with stack protection and zeroed variables.
- The compiler hardens on its own:
cc knows the flags from the OpenSSF guide – stack protection,
zeroing of variables and registers, endbr64, format checking, -pie.
- Memory checkers over everything: one check run runs all tool tests with ASan/UBSan – last time 402 tools without a single report.
- The audit searches all code for
strcpy, sprintf, gets & co.
Honestly: what's still missing
According to the security status: the ID card is still in transition (programs without an ID card may still do almost everything except admin stuff), and the guard that asks is missing. Also not there yet: random placement of programs in memory (ASLR), protection against side channels (KPTI, Spectre) and IOMMU against foreign DMA.
So the rule is: don't hang NULLos on the network unprotected yet.
Rules for all code
- Every input from outside is suspicious – check length, range, format before using it.
- No
strcpy/sprintf/gets; check for overflow before computing; every malloc is checked.
- Never use foreign data as a format string; no shell commands built from text snippets.
- Wipe secrets after use, compare in constant time, never keys in the repo.
- Whoever parses network data has few rights. Parsers get fuzz tests.