Acceso desde código a permisos, ACL, capabilities y atributos en ficheros

Para complementar los artículos sobre los permisos y atributos extendidos del sistema de ficheros de GNU/Linux vamos a ver como acceder a estos datos mediante código, usando las funciones del sistema statx, ioctl, listxattr, funciones acl_get_xxx y cap_xxx.

Para usar la función statx es necesario al menos el Kernel 4.11 y la versión 2.28 o superior de glibc. Si estamos usando Ubuntu 20.04 LTS cumplimos ambas condiciones. Podemos ver las versiones que tenemos por ejemplo con uname y la propia librería glibc:

$ uname -a
Linux udesk20gcc 5.6.0-050600-generic #202003292333 SMP Sun Mar 29 23:35:58 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

$ /lib/x86_64-linux-gnu/libc.so.6 --version
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9) stable release version 2.31.
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 9.3.0.
libc ABIs: UNIQUE IFUNC ABSOLUTE
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.

La aplicación se basa en una llamada a la función statx donde le pasamos como parámetro el nombre del fichero que queremos examinar. También buscaremos los i-node flags con ioctl, los atributos extendidos con listxattr, listaremos la ACL con funciones acl_get_xxx y los capabilities del fichero con las funciones cap_xxx.

 statx(AT_FDCWD , argv[1], AT_SYMLINK_NOFOLLOW, STATX_ALL, &sbx) == -1); 

En la llamada a statx le pasamos el path del fichero y como atributo le indicamos que no siga los enlaces AT_SYMLINK_NOFOLLOW. Además de la estructura donde almacenará los datos de la consulta (&sbx) en esta versión de statx tenemos que indicarle en una máscara los datos que queremos solicitar, que puede ser uno o una combinación de:

máscara solicita
STATX_TYPE stx_mode & S_IFMT
STATX_MODE stx_mode & ~S_IFMT
STATX_NLINK stx_nlink
STATX_UID stx_uid
STATX_GID stx_gid
STATX_ATIME stx_atime
STATX_MTIME stx_mtime
STATX_CTIME stx_ctime
STATX_INO stx_ino
STATX_SIZE stx_size
STATX_BLOCKS stx_blocks
STATX_BASIC_STATS [Todos los de arriba]
STATX_BTIME stx_btime
STATX_ALL [Todos los campos disponibles actualmente]

Cuando extraemos los datos de la estructura (&sbx) que le pasamos como parámetro, comprobamos antes si el dato está disponible:

 if (sbx->stx_mask & STATX_MODE) { 
        switch (fmode & S_IFMT) { 
         case S_IFREG: printf("fichero regular\n"); break; 
         case S_IFDIR: printf("directorio\n"); break; 
         case S_IFCHR: printf("dispositivo de caracteres\n"); break; 
         case S_IFBLK: printf("dispositivo de bloques\n"); break; 
         case S_IFLNK: printf("Enlace simbólico (soft link)\n"); break; 
         case S_IFIFO: printf("FIFO o pipe\n"); break; 
         case S_IFSOCK: printf("socket\n"); break; 
         default: printf("tipo desconocido de fichero\n"); break; 
        } 
    } 
 
    if (sbx->stx_mask & STATX_SIZE) 
        printf(" Número de hard links: %u\n", sbx->stx_nlink); 
    printf(" ID del dispositivo que contiene el i-node: %02x:%02x\n", 
                sbx->stx_dev_major, sbx->stx_dev_minor); 
    if (sbx->stx_mask & STATX_INO) 
        printf(" Número de I-node: %llu \n",  
                    (unsigned long long) sbx->stx_ino); 
    if (sbx->stx_mask & STATX_SIZE) 
        printf(" Tamaño total: %llu bytes\n", sbx->stx_size); 
    printf(" Tamaño optimo de bloque I/O: %u bytes\n", sbx->stx_blksize); 
    if (sbx->stx_mask & STATX_BLOCKS) 
        printf(" Bloques de 512B blocks asignados: %llu\n", sbx->stx_blocks); 
 
    if (sbx->stx_mask & STATX_MODE) { 
        printf("[+] Permisos absoluto/simbólico: %o (%s)\n", 
                fmode, str_perm(fmode)); 
        if (fmode & (S_ISUID | S_ISGID | S_ISVTX)) 
            printf("\tbits especiales: %s | %s | %s\n", 
                (fmode & S_ISUID) ? "set-userID" : " -- ", 
                (fmode & S_ISGID) ? "set-groupID" : " -- ", 
                (fmode & S_ISVTX) ? "sticky-bit" : " -- "); 
    } 
    if ((sbx->stx_mask & STATX_UID) && (sbx->stx_mask & STATX_GID)) 
        printf(" Propietario: UID=%d (%s) | GID=%d (%s) \n",  
         sbx->stx_uid, user_from_id(sbx->stx_uid), 
         sbx->stx_gid, group_from_id(sbx->stx_gid)); 
 
    //solo ficheros regulares y directorios 
    if (S_ISREG(fmode & S_IFMT) || S_ISDIR(fmode & S_IFMT)) { 
        print_flags_startx("[+] Atributos extra soportados: ",  
                            sbx->stx_attributes_mask); 
        print_flags_startx("[+] Atributos extra activos:",  
                            sbx->stx_attributes); 
 
        error_mode = 'c'; // continua; no finaliza con error 
        ifError(fget_flags(pathfile, &flags) == -1,  
         "Error al recuperar los i-node flags\n"); 
        if (error_mode == 'c') { // Si no ha habido error  
         print_flags_inode("[+] i-node flags: ", flags); 
         error_mode = 'e'; // volvemos a finalizar en caso de error 
        } 
    }  
         
    printf("[+] Fechas\n"); 
    if (sbx->stx_mask & STATX_ATIME) 
        print_time("\tUltimo acceso:\t\t ", sbx->stx_atime.tv_sec,  
                                            sbx->stx_atime.tv_nsec); 
    if (sbx->stx_mask & STATX_MTIME) 
        print_time("\tUltima modificación:\t ", sbx->stx_mtime.tv_sec,  
                                                sbx->stx_mtime.tv_nsec); 
    if (sbx->stx_mask & STATX_CTIME) 
        print_time("\tUltimo cambio de status: ", sbx->stx_ctime.tv_sec,  
                                                sbx->stx_ctime.tv_nsec); 
    if (sbx->stx_mask & STATX_BTIME) 
        print_time("\tFecha de creación:\t ", sbx->stx_btime.tv_sec,  
                                            sbx->stx_btime.tv_nsec); 

Statx nos da la posibilidad de ver los atributos extra soportados y asignados:

atributo detalle
STATX_ATTR_COMPRESSED Fichero comprimido por el sistema, puede necesitar recursos adicionales para acceder
STATX_ATTR_IMMUTABLE El archivo no se puede modificar: no se puede eliminar ni renombrar, crear enlaces o escribir datos en él
STATX_ATTR_APPEND El archivo solo se puede abrir para añadir
STATX_ATTR_NODUMP El archivo no es candidato para la copia de seguridad cuando se ejecuta un programa de copia de seguridad como dump
STATX_ATTR_ENCRYPTED Fichero encriptado por el sistema de archivos
STATX_ATTR_VERITY El archivo tiene fs-verity habilitado. No se puede escribir en él, y todas las lecturas se verificarán con un hash criptográfico que cubre todo el archivo

Como no todos los atributos están disponibles en todos los sistemas o tipos de ficheros, statx nos devuelve también una máscara con los atributos disponibles para ese fichero. Las opciones de encriptado y verificación son dos opciones del kernel muy interesantes que veremos próximamente.

Por otro lado una llamada a ioctl nos devuelve los i-node flags:

 /* obtener los i-node flags con ioctl */ 
int fget_flags (const char *name, unsigned long *flags)  
{ 
    int fd, r, f, save_errno = 0; 
 
    fd = open (name, O_RDONLY|O_NONBLOCK); 
    if (fd == -1) 
        return -1; 
    r = ioctl (fd, FS_IOC_GETFLAGS, &f); 
    if (r == -1) 
        save_errno = errno; 
    *flags = f; 
    close (fd); 
    if (save_errno) 
        errno = save_errno; 
    return r; 
} 

Aunque algunos coinciden con los devueltos por statx la lista de atributos de i-node flags es más amplia:

 /* atributos según i-node flags */  
static struct flags_name flags_array[] = { 
    { FS_SECRM_FL, "s", "Borrado seguro" }, 
    { FS_UNRM_FL, "u" , "El fichero no se puede borrar" }, 
    { FS_COMPR_FL, "c", "Contenido comprimido" }, 
    { FS_SYNC_FL, "S", "Fuerza que los cambios se escriban de forma sincrónica" }, 
    { FS_IMMUTABLE_FL, "i", "Inmutable, no están permitidos los cambios" }, 
    { FS_APPEND_FL, "a", "Solo permite añadir" }, 
    { FS_NODUMP_FL, "d", "No hacer backup" }, 
    { FS_NOATIME_FL, "A", "No actualizar nunca el tiempo de acceso" }, 
    { FS_ENCRYPT_FL, "E", "Encriptado" },  
    { FS_COMPRBLK_FL, "c", "Uno o más clusters comprimidos" }, 
    { FS_JOURNAL_DATA_FL, "j", "Mantienen un registro de los cambios" }, 
    { FS_NOTAIL_FL, "t", "Deshabilita la función de empaquetado posterior" }, 
    { FS_DIRSYNC_FL, "D", "Actulizaciones de directorios sincrónicas" }, 
    { FS_TOPDIR_FL, "T" , "Marca un directorio para un tratamiento especial" },  
    { FS_HUGE_FILE_FL, " ", "Reservado para ext4" }, 
    { FS_EXTENT_FL, "e", "Extensiones" },  
    { FS_EA_INODE_FL, " ", "Inode usado para EA grande" }, 
    { FS_EOFBLOCKS_FL, " ", "Reservado para ext4" }, 
    { FS_NOCOW_FL, "C", "No copy on write" }, 
    { FS_INLINE_DATA_FL, "N", "Reservado para ext4" }, 
    { FS_PROJINHERIT_FL, "P", "Creado con parents projid" }, 
    { FS_RESERVED_FL, " ", "Reservadp para ext2 lib" },  
    { FS_TOPDIR_FL, "T", "Top de jerarquía de directorio" }, 
    { STATX_ATTR_VERITY, "V", "No se puede escribir y lectura verificada"}, 
    { 0, NULL, NULL } 
}; 

Luego obtenemos los atributos extendidos con listxattr:

 listalen = listxattr(file, lista, sizeof(lista)); 
    ifError (listalen == -1, "Error al obtener los atributos extendidos\n"); 
      
    printf("[+] Atributos extendidos:\n"); 
 
    for (namesp = 0; namesp < listalen; namesp += strlen(&lista[namesp])+1) { 
        printf("\t%s\n", &lista[namesp]); 
 
        if (strcmp(&lista[namesp], "system.posix_acl_access") == 0) { 
         print_acl(file); 
        } else if (strcmp(&lista[namesp], "security.capability") == 0) { 
         print_capabilities(file); 
        } else if (strstr(&lista[namesp], "user.") != NULL) { 
         valorlen = getxattr (file, &lista[namesp], valor, sizeof(valor)); 
         if (valorlen != -1)  
            printf("\t = %.*s\n", (int) valorlen, valor); 
        } 
    } 

En el caso de estar presente system.posix_acl_access listamos la ACL con las funciones acl_get_xxx:

 type = ACL_TYPE_ACCESS; 
    acl = acl_get_file (file, type); 
    ifError (acl == NULL, "Error en acl_get_file\n"); 
 
    for (eID = ACL_FIRST_ENTRY; ; eID = ACL_NEXT_ENTRY) { 
        if(acl_get_entry (acl, eID, &entry) != 1) 
         break; 
         
        ifError(acl_get_tag_type(entry, &tag) == -1,  
                "Error en acl_get_tab_type\n"); 
         
        printf("\t %-12s", ret_acl_tag(tag)); 
     
        if (tag == ACL_USER) { 
         uidp = acl_get_qualifier(entry); 
         ifError (uidp == NULL, "Error en acl_get_qualifier\n"); 
         printf("%8d %-12s", *uidp, user_from_id(*uidp)); 
         ifError (acl_free(uidp) == -1, "Error en acl_free\n"); 
        } else if (tag == ACL_GROUP) { 
         gidp = acl_get_qualifier(entry); 
         ifError (gidp == NULL, "Error en acl_get_qualifier\n"); 
         printf("%8d %-12s", *gidp, group_from_id(*gidp)); 
         ifError (acl_free(gidp) == -1, "Error en acl_free\n"); 
        } else { 
         printf("%-20s "," "); 
        } 
 
        ifError (acl_get_permset (entry, &permset) == -1,  
                "Error en acl_get_permset\n"); 
 
        pVal = acl_get_perm (permset, ACL_READ); 
        ifError (pVal == -1, "Error en acl_get_perm - ACL_READ\n"); 
        printf("%c", (pVal == 1)? 'r': '-'); 
 
        pVal = acl_get_perm (permset, ACL_WRITE); 
        ifError (pVal == -1, "Error en acl_get_perm - ACL_WRITE\n"); 
        printf("%c", (pVal == 1)? 'w': '-'); 
 
        pVal = acl_get_perm (permset, ACL_EXECUTE); 
        ifError (pVal == -1, "Error en acl_get_perm - ACL_EXECUTE\n"); 
        printf("%c", (pVal == 1)? 'x': '-'); 
 
        printf("\n"); 
    } 

Si tenemos security.capability listamos las capabilities con las funciones cap_xxx :

 caps = cap_get_file(file); 
 
    if (caps == NULL) {  
        ifError (errno != ENODATA, "Error en cap_get_file\n"); 
    } else { 
        str = cap_to_text(caps, NULL); 
         
        ifError (str == NULL, "Error en cap_to_text\n"); 
 
        printf("\t %s\n", str); 
 
        cap_free(str); 
    } 

La gestión de errores la hago con una macro, creo es una opción interesante para programas pequeños de este tipo:

 static char error_mode = 'e'; 
static FILE *error_log = NULL; 
 
/* Macro para la gestión de errores */ 
#define ifError(assertion, ...) ({ \ 
    if(assertion) { \ 
        int errsv = errno; \ 
        fprintf (error_log ? error_log : stderr, "[%s:%d]:" \ 
            " \n [+] ", __FILE__, __LINE__); \ 
        fprintf (error_log ? error_log : stderr, __VA_ARGS__ ); \ 
         if (errsv != 0) \ 
            fprintf (error_log ? error_log : stderr, \ 
                        " [+] errno: [%s]\n", strerror(errsv)); \ 
        if (error_mode =='s') abort(); \ 
        else if (error_mode == 'e') exit(EXIT_FAILURE); \ 
        error_mode = 'e'; errno = 0; \ 
    } \ 
})  

Descargar y usar

Descargar código fuente: infofile-0.5.tar.gz
Firma sha256sum: 43603b9cf4bc8f33ff63fdc06a2fd39f2a1eab30e7f5cfdb35942f413bfe530a

Para construir e instalar:

$ wget https://clibre.io/files/infofile-0.5.tar.gz
$ sha256sum infofile-0.5.tar.gz
43603b9cf4bc8f33ff63fdc06a2fd39f2a1eab30e7f5cfdb35942f413bfe530a infofile-0.5.tar.gz
$ tar xvfz infofile-0.5.tar.gz
$ cd infofile/
$ make
$ sudo make install

Para construir e instalar `infofile` necesitamos las librerias acl y libcap.
En derivados de Debian/Ubuntu podemos instalarlas con:

sudo apt install libacl1-dev
sudo apt install libcap-dev

Si lanzamos el programa infofile con un fichero como parámetro tendremos algo como esto:

$ infofile /vda/file2
[+] Tipo de fichero: fichero regular
Número de hard links: 1
ID del dispositivo que contiene el i-node: fc:00
Número de I-node: 12
Tamaño total: 1000000 bytes
Tamaño óptimo de bloque I/O: 4096 bytes
Bloques de 512B asignados: 1976
[+] Permisos absoluto/simbólico: 100674 (rw-|rwx|r--)
Propietario: UID=1000 (david) | GID=1000 (david)
[+] Atributos extra soportados: (0x00100874)
(c):Contenido comprimido
(i):Inmutable, no están permitidos los cambios
(a):Solo permite añadir
(d):No hacer backup
(E):Encriptado
(V):No se puede escribir y lectura verificada
[+] Atributos extra activos: (0x00100000)
(V):No se puede escribir y lectura verificada
[+] i-node flags: (0x00180000)
(e):Extensiones
(V):No se puede escribir y lectura verificada
[+] Fechas
Ultimo acceso: dom 24 may 2020 22:12:12.088231010
Ultima modificación: dom 24 may 2020 22:10:27.804147981
Ultimo cambio de status: dom 24 may 2020 22:10:27.804147981
Fecha de creación: dom 24 may 2020 14:00:48.759825478
[+] Atributos extendidos:
user.dato
= Esto es un atributo nuevo
system.posix_acl_access
user_obj rw-
user 1000 david r-x
group_obj rw-
mask rwx
other r--

 

Modificado por última vez enJueves, 19 Noviembre 2020 18:01
(2 votos)

Deja un comentario

Asegúrese de introducir toda la información requerida, indicada por un asterisco (*). No se permite código HTML.