Limited Entropy Dot Com Not so random thoughts on security featured by Eloi Sanfèlix

8Dec/070

Anatomy Of… en IBM developerWorks

Posted by Eloi Sanfèlix

Ya dí un enlace a IBM developerWorks hace un tiempo en el post de inotify, y este post es simplemente para comentar que existe una serie de Tim Jones sobre el kernel Linux bastante interesante, que da una idea de algunos de los principales subsistemas de Linux.

La serie se llama Anatomy of the Linux... y por el momento incluye 6 artículos, sobre el slab allocator, sistema de ficheros, networking, el kernel en general, el subsistema SCSI y los métodos de sincronización presentes en el mismo.

Y ya puestos aprovecho para recomendar un excelente libro sobre el tema que leí el año pasado aunque no he puesto en práctica nada de lo aprendido allí: Linux Kernel Development de Robert M. Love . Es una excelente introducción al kernel Linux y a los sistemas operativos en general, en un lenguaje que se puede entender aunque no se conozcan muchos de los conceptos de SO de antemano. La segunda edición se basa en el kernel 2.6.10, aunque obviamente los conceptos aprendidos sirven para estudiar otras versiones.

Si se quiere un libro con mucho más detalle en las implementaciones de Linux, pero bastante más complejo de seguir también, se puede tirar por Understanding the Linux kernel (3a edición) y si se quiere aprender acerca de módulos y drivers de dispositivos, Linux Device Drivers (3a edición) que está disponible para descarga.

Filed under: GNU/Linux No Comments
11Nov/071

inotify: notificación de eventos del FS en Linux

Posted by Eloi Sanfèlix

Mucha gente a día de hoy usa sistemas de búsqueda rápida de ficheros como Beagle (Linux y otros unix - web oficial no disponible ahora mismo) o Spotlight (OS X), que devuelven resultados de manera prácticamente instantánea. ¿Cómo funciona un software de este estilo? La idea que nos viene a todos a la mente es indexando los directorios en los que se quiere buscar para que todo vaya rápido, pero indexar cada cierto tiempo los directorios es poco eficiente y se debería buscar un compromiso entre la reducción de rendimiento producida por el indexado y la latencia con la que aparecen los archivos en el sistema de búsqueda.

Es aquí donde sistemas de notificación de eventos del sistema de ficheros pueden ser útiles. Inotify es un subsistema del kernel Linux que ofrece la posibilidad de escuchar en ciertos ficheros/directorios para recibir notificaciones de eventos en los mismos. Viene por defecto en el kernel desde 2.6.13 :).

Los artículos Intro to inotify ( en Linux Journal, por Robert Love) y Monitor Linux file system events with inotify (IBM developerWorks) ofrecen una introducción bastante maja a inotify y su uso, así que no voy a comentar mucho sobre ellos aquí. Lo que sí voy a hacer es dejaros una pequeña utilidad ( watch.c , correspondiente a un ejercicio de la asignatura que está alimentando este blog últimamente) que escucha en $HOME y /tmp y notifica de los cambios.

Un pequeño fallo que le veo a inotify es que no proporciona la posibilidad de escuchar un directorio y todos sus subdirectorios de forma recursiva, pero eso no es tarea del subsistema del kernel, sino de algún wrapper que funcione sobre ello, por ejemplo dentro de la libc o de una hipotética libinotify. A falta de dicho wrapper, hay que hacer una función recursiva que añada watches a todos los subdirectorios (ver el código).

La mayor parte de la aplicación se concentra en la función report() que muestra la información de los eventos y actúa en consecuencia (eliminando watches en caso de borrado, actualizándolos en caso de cambios de nombre o movimientos de directorios, etc.), aunque no tiene mucha complicación.

Además decidí usar una lista enlazada para almacenar los watches, tanto los paths como los watch descriptor correspondientes.

Finalmente, notar el uso del campo cookie de la estructura inotify_event que permite sincronizar los eventos IN_MOVED_FROM e IN_MOVED_TO. De esta forma es posible saber si dos de estos eventos están relacionados (es el mismo archivo) o no. Además, inotify generará esos eventos de forma consecutiva, con lo que simplemente habrá que comprobar si el evento siguiente a un IN_MOVED_FROM es su correspondiente IN_MOVED_TO o no (comprobando la cookie ) y actuar en consecuencia.

La verdad es que me ha parecido curioso el tema de inotify y creo que la utilidad ha quedado bastante maja 🙂

Filed under: GNU/Linux, Seguridad 1 Comment
28Oct/070

Trazando procesos con ptrace()

Posted by Eloi Sanfèlix

Esta vez vamos a hablar un poco de ptrace(), una llamada al sistema de los sistemas Unix (al menos la mayoría de ellos) que permite trazar procesos: ejecutarlos paso a paso, ver qué hay en su memoria, en los registros, etcétera. Aunque por ejemplo la implementación de ptrace() de OS X es bastante poco útil y sólo permite ejecutar paso a paso, continuar la ejecución y poco más... pero bueno, de eso hablaré un poco al final 😉

En este post voy a explicar cómo va ptrace y algunas de las posibilidades que ofrece en linux, aunque para eso ya está la página del manual pero siempre aportará algo. Finalmente enlazaré un código que he hecho como ejercicio de la asignatura Linux Kernel & Hackers Hut: se crea un hijo, se traza con ptrace() y se ejecuta un comando pasado como parámetro en el hijo, pero se modifican todas las llamadas a open("/etc/passwd",...) por open("/tmp/passwd",...). También añadí las llamadas a stat64() porque la utilidad cp tiene una pequeña protección: primero se llama a stat64 sobre el fichero origen, luego se abre el fichero, y se llama a fstat64() sobre el descriptor. Tras esto, se comprueba que las dos estructuras devueltas por stat64 y fstat64 sean del mismo fichero, mirando que el i-node y el identificador de dispositivo coincidan.

11Oct/074

Namespaces: una visión distinta del árbol de directorios para cada proceso

Posted by Eloi Sanfèlix

Sigo dando la coña con los ejercicios de Linux Kernel & Hackers Hut como viene siendo habitual 😛 Esta vez le ha tocado el turno a los namespaces. Es de sobra conocido el concepto de los chroot, que se viene utilizando desde hace mucho tiempo: cambiamos el directorio raiz de nuestro proceso (y todos los hijos que cree éste) por otro distinto, restringiendo su visión del árbol de directorios.

Esto se puede hacer desde la consola con la utilidad chroot o con la propia llamada al sistema chroot. Para ejecutar algo en un chroot necesitaremos tener disponibles dentro del mismo tanto los ejecutables como las librerías de las que dependa, o bien compilarlo de forma estática. Con esto y usando los mínimos privilegios para un servicio dado podremos reducir el impacto de una posible vulnerabilidad en dicho servicio.

Pues bien, el tema de los namespaces, aunque relativamente similar, es algo distinto. El namespace es el concepto que tiene un proceso dado del árbol de directorios. En un principio, todos los procesos comparten dicho concepto, heredándolo de init. Sin embargo, desde 2.4.19 el núcleo de linux incluye la posibilidad de ejecutar nuevos procesos con una copia del namespace de su padre.

¿Y en qué influye esto? Pues en que a partir de ahora, si nuestro proceso realiza un mount sobre un directorio cualquiera, solamente él y sus hijos lo podrán ver, pues son los únicos que tienen acceso a dicho espacio de nombres. Si desde otro proceso se monta cualquier dispositivo (o directorio usando --bind ) sobre un directorio dado, nuestro proceso tampoco lo verá.

Un ejemplo de uso sería montar un /tmp diferente para cada nueva sesión de usuario (por ejemplo un bind de /tmp/uid sobre /tmp en el namespace del usuario), de forma que no puedan ver los ficheros temporales de otros usuarios.

Se pueden ver varios ejemplos en este artículo de IBM developerWorks. Los artículos muestran como aplicarlo el login usando PAM; no los he probado, quizás un día de estos que tenga menos trabajo...

Para acabar, comentar que la forma de crear un proceso con una copia del namespace de su padre es llamando a clone() [similar a fork()] con el flag NEW_NS. Está todo explicado en el man de clone ;).

24Sep/073

Sparse files: posible ahorro de espacio

Posted by Eloi Sanfèlix

Una vez más, Andries Brouwer en su asignatura me ha enseñado algo interesante, esta vez incluso sin ir a clase pues estaba en España. Tras el ejercicio anterior de las syscalls, y otro que no posteé porque no lo consideraba interesante ( explotar una format string normal y corriente, sin protecciones tipo va_patch ni nada... ) esta vez toca crear una utilidad que copie un archivo normal a un 'sparse file'.

Y qué es eso? Pues según wikipedia :

In computer science, a sparse file is a type of computer file that attempts to use file system space more efficiently. When space has been allocated to a file but not actually filled with data it is not written to the file system. Instead, meta-information about these "empty" regions is stored until they are filled with data.

File systems supporting sparse files include: VxFS, Apple DOS, CP/M, NTFS, ext2, ext3, GPFS, XFS, JFS, ReiserFS, Reiser4, UFS, ZFS, VMFS

Y cómo creamos uno de estos archivos a partir de su equivalente 'normal' ? Pues muy sencillo: cuando leemos un bloque de 0's, en lugar de escribirlo en disco lo que haremos será un lseek para avanzar el puntero esas posiciones. En mi caso, lo que he hecho ha sido leer byte a byte y hacer el lseek si el byte leído era 0 y hacer un write si no lo era. Algo así:

while( (n=read(ifd,&buf,1))>0 ){
if(buf==0){
if(lseek(ofd,n,SEEK_CUR)==-1)
goto error;
} else {
if(write(ofd,&buf,n)==-1)
goto error;
}
}

En este caso, ifd es el descriptor del archivo de entrada, ofd del de salida, y buf es un char. Seguramente sería más eficiente leer bloque a bloque, y para ello tenemos dos opciones: leer un bloque de tamaño fijo o bien averiguar el tamaño de bloque del dispositivo de salida. Para lo primero yo he usado un simple #define, y me he creado un buffer auxiliar para rellenarlo con 0's usando memset( ) y hacer la comparación con memcmp( ), derrochando 4k de memoria pero evitando hacer la comparación a mano que tenía pereza 😉

La segunda opción no la he implementado, pero existe la syscall [f]statfs que nos devuelve una estructura con información de un sistema de ficheros montado, entre ella el tamaño de bloque.

Para probar el programita yo lo que he hecho es lo siguiente:

~/work/lk_hh $ dd if=/dev/urandom of=my_no_zeros bs=4096 count=22+0 records in
2+0 records out
8192 bytes (8,2 kB) copied, 0,0042889 s, 1,9 MB/s
~/work/lk_hh $ dd if=/dev/zero of=my_zeros bs=4096 count=5
5+0 records in
5+0 records out
20480 bytes (20 kB) copied, 0,000177221 s, 116 MB/s
~/work/lk_hh $ cat my_no_zeros my_zeros my_no_zeros my_zeros my_no_zeros > my_no_sparse_file
~/work/lk_hh $ ./cpsparse my_no_sparse_file my_sparse_file
~/work/lk_hh $ du -sh *sparse_file
64K my_no_sparse_file
24K my_sparse_file
~/work/lk_hh $

Se puede comprobar que en este caso se han ahorrado 40k, correspondientes a las diez veces que aparece un bloque entero de bytes nulos: cinco por cada vez que concatené el archivo my_zeros.

Esto de los sparse files podría ser interesante para archivos grandes como imágenes .iso o similares que pudieran contener bloques de bytes nulos, aunque no sé si en la práctica será útil o no, no he probado con archivos comunes. Alguien tiene idea? Alguna sugerencia sobre el código o algo que aportar al respecto?

PD: Lo de usar goto para el control de errores es lo que se hace en el kernel y sugerencia del profesor en su primera clase. Así tienes el control de errores localizado y no mil if's anidados por cada posible error del código.  En la etiqueta error lo que se hace es el cleanup, en mi caso simplemente cierro los archivos y me cargo mediante unlink( ) el que he creado puesto que no es correcto.

Filed under: GNU/Linux 3 Comments
13Sep/076

Varias formas de llamar a una syscall en C

Posted by Eloi Sanfèlix

Como trabajo inicial de la asignatura Linux Kernel & Hackers Hut, después de la primera clase nos mandó hacer un pequeño programa en C que creara un directorio, de tres formas distintas: con la llamada correspondiente de la libc, con la función syscall de libc y saltándose la libc por completo.

Las dos primeras formas son bastante sencillas... vamos a ver el ejemplo de mkdir. En el primer caso, haríamos una simple llamada como ésta:

#include <sys/stat.h>
#include <sys/types.h>

[...]

mkdir(argv[1],0777);

Donde argv[1] es el nombre del directorio y 0777 son los permisos dados en notación octal.

En el segundo caso, simplemente necesitamos saber que la llamada al sistema se llama mkdir y que las constantes con los nombres de las llamadas al sistema tienen la forma SYS_xxxx, con xxxx el nombre de la llamada. Es importante esto, pues yo estaba usando __NR_mkdir tal y como se llama en el kernel 😳 .


#include <sys/syscall.h>
#include <unistd.h>

[...]

syscall(SYS_mkdir,argv[1],0777);

Finlamente, la última es un poco más especial. Necesitamos saber: tipo del valor de retorno, número de parámetros y sus tipos, así como el nombre de la llamada al sistema. Las macros se llaman _syscallX, donde la X va de 1 a 5 según el tipo de parámetros.

Así pues, sabiendo la llamada al sistema que queremos hacemos un man 2 y vemos el prototipo. Con éste, llamamos a la macro _syscallX correspondiente antes de nuestro main, y luego en el main ya podemos usar la syscall como si de cualquier otra función se tratase:


#include <linux/unistd.h>
#include <sys/types.h>
#include <errno.h>

//Dirty hack so it compiles on my x86_64 machine
#ifndef MAX_ERRNO
#define MAX_ERRNO 4095
#endif

_syscall2(int,mkdir,const char *, pathname, mode_t, mode);

int main(int argc, char **argv){
return mkdir(argv[1],0777);
}

En mi caso he tenido que añadir ese define condicional puesto que no compilaba debido a que la constante MAX_ERRNO no estaba definida. Busqué en google y parece ser que es un fallo en la libc para x86_64, al menos en la versión que yo tengo así es.

Las dos últimas formas son interesantes para cuando se añade una llamada al sistema que queremos usar y la libc no nos ofrece todavía un wrapper para ella, con lo que la debemos llamar a mano. Otra opción sería meter un poco de inline assembly, pero teniendo estas opciones me parece que es un poco bestia hacerlo, además de que se vuelve dependiente de la arquitectura, lo cual no es muy bueno... a no ser que necesitemos velocidad o control máximo y así lo podamos ganar, que tampoco sé la diferencia de velocidad entre usar la macro _syscallX y llamar directamente a la syscall desde ensamblador.

Por último, comentar que si la llamada al sistema tiene más de 5 parámetros no se pueden usar las macros, sino que se deben poner los parámetros en la pila y pasando un único parámetro: un puntero al bloque de argumentos en la pila.

Notar también que es interesante conocer ambos métodos, porque dependiendo de la libc que se use puede que se tenga la función syscall() o no, y si no la tenemos deberemos apoyarnos en las macros para hacer las llamadas.

Filed under: Erasmus, GNU/Linux 6 Comments
17Aug/073

Linux en macbook Core2Duo

Posted by Eloi Sanfèlix

Como ya comenté en el post anterior, he estado instalando y configurando Sabayon linux en mi macbook y voy a exponer aquí más o menos los pasos que he seguido y los problemas que me han surgido. Hay muchos manuales dispersos por ahí, y he leído un poco de todos los que he ido encontrando (al final pongo algunas referencias), pero prefiero tener aquí todo lo que me ha funcionado a mí para la próxima vez, y de paso igual le es útil a alguien :).

Actualmente tengo funcionando:

  • Teclado (incluyendo teclas especiales y simulación de AltGr)
  • Resolución a 1280x800 (necesita 915resolution)
  • Suspensión al bajar la tapa en KDE Ahora me falla la maldita suspensión 🙁
  • Apple Touchpad con scroll usando ambos dedos como en OS X
  • Sonido
  • Networking: Tarjeta wireless y gigabit ethernet

Me falta poner la iSight que me está dando algún problemilla, el mando y configurar bien los temas de cpufreq y los sensores de temperatura, pero lo dejaré para otro post que este ya tendrá bastante 🙂

24Mar/073

Instalando Gentoo con LVM2

Posted by Eloi Sanfèlix

Puesto que desde este verano tenía un disco duro SATA de 250G lleno de mierda debido al paso por la Campus Party y toda esa mierda lleva mucho tiempo almacenada en DVDs, hace tiempo que tenía pensado usar dicho HD para mi sistema y dejar el actual ( disco IDE de 80G ) para cualquier otra necesidad que surja. Así pues, hoy me he puesto a instalar Gentoo GNU/Linux en éste, y pensando qué particionamiento hacer he decidido no calentarme la cabeza, usar una partición para /boot, otra para / y otra para /home pero usando LVM2. Como no lo había hecho nunca, y pese a que hay documentación por ahí al respecto, dejo esto aquí como apunte para quien le interese y para que no se me olvide ;-).