code wiki / _hdl_build / nx_lan_signup_socket_gate.nx
nx_lan_signup_socket_gate.nx source
↩ module page · 154 lines · 10035 B
1// nx_lan_signup_socket_gate.nx -- proves the LAN-signup daemon's REAL router (lsd_route) end-to-end over a loopback
2// socket with a live OPAQUE context. Forks a server child that runs the EXACT daemon path (accept_with_addr ->
3// ls_ip4_from_sockaddr -> lsd_route) and a sovereign client that connects from loopback. Asserts:
4// (1) an INVITED handle from the LAN peer -> 200 + recovery mnemonic (full OPAQUE register + hra_claim over the wire);
5// (2) a NON-invited handle -> 403 (the invite gate runs on a real socket);
6// (3) GET /hr with NO session -> the owner-only stub, leaking NO roster (the /hr console is owner-gated by default-deny).
7// This closes "compiles" -> "works" for the new accept_with_addr plumbing AND the live /hr gating. license_tier: ORIGINAL expect_exit: 0
8import "nx_syscalls.nx"
9import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
10import "nx_lan_signup_routes.nx" // lsd_route + LSD_REALM/_N + helpers
11import "nx_opaque_login.nx" // olg_ctx_setup + NxAuthContext
12import "nx_lan_signup.nx" // ls_ip4_from_sockaddr
13import "nx_hr_admin.nx" // hra_invite
14import "nx_runpath.nx" // rp_wsid / rp_ensure / rp_path
15
16func g_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
17func g_n(v: i64) -> i64 { var m: i64=v; if m<0{g_w("-" as *u8);m=0-m} let t:*u8=sys_mmap(24); var k:i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i:i64=0; let o:*u8=sys_mmap(24); while i<k{o[i]=t[k-1-i];i=i+1} sys_write(1,o,k); return 0 }
18func g_row(id: *u8, ok: i64, pass: *i64) -> i64 { g_w(" " as *u8); g_w(id); g_w(": " as *u8); if ok==1 { g_w("OK\n" as *u8); pass[0]=pass[0]+1 } else { g_w("FAIL\n" as *u8) } return 0 }
19func sgt_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){dst[o]=s[i]; o=o+1; i=i+1} return o }
20func sgt_itoa(dst: *u8, off: i64, v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var o: i64=off; var q: i64=k-1; while q>=0{dst[o]=t[q];o=o+1;q=q-1} return o }
21func sgt_find(buf: *u8, n: i64, needle: *u8, nl: i64) -> i64 { if nl==0 {return 0} var i: i64=0; while i+nl<=n { var j: i64=0; var ok: i64=1; while j<nl { if buf[i+j]!=needle[j]{ok=0;j=nl} else {j=j+1} } if ok==1 {return i} i=i+1 } return 0-1 }
22func sgt_has(buf: *u8, n: i64, needle: *u8) -> i64 { var nl: i64=0; while needle[nl]!=(0 as u8){nl=nl+1} if sgt_find(buf,n,needle,nl)>=0 {return 1} return 0 }
23func sgt_nap(ms: i64) -> i64 { let ts: *i64 = sys_mmap(16) as *i64; ts[0]=0; ts[1]=ms*1000000; __syscall(35, ts as i64, 0, 0, 0, 0, 0); return 0 }
24func sgt_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
25
26// build a raw HTTP/1.0 POST with a urlencoded body. returns total length.
27func build_post(path: *u8, body: *u8, out: *u8) -> i64 {
28 let blen: i64 = sgt_slen(body)
29 var o: i64 = 0
30 o = sgt_cat(out, o, "POST " as *u8); o = sgt_cat(out, o, path)
31 o = sgt_cat(out, o, " HTTP/1.0\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: " as *u8)
32 o = sgt_itoa(out, o, blen)
33 o = sgt_cat(out, o, "\r\nConnection: close\r\n\r\n" as *u8)
34 o = sgt_cat(out, o, body)
35 return o
36}
37// connect 127.0.0.1:port (retry until the child is bound), send raw[0..rawlen], read the response.
38func cg_send(port: i64, raw: *u8, rawlen: i64, resp: *u8, cap: i64) -> i64 {
39 var tries: i64 = 0
40 while tries < 120 {
41 let fd: i64 = sys_socket(2, 1, 0)
42 if fd >= 0 {
43 let tv: *u8 = sys_mmap(16); tv[0]=3 as u8; var tz: i64=1; while tz<16 { tv[tz]=0 as u8; tz=tz+1 } // 3s
44 sys_setsockopt(fd, 1, 20, tv, 16)
45 let addr: *u8 = sys_mmap(16)
46 addr[0]=2 as u8; addr[1]=0 as u8; addr[2]=((port>>8)&0xff) as u8; addr[3]=(port&0xff) as u8
47 addr[4]=127 as u8; addr[5]=0 as u8; addr[6]=0 as u8; addr[7]=1 as u8
48 var z: i64=8; while z<16 { addr[z]=0 as u8; z=z+1 }
49 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) == 0 {
50 sys_write(fd, raw, rawlen)
51 let n: i64 = sys_read(fd, resp, cap)
52 sys_close(fd)
53 return n
54 }
55 sys_close(fd)
56 }
57 sgt_nap(15)
58 tries = tries + 1
59 }
60 return 0 - 1
61}
62
63// one request: the EXACT daemon path -- accept_with_addr -> ls_ip4_from_sockaddr -> lsd_route.
64func serve_one(ctx: *NxAuthContext, lfd: i64, hr: *u8) -> i64 {
65 let paddr: *u8 = sys_mmap(16); let palen: *i64 = sys_mmap(8) as *i64; palen[0]=16
66 let cfd: i64 = sys_accept_with_addr(lfd, paddr, palen)
67 if cfd < 0 { return 0 }
68 sys_set_socket_timeout(cfd, 5)
69 let ip4: *u8 = sys_mmap(8); ls_ip4_from_sockaddr(paddr, ip4)
70 let req: *u8 = sys_mmap(16384)
71 let rn: i64 = sys_read(cfd, req, 16383)
72 let he: i64 = sgt_find(req, rn, "\r\n\r\n" as *u8, 4)
73 var body: *u8 = req; var bn: i64 = 0
74 if he >= 0 { body = ((req as i64) + he + 4) as *u8; bn = rn - he - 4 }
75 let now: i64 = sys_now_realtime_sec()
76 lsd_route(ctx, LSD_REALM, LSD_REALM_N, ip4, req, rn, he, body, bn, now, hr, cfd)
77 sys_close(cfd)
78 return 0
79}
80
81func main() -> i64 {
82 let pass: *i64 = sys_mmap(8) as *i64; pass[0]=0
83 g_w("=== NX-LAN-SIGNUP SOCKET GATE (real lsd_route over a loopback socket: register + claim + /hr gating) ===\n" as *u8)
84 let wsid: *u8 = sys_mmap(64); rp_wsid(wsid); rp_ensure(wsid)
85 let ms: i64 = sys_now_ms()
86 let hrname: *u8 = sys_mmap(64); var ho: i64=0; let hp: *u8="lss_hr_" as *u8; while hp[ho]!=(0 as u8){hrname[ho]=hp[ho];ho=ho+1} let ht: *u8=sys_mmap(28); var hm: i64=ms; var hk: i64=0; if hm==0{ht[0]=48 as u8;hk=1} while hm>0{ht[hk]=(48+(hm%10)) as u8;hm=hm/10;hk=hk+1} var hz: i64=hk-1; while hz>=0{hrname[ho]=ht[hz];ho=ho+1;hz=hz-1} hrname[ho]=0 as u8
87 let hr: *u8 = sys_mmap(512); rp_path(wsid, hrname, hr) // FRESH seg_store prefix per run (no cross-run claim state)
88 let keys: *u8 = sys_mmap(512); rp_path(wsid, "lss_keys.bin" as *u8, keys)
89 let store:*u8 = sys_mmap(512); rp_path(wsid, "lss_store.log" as *u8, store)
90 let fam: *u8 = "andelin" as *u8
91 var psum: i64 = 0; var pj: i64 = 0; while wsid[pj]!=(0 as u8){ psum = psum + (wsid[pj] as i64); pj = pj + 1 }
92 let port: i64 = 19550 + (psum % 400)
93
94 // fresh HR store (brad invited, stranger NOT) -- written BEFORE fork so the child inherits it.
95 let fd0: i64 = sys_openat_wr(hr, 0x1a4); if fd0>=0 { sys_close(fd0) }
96 let cid: *u8 = sys_mmap(96)
97 hra_invite(hr, LSD_REALM, LSD_REALM_N, "brad" as *u8, 4, HRA_LVL_MEMBER, fam, 1000, "elderwesto" as *u8, cid)
98
99 // OPAQUE ctx (small argon2 for a fast test); fresh keys+store. Set up BEFORE fork so the child inherits it.
100 let fk: i64 = sys_openat_wr(keys, 0x1a4); if fk>=0 { sys_close(fk) }
101 let fs: i64 = sys_openat_wr(store, 0x1a4); if fs>=0 { sys_close(fs) }
102 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext
103 if olg_ctx_setup(ctx, keys, store, LSD_REALM, LSD_REALM_N, "Nishi family signup" as *u8, 19, 512, 3, 1) != 0 {
104 g_w("CTX-INIT-FAIL\n" as *u8); sys_exit(1); return 1
105 }
106
107 // bind in the PARENT, then fork the server child (it accepts on the inherited listening socket).
108 let addr: *u8 = sys_mmap(16)
109 addr[0]=2 as u8; addr[1]=0 as u8; addr[2]=((port>>8)&0xff) as u8; addr[3]=(port&0xff) as u8
110 var zi: i64=4; while zi<16 { addr[zi]=0 as u8; zi=zi+1 }
111 let lfd: i64 = sys_socket(2, 1, 0)
112 let optv: *u8 = sys_mmap(4); optv[0]=1 as u8
113 sys_setsockopt(lfd, 1, 2, optv, 4)
114 if sys_bind(lfd, addr, 16) < 0 { g_w("BIND-FAIL\n" as *u8); sys_exit(1); return 1 }
115 if sys_listen(lfd, 16) < 0 { g_w("LISTEN-FAIL\n" as *u8); sys_exit(1); return 1 }
116
117 let pid: i64 = sys_fork()
118 if pid == 0 {
119 serve_one(ctx, lfd, hr) // 1: register brad
120 serve_one(ctx, lfd, hr) // 2: register stranger
121 serve_one(ctx, lfd, hr) // 3: GET /hr (no session)
122 sys_close(lfd); sys_exit(0)
123 }
124 sys_close(lfd)
125
126 let raw: *u8 = sys_mmap(2048)
127 let resp: *u8 = sys_mmap(262144)
128 var rl: i64 = build_post("/register" as *u8, "handle=brad&pw=familypass1" as *u8, raw)
129 let n1: i64 = cg_send(port, raw, rl, resp, 262144)
130 let brad_ok: i64 = ((sgt_has(resp, n1, "200 OK" as *u8)==1) as i64) & ((sgt_has(resp, n1, "mnemonic" as *u8)==1) as i64)
131
132 rl = build_post("/register" as *u8, "handle=stranger&pw=x" as *u8, raw)
133 let n2: i64 = cg_send(port, raw, rl, resp, 262144)
134 let stranger_403: i64 = (sgt_has(resp, n2, "403" as *u8)==1) as i64
135
136 rl = sgt_cat(raw, 0, "GET /hr HTTP/1.0\r\nConnection: close\r\n\r\n" as *u8)
137 let n3: i64 = cg_send(port, raw, rl, resp, 262144)
138 let hr_stub: i64 = ((sgt_has(resp, n3, "owner-only" as *u8)==1) as i64) & ((sgt_has(resp, n3, "brad" as *u8)==0) as i64)
139
140 __syscall(129, pid, 9, 0, 0, 0, 0) // rv64 kill=129; raw x86 62 is an RV64 KEY translated to lseek(8) -- the kill never happened (debt idx 2277)
141 let st: *i64 = sys_mmap(8) as *i64; var rr: i64 = 0; while rr < 20 { sys_wait4(0 - 1, st, 1); rr = rr + 1 }
142
143 g_w(" port="); g_n(port); g_w(" bytes: reg_brad="); g_n(n1); g_w(" reg_stranger="); g_n(n2); g_w(" get_hr="); g_n(n3); g_w("\n")
144 g_row("INVITED handle from LAN -> 200 + recovery mnemonic (OPAQUE register + claim over the socket)" as *u8, brad_ok, pass)
145 g_row("NON-invited handle -> 403 (invite gate over the socket)" as *u8, stranger_403, pass)
146 g_row("GET /hr with NO session -> owner-only stub, NO roster leak" as *u8, hr_stub, pass)
147
148 g_w("LAN-SIGNUP-SOCKET rows=3 pass="); g_n(pass[0])
149 if pass[0]==3 { g_w(" verdict=GREEN\n" as *u8)
150 let lg: i64=sys_openat_append("knowledge/status/lan_signup_socket_gate.log" as *u8, 0x1a4)
151 if lg>=0 { sys_write(lg, "LAN-SIGNUP-SOCKET pass=3/3 verdict=GREEN\n" as *u8, 40); sys_close(lg) }
152 sys_exit(0); return 0 }
153 g_w(" verdict=RED\n" as *u8); sys_exit(1); return 1
154}