-[ 0x07 ]--------------------------------------------------------------------
-[ PAM y moviles ]-----------------------------------------------------------
-[ by FCA00000 ]-----------------------------------------------------SET-30--


/*
Al igual que en WindowsNT podemos sustituir los mdulos de logon
mediante GINA, en Linux podemos usar PAM para el mismo propsito.
PAM significa Pluggable Authentication Modules, o sea, mdulos
de autentificacin conectables.
Su cometido es gestionar un interface entre las aplicaciones y diversos
metodos de autentificacin.
Su utilidad es proveer mtodos para que una aplicacin cualquiera pueda
usar mecanismos ms seguros para verificar la identidad de los usuarios.
Incluso tambin a veces es necesario un nivel de seguridad ms bajo.

Por ejemplo, en el tpico UNIX, el usuario tiene que escribir su nombre y su
clave en el terminal que est sentado.
El beneficio que se obtiene con PAM es que se puede hacer que el usuario
no teclee la clave, sino que se lea de una tarjeta chip que hay que meter
en un lector al lado del teclado. O un mtodo de huella dactilar. O meter
la clave en un terminal bancario seguro, o en la pantalla del mvil.

Tambin es posible establecerlo a nivel de aplicacin. Por ejemplo, si quiero
que un programa concreto tenga una segunda clave, slo tengo que definir
que ese ejecutable usar un cierto mdulo de PAM.

E incluso se puede usar como librera. A veces necesitas que la aplicacin
pida una clave en un momento dado. Por ejemplo, una aplicacin bancaria
necesita que pases la tarjeta por el lector de banda magntica conectado
al teclado antes de realizar una transferencia.

En PAM hay 3 partes definidas:
-el mdulo de autentificacin
-la aplicacin que lo usa
-el conector ente ambos

Los mdulos son desarrollados por proveedores de seguridad, y generalmente
incluyen un interface con el sistema fsico que verifica la clave.
Por ejemplo, yo voy a "inventar" un mdulo que solicite la clave en el mvil.
Existen otros mdulos para implementar autentificacin mediante RSA, para
verificar en una base de datos, en un fichero .rhosts , en RADIUS, en un
servidor NT, en tarjetas chip, en tarjetas magnticas, en un disquete...

La aplicacin que pretende usar uno de estos mdulos no tiene ms que cargarlo
y llamar al mtodo pam_authenticate. Por ejemplo, el comando "su" puede, en
ls oportunas circunstancias, usar PAM. Lo mismo sucede con "login", "chage",
"ssh", y cualquier otro del cual tengas el cdigo fuente.

El conector es un fichero de configuracin que indica cuales programas
quieren usar PAM, y el mdulo que usan.
Estos ficheros se encuentran en el directorio /etc/pam.d/  y tienen el
nombre de la aplicacin, aunque tambin es posible definirlos globalmente
usando /etc/pam.conf
El contenido son lneas de texto con una lnea (regla) para cada opcin.
Cada una de las opciones contiene 3 o ms palabras:
-la primera define el tipo, es decir, la funcionalidad que provee:
  -auth , para verificar que el usuario es quien dice ser. Normalmente
   es el mtodo que solicita la clave al usuario y luego la verifica
  -password , para cambiar la clave
  -session , para funcionalidad que debe ser realizada justo antes de que
   el servicio (el programa cliente) se ponga en marcha. Tambin para cosas
   que hay que hacer cuando el programa finaliza.
  -account , para tareas administrativas. Por ejemplo, solicitar el cambio
   de clave cuando ha caducado.
-la segunda define el control, es decir, la verosimilitud obtenida:
  -requisite , que indica que si el proceso de autentificacin usando este
    metodo ha fallado, no deben intentarse otros
  -required , si falla esta autentificacin, se pueden intentar otras
  -sufficient , si ha tenido xito, no deben probarse otros metodos
  -optional , aunque ste haya tenido exito, tambin deben probarse otros.
-el archivo del mdulo. Debe existir en /lib/security y es una librera

Lo bueno es que esas reglas se pueden apilar, por ejemplo para solicitar
inicialmente una clave, y, si tiene exito, solicitar otra mediante algn otro
mtodo ms seguro a decisin del usuario, pongamos por caso elegir entre
tarjeta magntica, reconocimiento de voz, o anlisis de sange inmediato (?qu
pasa, no habis visto Gattaca?)

Esto es lo que est instalado en mi Linux en  /etc/pam.d/login
auth requisite  pam_unix2.so    nullok     #set_secrpc
auth required   pam_securetty.so
auth required   pam_nologin.so
#auth    required       pam_homecheck.so
auth required   pam_env.so
auth required   pam_mail.so
account required        pam_unix2.so
password required       pam_pwcheck.so  nullok
password required       pam_unix2.so    nullok use_first_pass use_authtok
session required        pam_unix2.so    none     # debug or trace
session required        pam_limits.so

O sea, que pam_unix2 tiene que funcionar obligatoriamente.
Este modulo es del estndar de UNIX que solicita login y password y
los verifica en /etc/password

Adicionalente se prueban pam_securetty, pam_nologin, pam_env y pam_mail
pero no pasa nada si fracasan.
Hasta aqu, para autentificar la clave.

A continuacin se arranca pam_unix2 con tipo account, lo cual sirve en este
caso para asegurar que la cuenta todava est activa. Esta comprobacin se
poda haber realizado con tipo pam_unix2, pero como los tipos son distintos,
se necesitan 2 entradas: una para requisite, y la otra para required.

Despus se define que para cambiar la clave se usan los mdulos pam_pwcheck
y pam_unix2, o sea, los tpicos de UNIX, incluso en versiones sin PAM.
En realidad lo que han hecho los inventores de PAM es separar en 2 rutinas
diferentes la parte de solicitud de clave y la de provisin de permisos.

Por ltimo, a nivel de sesin se verifican los parmetros habituales, y
tambin los lmites de UNIX, para establecer que el usuario no puede usar
ms de un cierto numero de archivos, o ms de una cantidad de tiempo de CPU.

En mi caso, para empezar con algo sencillo que no sea crtico, anadimos
una linea a /etc/pam.d/chage   que dice
auth  sufficient        FCA_PAM.so

As cuando intente ejecutar el programa   chage  para cambiar la fecha de
expiracin de la clave, me pedir la clave de   root   en el mvil.
Claro que la clave se pedir cuando   chage   intente la autorizacin, no
simplemente cuando intentemos ejecutar el programa.

Bueno; ya tenemos el cliente, y tambin esta definido el vnculo. Ahora
falta la parte mas entretenida: el mdulo servidor.

Es un programa que debe ser compilado como librera, preferiblemente
compartida (shared) para que no ocupe demasiado.
Entonces hay que elegir si queremos un mdulo esttico o dinmico.
La diferencia es que uno esttico debe hacer una inicializacin slo
la primera vez que es invocado, mientras que un mdulo dinmico puede
ser descargado, con lo que cada vez hay que inicializar los datos.
En mi caso debe ser dinmico, ya que cada usuario que intenta
acceder al sistema usa su propio mvil, y hay que inicializar el
puerto de comunicaciones cada vez. Pero tambien he hecho la parte
de inicializacion esttica, para el caso de que el   linker  lo decida as.
Para otros sistemas, por ejemplo de huella dactilar, hay que inicializar
el hardware slo una vez, por lo que es mejor un mdulo esttico.
Si se opta por un modulo esttico, hay que definir 6 funciones (pueden
ser NULL) a las que hay que apuntar con una estructura de tipo pam_module .
Cuando el cliente necesite una autentificacin, llamara a PAM, que
identificar la librera a cargar.
Es por eso que todos los mdulos necesitan una estructura similar, con
unos puntos de entrada conocidos.
struct pam_module _FCA_PAM_modstruct = {
    "FCA_PAM", pam_sm_authenticate, pam_sm_setcred, NULL, NULL, NULL, NULL,
};


Si elegimos un mdulo dinmico, debemos definir variables para
que se incluya el prototipo (la signatura) de cada tipo
de funcin que queremos implementar, y en este caso la funcin llamada
por el modulo cliente debe tener un nombre definido:
PAM_SM_AUTH, funcin pam_sm_authenticate y pam_sm_setcred ; para autentificar
PAM_SM_ACCOUNT, funcin pam_sm_acct_mgmt ; para gestin de la cuenta
PAM_SM_SESSION, funcin pam_sm_open_session y pam_sm_close_session
PAM_SM_PASSWORD, funcin pam_sm_chauthtok ; gestin de claves

Justamente stas son las 6 funciones apuntadas por los elementos
de _FCA_PAM_modstruct .

Como yo implemento la autentificacin, eso me obliga a definir PAM_SM_AUTH, lo
que a su vez obliga a definir la funcin pam_sm_setcred, aunque no haga nada.
Las otras funcines apuntan a NULL.

Ahora ya podemos incluir
security/pam_modules.h  y  security/_pam_macros.h  justo despus de PAM_SM_AUTH

Entre las cosas que PAM nos permite, y que casi seguro que usaremos, son las
funciones pam_get_item , pam_set_item, y pam_authenticate .

pam_get_item permite obtener informacin sobre el usuario que intenta
autentificarse. El dato ms importante es el nombre del usuario, por supuesto.
pam_set_item permite especificar valores a variables, por ejemplo
  un nombre de usuario con PAM_USER.

En mi caso lo uso solamente para comprobar que el usuario existe.
El mtodo de verificacin de la clave es muy tonto: la clave
es "12345678" para todos los usuarios.

As que despus de verificar el usuario, abro el puerto de comunicaciones.
Inicializo los parmetros adecuados, y mando el comando AT que le dir
a la tarjeta SIM que tiene que solicitar una clave.
Estos comandos son particulares para el mvil SiemensS45. Ms detalles
se pueden encontrar en otros artculos de esta misma publicacin.

Espero hasta que haya una respuesta. Si pasan mas de 20 intentos, cada uno
con un timeout de 1 segundo, devuelvo fallo: PAM_AUTH_ERR
Tomamos la clave escrita en el mvil, y extraigo los digitos exactos.
Recordar que la respuesta es algo as como "313233335363738" si la clave
escrita es "12345678" , as que tengo que obtener los caracteres de 2 en 2.
Al final, si la clave es correcta, devuelvo PAM_SUCCESS

Como cualquier programador puede ver claramente, el cdigo no es lo mas
limpio posible. Y adems no tiene chequeos (por ejemplo, asumo que
siempre se puede abrir el puerto). Y la clave es siempre la misma.
Vamos, que es una chapuza de cdigo. Pero funciona.

Notas:
El principal propsito de PAM es una autentificacin ms fuerte o ms
dbil que la estndar. La mayora de los programas que necesitan
autorizacin extra son aquellos que interaccionan con el propio sistema
de seguridad. Este es el caso de   chage ,  passwd,  login ,...
que son programas que tienen "superprivilegios", tambin conocido
como "sticky bit". Aunque sea un usuario normal el que los invoca, estos
programas se ejecutan impersonando a root, as que tienen privilegios
mximos. Eso tiene de bueno que podemos hacer cosas como abrir el puerto
o acceder al hardware. A cambio, cualquier mdulo PAM que este mal
programado puede comprometer todo el sistema.
Por ejemplo, mi programa espera una respuesta del mvil del tipo
"SSTK: D0xxxxxxxxxxxxxxxxxxxxxxxxxxxxx3132333435363738".
Si alguien conectara un terminal en vez de un telfono mvil,
podra mandar la respuesta
"SSTK: yyzz..........................."
que provocara que p[datos] apuntara a una direccin sin definir, lo que
en el mejor de los caso generara un   core  , y en el peor caso, un
buffer overflow, con posibilidad de un exploit.

Hay que poner especial atencin a los mdulos estticos para que los
datos de un usuario se borren al acabar la transaccin. Si adems es
posible que 2 autentificaciones se produzcan simultneamente en 2 terminales
distintas, es fundamental verificar que el cdigo es reentrante, y evitar
las variables globales.

En mi sistema hay instalados 40 mdulos de seguridad distintos. Desde
el simple   pam_nologin  que nunca permite el acceso, hasta el
complejo  pam_userdb  que busca el usuario en una base de datos (un fichero)
que es posible definir en lnea de comandos.
Es ms: este mdulo tiene una opcin DEBUG para mostrar la clave del usuario
antes de verificar que es correcta. ?Qu mtodo de seguridad es ste, que
te dice la clave en caso de que no la sepas?
Lo que quiero decir es que es posible que alguno de ellos tenga un fallo
de programacin que permita hacer algo para saltarse las limitaciones.

Otro punto a considerar es que la mayora de las liberas en Linux son
dinmicas, y bajo algunas circunstancias (LD_LIBRARY_PATH+chroot) es posible
definir dnde se encuentran esas librerias. Esto tambin puede hacer que
el mdulo o el programa cliente no se comporten como es de esperar.

La idea de PAM est bien, pero obliga a los administradores a disear
con cuidado los metodos que permiten autentificacin, los programas que
los usan, y los permisos que garantizarn.

Pero es un entorno de trabajo bastante sencillo de entender, y fcilmente
adaptable a nuestras necesidades.

 modulo FCA_PAM
 * Compilar con
 *   gcc -fPIC -Wall -c FCA_PAM.c -O
 *   ld -x --shared -o FCA_PAM.so FCA_PAM.o -lpam -lcrypt -lc -ldl
 * Copiar con
 *   cp FCA_PAM.so /lib/security/
 * Activar (solo una vez) con
 *   echo "auth sufficient FCA_PAM.so" >> /etc/pam.d/chage
 * Probar con
 *   chage
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <syslog.h>
#include <stdarg.h>
#include <pwd.h>
#include <fcntl.h>
#include <string.h>
#include <crypt.h>
#include <asm/io.h>
#include <sys/perm.h>
#include <time.h>
#include <termios.h>
#include <sys/ioctl.h>

#define PAM_SM_AUTH
#include <security/pam_modules.h>
#include <security/_pam_macros.h>

PAM_EXTERN
int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,const char **argv)
{
int retval = PAM_AUTH_ERR;
const char *user=NULL;
char clave_movil[]="00000000000000000000000000000";

fd_set ttyset;
struct timeval tv;
int actual,i=0;
int datos = 10;

char tmpbuf[200] = {0,};
char CTRL_Z[] = {0x1A, 0x00};
char *p=NULL;
int done = 20;
int cmdlen;

int ttyfd;
struct termios oldtio, newtio;

// Obtener ul usuario
retval = pam_get_user(pamh, &user, NULL);
if (retval != PAM_SUCCESS) {
	_pam_log(LOG_ERR, "get user returned error: %s",
	pam_strerror(pamh,retval));
	return retval;
}
if (user == NULL || *user == '\0') {
	_pam_log(LOG_ERR, "username not known");
	return PAM_AUTH_ERR;
}

if( (ttyfd = open("/dev/ttyS0", O_RDWR | O_NONBLOCK/* | O_NOCTTY*/, 0)) < 0 )
{
 fprintf(stderr, "Error: Can't open tty\n");
 return PAM_AUTH_ERR;
}

tcgetattr(ttyfd, &oldtio);
memset(&newtio, 0, sizeof(newtio));
newtio.c_cflag = B9600 | CS8 | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
tcflush(ttyfd, TCIFLUSH);
tcsetattr(ttyfd, TCSANOW, &newtio);

strcpy(tmpbuf,"at^sstk=22,0\r");
cmdlen = strlen(tmpbuf);

if(write(ttyfd, tmpbuf, cmdlen) != cmdlen) {
	printf("mal write\n" );
	return PAM_AUTH_ERR;
}

sleep(1);
strcpy(tmpbuf,"D0138103012300820281028D040453493F11020508");
strcat(tmpbuf, CTRL_Z );
cmdlen = strlen(tmpbuf);

if(write(ttyfd, tmpbuf, cmdlen) != cmdlen) {
	printf("mal write\n" );
	return PAM_AUTH_ERR;
}

while(done>0) {
	FD_ZERO(&ttyset);
	FD_SET(ttyfd, &ttyset);
	tv.tv_sec = 1;
	tv.tv_usec = 0;
	done--;
	printf("done=%i\n", done );
	sleep(1);

	if(select(ttyfd+1, &ttyset, NULL, NULL, &tv))	{
		// usleep(100);
		for(datos=0;datos<200;datos++)
			tmpbuf[datos]=0;
		actual = read(ttyfd, tmpbuf, sizeof(tmpbuf));
		printf("tmpbuf=%s\n", tmpbuf );
		if(actual < 0)
			done=0;
		p=strchr(tmpbuf, ':');
		if(p!=NULL)
			done=-5;
	}
}
close(ttyfd);

i=0;
for(datos=32;datos<52 && p!=NULL;datos+=2)
 {
 if(p[datos]==0)
	break;
 if(p[datos]!='3') //los caracteres son '3x' donde x es la tecla
	break;
 clave_movil[i++]=p[datos+1];
 }
clave_movil[i++]=0;

printf("clave_movil=%s\n", clave_movil );
if(strcmp(clave_movil,"12345678")!=NULL)
	return PAM_SUCCESS;
return PAM_AUTH_ERR;
}

PAM_EXTERN
int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
		   ,const char **argv)
{
     return PAM_SUCCESS;
}


#ifdef PAM_STATIC
struct pam_module _FCA_PAM_modstruct = {
     "FCA_PAM",
     pam_sm_authenticate,
     pam_sm_setcred,
     NULL,
     NULL,
     NULL,
     NULL,
};
#endif

*EOF*