Eche un vistazo a samba para ver un ejemplo de esto. El daemon samba se ejecuta como root pero se bifurca y asume las credenciales de un usuario normal tan pronto como sea posible.
Los sistemas Unix tienen dos juegos separados de credenciales: los identificadores de usuario/grupo reales y los identificadores de usuario/grupo efectivos. El conjunto real identifica quién eres en realidad, y el conjunto efectivo define a qué puedes acceder. Puede cambiar el uid/gid efectivo como lo desee si es root — incluyendo a un usuario ordinario y viceversa — ya que los ID de usuario/grupo reales permanecen en la raíz durante la transición. Entonces, una forma alternativa de hacerlo en un solo proceso es usar seteuid/gid
para aplicar los permisos de diferentes usuarios hacia adelante y hacia atrás según sea necesario. Si su daemon de servidor se ejecuta como root o tiene CAP_SETUID
, esto será permitido.
Sin embargo, observe que si tiene la capacidad de cambiar el uid/gid efectivo a su antojo y su aplicación es subvertida, entonces esa subversión podría volver a poner el uid/gid efectivo en 0 y podría tener una seguridad seria vulnerabilidad. Es por eso que es prudente soltar todos los privilegios de forma permanente tan pronto como sea posible, incluido su usuario real uid/gid.
Por esta razón, es normal y más seguro tener un único socket de escucha ejecutándose como root, luego desviar y cambiar los identificadores de usuario reales y efectivos llamando al setuid
. Entonces no puede cambiar de nuevo. Su proceso bifurcado tendría el socket que estaba accept()
ed ya que es una bifurcación. Cada proceso simplemente cierra los descriptores de archivos que no necesitan; los sockets permanecen vivos ya que los descriptores de archivo hacen referencia a ellos en los procesos opuestos.
También podría intentar y aplicar los permisos examinándolos individualmente usted mismo, pero espero que sea obvio que esto es potencialmente propenso a errores, tiene muchos casos extremos y más probabilidades de salir mal (por ejemplo, no lo hará). trabajar con POSIX ACL a menos que implemente específicamente eso también).
tanto, usted tiene tres opciones:
- tenedor y
setgid()
/setuid()
para el usuario que desea. Si se requiere comunicación, use pipe(2)
o socketpair(2)
antes de techar.
- No fork y
seteuid()
/setegid()
alrededor según sea necesario (menos seguro: más probabilidades de comprometer su servidor por accidente).
- No se meta con las credenciales del sistema; hacer la aplicación de permisos de forma manual (menos seguro: más probabilidades de obtener una autorización incorrecta).
Si necesita comunicarse con el daemon, entonces aunque puede ser más difícil hacerlo con un zócalo o un tubo, la primera opción es realmente la manera segura de hacerlo. Ver how ssh does privilege separation, por ejemplo. También puede considerar si puede cambiar su arquitectura, de modo que en lugar de cualquier comunicación, el proceso puede compartir algo de memoria o espacio en disco.
Mencione que considera tener una ejecución de proceso separada para cada usuario, pero necesita un solo puerto TCP de escucha. Aún puedes hacer esto. Solo haga que un daemon maestro escuche en el puerto TCP y envíe las solicitudes a cada daemon de usuario y comuníquese según sea necesario (por ejemplo, a través de sockets de dominio Unix). Esto realmente sería casi lo mismo que tener un daemon maestro de bifurcación; Creo que este último resultaría más fácil de implementar.
Lectura adicional: la página de manual credentials(7)
. También tenga en cuenta que Linux tiene uid/gids del sistema de archivos; esto es casi lo mismo que uid/gid efectivo a excepción de otras cosas como enviar señales. Si sus usuarios no tienen acceso al shell y no pueden ejecutar código arbitrario, entonces no necesita preocuparse por la diferencia.
¡Gracias por una respuesta muy bien escrita! ¿Sería posible crear un proceso maestro que escuche en un puerto y luego envíe los datos a otro proceso a través de otra conexión de socket? ¿Es eso lo que dices en tu penúltimo párrafo? –
Sí, así es, y tú podrías hacer eso. Sin embargo, basándome en todo lo que dices, estoy bastante seguro de que sería más fácil y más simple para ti implementar un daemon maestro de escucha de bifurcación y hacer que el niño pierda privilegios.El proceso bifurcado tendrá automáticamente acceso al socket de solicitud tal como lo hace el daemon maestro; no hay necesidad de comunicar esa parte. –
Bien. Estoy buscando una solución que también funcione bien con Qt, aunque no es un requisito. Así que lo tomo en consideración a la hora de decidir el enfoque correcto. –