code wiki / _hdl_build / nx_aw_sudo.nx
nx_aw_sudo.nx source
↩ module page · 135 lines · 7236 B
1// nx_aw_sudo.nx -- run a command as ROOT on the NAS via `sudo -S`, feeding the vault-held elderwesto
2// password to sudo's stdin over the sovereign SSH channel. (sudo -l confirms: elderwesto may run
3// (ALL) ALL with a password.) The root command is read from awsudo.cmd and MUST begin "sudo -S ".
4// Spine cloned from nx_aw_push (vault unlock + ssh_put_file stream); here the STREAMED stdin is the
5// password (+newline) that sudo consumes. license_tier: ORIGINAL
6import "nx_syscalls.nx"
7import "nx_ssh_lib.nx"
8const S_MAGIC_1000000: i64 = 1000000
9const S_MAGIC_65536: i64 = 65536
10const S_MAGIC_1048576: i64 = 1048576
11const S_MAGIC_16384: i64 = 16384
12const S_MAGIC_1024: i64 = 1024
13
14const S_CMD: *u8 = "/mnt/c/Users/elder/AppData/Local/Temp/awsudo.cmd" as *u8
15const S_SECRET_OUT: *u8 = "/tmp/nxsecret.out" as *u8
16const S_VAULT_NV: *u8 = "/home/elderwesto/.nishi/secrets/nas.nv" as *u8
17
18func s_run(path: *u8, a1: *u8, a2: *u8) -> i64 {
19 let pid: i64 = sys_fork()
20 if pid == 0 {
21 let argv: *i64 = sys_mmap(64) as *i64
22 argv[0] = path as i64
23 var ai: i64 = 1
24 if (a1 as i64) != 0 { argv[ai] = a1 as i64; ai = ai + 1 }
25 if (a2 as i64) != 0 { argv[ai] = a2 as i64; ai = ai + 1 }
26 argv[ai] = 0
27 let envp: *i64 = sys_mmap(16) as *i64
28 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0
29 sys_execve(path, argv, envp)
30 sys_exit(127)
31 }
32 let st: *i64 = sys_mmap(16) as *i64
33 sys_wait4(pid, st, 0)
34 return (st[0] >> 8) & 0xff
35}
36func s_unlink(path: *u8) -> i64 { __syscall(263, AT_FDCWD, path, 0, 0, 0, 0) return 0 }
37func s_napms(ms: i64) -> i64 { let ts: *i64 = sys_mmap(16) as *i64; ts[0] = 0; ts[1] = ms * S_MAGIC_1000000; __syscall(35, ts as i64, 0, 0, 0, 0, 0); return 0 }
38
39func s_drain(st: *SshState) -> i64 {
40 let pfd: *u8 = sys_mmap(8); let fdv: i64 = st.fd
41 pfd[0]=(fdv&0xff) as u8; pfd[1]=((fdv>>8)&0xff) as u8; pfd[2]=((fdv>>16)&0xff) as u8; pfd[3]=((fdv>>24)&0xff) as u8
42 pfd[4]=1 as u8; pfd[5]=0 as u8
43 let buf: *u8 = sys_mmap(S_MAGIC_65536); var run: i64 = 1
44 while run == 1 { let r: i64 = sys_poll(pfd, 1, 0); if r <= 0 { run = 0 } else { if ssh_enc_recv(st, buf) < 0 { run = 0 } } }
45 return 0
46}
47// Open an exec channel for `wcmd`, stream `data` (the password+newline) as its stdin, drain to EOF.
48func s_put(st: *SshState, wcmd: *u8, wcmdlen: i64, data: *u8, datalen: i64) -> i64 {
49 let co: *u8 = sys_mmap(64); var c: i64 = 0
50 c = ssh_put_byte(co, c, 90); c = ssh_put_str(co, c, "session" as *u8, 7)
51 c = ssh_put_u32(co, c, 0); c = ssh_put_u32(co, c, S_MAGIC_1048576); c = ssh_put_u32(co, c, S_MAGIC_16384)
52 ssh_enc_send(st, co, c)
53 let rep: *u8 = sys_mmap(S_MAGIC_65536); var rcid: i64 = 0 - 1; var guard: i64 = 0
54 while rcid < 0 {
55 if guard > 16 { return 0 - 1 }
56 let rl: i64 = ssh_enc_recv(st, rep); if rl < 0 { return 0 - 1 }
57 if rep[0] == 91 as u8 { rcid = ssh_u32be(rep, 5) }
58 if rep[0] == 92 as u8 { return 0 - 2 }
59 guard = guard + 1
60 }
61 let cr: *u8 = sys_mmap(S_MAGIC_1024); var q: i64 = 0
62 q = ssh_put_byte(cr, q, 98); q = ssh_put_u32(cr, q, rcid); q = ssh_put_str(cr, q, "exec" as *u8, 4); q = ssh_put_byte(cr, q, 1); q = ssh_put_str(cr, q, wcmd, wcmdlen)
63 ssh_enc_send(st, cr, q)
64 var off: i64 = 0
65 while off < datalen {
66 var nn: i64 = datalen - off; if nn > S_MAGIC_16384 { nn = S_MAGIC_16384 }
67 let dp: *u8 = sys_mmap(nn + 64); var p: i64 = 0
68 p = ssh_put_byte(dp, p, 94); p = ssh_put_u32(dp, p, rcid); p = ssh_put_u32(dp, p, nn)
69 var i: i64 = 0; while i < nn { dp[p + i] = data[off + i]; i = i + 1 } p = p + nn
70 ssh_enc_send(st, dp, p)
71 s_drain(st)
72 off = off + nn
73 }
74 let eo: *u8 = sys_mmap(16); var e: i64 = 0; e = ssh_put_byte(eo, e, 96); e = ssh_put_u32(eo, e, rcid); ssh_enc_send(st, eo, e)
75 let pfd2: *u8 = sys_mmap(8); let fdv2: i64 = st.fd
76 pfd2[0]=(fdv2&0xff) as u8; pfd2[1]=((fdv2>>8)&0xff) as u8; pfd2[2]=((fdv2>>16)&0xff) as u8; pfd2[3]=((fdv2>>24)&0xff) as u8
77 pfd2[4]=1 as u8; pfd2[5]=0 as u8
78 var done: i64 = 0; var quiet: i64 = 0
79 while done == 0 {
80 let pr: i64 = sys_poll(pfd2, 1, 500)
81 if pr <= 0 { quiet = quiet + 1; if quiet >= 8 { done = 1 } }
82 else { let rl: i64 = ssh_enc_recv(st, rep); if rl < 0 { done = 1 } else { if rep[0] == 97 as u8 { done = 1 } } }
83 }
84 let clo: *u8 = sys_mmap(16); var k: i64 = 0; k = ssh_put_byte(clo, k, 97); k = ssh_put_u32(clo, k, rcid); ssh_enc_send(st, clo, k)
85 return 0
86}
87
88func main(argc: i64, argv: *i64) -> i64 {
89 // backward-compatible: no args -> fixed S_CMD (awsudo.cmd); with args, argv[1]=cmd-file so a SECOND
90 // workflow can run a root command WITHOUT sharing awsudo.cmd (defeats the staging-file race).
91 var cmd_file: *u8 = S_CMD
92 if argc >= 2 { cmd_file = argv[1] as *u8 }
93 // RESILIENT secret derivation (mirrors nx_aw_push): DSM's Warden shreds /tmp/nxpass ephemerally + WSL churns
94 // /tmp mid-op, so machine-key -> vault-open -> read-secret can transiently fail. Retry up to 12x instead of
95 // aborting -- a root/firewall op must NOT fail on a transient vault race (CLAUDE.md #14 graceful degradation).
96 let pwbox: *i64 = sys_mmap(16) as *i64
97 var pw: *u8 = 0 as *u8
98 var att: i64 = 0
99 while att < 12 {
100 if s_run("_offc/nx_machine_key.elf" as *u8, 0 as *u8, 0 as *u8) == 0 {
101 if s_run("_offc/nx_vault.elf" as *u8, "open" as *u8, S_VAULT_NV) == 0 {
102 pw = sys_read_file(S_SECRET_OUT, pwbox)
103 if (pw as i64) != 0 { att = 1000 }
104 }
105 }
106 if att < 1000 { att = att + 1; s_napms(150) }
107 }
108 s_unlink("/tmp/nxpass" as *u8)
109 if (pw as i64) == 0 { ssh_puts("sudo: secret derivation FAIL after 12 retries\n" as *u8); return 3 }
110 var pwlen: i64 = pwbox[0]
111 while pwlen > 0 { if pw[pwlen-1] == 10 as u8 { pwlen = pwlen - 1 } else { if pw[pwlen-1] == 13 as u8 { pwlen = pwlen - 1 } else { break } } }
112
113 let dbox: *i64 = sys_mmap(16) as *i64
114 let dcmd: *u8 = sys_read_file(cmd_file, dbox)
115 if (dcmd as i64) == 0 { ssh_puts("sudo: no awsudo.cmd\n" as *u8); s_unlink(S_SECRET_OUT); return 6 }
116 var dlen: i64 = dbox[0]
117 while dlen > 0 { if dcmd[dlen-1] == 10 as u8 { dlen = dlen - 1 } else { break } }
118
119 let st: *SshState = sys_mmap(SSH_STATE_BYTES) as *SshState
120 if ssh_open_session(st, (192 << 24) | (168 << 16) | (8 << 8) | 227) != 0 { ssh_puts("sudo: SSH session FAIL\n" as *u8); s_unlink(S_SECRET_OUT); return 7 }
121 if ssh_userauth_password(st, "elderwesto" as *u8, 10, pw, pwlen) != 1 { ssh_puts("sudo: SSH auth FAIL\n" as *u8); sys_close(st.fd); s_unlink(S_SECRET_OUT); return 8 }
122 s_unlink(S_SECRET_OUT)
123
124 // stdin for `sudo -S <cmd>` = the password + newline.
125 let sin: *u8 = sys_mmap(pwlen + 4)
126 var i: i64 = 0; while i < pwlen { sin[i] = pw[i]; i = i + 1 }
127 sin[pwlen] = 10 as u8
128 ssh_puts("sudo: [auth ok] running root command\n" as *u8)
129 s_put(st, dcmd, dlen, sin, pwlen + 1)
130 let vcmd: *u8 = "echo ===FG-RESULT===; cat /tmp/sfg.txt 2>/dev/null; echo ===SUDO-DONE===" as *u8
131 var vl: i64 = 0; while vcmd[vl] != (0 as u8) { vl = vl + 1 }
132 ssh_exec(st, vcmd, vl)
133 sys_close(st.fd)
134 return 0
135}