domingo, 20 de noviembre de 2011

Clase de buceo

En el post anterior[1] comentaba cómo configurar las PAM para desactivar el tiempo de espera en caso de haberte equivocado escribiendo un password. Este post explica el proceso que seguí para saber que no es posible establecer un tiempo de espera personalizado. Siempre te tienes que esperar dos segundos en caso de haberte equivocado de password, o puedes desactivar el tiempo de espera. Lo que no puedes hacer es decir, "espérate 5 segundos en caso de que me haya equivocado de password antes de volver a preguntarme".. También se puede ver lo bonito que es el OpenSource, y la de que el código fuente sea público.

[Preámbulo]

Resulta que en el fichero /etc/pam.d/login hay una opción que pone: "auth optional pam_faildelay.so delay=3000000". Un comentario al lado explica claramente lo que hace esto:

"Enforce a minimal delay in case of failure (in microseconds)". O lo que es lo mismo: Fuerza una pequeña espera en caso de fallo en la autenticación. 3 segundos, para ser mas exactos.

Pues parece obvio pensar que si cambio 3000000 a 1000000, el tiempo de espera cuado haga login fallido se reducirá a 1 segundo.

Probemos antes de hacer el cambio:
$ sudo echo 1 # Comando chorra, pero lo importante es que nos pida el password
[sudo] password for inedit: badpassword
--- pasan unos dos segundos, aproximadamente ---

Hacemos el cambio en el fichero, poniendo el parámetro "delay=1000000". Probamos de nuevo:
$ sudo echo 1
[sudo] password for inedit: badpassword
--- pasan unos dos segundos, aproximadamente. Lo mismo que antes... ---


mmmm... mierda, no funciona. Probemos lo propuesto en el post anterior, o sea, poner el parámetro nodelay en el fichero /etc/pam.d/common-auth:
auth [success=2 default=ignore] pam_unix.so nullok_secure nodelay

Probamos de nuevo:

$ sudo echo 1
[sudo] password for inedit: badpassword
--- inmediatamente despues nos dice que la contraseña es incorrecta y que lo intentemos de nuevo. Tiempo de espera: cero segunos ---

Genial. Parece que el parámetro "nodelay" funciona. Pero ¿Por qué no funciona el "delay=3000000"?


[Siguiendo pasos lógicos]
Vale, resulta que el parámetro "nodelay" se lo ponemos como parámetro a un fichero llamado "pam_unix.so". Ni idea de qué coño hace este fichero, pero busquemos a ver si existe ene el sistema:

$ locate pam_unix.so
/lib/x86_64-linux-gnu/security/pam_unix.so

Touché, existe. mmmm..... vale. ¿Ahora qué hacemos con el? Ni idea de que hace realmente el fichero. Lo he abierto y es un binario....mmmm.... Vale, ya lo tengo. Descubramos qué paquete ha creado este fichero:

$ dpkg-query -S /lib/x86_64-linux-gnu/security/pam_unix.so
libpam-modules: /lib/x86_64-linux-gnu/security/pam_unix.so


Con que libpam-modules, eh? Vaaaale, pues mira, estamos siguiendo la pista y no nos va mal. Ahora ¿cuál puede ser el siguiente paso? Pues descargar el código fuente y que hace realmente el código:
$ mkdir ~/killme
$ cd ~/killme
$ apt-get source libpam-modules

Esto nos habrá creado un par de ficheros en el directorio "killme" [2].

[Buceando en el código]
Vale, ahora tenemos el código de la "libpam". ¿Qué hacemos? Lo obvio, aquí sería buscar alguna referéncia a "pam_unix.so". Pero como los ficheros ".so" son compilados y nosotros nos hemos descargado el código fuente, vamos a probar lo siguiente:
$ find -name "pam_unix*"

Oh! Genial, tenemos resultados! Parece que existe una carpeta interesante: "./pam-1.1.3/modules/pam_unix"

Vale, ahora deberíamos buscar algo de utilidad dentro de esta carpeta. Juguemos con "grep", a ver si hay suerte:
$ cd ./pam-1.1.3/modules/pam_unix2
$ grep nodelay -i # -i es para hace "ignore-case". O sea, que no distinga entre mayúsculas y minúsculas.
pam_unix.8:\fBnodelay\fR
pam_unix.8.xml:
README:nodelay
support.c: if (off(UNIX_NODELAY, ctrl)) {
support.h:#define UNIX_NODELAY 16 /* admin does not want a fail-delay */
support.h:/* UNIX_NODELAY */ {"nodelay", _ALL_ON_, 0100000},



Tenemos resultados! Parecen interesantes los ficheros "support.c" y "support.h". Revisado el fichero "support.h" parece que solo hay la declaración del parámetro UNIX_NODELAY.
Veamos el fichero "support.c", a ver que contiene:

[...]
#ifdef HAVE_PAM_FAIL_DELAY
if (off(UNIX_NODELAY, ctrl)) {
D(("setting delay"));
(void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
}
#endif
[...]


Eureka! Lo hemos encontrado! Vale, yo no tengo ni idea de C. Nunca he programado nada serio en C, ni mucho menos una librería de sistema para Linux. Pero si hemos llegado hasta este fichero buscando cosas lógicas, ahora no nos va a detener un poco de código en C. Parece que es el código fuente de "pam_unix.so".

Vamos a intentar leer lo que pone este pedacito de código. La condición "if" parece que comprueba lo siguiente:
SI (el parámetretro UNIX_NODELAY és igual a falso) ENTONCES {
Espérate 2 segundos
}


Pues ya lo tenemos, señores. Resulta que no se puede definir un tiempo de espera, ya que el tiempo de espera está puesto de modo estático en el código. En ningún momento el código va a leer la variable "delay=3000000", por lo que si ponemos "delay=5000000" es normal que lo ignore.

[conclusión]
¿Cómo hemos podido sacar todo esto? Pues la respuesta es muy simple: porque es Software Libre y se puede conseguir el código fuente muy facilmente a través de Internet. Inspeccionando el código vemos claramente lo que hace ( y mejor todavía, lo que no hace ). En este punto, y si tuviese los conocimientos de C necesarios, podría reescribir el código fuente para que aceptara un parámetro "delay" y que pudiese ser definido des de la pam.d/login. Una vez hechos los cambios, podría mandarlos al mantenedor del paquete y en caso de gustarle dichos cambios, los incorporaría en el código de la libpam. Y el mundo sería un lugar mejor y lloverían gominolas del cielo :)


[1] Recomiendo que te lo leas, sinó este pot no tiene mucho sentido.
[2] Me gusta bastante crear directorios con el nombre "kill" o "killme" para usos temporales, ya que es un modo de saber si puedo borrar el directorio sin ni tan solo preocuparme de ver que contiene. En cambio, un directorio que llamado "temp" o "temporal".... bueno, si, son datos temporales. Pero hasta cuando? Servirá lo que hay dentro? Es algo lioso. Usando "killme" el tema está claro, puedes borrar la carpeta cuando quieras sin ningun problema, ya que el contenido de la misma no es importante ;)