nx_host_recover2.nx source
↩ module page · 114 lines · 6116 B
1// nx_host_recover2.nx -- run the PROVEN root recovery (reconcile2.sh) on the NAS via
2// our sovereign SSH, bringing the dead nx_hostctl supervisor (and thus sites.elf) back.
3// The script-FILE + setsid idiom survives the channel teardown where an inline launch
4// dies. The sudo password is streamed as the remote command's STDIN (sudo -S) so it
5// never appears in any cmdline. Reuses the vault preamble + ssh_put_file stdin-stream
6// from nx_aw_push. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_ssh_lib.nx"
9const P_MAGIC_65536: i64 = 65536
10const P_MAGIC_1048576: i64 = 1048576
11const P_MAGIC_16384: i64 = 16384
12const P_MAGIC_1024: i64 = 1024
13
14const P_SECRET_OUT: *u8 = "/tmp/nxsecret.out" as *u8
15const P_VAULT_NV: *u8 = "/home/elderwesto/.nishi/secrets/nas.nv" as *u8
16
17func p_run(path: *u8, a1: *u8, a2: *u8) -> i64 {
18 let pid: i64 = sys_fork()
19 if pid == 0 {
20 let argv: *i64 = sys_mmap(64) as *i64
21 argv[0] = path as i64
22 var ai: i64 = 1
23 if (a1 as i64) != 0 { argv[ai] = a1 as i64; ai = ai + 1 }
24 if (a2 as i64) != 0 { argv[ai] = a2 as i64; ai = ai + 1 }
25 argv[ai] = 0
26 let envp: *i64 = sys_mmap(16) as *i64
27 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0
28 sys_execve(path, argv, envp)
29 sys_exit(127)
30 }
31 let st: *i64 = sys_mmap(16) as *i64
32 sys_wait4(pid, st, 0)
33 return (st[0] >> 8) & 0xff
34}
35func p_unlink(path: *u8) -> i64 { __syscall(263, AT_FDCWD, path, 0, 0, 0, 0) return 0 }
36func slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
37
38// --- ssh_put_file + drain, copied from nx_aw_push (stream `data` as the remote
39// command's stdin over an exec channel) ---
40func ssh_drain(st: *SshState) -> i64 {
41 let pfd: *u8 = sys_mmap(8); let fdv: i64 = st.fd
42 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
43 pfd[4]=1 as u8; pfd[5]=0 as u8
44 let buf: *u8 = sys_mmap(P_MAGIC_65536); var run: i64 = 1
45 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 } } }
46 return 0
47}
48func ssh_put_file(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, P_MAGIC_1048576); c = ssh_put_u32(co, c, P_MAGIC_16384)
52 ssh_enc_send(st, co, c)
53 let rep: *u8 = sys_mmap(P_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(P_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 > P_MAGIC_16384 { nn = P_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 ssh_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 >= 12 { 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() -> i64 {
89 if p_run("_offc/nx_machine_key.elf" as *u8, 0 as *u8, 0 as *u8) != 0 { ssh_puts("recover2: machine-key FAIL\n" as *u8); return 1 }
90 if p_run("_offc/nx_vault.elf" as *u8, "open" as *u8, P_VAULT_NV) != 0 { ssh_puts("recover2: vault FAIL\n" as *u8); return 2 }
91 p_unlink("/tmp/nxpass" as *u8)
92 let pwbox: *i64 = sys_mmap(16) as *i64
93 let pw: *u8 = sys_read_file(P_SECRET_OUT, pwbox)
94 if (pw as i64) == 0 { ssh_puts("recover2: secret FAIL\n" as *u8); return 3 }
95 var pwlen: i64 = pwbox[0]
96 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 } } }
97
98 let st: *SshState = sys_mmap(SSH_STATE_BYTES) as *SshState
99 if ssh_open_session(st, (192 << 24) | (168 << 16) | (8 << 8) | 227) != 0 { ssh_puts("recover2: SSH FAIL\n" as *u8); p_unlink(P_SECRET_OUT); return 7 }
100 if ssh_userauth_password(st, "elderwesto" as *u8, 10, pw, pwlen) != 1 { ssh_puts("recover2: AUTH FAIL\n" as *u8); sys_close(st.fd); p_unlink(P_SECRET_OUT); return 8 }
101 p_unlink(P_SECRET_OUT)
102
103 // stdin for the remote sudo: the password + newline (sudo -S reads it; never in a cmdline)
104 let payload: *u8 = sys_mmap(64); var pi: i64 = 0
105 while pi < pwlen { payload[pi] = pw[pi]; pi = pi + 1 }
106 payload[pwlen] = 10 as u8
107
108 let dst: *u8 = "sudo -S -p '' sh /volume1/homes/elderwesto/nishihost/reconcile2.sh > /volume1/homes/elderwesto/nishihost/reconcile_run.log 2>&1" as *u8
109 ssh_puts("recover2: [auth ok] running reconcile2.sh as root...\n" as *u8)
110 ssh_put_file(st, dst, slen(dst), payload, pwlen + 1)
111 ssh_puts("recover2: reconcile dispatched\n" as *u8)
112 sys_close(st.fd)
113 return 0
114}