21 de Febrero, 2005

Introducción a systrace

La semana pasada dediqué un rato a altas horas de la noche a investigar este tema que ya le tenía ganas. systrace es uno de los mecanismos de seguridad disponibles en OpenBSD que más me impresionó al leer Absolute OpenBSD.

También funciona en otros sistemas tipo UNIX, aunque solo viene integrado por ahora en NetBSD y en OpenDarwin.

Gracias a systrace el administrador puede decidir qué llamadas al sistema pueden realizarse por qué programas.

De esta forma podríamos protegernos del posible abuso del proceso en caso de un fallo de programación, permitiéndole realizar única y exclusivamente las llamadas al sistema que necesita para realizar su trabajo.

Pero vayamos por partes, ¿qué es eso de una llamada al sistema? Se trata de funciones, digámoslo así, que permiten a los programas comunicarse con el sistema operativo.

El tema de las llamadas en los sistemas tipo UNIX siempre ha resultado algo confuso. Esto es porque no se realizan llamadas al sistema directamente, sino a funciones de librería, que pueden desembocar en una llamada al sistema o no.

El típico ejemplo en estos casos es el uso de funciones matemáticas: se realiza una llamada a función de biblioteca para calcular el seno, pero eso no implica una llamada al sistema.

Esto es muy importante porque para poder limitar qué llamadas al sistema puede realizar una aplicación, tendremos que conocerla lo suficiente como para poder identificar las llamadas al sistema que realiza.

En primer lugar habría que identificar esas llamadas a función, para luego mediante la consulta de las páginas de manual de la sección 2 (llamadas al sistema), poder identificar qué funciones son o realizan llamadas al sistema.

Aún contando con la ayuda del manual, no siempre será fácil. Una llamada a fprintf, por ejemplo, disponible en la librería de estándar C, lleva implícita una llamada al sistema de lectura o escritura de fichero, y esto no siempre puede estar tan claro.

Una vez que tenemos acotada la lista de llamadas al sistema que realiza nuestro programa, pasaremos a elaborar una política de systrace (systrace policy).

Como se puede ir deduciendo de lo que llevo explicado, esto no es nada fácil. Afortunadamente contaremos con ayuda, como veremos más adelante.

Resumiendo mucho, una política nos proporciona mecanismos para permitir o denegar (pudiendo indicar incluso el error que recibirá la aplicación) el uso de cada llamada al sistema de forma condicional.

Ese control condicional es muy importante, y lógico. Probablemente nuestra aplicación tenga la necesidad de abrir ficheros, pero puede que sean ficheros concretos. Así sería poco adecuado permitir abrir ficheros si solo se van a abrir uno o dos ficheros específicos, con lo que las reglas de la política irían destinadas a permitir las llamadas al sistema relacionadas con esos casos particulares, denegando el resto.

No voy a entrar en detalles sobre la sintáxis de las políticas, para eso está la página del manual de systrace.

Con lo dicho hasta ahora ya es suficiente para que tengamos una idea sobre cómo va en invento, y también debe estar claro que no es nada fácil escribir una política, porque requiere de tiempo y pruebas que a lo mejor quedan fuera de nuestras posibilidades.

En este punto creo que hay una buena solución: si existe una política de la que nos podemos fiar, la usamos.

Hay un repositorio de políticas para gran cantidad de programas en el proyecto Hairy Eyeball (la imagen en la página disipa cualquier duda acerca del nombre :D). El proyecto no está muy activo (el último paquete es del 2002), pero es un recurso bastante interesante.

¿Qué pasa si no existe esa política? Pues nos toca dedicarle un rato a hacerla nosotros mismos. Eso es lo que he tenido que hacer con bogom.

systrace nos permite crear una política de forma pseudo-automática. Bueno, en realidad captura todas las llamadas al sistema y las guarda en el formato que lee systrace, lo cual dista un poco de ser una política usable.

Vamos, un poco por encima, con el ejemplo de bogom:

# systrace -A bogom

De esta forma se ejecuta el programa controlado por sytrace, capturando todas las llamadas al sistema que realice. Es el momento de usar el programa :). Tenemos que intentar que se den todos los casos posibles de uso, para que se queden registradas todas las llamadas al sistema posibles.

Una vez estemos seguros de haber utilizado el programa lo suficiente, terminamos el proceso de bogom y no el de systrace -importante-, que ya terminará normalmente al acabar el proceso que está monitorizando.

Al terminar systrace generará la política en el directorio $HOME/.systrace. Es interesante saber que cualquier usuario puede ejecutar procesos con sus propias políticas locales, aunque en este caso bogom tiene que correr como root (aunque luego abandona los privilegios empleando otro usuario para realizar su trabajo).

Si miramos en el directorio tenemos:

# ls
usr_local_bin_bogofilter   usr_local_sbin_bogom

Los nombres de las políticas se corresponen con el nombre del ejecutable junto con su ruta, pero sustituyendo las barras por subrayados.

Llama la atención que se hayan creado dos políticas. Esto es porque bogom ejecuta a bogofilter para analizar cada correo, así que systrace también ha generado una política para él.

Como he comentado ya, estas políticas no son usables en absoluto, hecho que se hace más evidente cuando editamos uno de los ficheros.

En el caso de bogom vemos como se realizan operaciones sobre varios ficheros en /tmp. En realidad se emplean muchos más, ya que los mensajes a analizar se almacenan en ficheros temporales con la forma /tmp/bogom-msg.<aleatorio>. Ahí tenemos un ejemplo de ajuste: borraremos todas lineas referentes a esos ficheros menos una, que la modificaremos para que tenga el siguiente aspecto:

native-fswrite: filename match "/tmp/bogom-msg.*" then permit

Hemos cambiado el eq (equal, concordancia exacta) por un match (nos permite usar patrones de sustitución típicos de un shell).

Aún hará falta hacer algunos ajustes más, pero no quiero extenderme demasiado. Se puede consultar el resultado final en estas políticas (para una configuración por defecto del milter y sin emplear auto-entrenamiento).

Finalmente comentar que el ajuste de las políticas puede ser muy complicado, y el ensayo error puede llevarnos mucho tiempo. Afortunadamente systrace nos permite ajustar la política en tiempo real, monitorizando el proceso y contestando a preguntas en caso de que una llamada al sistema no esté contemplada (el comportamiento normal es denegarla). Será más fácil, pero no menos complicado :).

Buscando un poco he encontrado que Michael W. Lucas (autor del citado libro sobre OpenBSD) publicó un pequeño artículo sobre systrace que puede servir para completar un poco esta anotación (aunque hablamos casi de lo mismo).

Anotación por Juan J. Martínez.

Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.