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}