2010-07-28 12 views
45

Tengo un daemon que se inicia como root (por lo que puede vincularse a puertos bajos). Después de la inicialización, me gustaría que deje caer los privilegios de root por razones de seguridad.Oprimir privilegios de root

¿Alguien me puede señalar en un código correcto conocido en C que hará esto?

He leído las páginas man, he visto varias implementaciones de esto en diferentes aplicaciones, y todas son diferentes, y algunas de ellas son realmente complejas. Este es un código relacionado con la seguridad, y realmente no quiero reinventar los mismos errores que otras personas están cometiendo. Lo que estoy buscando es una mejor práctica, una buena biblioteca portátil conocida que pueda usar sabiendo que lo hará bien. ¿Existe tal cosa?

Como referencia: Estoy comenzando como root; Necesito cambiar para ejecutar bajo otro uid y gid; Necesito tener los grupos suplementarios configurados correctamente; No necesito volver a privilegios de root después.

+5

Esto varía bastante entre unixes, ¿hay alguno en particular? Si necesita una solución "portátil", va a ser un desastre, y es mejor que no acepte, por ejemplo, la función permanently_set_uid() de OpenSSH - en el uidswap.c archivo – nos

Respuesta

43

Para descartar todos los privilegios (usuario y grupo), debe soltar el grupo antes que el usuario. Teniendo en cuenta que userid y groupid contiene los ID de usuario y el grupo al que desea bajar a, y suponiendo que los ID eficaces son también la raíz, esto se logra llamando setuid() y setgid():

if (getuid() == 0) { 
    /* process is running as root, drop privileges */ 
    if (setgid(groupid) != 0) 
     fatal("setgid: Unable to drop group privileges: %s", strerror(errno)); 
    if (setuid(userid) != 0) 
     fatal("setuid: Unable to drop user privileges: %S", strerror(errno)); 
} 

Si usted es paranoico , puede intentar recuperar sus privilegios de root, que deberían fallar. Si no falla, que rescate:

if (setuid(0) != -1) 
    fatal("ERROR: Managed to regain root privileges?"); 

Además, si usted todavía está paranoica, puede que desee seteuid() y setegid() también, pero no debería ser necesario, ya que setuid() y setgid() ya configura todos los ID si el proceso es propiedad de root.

La lista de grupos suplementarios es un problema, porque no hay una función POSIX para establecer grupos suplementarios (hay getgroups(), pero no setgroups()). Hay una extensión BSD y Linux setgroups() que puede usar, esto le concierne.

También debe chdir("/") o en cualquier otro directorio, para que el proceso no permanezca en un directorio propiedad de la raíz.

Dado que su pregunta es acerca de Unix en general, este es el enfoque general. Tenga en cuenta que en Linux este ya no es el enfoque preferido. En las versiones actuales de Linux, debe establecer CAP_NET_BIND_SERVICE capability en el ejecutable y ejecutarlo como un usuario normal. No se necesita acceso a la raíz.

+1

También querrá configurar el gid, y esto podría variar ligeramente en unixes con respecto a si setuid/setgid realmente se equivoca con los identificadores reales y guardados – nos

+1

. De hecho, tiene que ser una solución portátil, por lo que no hay Linux capacidad fu permitido, me temo. Y de hecho, he intentado el enfoque simple con setuid() y setgid(); no establece los grupos correctamente (y si no llama a setgroups(), aparentemente puede terminar siendo miembro de algunos de los grupos de root). –

+0

@nos Gracias. Ampliado para cubrir grupos. Si el proceso es propiedad de root (como se mencionó OP) o si es setuid-root, entonces setuid() y setgid() ya configuraron todos los ID (reales, efectivos y guardados). Esto está en la especificación. De lo contrario, la implementación no sería conforme a POSIX. – Juliano

1

Esto era lo que podía hacer mejor:

#define _GNU_SOURCE // for secure_getenv() 


int drop_root_privileges(void) { // returns 0 on success and -1 on failure 
    gid_t gid; 
    uid_t uid; 

    // no need to "drop" the privileges that you don't have in the first place! 
    if (getuid() != 0) { 
     return 0; 
    } 

    // when your program is invoked with sudo, getuid() will return 0 and you 
    // won't be able to drop your privileges 
    if ((uid = getuid()) == 0) { 
     const char *sudo_uid = secure_getenv("SUDO_UID"); 
     if (sudo_uid == NULL) { 
      printf("environment variable `SUDO_UID` not found\n"); 
      return -1; 
     } 
     errno = 0; 
     uid = (uid_t) strtoll(sudo_uid, NULL, 10); 
     if (errno != 0) { 
      perror("under-/over-flow in converting `SUDO_UID` to integer"); 
      return -1; 
     } 
    } 

    // again, in case your program is invoked using sudo 
    if ((gid = getgid()) == 0) { 
     const char *sudo_gid = secure_getenv("SUDO_GID"); 
     if (sudo_gid == NULL) { 
      printf("environment variable `SUDO_GID` not found\n"); 
      return -1; 
     } 
     errno = 0; 
     gid = (gid_t) strtoll(sudo_gid, NULL, 10); 
     if (errno != 0) { 
      perror("under-/over-flow in converting `SUDO_GID` to integer"); 
      return -1; 
     } 
    } 

    if (setgid(gid) != 0) { 
     perror("setgid"); 
     return -1; 
    } 
    if (setuid(uid) != 0) { 
     perror("setgid"); 
     return -1;  
    } 

    // change your directory to somewhere else, just in case if you are in a 
    // root-owned one (e.g. /root) 
    if (chdir("/") != 0) { 
     perror("chdir"); 
     return -1; 
    } 

    // check if we successfully dropped the root privileges 
    if (setuid(0) == 0 || seteuid(0) == 0) { 
     printf("could not drop root privileges!\n"); 
     return -1; 
    } 

    return 0; 
} 
Cuestiones relacionadas