2010-03-20 20 views
29

estoy escribiendo algún tipo de software (en C++, para Linux/Mac OS X), que se ejecuta como un usuario sin privilegios, pero necesita privilegios de root en un cierto punto (para crear un nuevo dispositivo virtual).¿Cómo obtener privilegios de root mediante programación?

La ejecución de este programa como root no es una opción (principalmente para los problemas de seguridad) y necesito saber la identidad (UID) del usuario "real".

¿Hay una manera de imitar el comportamiento comando "sudo" (pide la contraseña de usuario) para obtener privilegios de root temporalmente y llevar a cabo la tarea en particular? Si es así, ¿qué funciones usaría?

Muchas gracias por su ayuda!

Respuesta

12

Respuesta original

Usted podría considerar el interruptor setuid en el propio ejecutable. Wikipedia tiene un article en él, que incluso le muestra la diferencia entre geteuid() y getuid() con bastante eficacia, siendo el primero para averiguar quién está "emulando" y el segundo para que usted "". El proceso de sudo, por ejemplo, geteuid debe devolver 0 (root) y getuid tu ID de usuario, sin embargo, sus sub-procesos no realmente ejecutados como root (se puede verificar esto con sudo id -u -r).

No creo que hay una manera de obtener fácilmente mediante programación acceso de root - después de todo, aplicar el principio de privilegio mínimo, ¿por qué se necesita? La práctica común es ejecutar solo partes limitadas de código con privilegios elevados. Muchos daemons, etc., también se configuran en sistemas modernos para que se ejecuten como sus propios usuarios con la mayoría de los privilegios que necesitan. Es solo para operaciones muy específicas (montaje, etc.) que los privilegios de root son realmente necesarios.

2013 actualización

Mi respuesta original se encuentra (aunque mi 2013 sí podría hacer un mejor trabajo de lo que mi 2010 uno), pero si está diseñando una aplicación que requiere acceso de root, es posible que desee considere exactamente qué tipo de acceso raíz se necesita y considere el uso de POSIX Capabilities(man page). Estos son diferentes a capability-based security como se implementa en L4 y col. Las capacidades de POSIX permiten que su aplicación tenga un subconjunto de poderes de raíz. Por ejemplo, CAP_SYS_MODULE le permitirá insertar módulos de kernel, pero no le dará ningún otro poder de raíz. Esto está en uso en distribuciones, p. Fedora has a feature to completely remove setuid binaries con acceso raíz indiscriminado.

Esto es importante porque como programador, ¡su código es obviamente perfecto! Pero, las bibliotecas de las que dependes (suspira, ¡si tan solo las hubieras escrito!) Podrían tener vulnerabilidades en ellas. Mediante el uso de capacidades, puede limitar el uso de este exploit y salvarse a sí mismo y a su empresa del escrutinio relacionado con la seguridad. Esto hace que todos sean más felices.

+0

Sí, mi enfoque no era el correcto. Configurar el bit adhesivo es la mejor forma de hacerlo. Muchas gracias. – ereOn

+0

¡Funciona como un encanto! Gracias;) – ereOn

+2

Al hacer esto, debe ser * muy * cuidadoso con la seguridad, ya que su aplicación ahora es un objetivo para los exploits de Escalation of Privilege –

0

Usted puede tratar de lanzar el comando para crear el dispositivo virtual (incluyendo sudo) a través de un fondo de depósito. Pida la contraseña de los usuarios en un cuadro de diálogo propio y canalícela al shell cuando sudo lo solicite. Existen otras soluciones, como el uso de gksu, pero no está garantizado que estén disponibles en todas las máquinas.

Usted no se quede todo el programa como root, pero sólo la pequeña parte de lo que necesita la raíz. Debe generar un proceso separado para eso y sudo puede ser de ayuda para usted.

+0

Gracias por su respuesta. Desafortunadamente, el programa no siempre necesita privilegios de root. A veces puede querer utilizarlo de una manera que no necesite realizar esa tarea en particular. En tal caso, no es necesario pedir la contraseña. He encontrado hilos que hablan sobre setuid(). Voy a investigar esto. – ereOn

+2

¿Está sudo garantizado que estará disponible en todas las máquinas? He visto instalaciones sin eso. – ziggystar

+0

No, no está configurado en fedora de forma predeterminada. También bloqueo personalmente a usuarios que no están en el grupo 'rueda' de usar sudo o su como una extensión del comportamiento tradicional de Unix. –

19

Si necesita privilegios de root cada vez, lo mejor es iniciar su programa como root y soltarlos (en un subproceso) con setuid y setgid. Eso es lo que hace Apache cuando se van a enlazar con el puerto restringido 80.

Si la obtención de los derechos de la raíz es la excepción en lugar de la regla y el programa se ejecuta de forma interactiva, otra forma es escribir un add_interface programa y ejecutar

sudo add_interface args 

y dejar que la autenticación de mango sudo para usted. En lugar de sudo, es posible que desee utilizar una interfaz gráfica como gksu, sudo, kdesu, o kdesudo. No trataría de implementar la contraseña segura de entrada yo mismo; puede ser un problema difícil y probablemente dejará huecos de seguridad y problemas de funcionalidad (¿Admite lectores de huellas digitales?).

Otra alternativa es polkit, anteriormente llamado PolicyKit.

+0

Aaaah, acabo de publicar mi respuesta y esto surgió, este es un buen consejo. +1. –

+0

+1 para PolicyKit. –

7

No puede obtener privilegios de root, debe comenzar con ellos y reducir sus privilegios según sea necesario. La forma habitual de hacerlo es instalar el programa con el conjunto de bits "setuid": esto ejecuta el programa con el ID de usuario efectivo del propietario del archivo. Si ejecuta ls -l en sudo, verá que se instala de esta manera:

-rwsr-xr-x 2 root root 123504 2010-02-25 18:22 /usr/bin/sudo 

Si bien el programa se ejecuta con privilegios de root, puede llamar a la llamada setuid(2) sistema para cambiar su identificador de usuario efectivo para algunos no usuario privilegiado Creo (pero no lo he intentado) que puede instalar su programa como root con el bit de setuid activado, reducir inmediatamente el privilegio y restaurar el privilegio según sea necesario (sin embargo, es posible que una vez que disminuya su privilegio no lo haga). ser capaz de restaurarlo).

Una mejor solución es dividir la parte de su programa que necesita ejecutarse como raíz, e instalarlo con el bit de setuid activado. Por supuesto, deberá tomar precauciones razonables para que no pueda invocarse fuera de su programa maestro.

+0

Gracias! Parece que este es el camino a seguir, de hecho. Voy a intentar devolver los resultados;) – ereOn

2

Es posible que desee echar un vistazo a estas API:

setuid, seteuid, setgid, setegid, ... 

Están definidas en la cabecera <unistd.h> en sistemas Linux (no saben mucho acerca de MAC, pero usted debe tener un encabezado similar existe también).

Un problema que puedo ver es que el proceso debe tener suficientes privilegios para cambiar sus ID de usuario/grupo. De lo contrario, las llamadas a las funciones anteriores producirán un error con errorno establecido en EPERM.

Le sugiero que ejecute su programa como el usuario root, cambie la identificación de usuario efectiva (usando seteuid) a un usuario desfavorecido desde el principio. Luego, siempre que necesite elevar los permisos, solicite una contraseña y luego vuelva a usar seteuid para volver al usuario root.

4

Normalmente esto se hace creando su raíz suid binaria.

Una manera de manejar esto de modo que los ataques contra su programa son difíciles es reducir al mínimo el código que se ejecuta como root, así:

int privileged_server(int argc, char **argv); 
int unprivileged_client(int argc, char **argv, int comlink); 


int main(int argc, char **argv) { 
    int sockets[2]; 
    pid_t child; 
    socketpair(AF_INET, SOCK_STREAM, 0); /* or is it AF_UNIX? */ 

    child = fork(); 
    if (child < 0) { 
     perror("fork"); 
     exit(3); 
    } elseif (child == 0) { 
     close(sockets[0]); 
     dup2(sockets[1], 0); 
     close(sockets[1]); 
     dup2(0, 1); 
     dup2(0, 2); /* or not */ 
     _exit(privileged_server(argc, argv)); 
    } else { 
     close(sockets[1]); 
     int rtn; 
     setuid(getuid()); 
     rtn = unprivileged_client(argc, argv, sockets[0]); 
     wait(child); 
     return rtn; 
    } 
} 

ahora las conversaciones de código sin privilegios al código privilegiado a través del comunicador fd (que es un socket conectado). El código privilegiado correspondiente utiliza stdin/stdout como su extremo del comlink.

El código privilegiado necesita verificar la seguridad de cada operación que necesita hacer, pero como este código es pequeño en comparación con el código sin privilegios, esto debería ser razonablemente fácil.

+0

tu segundo bloque creo que te refieres a 'child> 0' –

+0

En realidad, creo que debería ser' else if (child == 0) '- el bloque' else' 'wait' s para el niño, por lo que el niño debe ser el bloque medio' else if'. – user470379

+0

user470379 es correcto. Fijo. – Joshua

2

En OS X, puede usar la función AuthorizationExecuteWithPrivileges. La página en Authorization Services Tasks tiene alguna discusión elaborada de esta (y relacionada) funciones.

Aquí hay un poco de código C++ para ejecutar un programa con privilegios de administrador:

static bool execute(const std::string &program, const std::vector<std::string> &arguments) 
{ 
    AuthorizationRef ref; 
    if (AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &ref) != errAuthorizationSuccess) { 
     return false; 
    } 

    AuthorizationItem item = { 
     kAuthorizationRightExecute, 0, 0, 0 
    }; 
    AuthorizationRights rights = { 1, &item }; 
    const AuthorizationFlags flags = kAuthorizationFlagDefaults 
            | kAuthorizationFlagInteractionAllowed 
            | kAuthorizationFlagPreAuthorize 
            | kAuthorizationFlagExtendRights; 

    if (AuthorizationCopyRights(ref, &rights, kAuthorizationEmptyEnvironment, flags, 0) != errAuthorizationSuccess) { 
     AuthorizationFree(ref, kAuthorizationFlagDestroyRights); 
     return false; 
    } 

    std::vector<char*> args; 
    for (std::vector<std::string>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) { 
     args.push_back(it->c_str()); 
    } 
    args.push_back(0); 

    OSStatus status = AuthorizationExecuteWithPrivileges(ref, program.c_str(), kAuthorizationFlagDefaults, &args[0], 0); 

    AuthorizationFree(ref, kAuthorizationFlagDestroyRights); 
    return status == errAuthorizationSuccess; 
} 
+0

¿Dónde llamamos a esta función? ¿Y cuál sería el parámetro de argumentos? Traté de llamar al principio de la función principal, pero se bloqueó. los argumentos eran nulos. Mi problema es crear un archivo en/var/log. Pero el permiso es necesario. ¿Cómo puedo lidiar con este problema? – gkhanacer

+0

Sería bueno mencionar todas las bibliotecas que este código necesita, ¿no? – Konstantin

Cuestiones relacionadas