code wiki / (root) / nx_signup_origin_gate.nx

nx_signup_origin_gate.nx source

↩ module page · 128 lines · 6063 B

1// nx_signup_origin_gate.nx -- proves POST /register is gated on LAN-AND-INVITED, not on a global flag. 2// 3// WHY (debt 1785445694): public registration was open to the internet because /register consulted ONE 4// global boolean. nx_lan_signup.ls_signup_allowed (LAN AND invited) existed and was gate-proven, but was 5// NOT WIRED to the route. This gate proves the wiring, which is the half that was missing. 6// 7// EVERY TOOTH IS A DENIAL. No tooth registers an account: the ALLOW path is deliberately untested here 8// because exercising it would create a real credential. The ALLOW semantics are already proven 17/17 by 9// nx_lan_signup_gate; what this file adds is that the ROUTE actually consults that decision. 10// 11// T4 IS THE NON-VACUITY TOOTH and the reason this gate is worth anything: with the flag CLOSED the route 12// must answer "registration closed". That proves T1-T3's 403s come from the NEW origin/invite checks and 13// are not just the old flag firing -- without it, a permanently-shut flag would make T1-T3 pass for the 14// wrong reason, which is exactly the vacuous-green class. 15// 16// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 17import "nx_opaque_login_routes.nx" 18import "nx_opaque_login.nx" 19import "nx_syscalls.nx" 20 21const SG_RESP: i64 = 262144 22const SG_MODE: i64 = 0x1a4 23 24func g_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 25func rfind(hay: *u8, n: i64, needle: *u8) -> i64 { 26 var nl: i64 = 0; while needle[nl] != (0 as u8) { nl = nl + 1 } 27 if nl == 0 { return 0 } 28 var i: i64 = 0 29 while i + nl <= n { 30 var j: i64 = 0; var ok: i64 = 1 31 while j < nl { if hay[i + j] != needle[j] { ok = 0; j = nl } else { j = j + 1 } } 32 if ok == 1 { return i } 33 i = i + 1 34 } 35 return 0 - 1 36} 37func chk(name: *u8, got: i64, want: i64, st: *i64) -> i64 { 38 st[1] = st[1] + 1 39 g_puts(" "); g_puts(name) 40 if got == want { st[0] = st[0] + 1; g_puts(": PASS\n") } else { g_puts(": FAIL\n") } 41 return 0 42} 43func sg_writeflag(path: *u8, ch: i64) -> i64 { 44 let fd: i64 = sys_openat_wr(path, SG_MODE) 45 if fd < 0 { return 0 - 1 } 46 let b: *u8 = sys_mmap(8) 47 b[0] = ch as u8 48 sys_write(fd, b, 1) 49 sys_close(fd) 50 sys_munmap(b, 8) 51 return 0 52} 53func sg_ip(a: i64, b: i64, c: i64, d: i64) -> *u8 { 54 let p: *u8 = sys_mmap(8) 55 p[0] = a as u8; p[1] = b as u8; p[2] = c as u8; p[3] = d as u8 56 return p 57} 58 59func main() -> i64 { 60 let st: *i64 = sys_mmap(16) as *i64 61 st[0] = 0; st[1] = 0 62 g_puts("nx_signup_origin_gate -- POST /register must require LAN origin AND an invite\n\n") 63 64 let keys: *u8 = "/tmp/nx_sgorigin_keys.log\x00" as *u8 65 let store: *u8 = "/tmp/nx_sgorigin_store.log\x00" as *u8 66 let fd1: i64 = sys_openat_wr(keys, SG_MODE); if fd1 >= 0 { sys_close(fd1) } 67 let fd2: i64 = sys_openat_wr(store, SG_MODE); if fd2 >= 0 { sys_close(fd2) } 68 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext 69 if olg_ctx_setup(ctx, keys, store, "nishi_site_admin" as *u8, 16, "Nishi site admin" as *u8, 16, 8192, 1, 1) != 0 { 70 g_puts("ctx-setup FAIL\n"); return 1 71 } 72 73 let fopen: *u8 = "/tmp/nx_sgorigin_open.flag\x00" as *u8 74 let fclosed: *u8 = "/tmp/nx_sgorigin_closed.flag\x00" as *u8 75 sg_writeflag(fopen, 49) // ASCII 1 = open 76 sg_writeflag(fclosed, 48) // ASCII 0 = closed 77 78 // an HR store that does not exist -> hra_is_invited returns 0 -> nobody is invited 79 let hrs: *u8 = "/tmp/nx_sgorigin_hr_absent.log\x00" as *u8 80 81 let req: *u8 = "POST /register HTTP/1.1\r\nHost: nishifamily\r\nContent-Length: 29\r\n\r\nhandle=stranger&pw=abcdefghij\x00" as *u8 82 var rn: i64 = 0; while req[rn] != (0 as u8) { rn = rn + 1 } 83 let now: i64 = sys_now_realtime_sec() 84 let resp: *u8 = sys_mmap(SG_RESP) 85 86 let wan: *u8 = sg_ip(8, 8, 8, 8) 87 let lan: *u8 = sg_ip(192, 168, 8, 5) 88 89 // T1 -- UNKNOWN ORIGIN (ip4=0, what the rule-19 wrapper passes) must be treated as WAN and denied. 90 let n1: i64 = olg_route_ip(ctx, req, rn, now, resp, SG_RESP, 0 as *u8, hrs, "fam" as *u8, 3, fopen) 91 var t1: i64 = 0 92 if rfind(resp, n1, "403\x00" as *u8) >= 0 { if rfind(resp, n1, "requires LAN origin\x00" as *u8) >= 0 { t1 = 1 } } 93 chk("T1 unknown origin (ip4=0) -> 403, never dereferenced", t1, 1, st) 94 95 // T2 -- a real WAN address is denied even with the flag OPEN. 96 let n2: i64 = olg_route_ip(ctx, req, rn, now, resp, SG_RESP, wan, hrs, "fam" as *u8, 3, fopen) 97 var t2: i64 = 0 98 if rfind(resp, n2, "403\x00" as *u8) >= 0 { t2 = 1 } 99 chk("T2 WAN 8.8.8.8 + flag OPEN -> 403 (flag cannot open the internet)", t2, 1, st) 100 101 // T3 -- LAN but NOT invited is denied (only-the-family). 102 let n3: i64 = olg_route_ip(ctx, req, rn, now, resp, SG_RESP, lan, hrs, "fam" as *u8, 3, fopen) 103 var t3: i64 = 0 104 if rfind(resp, n3, "403\x00" as *u8) >= 0 { if rfind(resp, n3, "invite\x00" as *u8) >= 0 { t3 = 1 } } 105 chk("T3 LAN 192.168.8.5 + uninvited -> 403 (only-the-family)", t3, 1, st) 106 107 // T4 -- NON-VACUITY. Flag CLOSED must produce the FLAG message, not the origin message. This proves 108 // T1-T3 above are the new checks firing and not the old global flag. 109 let n4: i64 = olg_route_ip(ctx, req, rn, now, resp, SG_RESP, lan, hrs, "fam" as *u8, 3, fclosed) 110 var t4: i64 = 0 111 if rfind(resp, n4, "registration closed\x00" as *u8) >= 0 { t4 = 1 } 112 chk("T4 NEG-CONTROL flag CLOSED -> registration closed (T1-T3 were NOT the flag)", t4, 1, st) 113 114 // T5 -- the rule-19 wrapper keeps the 6-arg contract and denies signup through the WAN door. 115 let n5: i64 = olg_route(ctx, req, rn, now, resp, SG_RESP) 116 var t5: i64 = 0 117 if rfind(resp, n5, "403\x00" as *u8) >= 0 { t5 = 1 } 118 chk("T5 olg_route wrapper (production flag) -> 403, signup shut by construction", t5, 1, st) 119 120 g_puts("\nNX-SIGNUP-ORIGIN passed ") 121 if st[0] == st[1] { 122 g_puts("ALL verdict=GREEN (register requires LAN origin AND an invite)\n") 123 sys_exit(0); return 0 124 } 125 g_puts("verdict=RED\n") 126 sys_exit(1) 127 return 1 128}