Actualmente estoy trabajando en un proyecto en el que tengo un proceso principal que configura un par de sockets, se bifurca y luego usa este par de sockets para comunicarse. El hijo, si quiere abrir un archivo (o cualquier otro recurso basado en descriptores de archivo) siempre debe ir al padre, solicitar el recurso y recibirlo a fd
través del socketpair. Además, quiero evitar que el niño abra cualquier descriptor de archivo por sí mismo.
Me topé con lo setrlimit
que impide que el niño abra nuevos descriptores de archivos, pero también parece invalidar cualquier descriptor de archivos enviado a través de la conexión inicial del socket. ¿Hay algún método en Linux que permita que un solo proceso abra cualquier archivo, envíe su descriptor de archivo a otros procesos y les permita usarlos sin permitir que estos otros procesos abran cualquier descriptor de archivo por sí mismos?
Para mi caso de uso, puede ser cualquier configuración de kernel, llamada al sistema, etc., siempre y cuando se pueda aplicar después de fork y siempre que se aplique a todos los descriptores de archivos (no solo archivos, sino también sockets, socketpairs, etc.).
fuente
Respuestas:
Lo que tienes aquí es exactamente el caso de uso de seccomp .
Usando seccomp, puede filtrar las llamadas al sistema de diferentes maneras. Lo que se quiere hacer en esta situación es, justo después
fork()
, la instalación de unseccomp
filtro que no permite el uso deopen(2)
,openat(2)
,socket(2)
(y más). Para lograr esto, puede hacer lo siguiente:seccomp_init(3)
con el comportamiento predeterminado deSCMP_ACT_ALLOW
.seccomp_rule_add(3)
para cada llamada al sistema que desee negar. Puede usarSCMP_ACT_KILL
para matar el proceso si se intenta la llamada al sistema,SCMP_ACT_ERRNO(val)
para hacer que la llamada al sistema no devuelva elerrno
valor especificado o cualquier otroaction
valor definido en la página del manual.seccomp_load(3)
para hacerlo efectivo.Antes de continuar, TENGA EN CUENTA que un enfoque de lista negra como este es en general más débil que un enfoque de lista blanca. Permite cualquier llamada al sistema que no esté explícitamente prohibida, y podría provocar un bypass del filtro . Si cree que el proceso secundario que desea ejecutar podría estar tratando maliciosamente de evitar el filtro, o si ya sabe qué syscalls necesitarán los menores, un enfoque de lista blanca es mejor, y debe hacer lo contrario de lo anterior: crear filtro con la acción predeterminada de
SCMP_ACT_KILL
y permitir las llamadas al sistema necesarias conSCMP_ACT_ALLOW
. En términos de código, la diferencia es mínima (la lista blanca probablemente sea más larga, pero los pasos son los mismos).Aquí hay un ejemplo de lo anterior (lo estoy haciendo
exit(-1)
en caso de error solo por simplicidad):Ahora, en su programa, puede llamar a la función anterior para aplicar el filtro seccomp justo después de
fork()
, de esta manera:Algunas notas importantes sobre seccomp:
fork(2)
oclone(2)
no, los procesos secundarios estarán restringidos por el mismo filtro.execve(2)
está permitido, el filtro existente se conservará en una llamada aexecve(2)
.prctl(2)
se permite la llamada al sistema, el proceso puede aplicar más filtros.fuente
socket(2)
también puede crear unfd
, por lo que también debería bloquearse. Si conoce el proceso secundario, es mejor un enfoque de lista blanca.creat()
,dup()
, ydup2()
son todas las llamadas del sistema Linux que los descriptores de fichero de retorno. Hay muchas maneras de evitar una lista negra ...