gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1543Shell 203Rust 109Markdown 993728 Definitions

⎇ 4eecba06f4847b68b307acf232a1fc04f87b86f6 scripts/harkaq/harkaq-exec.c C · 159 lines · 6.9 KB History Who touched what raw

harkaq: Fase 1 — la cadena corre de punta a punta (Hermetico / Impuro sobre kernel real) 4eecba06f · sergio ·

1// harkaq-exec — aplica la política y ejecuta el builder (SDD 16 §4). Corre DENTRO de bwrap,
2// como último eslabón antes del `sh -c` del build: bwrap ya puso los namespaces, el overlay y
3// la ausencia de red; esto pone el grano fino y —lo que importa— abre el canal de evidencia.
4//
5//   harkaq-exec --policy <fichero> [--abi-min 3] -- <cmd> [args...]
6//
7// Fichero de política (lo genera harkaq-policy a partir de la clausura; D1: la política se
8// DERIVA, no se escribe):
9//   ro /store/<hash>-zlib
10//   rw /out
11//
12// Estático a propósito: corre dentro del rootfs Alpine (musl) del sandbox.
13//
14// D7 (fallo cerrado, honesto sobre el ABI):
15//   ABI < min      → RECHAZA el build (sin REFER/TRUNCATE no compila software real)
16//   min <= ABI < 7 → corre, pero avisa SIN EVIDENCIA (no hay audit de denegaciones)
17//   ABI >= 7       → corre con LOG_NEW_EXEC_ON: la evidencia existe
18// Nunca se afirma evidencia que el kernel no respaldó.
19
20#define _GNU_SOURCE
21#include <fcntl.h>
22#include <linux/landlock.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/prctl.h>
27#include <sys/syscall.h>
28#include <unistd.h>
29
30static int ll_create(const struct landlock_ruleset_attr *a, size_t n, __u32 f) {
31    return syscall(SYS_landlock_create_ruleset, a, n, f);
32}
33static int ll_add(int fd, enum landlock_rule_type t, const void *a, __u32 f) {
34    return syscall(SYS_landlock_add_rule, fd, t, a, f);
35}
36static int ll_restrict(int fd, __u32 f) {
37    return syscall(SYS_landlock_restrict_self, fd, f);
38}
39
40// Conjuntos de derechos por ABI. Cada uno AÑADE a los anteriores; recortamos al ABI real del
41// kernel (D7 / §3.1: se consulta el ABI por syscall, JAMÁS por versión de kernel — hay
42// backports y el número miente).
43#define FS_ABI1                                                              \
44    (LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_WRITE_FILE |            \
45     LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR |            \
46     LANDLOCK_ACCESS_FS_REMOVE_DIR | LANDLOCK_ACCESS_FS_REMOVE_FILE |        \
47     LANDLOCK_ACCESS_FS_MAKE_CHAR | LANDLOCK_ACCESS_FS_MAKE_DIR |            \
48     LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_MAKE_SOCK |            \
49     LANDLOCK_ACCESS_FS_MAKE_FIFO | LANDLOCK_ACCESS_FS_MAKE_BLOCK |          \
50     LANDLOCK_ACCESS_FS_MAKE_SYM)
51#define FS_ABI2 (FS_ABI1 | LANDLOCK_ACCESS_FS_REFER)
52#define FS_ABI3 (FS_ABI2 | LANDLOCK_ACCESS_FS_TRUNCATE)
53#define FS_ABI5 (FS_ABI3 | LANDLOCK_ACCESS_FS_IOCTL_DEV)
54
55#define RO_RIGHTS \
56    (LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_EXECUTE)
57
58struct rule {
59    char path[4096];
60    int rw;
61};
62
63int main(int argc, char **argv) {
64    const char *policy = NULL;
65    int abi_min = 3, i;
66    for (i = 1; i < argc; i++) {
67        if (!strcmp(argv[i], "--policy") && i + 1 < argc) policy = argv[++i];
68        else if (!strcmp(argv[i], "--abi-min") && i + 1 < argc) abi_min = atoi(argv[++i]);
69        else if (!strcmp(argv[i], "--")) { i++; break; }
70    }
71    if (!policy || i >= argc) {
72        fprintf(stderr, "uso: harkaq-exec --policy <f> [--abi-min N] -- <cmd> [args...]\n");
73        return 2;
74    }
75    char **cmd = &argv[i];
76
77    int abi = ll_create(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
78    if (abi < abi_min) {
79        // Fallo CERRADO: no correr un build que creemos enjaulado y no lo está.
80        fprintf(stderr,
81                "[harkaq] RECHAZO: ABI de Landlock = %d, se exige >= %d.\n"
82                "         Sin REFER/TRUNCATE no compila software real y la jaula sería ficción.\n",
83                abi, abi_min);
84        return 4;
85    }
86
87    __u64 handled = abi >= 5 ? FS_ABI5 : abi >= 3 ? FS_ABI3 : abi >= 2 ? FS_ABI2 : FS_ABI1;
88    struct landlock_ruleset_attr attr = {.handled_access_fs = handled};
89    int rs = ll_create(&attr, sizeof(attr), 0);
90    if (rs < 0) { perror("[harkaq] create_ruleset"); return 4; }
91
92    FILE *f = fopen(policy, "r");
93    if (!f) { perror("[harkaq] abrir política"); return 4; }
94    char line[4200];
95    int n_ro = 0, n_rw = 0;
96    while (fgets(line, sizeof(line), f)) {
97        char *nl = strchr(line, '\n');
98        if (nl) *nl = 0;
99        if (!line[0] || line[0] == '#') continue;
100        int rw;
101        char *p;
102        if (!strncmp(line, "ro ", 3)) { rw = 0; p = line + 3; }
103        else if (!strncmp(line, "rw ", 3)) { rw = 1; p = line + 3; }
104        else {
105            fprintf(stderr, "[harkaq] política inválida: %s\n", line);
106            return 4;
107        }
108        int dfd = open(p, O_PATH | O_CLOEXEC);
109        if (dfd < 0) {
110            // Una entrada de la clausura que no existe es un bug de harkaq-policy, no del
111            // build. Fallar ruidoso: si la ignoráramos, la política sería MÁS chica que la
112            // clausura y el build denegaría cosas legítimas — falsos positivos que matan
113            // el proyecto (§7).
114            fprintf(stderr, "[harkaq] la política nombra un path inexistente: %s (%m)\n", p);
115            return 4;
116        }
117        struct landlock_path_beneath_attr pb = {.allowed_access = rw ? handled : RO_RIGHTS,
118                                                .parent_fd = dfd};
119        // REFER sólo se concede sobre directorios; sobre un fichero suelto el kernel rechaza
120        // la regla entera con EINVAL. Reintentamos sin él antes de darlo por perdido.
121        if (ll_add(rs, LANDLOCK_RULE_PATH_BENEATH, &pb, 0) < 0) {
122            pb.allowed_access &= ~(__u64)LANDLOCK_ACCESS_FS_REFER;
123            if (ll_add(rs, LANDLOCK_RULE_PATH_BENEATH, &pb, 0) < 0) {
124                fprintf(stderr, "[harkaq] add_rule(%s): %m\n", p);
125                close(dfd);
126                return 4;
127            }
128        }
129        close(dfd);
130        rw ? n_rw++ : n_ro++;
131    }
132    fclose(f);
133
134    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { perror("[harkaq] no_new_privs"); return 4; }
135
136    // LOG_NEW_EXEC_ON: el flag del que cuelga TODO. harkaq restringe y DESPUÉS ejecuta el
137    // builder; sin este flag el kernel no loguea ninguna denegación posterior al execve y
138    // harkaq certificaría como herméticos todos los builds, para siempre, sin un solo error
139    // visible (medido: §3.4). No es configuración, es la diferencia entre el sistema y un
140    // sello de goma.
141    __u32 flags = 0;
142    if (abi >= 7) flags |= LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;
143    if (abi >= 8) flags |= LANDLOCK_RESTRICT_SELF_TSYNC;  // D5: cubre todos los hilos
144    if (ll_restrict(rs, flags) < 0) { perror("[harkaq] restrict_self"); return 4; }
145    close(rs);
146
147    if (abi < 7)
148        fprintf(stderr,
149                "[harkaq] AVISO: ABI %d < 7 ⇒ el kernel no audita denegaciones. El build corre "
150                "ENJAULADO pero SIN EVIDENCIA; el veredicto no podrá afirmar hermeticidad.\n",
151                abi);
152    else
153        fprintf(stderr, "[harkaq] jaula puesta: ABI %d, %d ro + %d rw, logging post-exec ON\n",
154                abi, n_ro, n_rw);
155
156    execvp(cmd[0], cmd);
157    fprintf(stderr, "[harkaq] execvp %s: %m\n", cmd[0]);
158    return 127;
159}