code wiki / _hdl_build / nx_account_cleanup.nx
nx_account_cleanup.nx source
↩ module page · 145 lines · 7179 B
1// nx_account_cleanup.nx -- one-shot sovereign cleanup of the nishifamily OPAQUE account store.
2//
3// Keeps ONLY elderwesto: its main credential AND its "|rec" recovery credential, both computed from
4// the REAL derivation (nx_ncs_derive_user_id_hash, same path register/login use) -- never from a guess.
5// Every OTHER identity in the store is SOFT-disabled (append is_current=0; additive-only per global
6// rule 13 -- history sacred, fully reversible by re-appending is_current=1).
7//
8// SAFETY:
9// * FAIL-CLOSED: if elderwesto's main uid is NOT present in the store (realm/derivation mismatch),
10// ABORT and disable nobody (negative control against a wrong realm wiping the real account).
11// * DRY-RUN: with AC_DRYRUN=1 it only PRINTS what it would do (read the output before flipping to 0).
12// * IDEMPOTENT (rule 10): an identity whose latest row is already is_current=0 is skipped.
13//
14// Run AFTER backing up the store. Store path = the live NAS store via the homes drvfs mount.
15// COMPOSES: hub/nx_user_account_store (nx_uas_append + _uas_hex_dec), hub/nx_no_cookie_session (derive).
16import "nx_syscalls.nx"
17import "hub/nx_user_account_store.nx"
18import "hub/nx_no_cookie_session.nx"
19
20const AC_DRYRUN: i64 = 0
21const AC_STORE: *u8 = "/mnt/nas_homes/elderwesto/nishihost/opaque_store.log"
22const AC_REALM: *u8 = "nishi_site_admin"
23const AC_REALM_N: i64 = 16
24const AC_MAXACC: i64 = 256
25
26func ac_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return n }
27func ac_put_hex(b: *u8, n: i64) -> i64 {
28 let hx: *u8 = "0123456789abcdef" as *u8
29 let out: *u8 = sys_mmap(n * 2 + 1)
30 var i: i64 = 0
31 while i < n { let c: i64 = (b[i] as i64) & 255; out[i*2] = hx[(c>>4)&15]; out[i*2+1] = hx[c&15]; i = i + 1 }
32 sys_write(1, out, n * 2); return 0
33}
34func ac_put_i64(v: i64) -> i64 {
35 let t: *u8 = sys_mmap(28); var m: i64 = v; var k: i64 = 0
36 if m == 0 { t[0] = 48 as u8; k = 1 }
37 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
38 let o: *u8 = sys_mmap(28); var q: i64 = k - 1; var w: i64 = 0
39 while q >= 0 { o[w] = t[q]; w = w + 1; q = q - 1 }
40 sys_write(1, o, w); return 0
41}
42func ac_eq32(a: *u8, b: *u8) -> i64 {
43 var i: i64 = 0; var ok: i64 = 1
44 while i < 32 { if a[i] != b[i] { ok = 0; i = 32 } else { i = i + 1 } }
45 return ok
46}
47func ac_cpy(dst: *u8, src: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { dst[i] = src[i]; i = i + 1 } return n }
48
49func main(argc: i64, argv: *i64) -> i64 {
50 // ---- compute the two keep-uids from the real derivation ----
51 let keep_main: *u8 = sys_mmap(32)
52 let keep_rec: *u8 = sys_mmap(32)
53 nx_ncs_derive_user_id_hash(AC_REALM, AC_REALM_N, "elderwesto" as *u8, 10, keep_main)
54 nx_ncs_derive_user_id_hash(AC_REALM, AC_REALM_N, "elderwesto|rec" as *u8, 14, keep_rec)
55 if AC_DRYRUN == 1 { ac_puts("=== DRY RUN (no writes) ===\n" as *u8) } else { ac_puts("=== EXECUTE (appending is_current=0) ===\n" as *u8) }
56 ac_puts("KEEP_MAIN=" as *u8); ac_put_hex(keep_main, 32); ac_puts("\n" as *u8)
57 ac_puts("KEEP_REC =" as *u8); ac_put_hex(keep_rec, 32); ac_puts("\n" as *u8)
58
59 // ---- read the store ----
60 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0
61 let data: *u8 = sys_read_file(AC_STORE, lb)
62 if (data as i64) == 0 { ac_puts("ABORT: cannot read store\n" as *u8); sys_exit(1); return 1 }
63 let dn: i64 = lb[0]
64
65 // ---- collect distinct uids with their LATEST record + is_current ----
66 let uids: *u8 = sys_mmap(AC_MAXACC * 32)
67 let recs: *u8 = sys_mmap(AC_MAXACC * 129)
68 let curs: *i64 = sys_mmap(AC_MAXACC * 8) as *i64
69 var nacc: i64 = 0
70 let tmp_uid: *u8 = sys_mmap(32)
71 let tmp_rec: *u8 = sys_mmap(129)
72
73 var pos: i64 = 0
74 while pos < dn {
75 var eol: i64 = pos; var sc: i64 = 1
76 while sc == 1 { if eol >= dn { sc = 0 } else { if (data[eol] as i64) == 10 { sc = 0 } else { eol = eol + 1 } } }
77 let line_n: i64 = eol - pos
78 if line_n >= 87 + 258 {
79 if (data[pos] as i64) == 0x41 {
80 if _uas_hex_dec((data as i64 + pos + 3) as *u8, 32, tmp_uid) == NX_UAS_OK {
81 if _uas_hex_dec((data as i64 + pos + 87) as *u8, 129, tmp_rec) == NX_UAS_OK {
82 let cur: i64 = (data[pos + 68] as i64) - 48
83 var idx: i64 = 0 - 1; var f: i64 = 0
84 while f < nacc { if ac_eq32((uids as i64 + f*32) as *u8, tmp_uid) == 1 { idx = f; f = nacc } else { f = f + 1 } }
85 if idx < 0 {
86 if nacc < AC_MAXACC {
87 ac_cpy((uids as i64 + nacc*32) as *u8, tmp_uid, 32)
88 ac_cpy((recs as i64 + nacc*129) as *u8, tmp_rec, 129)
89 curs[nacc] = cur
90 nacc = nacc + 1
91 }
92 } else {
93 ac_cpy((recs as i64 + idx*129) as *u8, tmp_rec, 129)
94 curs[idx] = cur
95 }
96 }
97 }
98 }
99 }
100 pos = eol + 1
101 }
102 ac_puts("distinct_uids=" as *u8); ac_put_i64(nacc); ac_puts("\n" as *u8)
103
104 // ---- fail-closed: elderwesto's main uid MUST be present ----
105 var have_main: i64 = 0; var have_rec: i64 = 0; var g: i64 = 0
106 while g < nacc {
107 if ac_eq32((uids as i64 + g*32) as *u8, keep_main) == 1 { have_main = 1 }
108 if ac_eq32((uids as i64 + g*32) as *u8, keep_rec) == 1 { have_rec = 1 }
109 g = g + 1
110 }
111 ac_puts("have_main=" as *u8); ac_put_i64(have_main); ac_puts(" have_rec=" as *u8); ac_put_i64(have_rec); ac_puts("\n" as *u8)
112 if have_main == 0 {
113 ac_puts("ABORT: elderwesto main uid NOT in store (realm/derivation mismatch) -- disabling NOBODY\n" as *u8)
114 sys_exit(2); return 2
115 }
116
117 // ---- disable every other identity (or report, in dry-run) ----
118 let now: i64 = sys_now_realtime_sec()
119 var disabled: i64 = 0; var already: i64 = 0; var i2: i64 = 0
120 while i2 < nacc {
121 let up: *u8 = (uids as i64 + i2*32) as *u8
122 var keep: i64 = 0
123 if ac_eq32(up, keep_main) == 1 { keep = 1 }
124 if ac_eq32(up, keep_rec) == 1 { keep = 1 }
125 if keep == 0 {
126 if curs[i2] == 1 {
127 if AC_DRYRUN == 1 {
128 ac_puts("WOULD-DISABLE " as *u8); ac_put_hex(up, 32); ac_puts("\n" as *u8)
129 disabled = disabled + 1
130 } else {
131 if nx_uas_append(AC_STORE, up, (recs as i64 + i2*129) as *u8, now, 0) == NX_UAS_OK {
132 ac_puts("DISABLED " as *u8); ac_put_hex(up, 32); ac_puts("\n" as *u8)
133 disabled = disabled + 1
134 } else {
135 ac_puts("APPEND-FAIL " as *u8); ac_put_hex(up, 32); ac_puts("\n" as *u8)
136 }
137 }
138 } else { already = already + 1 }
139 }
140 i2 = i2 + 1
141 }
142 ac_puts("SUMMARY disabled=" as *u8); ac_put_i64(disabled)
143 ac_puts(" already_disabled=" as *u8); ac_put_i64(already); ac_puts(" kept=2\n" as *u8)
144 sys_exit(0); return 0
145}