setuid no funciona con una cuenta de usuario estándar

setuid no funciona con una cuenta de usuario estándar

Mira este programa en c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    printf("UID:  %d\n", getuid());
    printf("EUID: %d\n", geteuid());
    system("id");

    printf("res=%d\n", setuid(1001));

    printf("UID:  %d\n", getuid());
    printf("EUID: %d\n", geteuid());
    system("id");
    return 0;
}

Mi cuenta de usuario es "prueba" (id 1000). Tengo una segunda cuenta de usuario: "test2" (id 1001).

Esto es lo que he hecho:

gcc test.c -o ./a.out
sudo chown test2 ./a.out
sudo chmod u+s ./a.out

Ahora, esto es lo que sucede si ejecuto ./a.out:

UID:  1000
EUID: 1001
uid=1000(test) gid=1000(test) groups=1000(test),...
res=0
UID:  1000
EUID: 1001
uid=1000(test) gid=1000(test) groups=1000(test),...
          

No entiendo por qué no veo uid=1001 en la segunda parte...

He probado esto:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    printf("UID:  %d\n", getuid());
    printf("EUID: %d\n", geteuid());
    system("id");

    printf("res=%d\n", setuid(0));  // <- Here is the change

    printf("UID:  %d\n", getuid());
    printf("EUID: %d\n", geteuid());
    system("id");
    return 0;
}
          
gcc test.c -o ./a.out
sudo chown root ./a.out
sudo chmod u+s ./a.out

Esto es lo que obtengo cuando ejecuto ./a.out:

UID:  1000
EUID: 0
uid=1000(test) gid=1000(test) groups=1000(test),...
res=0
UID:  0
EUID: 0
uid=0(root) gid=1000(test) groups=1000(test),...
           

Ahora funciona.

Entonces no entiendo por qué setuid no funciona con el usuario test2 y funciona con los usuarios root...

Alguna idea ?

Gracias

Respuesta1

Están sucediendo un par de cosas aquí que causan esta confusión. Setuid funciona en ambos casos, simplemente no funciona como crees. En primer lugar, está la cuestión de las identificaciones de usuario reales y efectivas. De acuerdo con lapágina de manual setuid(2)

Si el proceso que llama tiene privilegios (más precisamente: si el proceso tiene la CAP_SETUIDcapacidad en su espacio de nombres de usuario), también se configuran el UID real y el conjunto de ID de usuario guardado.

Esto también significa que el proceso de llamada nonotiene la CAP_SETUIDcapacidad (que es el caso de sus usuarios normales), entonces el UIDnocambio, solo el EUID.

Entonces pasamos a la system()llamada. Llamar system("anything")es el equivalente a llamar a esto:

execl("/bin/sh", "sh", "-c", "anything", (char *) NULL);

Entonces genera /bin/sh, proporcionando argumentos para que un nuevo shell ejecute su comando. Supongo que usas bash como tu shell, porque bash funciona así

Si el shell se inicia con la identificación del usuario (grupo) efectivo que no es igual a la identificación del usuario (grupo) real, y no se proporciona la opción -p, (...) la identificación del usuario efectivo se establece en la identificación del usuario real.

Entonces, si su proceso no tiene la CAP_SETUIDcapacidad, no cambiará la ID de usuario real, solo la UID efectiva. Luego, cuando se genera bash, elimina el UID efectivo, ya que UID no es igual a EUID. Puedes confirmarlo llamando

system("touch /tmp/blah")

y agregando

FILE *file = fopen("/tmp/blah2", "w+");
fclose(file);

a su programa. Verá que el propietario de /tmp/blahserá "test" (ya que el EUID se eliminará durante la ejecución del shell), pero /tmp/blah2pertenecerá a "test2".

información relacionada