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}