← Sistemas de Archivos y E/SBloque 8 · Permisos y protección · Tema 28 de 50

Tema 28

setuid, sticky, ACLs y atributos extendidos

El modo rwx es solo una parte de la protección de un archivo. Estudiaremos los bits especiales, las listas que conceden permisos a identidades concretas y los metadatos adicionales del sistema de archivos. Dos programas en C, un laboratorio con objetos propios y dos simuladores permitirán distinguir identidad al ejecutar, herencia de grupo, restricciones sobre nombres y permisos efectivos de una ACL.

1. Mecanismos distintos sobre un mismo objeto

En el tema 27 cambiamos modo, propietario y grupo, y calculamos umask. Ahora ampliaremos ese modelo. Antes de interpretar un resultado conviene identificar qué mecanismo interviene y sobre qué operación actúa.

MecanismoPregunta que respondeConsulta habitual en Linux
setuid / setgid del ejecutable¿Cambian las identidades efectivas al ejecutar?stat, ls -l y credenciales del proceso.
setgid del directorio¿Qué grupo heredarán los objetos nuevos?stat del padre y del objeto creado.
Sticky del directorio¿Quién puede retirar o renombrar una entrada?Modo y propietarios del directorio y del objeto.
ACL¿Qué permisos corresponden a usuarios y grupos concretos?getfacl.
xattr¿Qué pares nombre/valor adicionales están asociados?getfattr o interfaces getxattr.
Flags del sistema de archivos¿Existen restricciones como inmutable?lsattr.

Ampliar la protección no significa sumar todos los mecanismos como permisos concedidos. Una ACL participa en el control discrecional; un flag inmutable o una política de seguridad puede impedir una operación aunque ese control la permita.

Trabajaremos con Linux y las ACL de tipo POSIX que ofrecen sus herramientas habituales. Otros sistemas y las ACL de NFSv4 o Windows tienen modelos diferentes; sus reglas no se deducen trasladando literalmente los ejemplos.

2. Los tres bits especiales

Delante de las tres cifras rwx puede aparecer una cifra que combina setuid, setgid y sticky. La suma se hace entre bits de esa cifra: 4, 2 y 1 respectivamente. El tipo de objeto continúa siendo un campo distinto del modo mostrado aquí.

modo · cuatro cifras octales
4000  setuid       S_ISUID
2000  setgid       S_ISGID
1000  sticky       S_ISVTX

4755  -rwsr-xr-x   ejecutable con setuid y x del propietario
2750  drwxr-s---   directorio con setgid y búsqueda del grupo
1777  drwxrwxrwt   directorio compartido con sticky
4700  -rws------   setuid y rwx del propietario
4600  -rwS------   setuid presente, sin x del propietario
1766  drwxrw-rwT   sticky presente, sin x de otros

La s reemplaza visualmente la x del propietario o del grupo si ambos bits están presentes; la S indica que falta esa x. Para sticky sucede lo mismo con t/T en la posición de otros. La mayúscula no significa «más privilegio»: informa de la ausencia de ejecución o búsqueda en esa posición.

terminal · sintaxis sobre objetos propios del laboratorio
chmod u+s programa      # añadir setuid
chmod g+s equipo        # añadir setgid
chmod +t compartida     # añadir sticky
chmod u-s,g-s programa  # retirar los dos bits s
chmod -t compartida     # retirar sticky

Consultar el modo después de cambiarlo es parte de la operación. Un cambio de propietario o una escritura sobre un ejecutable puede limpiar bits especiales según las credenciales y el sistema. No hay que suponer que una marca consultada antes siga presente después.

3. setuid y setgid al ejecutar

En un ejecutable binario, setuid solicita que el UID efectivo pase al propietario del archivo; setgid hace lo correspondiente con el GID efectivo. No eligen necesariamente root: el resultado depende de los UID/GID del objeto. Los identificadores reales y los grupos suplementarios no cambian por ese mecanismo.

Linux suprime estos cambios en situaciones como no_new_privs, montajes nosuid o determinados casos de trazado. Además, ignora setuid/setgid en scripts de intérprete. Que ls muestre s no demuestra que una ejecución haya adquirido otra identidad; hay que observar las credenciales y el entorno. Véase execve(2).

ejemplo conceptual · binario propiedad de UID 1200
Antes: UID real=1000, UID efectivo=1000
Archivo: UID propietario=1200, modo=4755
exec satisfactorio con transición permitida:
Después: UID real=1000, UID efectivo=1200

Si el archivo pertenece al propio UID 1000,
setuid no produce una identidad efectiva diferente.
Diseño de programas privilegiados

El entorno, las rutas y los descriptores heredados requieren un tratamiento específico. Este laboratorio observa un binario propio y no necesita cambiar su propietario a root. Poner s en un programa no lo convierte automáticamente en una herramienta de administración correcta.

4. setgid en directorios

En un directorio Linux, setgid hace que los objetos nuevos hereden su grupo propietario. Los subdirectorios nuevos heredan también setgid. Eso facilita mantener un grupo común en un árbol de trabajo; no agrega usuarios a ese grupo ni concede automáticamente escritura.

política de equipo · piezas que cumplen tareas diferentes
Directorio: GID=equipo, modo=2770
    setgid       → herencia del GID en objetos nuevos
    g+rwx        → acceso del grupo al directorio
    umask o ACL  → permisos iniciales de los hijos
    pertenencia  → credenciales vigentes de cada proceso

Un archivo creado con petición 0600 puede heredar el GID del directorio y seguir siendo privado. Tampoco se cambian los grupos de los hijos que ya existían. Las reglas de creación de directorios están documentadas en mkdir(2).

setuid sobre un directorio no tiene en Linux la interpretación equivalente de «heredar UID»; no debe utilizarse como sustituto de una política de propiedad. La semántica depende del tipo de objeto, no solo de la cifra octal.

5. Sticky y protección de entradas

Escritura y búsqueda sobre el directorio permiten normalmente retirar entradas. Sticky añade una restricción: en Linux, quien retira una entrada debe ser propietario del archivo, propietario del directorio o disponer del privilegio correspondiente. Las comprobaciones ordinarias del directorio siguen siendo necesarias.

El ejemplo típico es un directorio 1777: varias personas pueden crear entradas y sticky limita que retiren las ajenas. No impide leer o modificar el contenido si los permisos del archivo lo permiten. Linux no asigna al sticky de un archivo regular esa protección de entradas.

decisión simplificada · sin capacidades ni otras restricciones
¿Hay escritura y búsqueda en el directorio padre?
    no → denegar
    sí → ¿el directorio tiene sticky?
             no → permite esta fase
             sí → ¿soy dueño del archivo o del directorio?
                       sí → permite esta fase
                       no → denegar

Renombrar puede involucrar dos directorios y un destino que ya existe; se comprueban las restricciones que correspondan a cada lado. La referencia de unlink(2) detalla la eliminación. Aprobar esta fase no descarta un montaje de solo lectura o un objeto inmutable.

6. ACL de acceso y entradas

Una ACL de acceso permite expresar permisos para usuarios y grupos adicionales. Sus entradas básicas son propietario, grupo propietario y otros; una ACL extendida incorpora identidades nombradas y una máscara.

getfacl · ejemplo de ACL extendida
user::rw-
user:1201:rw-
group::r--
group:2200:rwx
mask::r-x
other::---

Los campos vacíos de user:: y group:: designan al propietario y al grupo del archivo. Un número tras user: o group: identifica otra entrada por UID o GID. Son permisos concedidos, no una secuencia de reglas allow/deny como la de otros modelos.

getfacl muestra la lista y las limitaciones efectivas; -n conserva identificadores numéricos y -e pide mostrar todos los comentarios de derechos efectivos. Con una ACL extendida, la terna de grupo presentada por ls corresponde a la máscara. El signo + habitual de ls invita a inspeccionar la ACL completa, no a adivinarla. Véase getfacl(1).

7. Máscara y permisos efectivos

La entrada mask limita al usuario nombrado, al grupo propietario y a los grupos nombrados. No limita user:: ni other::. Por ejemplo, rw- intersectado con r-x queda r--. No debe confundirse esta máscara del objeto con la umask del proceso.

ejemplo anterior · intersección por bits
user:1201:rw-  & mask::r-x → r--
group::r--    & mask::r-x → r--
group:2200:rwx & mask::r-x → r-x
user::rw-                 → rw- (sin máscara)
other::---                → --- (sin máscara)

setfacl -m modifica o incorpora entradas y -x las elimina. Normalmente recalcula la máscara como unión de los permisos de la clase de grupo, salvo que se indique una máscara explícita; -n evita ese recálculo. Por eso añadir una entrada puede ampliar los derechos efectivos de otra que ya estaba limitada.

terminal · sintaxis con UID ilustrativo
setfacl -m u:1201:rw-,m::r-- nota
getfacl -n -e nota
setfacl -n -m u:1201:rwx nota  # conserva máscara actual
getfacl -n -e nota
setfacl -x u:1201 nota        # puede recalcular máscara
setfacl -b nota               # elimina entradas extendidas

Estas órdenes actúan sobre la ACL de acceso; no eliminan por sí solas una ACL predeterminada del directorio, que tiene su operación específica -k. La sintaxis y las reglas de recálculo se detallan en setfacl(1).

Con ACL extendida, chmod g= modifica la máscara: puede restringir permisos efectivos de varios usuarios sin borrar sus entradas. Hay que volver a consultar la lista después de mezclar chmod y setfacl.

8. Cómo se selecciona una entrada

La selección evita combinar libremente todas las entradas. El propietario usa user::. Si existe una entrada de usuario nombrado coincidente, se usa esa entrada limitada por mask y no se continúa buscando una alternativa más favorable.

  1. Propietario coincidente.Comprobar la petición contra user::, sin máscara.
  2. Usuario nombrado coincidente.Comprobar su entrada intersectada con mask.
  3. Uno o más grupos coincidentes.Conceder si alguna entrada de grupo coincidente, limitada por mask, contiene toda la petición.
  4. Ningún grupo coincide.Usar other::, sin máscara.

Si hay coincidencia de grupo y ninguna entrada satisface la petición completa, se deniega sin recurrir a otros. Para una petición conjunta rw, una entrada r-- y otra -w- no la satisfacen individualmente. Dos comprobaciones separadas de lectura y escritura no equivalen a esa petición conjunta.

Este es el algoritmo de ACL descrito en acl(5). Los simuladores no incluyen capacidades, políticas de seguridad ni la resolución de los directorios de la ruta.

9. ACL predeterminadas y creación

Un directorio puede tener una ACL de acceso para sí mismo y otra predeterminada como plantilla para los hijos nuevos. Las líneas default: de getfacl describen la segunda. Cambiar la plantilla no reescribe los objetos existentes.

Con ACL predeterminada, Linux deriva la ACL de acceso del nuevo objeto de esa plantilla y limita los permisos correspondientes a las clases del modo solicitado. No se aplica además la fórmula simple petición & ~umask. Sin esa plantilla, vuelve a intervenir umask; véase umask(2).

creación · plantilla que se comprobará en el laboratorio
default:user::rwx
default:group::rwx
default:mask::rwx
default:other::---

Petición 0666 → modo 0660; la máscara de acceso queda rw-
Petición 0600 → modo 0600; la máscara de acceso queda ---
Petición 0777 → modo 0770; la máscara de acceso queda rwx

Un subdirectorio hereda además la plantilla para sus futuras creaciones. En un regular pedido sin ejecución, la máscara puede ocultar x que permanezca escrita en una entrada de grupo: se debe leer el permiso efectivo. setgid y ACL predeterminada resuelven partes diferentes de la política: grupo heredado y permisos heredados.

10. Atributos extendidos

Los xattr son pares nombre/valor asociados al objeto, separados de sus bytes de contenido. El valor puede ser binario y estar vacío; no es necesariamente una cadena terminada en cero. Sus límites y soporte dependen del sistema de archivos.

Espacio de nombresUso orientativo
user.*Metadatos de aplicaciones, sujetos a permisos y tipo de objeto.
system.*Información del sistema; puede incluir representación de ACL.
security.*Etiquetas de seguridad y capacidades, con reglas específicas.
trusted.*Atributos cuyo acceso exige privilegios específicos.

Una aplicación que escribe user.clasificacion=privado agrega una etiqueta: el kernel no convierte esa palabra en una denegación de acceso. Tampoco se deben editar los xattr internos de ACL como si fueran notas de texto. Se utilizan sus interfaces especializadas. Véase xattr(7).

terminal · herramientas habituales del paquete attr
setfattr -n user.curso -v 'tema28' nota
getfattr -n user.curso nota
getfattr -d -m '^user\.' nota
setfattr -x user.curso nota

Los enlaces duros comparten los atributos del objeto. Copiar bytes a otro archivo crea una situación distinta: la herramienta debe decidir qué metadatos preserva y el destino debe poder representarlos.

11. lsattr, chattr y el atributo inmutable

Los flags consultados con lsattr son otro mecanismo. chattr +i solicita inmutabilidad: impide modificar contenido y restringe operaciones como borrar o renombrar el objeto. +a limita la escritura a anexar. No son bits rwx ni etiquetas user.*.

En Linux, establecer o retirar i y a requiere el privilegio correspondiente, como CAP_LINUX_IMMUTABLE; ser propietario ordinario no basta. El soporte depende del sistema de archivos. Un contenedor o incluso un proceso con UID 0 puede carecer de esa capacidad. Véase chattr(1).

administración · secuencia conceptual en un entorno de práctica autorizado
lsattr archivo-propio
chattr +i archivo-propio   # requiere capacidad y soporte
lsattr archivo-propio
chattr -i archivo-propio   # retirar la restricción al terminar

El laboratorio obligatorio no necesita activar este flag. Si se hace la prueba opcional en una máquina administrada por el alumno, se conserva la ruta exacta del objeto de ensayo y se retira i antes de limpiar. Un error de soporte no se interpreta como una aplicación exitosa.

12. Programa en C: modo y credenciales

Guarda este programa como inspeccionar-especiales.c. Consulta un objeto con lstat, imprime su modo de cuatro cifras y muestra las identidades reales y efectivas del proceso. lstat observa el enlace simbólico final, si lo hay; no sus destinos. Es una fotografía de atributos, no una decisión completa de acceso.

inspeccionar-especiales.c · Linux
#define _DEFAULT_SOURCE
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <inttypes.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    struct stat st;
    if (argc != 2) {
        fprintf(stderr, "Uso: %s ruta\n", argv[0]);
        return 2;
    }
    if (lstat(argv[1], &st) == -1) {
        perror("lstat");
        return 1;
    }
    printf("UID real=%ju efectivo=%ju\n",
           (uintmax_t)getuid(), (uintmax_t)geteuid());
    printf("GID real=%ju efectivo=%ju\n",
           (uintmax_t)getgid(), (uintmax_t)getegid());
    printf("Objeto: UID=%ju GID=%ju modo=%04jo\n",
           (uintmax_t)st.st_uid, (uintmax_t)st.st_gid,
           (uintmax_t)(st.st_mode & 07777));
    printf("setuid=%d setgid=%d sticky=%d\n",
           !!(st.st_mode & S_ISUID), !!(st.st_mode & S_ISGID),
           !!(st.st_mode & S_ISVTX));
    if (S_ISDIR(st.st_mode))
        puts("Directorio: interpretar setgid y sticky sobre entradas.");
    else if (S_ISREG(st.st_mode))
        puts("Regular: bits s no demuestran una transición al ejecutar.");
    else
        puts("Otro tipo: no aplicar automáticamente la regla del regular.");
    return 0;
}
terminal · compilar y observar el propio binario
$ cc -std=c11 -Wall -Wextra -Wpedantic -O2 inspeccionar-especiales.c -o inspeccionar-especiales
$ ./inspeccionar-especiales ./inspeccionar-especiales
$ chmod u+s ./inspeccionar-especiales
$ ./inspeccionar-especiales ./inspeccionar-especiales
$ chmod u-s ./inspeccionar-especiales

Si el ejecutor y el propietario ya tienen el mismo UID, ambas identidades seguirán coincidiendo. La prueba comprueba observación y representación; no demuestra una elevación. Un montaje noexec puede impedir ejecutar el binario aunque el compilador lo haya generado correctamente.

La distinción entre stat, lstat y fstat se documenta en stat(2). Consultar una ruta ahora no garantiza que una operación posterior resuelva el mismo objeto: esa carrera será el centro del tema 29.

13. Programa en C: escribir y leer un xattr

Guarda el segundo ejemplo como xattr-propio.c. Crea un regular nuevo con O_EXCL en una ruta de laboratorio de confianza, escribe un atributo user.curso y lo lee por el descriptor. No sustituye un objeto ya existente. El archivo queda conservado para inspección, incluso si una operación posterior falla.

xattr-propio.c · valor binario y referencia por descriptor
#define _POSIX_C_SOURCE 200809L
#include <sys/types.h>
#include <sys/xattr.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
    const char nombre[] = "user.curso";
    const unsigned char valor[] = {'t', '2', '8', 0, 'C'};
    unsigned char buffer[64];
    int resultado = 1;
    if (argc != 2) {
        fprintf(stderr, "Uso: %s archivo-nuevo\n", argv[0]);
        return 2;
    }
    int fd = open(argv[1], O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
    if (fd == -1) {
        perror("open nuevo");
        return 1;
    }
    if (fsetxattr(fd, nombre, valor, sizeof valor, XATTR_CREATE) == -1) {
        perror("fsetxattr");
        goto fin;
    }
    ssize_t n = fgetxattr(fd, nombre, buffer, sizeof buffer);
    if (n == -1) {
        perror("fgetxattr");
        goto fin;
    }
    if ((size_t)n != sizeof valor || memcmp(buffer, valor, sizeof valor)) {
        fprintf(stderr, "Valor distinto del esperado\n");
        goto fin;
    }
    printf("%s: %zd bytes; hexadecimal:", nombre, n);
    for (ssize_t i = 0; i < n; ++i)
        printf(" %02x", (unsigned int)buffer[i]);
    putchar('\n');
    if (fremovexattr(fd, nombre) == -1) {
        perror("fremovexattr");
        goto fin;
    }
    errno = 0;
    if (fgetxattr(fd, nombre, buffer, sizeof buffer) != -1 || errno != ENODATA) {
        fprintf(stderr, "Se esperaba ENODATA tras retirar el atributo\n");
        goto fin;
    }
    puts("Atributo retirado; archivo conservado.");
    resultado = 0;
fin:
    if (close(fd) == -1) {
        perror("close");
        resultado = 1;
    }
    return resultado;
}

XATTR_CREATE exige que el atributo no exista; XATTR_REPLACE exige que ya exista, y cero permite establecerlo en ambos casos. Son condiciones del atributo, distintas de O_EXCL sobre el nombre de archivo. Véase setxattr(2).

El buffer fijo basta para los cinco bytes del ejemplo. Un lector general debe manejar ERANGE si el valor no cabe; consultar antes su tamaño no impide que cambie entre llamadas. ENODATA indica ausencia del atributo, no ausencia del archivo. Véase getxattr(2).

terminal · compilar y ejecutar con nombre nuevo
$ cc -std=c11 -Wall -Wextra -Wpedantic -O2 xattr-propio.c -o xattr-propio
$ ./xattr-propio dato-xattr
user.curso: 5 bytes; hexadecimal: 74 32 38 00 43
Atributo retirado; archivo conservado.

14. Laboratorio y comprobaciones

Usa Bash en Linux, GNU coreutils y una cuenta sin privilegios, sobre un sistema de archivos que admita ACL y user xattr. En WSL conviene usar el sistema de archivos Linux de la distribución. Se necesitan compilador C, Python 3 y herramientas de los paquetes acl y attr. Comprueba mkdir --version y chmod --version si tu distribución ofrece implementaciones alternativas. Los comandos se ejecutan por etapas: si una falla, se investiga antes de continuar.

Preparar un árbol privado

terminal · nombres nuevos y grupo propio
$ LAB28=$(mktemp -d "$HOME/tema28.XXXXXX")
$ cd -- "$LAB28"
$ id
$ command -v getfacl setfacl getfattr setfattr cc python3
$ setfacl -b .
$ setfacl -k .
$ chmod 700 .
$ mkdir equipo compartida
$ chgrp -- "$(id -gn)" equipo
$ chmod 2770 equipo
$ chmod 1777 compartida
$ (umask 027; touch equipo/nota; mkdir equipo/sub)
$ stat -c '%n uid=%u gid=%g modo=%a simbolico=%A' . equipo equipo/nota equipo/sub compartida

Sin ACL predeterminada en este árbol, se espera 0640 en nota y 2750 en sub. Ambos deben tener el GID de equipo. El padre privado impide el acceso de otras cuentas a compartida: 1777 permite estudiar el bit sin publicar un directorio de práctica al resto del sistema.

Como todos estos objetos son propios, borrar una entrada propia no demuestra que sticky impida borrar entradas ajenas. Esa parte se analiza con las reglas anteriores; una prueba real con varias identidades exige un entorno preparado para ellas.

Observar máscara de ACL y chmod

El UID 60001 se usa solo como identificador de una entrada de práctica, sin crear una cuenta. Si coincide con el UID propio, se elige 60002. El objetivo es consultar metadatos; no se está ejecutando un proceso con ese UID.

terminal · entrada nombrada limitada por máscara
$ ACL_UID28=60001; if [ "$(id -u)" = "$ACL_UID28" ]; then ACL_UID28=60002; fi
$ (umask 077; touch acl-nota)
$ setfacl -m "u:${ACL_UID28}:rw-,m::r--" acl-nota
$ getfacl -n -e acl-nota
$ chmod g=rw acl-nota
$ getfacl -n -e acl-nota
$ stat -c 'modo=%a simbolico=%A' acl-nota

Primero la entrada nombrada tiene rw- almacenado y r-- efectivo. Después chmod g=rw lleva la máscara a rw-, por lo que esa entrada obtiene escritura efectiva. No cambió la identidad propietaria ni se añadió una entrada nueva.

Comprobar herencia frente a umask

terminal · plantilla explícita y nombres nuevos
$ mkdir heredada
$ setfacl -d -m u::rwx,g::rwx,m::rwx,o::--- heredada
$ (umask 077; touch heredada/normal; mkdir heredada/sub)
$ python3 -c 'import os; os.umask(0o077); fd=os.open("heredada/privada", os.O_CREAT|os.O_EXCL|os.O_WRONLY, 0o600); os.close(fd)'
$ stat -c '%n modo=%a' heredada/normal heredada/privada heredada/sub
$ getfacl -n -e heredada/normal heredada/privada heredada/sub

Se esperan 0660, 0600 y 0770. La petición 0600 sigue limitando el archivo privada aunque la plantilla permita más. El subdirectorio debe mostrar líneas default: para sus futuros hijos.

Verificar atributos y programas

Guarda y compila los dos programas de las secciones anteriores dentro de LAB28. Ejecuta la prueba del binario propio, retira su setuid y ejecuta xattr-propio una sola vez con dato-xattr nuevo. A continuación conserva otro atributo para observarlo por dos nombres duros.

terminal · el atributo pertenece al objeto
$ setfattr -n user.curso -v 'tema28' acl-nota
$ ln acl-nota acl-alias
$ getfattr -n user.curso acl-nota acl-alias
$ ./inspeccionar-especiales equipo
$ ./inspeccionar-especiales compartida

Guarda como verificar-tema28.py el siguiente verificador y ejecútalo desde LAB28 después de las etapas anteriores. Comprueba estado real y contenido binario del ejemplo; no pretende probar una denegación entre usuarios diferentes.

verificar-tema28.py · comprobación del laboratorio
import errno
import os
from pathlib import Path
import stat
import subprocess

def modo(ruta):
    return stat.S_IMODE(Path(ruta).stat().st_mode)

assert os.geteuid() != 0, "Usar una cuenta ordinaria"
assert modo(".") == 0o700
assert modo("equipo") == 0o2770
assert modo("equipo/nota") == 0o640
assert modo("equipo/sub") == 0o2750
gid = Path("equipo").stat().st_gid
assert all(Path(p).stat().st_gid == gid
           for p in ("equipo/nota", "equipo/sub"))
assert modo("compartida") == 0o1777
assert modo("acl-nota") == 0o660
assert modo("heredada/normal") == 0o660
assert modo("heredada/privada") == 0o600
assert modo("heredada/sub") == 0o770
acl = subprocess.check_output(
    ["getfacl", "-c", "-n", "heredada/sub"], text=True)
assert "default:user::rwx" in acl
a, b = Path("acl-nota").stat(), Path("acl-alias").stat()
assert (a.st_dev, a.st_ino) == (b.st_dev, b.st_ino)
assert os.getxattr("acl-alias", "user.curso") == b"tema28"
assert Path("dato-xattr").stat().st_size == 0
try:
    os.getxattr("dato-xattr", "user.curso")
except OSError as exc:
    assert exc.errno == errno.ENODATA
else:
    raise AssertionError("El programa debía retirar user.curso")
assert not (modo("inspeccionar-especiales") & stat.S_ISUID)
print("Correctos: bits, grupo, ACL heredada, modo y xattr compartido")
terminal · verificar y conservar resultados
$ python3 verificar-tema28.py
$ printf 'Laboratorio conservado en %s\n' "$LAB28"

Si falla una aserción, compara stat, getfacl y el punto de montaje. No ajustes el resultado esperado para ocultar falta de soporte. Repetir el ensayo requiere un directorio nuevo; touch sobre un nombre existente no recalcula su ACL ni su modo inicial.

15. Simuladores de bits y ACL

Leer s/S y t/T

Introduce cuatro cifras octales. El panel representa el modo y explica el alcance de cada bit según el tipo elegido. No ejecuta archivos ni modifica el equipo.

Bits especiales y representación

representación del modo
BitPresenteInterpretación

Decidir una petición con ACL extendida

Modelo fijo: propietario UID 1000, grupo GID 2000 y usuario nombrado UID 1201. El proceso puede pertenecer a los GID 2000 y 2200. Se evalúa una petición conjunta de bits sobre el objeto final, sin capacidades ni políticas adicionales. La máscara no afecta a propietario ni a otros.

Selección de entrada y máscara

ACL y decisión del modelo
EntradaAlmacenadoEfectivoPapel en esta petición

Prueba UID 1300, ambos grupos, máscara 7 y petición rw: con group::r-- y group:2200:-w- la petición conjunta se deniega. Después prueba r y w por separado. Cambia a UID 1201 con entrada 0 y other::7: la coincidencia de usuario sigue denegando, sin recurrir a otros.

16. Diagnóstico y conservación de metadatos

SíntomaQué revisar
El grupo parece tener escritura, pero una persona no.Entrada de usuario nombrado, máscara de ACL y credenciales vigentes.
El nuevo archivo no coincide con petición & ~umask.ACL predeterminada del padre y petición real de la aplicación.
setgid heredó grupo, pero el equipo no puede escribir.Modo o ACL efectiva del hijo y búsqueda en toda la ruta.
No se puede borrar un archivo con contenido escribible.Permisos del padre, sticky, propietarios, inmutable y montaje.
El modo muestra s sin cambio de identidad.Propietario igual al ejecutor, nosuid, no_new_privs, script y condiciones de exec.
Falla setfacl, xattr o chattr.Retorno, soporte del destino, capacidades, cuota y políticas.

Una copia de contenido no equivale a una copia completa del estado. Antes de respaldar o migrar, determina si se preservan UID/GID, modo, ACL, xattr y flags, y si el destino los admite. Verifica el resultado con las herramientas de cada mecanismo.

Los metadatos pueden cambiar entre consultas. Estos ejemplos parten de un árbol privado; no convierten una comprobación previa en autorización permanente para usar una ruta mutable. En el tema 29 estudiaremos access y la carrera TOCTOU.

17. Resumen

18. Preguntas de repaso y práctica

1. ¿Qué expresa 6750?

setuid y setgid, propietario rwx, grupo r-x y otros sin permisos. En un regular se representa -rwsr-s---.

2. ¿S equivale a s?

Ambas indican el bit especial, pero S señala que falta x en esa posición. No expresa una concesión adicional.

3. ¿setuid significa ejecutar como root?

Depende del propietario del binario y de que la transición esté permitida. No designa root por sí mismo.

4. ¿Poner setuid en un script activa ese cambio en Linux?

Linux ignora esos bits sobre scripts de intérprete. La marca en el modo no demuestra un cambio de identidad.

5. ¿setgid del directorio garantiza escritura de equipo?

No. Hereda el GID, mientras el modo o la ACL del hijo determinan sus permisos.

6. ¿El directorio setuid hereda UID en Linux?

No tiene esa semántica equivalente a la herencia de grupo por setgid.

7. ¿Sticky impide editar un archivo ajeno?

No por sí solo. Se refiere a entradas del directorio; la escritura del contenido depende de la protección del archivo.

8. ¿El dueño del directorio sticky puede retirar una entrada ajena?

La regla sticky lo admite, pero deben cumplirse los permisos del directorio y las demás restricciones.

9. ¿Qué limita mask::?

Usuarios nombrados, grupo propietario y grupos nombrados. No limita user:: ni other::.

10. ¿rw- con máscara r-x permite escribir?

No. La intersección efectiva es r--.

11. ¿Un usuario nombrado con cero puede aprovechar other::rwx?

No. La coincidencia de usuario determina la entrada; no se busca otra más permisiva.

12. ¿Dos grupos con r y w separados satisfacen rw conjunto?

En el algoritmo de ACL descrito se necesita una entrada de grupo coincidente que contenga toda la petición, limitada por la máscara.

13. ¿chmod g= cambia solo group:: cuando hay ACL extendida?

Cambia la máscara de la clase de grupo y puede afectar a los permisos efectivos de entradas nombradas.

14. ¿Añadir una entrada puede cambiar derechos efectivos de otra?

Sí, si setfacl recalcula y amplía la máscara. Se puede indicar una máscara explícita o usar -n según la intención.

15. ¿Una ACL predeterminada modifica hijos antiguos?

No. Es una plantilla para creaciones posteriores; la ACL de acceso del propio directorio es otra lista.

16. ¿Una plantilla permisiva añade permisos a una petición 0600?

No amplía las clases del modo solicitado: en el ejemplo la máscara de acceso queda vacía y el modo es 0600.

17. ¿El cero intermedio del xattr termina su valor?

No. El valor es una secuencia de bytes con longitud explícita; el ejemplo conserva cinco bytes, incluido ese cero.

18. ¿user.curso es un mecanismo de denegación?

No. Es un nombre de aplicación que almacena metadatos. No crea por sí mismo una regla de acceso del kernel.

19. ¿El propietario ordinario puede establecer inmutable?

No basta con ser propietario. Se necesita el privilegio específico y soporte del sistema de archivos.

20. ¿El alias duro tiene otro xattr independiente?

No. Refiere al mismo objeto y comparte sus atributos. Una copia a un objeto nuevo requiere una política de preservación.

Para practicar

  1. Representa 4755, 2640, 1770 y 7000 con s/S y t/T para regular y directorio.
  2. Explica por qué observar el propio binario setuid no demuestra un cambio de usuario.
  3. Diseña un directorio de equipo que combine GID, setgid, acceso del padre y permisos de hijos.
  4. Separa las condiciones necesarias para editar un archivo y para retirar su nombre de un directorio sticky.
  5. Calcula derechos efectivos de tres entradas con máscaras 7, 5, 4 y 0.
  6. Usa el simulador para demostrar que una entrada nombrada impide recurrir a otros.
  7. Compara una petición rw conjunta con dos peticiones separadas bajo grupos r-- y -w-.
  8. Registra la ACL antes y después de chmod g=rw; identifica las entradas cuyo permiso efectivo cambia.
  9. Crea objetos con peticiones 0666 y 0600 bajo la misma plantilla y explica la diferencia.
  10. Modifica el xattr binario del programa, incluyendo otro byte cero, y comprueba su longitud.
  11. Verifica qué metadatos conserva una herramienta de copia en tu sistema y destino de práctica.
  12. Describe qué consultas harías ante un EPERM con modo aparentemente permisivo.

El siguiente tema estudia access y la carrera TOCTOU: por qué comprobar una ruta y usarla después son operaciones distintas y cómo trabajar con open y fstat.