code wiki / _hdl_build / nx_evict.nx

nx_evict.nx source

↩ module page · 164 lines · 8449 B

1// nx_evict.nx -- THE SOVEREIGN JANITOR MOVER (debt seq221 second half; retires the "sanctioned 2// janitor ssh mv" residual class). REVERSIBLE eviction: atomic sys_renameat of a nishihost-relative 3// file into retired/ with the path FLATTENED (each '/' -> '__'), bytes preserved (additive-only law 4// rule 13 -- nothing is ever deleted, history is sacred). restore = the exact inverse, refusing to 5// clobber an existing destination (fail-safe). 6// DENY BY CONSTRUCTION (not config): absolute paths, '..' traversal, secret material (secret/key/ 7// token/passw/.pem/opaque needles), live binaries (.elf -- promote/rollback owns those), the retired/ 8// namespace itself, and the protected registries (tool_allowlist.conf, daemons.reg, clock_jobs.tsv, 9// proxy_routes.conf, sites.conf). Over-deny is the safe failure mode; needles are lowercase-literal 10// (declared envelope). 11// nx_evict evict <relpath> -> retired/<flattened> (atomic, reversible) 12// nx_evict restore <name> <destpath> -> retired/<name> -> <destpath> (refuses if dest exists) 13// nx_evict selftest -> probe evict/restore round-trip + refusal teeth, exit 0/5 14// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 15import "nx_syscalls.nx" 16 17const EV_STDERR: i64 = 2 18const EV_PATHCAP: i64 = 512 19const EV_SLASH: i64 = 47 20const EV_UND: i64 = 95 21const EV_MODE: i64 = 0x1a4 22const EV_EXIT_USAGE: i64 = 2 23const EV_EXIT_REFUSED: i64 = 3 24const EV_EXIT_IO: i64 = 4 25const EV_EXIT_SELF: i64 = 5 26const EV_V_E: i64 = 101 27const EV_V_R: i64 = 114 28const EV_V_S: i64 = 115 29const EV_ARGC_EVICT: i64 = 3 30const EV_ARGC_RESTORE: i64 = 4 31 32func ev_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 33func ev_werr(s: *u8) -> i64 { sys_write(EV_STDERR, s, ev_slen(s)); return 0 } 34func ev_cat(d: *u8, off: i64, s: *u8) -> i64 { 35 var o: i64 = off 36 var j: i64 = 0 37 while s[j] != (0 as u8) { d[o] = s[j]; o = o + 1; j = j + 1 } 38 return o 39} 40func ev_eq(a: *u8, b: *u8) -> i64 { 41 var i: i64 = 0 42 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 43 if b[i] != (0 as u8) { return 0 } 44 return 1 45} 46func ev_has(s: *u8, sub: *u8) -> i64 { 47 let n: i64 = ev_slen(s) 48 let m: i64 = ev_slen(sub) 49 if m == 0 { return 0 } 50 var i: i64 = 0 51 while i + m <= n { 52 var j: i64 = 0 53 var ok: i64 = 1 54 while j < m { if s[i+j] != sub[j] { ok = 0; j = m } else { j = j + 1 } } 55 if ok == 1 { return 1 } 56 i = i + 1 57 } 58 return 0 59} 60// 1 = refused (fail-safe over-deny), 0 = allowed 61func ev_deny(p: *u8) -> i64 { 62 if p[0] == (EV_SLASH as u8) { return 1 } 63 if ev_has(p, ".." as *u8) == 1 { return 1 } 64 if ev_has(p, "secret" as *u8) == 1 { return 1 } 65 if ev_has(p, "key" as *u8) == 1 { return 1 } 66 if ev_has(p, "token" as *u8) == 1 { return 1 } 67 if ev_has(p, "passw" as *u8) == 1 { return 1 } 68 if ev_has(p, ".pem" as *u8) == 1 { return 1 } 69 if ev_has(p, "opaque" as *u8) == 1 { return 1 } 70 if ev_has(p, ".elf" as *u8) == 1 { return 1 } 71 if ev_has(p, "retired/" as *u8) == 1 { return 1 } 72 if ev_eq(p, "tool_allowlist.conf" as *u8) == 1 { return 1 } 73 if ev_eq(p, "daemons.reg" as *u8) == 1 { return 1 } 74 if ev_eq(p, "clock_jobs.tsv" as *u8) == 1 { return 1 } 75 if ev_eq(p, "proxy_routes.conf" as *u8) == 1 { return 1 } 76 if ev_eq(p, "sites.conf" as *u8) == 1 { return 1 } 77 return 0 78} 79// dst = "retired/" + src with each '/' flattened to "__" 80func ev_flat(src: *u8, dst: *u8) -> i64 { 81 var o: i64 = ev_cat(dst, 0, "retired/" as *u8) 82 var i: i64 = 0 83 while src[i] != (0 as u8) { 84 if src[i] == (EV_SLASH as u8) { dst[o] = EV_UND as u8; o = o + 1; dst[o] = EV_UND as u8; o = o + 1 } 85 else { dst[o] = src[i]; o = o + 1 } 86 i = i + 1 87 } 88 dst[o] = 0 as u8 89 return o 90} 91// file exists? (openat_rd probe) 92func ev_exists(p: *u8) -> i64 { 93 let fd: i64 = sys_openat_rd(p) 94 if fd < 0 { return 0 } 95 sys_close(fd) 96 return 1 97} 98 99func main(argc: i64, argv: *i64) -> i64 { 100 if argc < 2 { ev_werr("usage: nx_evict {evict <relpath> | restore <name> <destpath> | selftest}\n" as *u8); sys_exit(EV_EXIT_USAGE); return EV_EXIT_USAGE } 101 let v: *u8 = argv[1] as *u8 102 103 if v[0] == (EV_V_S as u8) { 104 var ok: i64 = 1 105 if ev_deny("tool_allowlist.conf" as *u8) == 0 { ok = 0 } 106 if ev_deny("../up.txt" as *u8) == 0 { ok = 0 } 107 if ev_deny("tools_cap_secret.key" as *u8) == 0 { ok = 0 } 108 if ev_deny("/etc/hosts" as *u8) == 0 { ok = 0 } 109 if ev_deny("nx_hostctl.elf" as *u8) == 0 { ok = 0 } 110 if ev_deny("mctest-seg-0.docs" as *u8) == 1 { ok = 0 } 111 if ok == 0 { ev_werr("EV-SELFTEST FAIL verdict=RED deny-teeth\n" as *u8); sys_exit(EV_EXIT_SELF); return EV_EXIT_SELF } 112 let fd: i64 = sys_openat_wr("evict_probe.tmp" as *u8, EV_MODE) 113 if fd < 0 { ev_werr("EV-SELFTEST FAIL verdict=RED probe-create\n" as *u8); sys_exit(EV_EXIT_SELF); return EV_EXIT_SELF } 114 sys_write(fd, "x" as *u8, 1) 115 sys_close(fd) 116 if sys_renameat("evict_probe.tmp" as *u8, "retired/evict_probe.tmp" as *u8) < 0 { ev_werr("EV-SELFTEST FAIL verdict=RED evict\n" as *u8); sys_exit(EV_EXIT_SELF); return EV_EXIT_SELF } 117 if ev_exists("retired/evict_probe.tmp" as *u8) == 0 { ev_werr("EV-SELFTEST FAIL verdict=RED evict-verify\n" as *u8); sys_exit(EV_EXIT_SELF); return EV_EXIT_SELF } 118 if sys_renameat("retired/evict_probe.tmp" as *u8, "evict_probe.tmp" as *u8) < 0 { ev_werr("EV-SELFTEST FAIL verdict=RED restore\n" as *u8); sys_exit(EV_EXIT_SELF); return EV_EXIT_SELF } 119 if ev_exists("evict_probe.tmp" as *u8) == 0 { ev_werr("EV-SELFTEST FAIL verdict=RED restore-verify\n" as *u8); sys_exit(EV_EXIT_SELF); return EV_EXIT_SELF } 120 if sys_renameat("evict_probe.tmp" as *u8, "retired/evict_probe.tmp" as *u8) < 0 { ev_werr("EV-SELFTEST FAIL verdict=RED re-evict\n" as *u8); sys_exit(EV_EXIT_SELF); return EV_EXIT_SELF } 121 ev_werr("EV-SELFTEST OK verdict=GREEN\n" as *u8) 122 sys_exit(0) 123 return 0 124 } 125 126 if v[0] == (EV_V_E as u8) { 127 if argc < EV_ARGC_EVICT { ev_werr("evict needs <relpath>\n" as *u8); sys_exit(EV_EXIT_USAGE); return EV_EXIT_USAGE } 128 let p: *u8 = argv[2] as *u8 129 if ev_deny(p) == 1 { ev_werr("EV-REFUSED (deny-by-construction: absolute/traversal/secret/elf/protected)\n" as *u8); sys_exit(EV_EXIT_REFUSED); return EV_EXIT_REFUSED } 130 if ev_exists(p) == 0 { ev_werr("EV-FAIL src absent\n" as *u8); sys_exit(EV_EXIT_IO); return EV_EXIT_IO } 131 let dst: *u8 = sys_mmap(EV_PATHCAP) 132 ev_flat(p, dst) 133 if ev_exists(dst) == 1 { ev_werr("EV-REFUSED retired name already exists (no clobber)\n" as *u8); sys_exit(EV_EXIT_REFUSED); return EV_EXIT_REFUSED } 134 if sys_renameat(p, dst) < 0 { ev_werr("EV-FAIL rename\n" as *u8); sys_exit(EV_EXIT_IO); return EV_EXIT_IO } 135 ev_werr("EV-OK evicted -> " as *u8) 136 ev_werr(dst) 137 ev_werr("\n" as *u8) 138 sys_exit(0) 139 return 0 140 } 141 142 if v[0] == (EV_V_R as u8) { 143 if argc < EV_ARGC_RESTORE { ev_werr("restore needs <name> <destpath>\n" as *u8); sys_exit(EV_EXIT_USAGE); return EV_EXIT_USAGE } 144 let nm: *u8 = argv[2] as *u8 145 let dp: *u8 = argv[3] as *u8 146 if ev_has(nm, "/" as *u8) == 1 { ev_werr("EV-REFUSED name must be bare (no /)\n" as *u8); sys_exit(EV_EXIT_REFUSED); return EV_EXIT_REFUSED } 147 if ev_has(nm, ".." as *u8) == 1 { ev_werr("EV-REFUSED traversal\n" as *u8); sys_exit(EV_EXIT_REFUSED); return EV_EXIT_REFUSED } 148 if ev_deny(dp) == 1 { ev_werr("EV-REFUSED dest (deny-by-construction)\n" as *u8); sys_exit(EV_EXIT_REFUSED); return EV_EXIT_REFUSED } 149 if ev_exists(dp) == 1 { ev_werr("EV-REFUSED dest exists (no clobber)\n" as *u8); sys_exit(EV_EXIT_REFUSED); return EV_EXIT_REFUSED } 150 let sp: *u8 = sys_mmap(EV_PATHCAP) 151 var o: i64 = ev_cat(sp, 0, "retired/" as *u8) 152 o = ev_cat(sp, o, nm) 153 sp[o] = 0 as u8 154 if ev_exists(sp) == 0 { ev_werr("EV-FAIL retired name absent\n" as *u8); sys_exit(EV_EXIT_IO); return EV_EXIT_IO } 155 if sys_renameat(sp, dp) < 0 { ev_werr("EV-FAIL rename\n" as *u8); sys_exit(EV_EXIT_IO); return EV_EXIT_IO } 156 ev_werr("EV-OK restored\n" as *u8) 157 sys_exit(0) 158 return 0 159 } 160 161 ev_werr("usage: nx_evict {evict <relpath> | restore <name> <destpath> | selftest}\n" as *u8) 162 sys_exit(EV_EXIT_USAGE) 163 return EV_EXIT_USAGE 164}