#!/bin/sh # Prueba del bloque REAL de liberación de symlinks: se extrae del propio instalador-variantes.sh # (no una copia) y se corre contra un árbol de juguete. Lo que se mide es si `bin/su` de shadow # llega entero al árbol compuesto en vez de quedar tapado por el symlink a busybox del piso. set -eu cd "$(cd "$(dirname "$0")/.." && pwd)" W=/tmp/probar-fusion.$$ trap 'rm -rf "$W"' EXIT C="$W/C"; H="$W/H" mkdir -p "$C/bin" "$C/usr/bin" "$H/bin" "$H/usr/bin" # El piso: busybox y sus symlinks (lo que trae product-rootfs). printf 'soy busybox\n' > "$C/bin/busybox"; chmod 755 "$C/bin/busybox" ln -s busybox "$C/bin/su" ln -s ../../bin/busybox "$C/usr/bin/passwd" ln -s busybox "$C/bin/ls" # un applet SIN [[setuid]]: NO debe tocarse # El perfil: los binarios de verdad, ya con su bit (como los deja `setuid --apply`). printf 'soy el su de shadow\n' > "$H/bin/su"; chmod 4755 "$H/bin/su" printf 'soy el passwd de shadow\n' > "$H/usr/bin/passwd"; chmod 4755 "$H/usr/bin/passwd" printf 'soy el ls de uutils\n' > "$H/bin/ls"; chmod 755 "$H/bin/ls" p=base # ⚠ Sin el binario del verbo no hay nada que medir: el bloque pregunta las rutas declaradas con # `takana setuid --json`. Se SALTA diciéndolo (exit 2), que no es lo mismo que pasar. [ -x ./target/release/takana ] || { echo " ⊘ SALTADO: falta ./target/release/takana — el bloque no puede preguntar las declaraciones" exit 2 } # ── el bloque REAL, extraído del script que va a correr ──────────────────────────────────────── sed -n '/^ _recetas_p=""/,/^ \[ "\$_liberados" -gt 0 \]/p' scripts/instalador-variantes.sh > "$W/bloque.sh" [ -s "$W/bloque.sh" ] || { echo "✗ no pude extraer el bloque del armador"; exit 1; } echo " (bloque extraído: $(wc -l < "$W/bloque.sh") líneas)" # `set +e` alrededor del bloque: su última línea es un `[ ... ] && echo`, que devuelve 1 cuando no # libera nada, y con `set -e` eso mataba el test antes de comprobar una sola cosa. Un test que muere # en silencio pasa por verde. set +e . "$W/bloque.sh" set -e # ── y la copia que viene después, igual que en el armador ────────────────────────────────────── ( cd "$H" && find . -type d -exec mkdir -p "$C/{}" \; ) 2>/dev/null || true ( cd "$H" && find . ! -type d -exec cp -a -l -n {} "$C/{}" \; ) 2>/dev/null || true echo "══ cómo quedó el árbol compuesto ══" fallo=0 for b in bin/su usr/bin/passwd; do if [ -L "$C/$b" ]; then echo " ✗ $b sigue siendo un symlink a $(readlink "$C/$b")"; fallo=1 elif [ "$(stat -c%a "$C/$b")" = 4755 ] && grep -q shadow "$C/$b"; then echo " ✓ $b es el binario de shadow, 4755" else echo " ✗ $b quedó raro: $(stat -c%A "$C/$b")"; fallo=1; fi done # El brazo de control: un applet sin declaración NO se toca. Si se tocara, este arreglo habría # dejado de estar acotado y estaría reemplazando los 331 symlinks sin medición. if [ -L "$C/bin/ls" ] && [ "$(readlink "$C/bin/ls")" = busybox ]; then echo " ✓ bin/ls (sin [[setuid]]) sigue siendo el applet: el arreglo está acotado" else echo " ✗ bin/ls se tocó, y no tiene declaración de privilegio"; fallo=1 fi [ -f "$C/bin/busybox" ] || { echo " ✗ se perdió el busybox del piso"; fallo=1; } [ "$fallo" = 0 ] && echo "✓✓ la fusión deja pasar el binario privilegiado y no toca lo demás" || exit 1