Une clef USB pour serrure GNU/Linux

Le but de cette page est de décrire l'utilisation d'une clef USB pour s'authentifier sur une machine GNU/Linux. Dans la cadre de cette page, la machine concernée est un PC sous DEBIAN unstable mais tout ce qui est décrit ici peut-être transposé à n'importe quelle distribution GNU/Linux.

ATTENTION !!! Les manipulations décrites sur cette page peuvent créer des failles de sécurités sur votre système. Utilisez cette solution à vos risque et périls.

Ce document est un document de travail qui demande à être amélioré. Si vous constatez des erreurs ou si vous avez de idées d'amélioration contactez sylvain DOT collilieux CHEZ wanadoo POINT fr

Cahier des charges

Le cahier des charges a évolué dans le temps. En effet, j'ai commencé avec un besoin simple et grâce à la magie des logiciels libres, j'ai rapidement ajouté des fonctionnalités. Liste des fonctionnalités souhaitées : Utiliser une clef USB et un mot de passe correspond à une authentification avec ce que l'on possède et ce que l'on sait. C'est un couple de plus en plus souvent utiliser pour améliorer la sécurité mais davantage avec des certificats numériques.

Pré requis

Pour mieux comprendre le système et le mettre en place, il est conseillé de connaître un minimum :

Mise en place de la solution

pam usb : essayé mais pas adopté

Ma première idée fût d'utiliser pam usb. Ce projet permet de s'authentifier avec une clef USB et fournit un module PAM. Que fait pam usb ? Il génére une clef RSA sur la clef. La signature de la clef est sauvegardée dans le répertoire .auth de l'utilisateur. Cette clef peut-être chiffrée avec une passphrase. Il est également possible d'ajouter une access-list contenant les n° de série des clefs autorisées

Lors de l'insertion de la clef USB, pam usb vérifie la présence de la clef RSA et compare sa signature avec le .auth. Si la clef est chiffrée, le module PAM de pam usb demande la passphrase. En cas de présence d'une acl, le n° de série est comparé aux n° autorisés.

Premier inconvénient de pam usb, il n'y pas de paquet DEBIAN. La compilation est cependant très facile. Cette solution ne permet de déchiffrée la clef ssh et de l'ajoutée à l'agent ssh. Pour cela il faut utiliser pam ssh pour lequel il existe un paquet DEBIAN pour stable, testing et unstable. pam ssh fournit également un module PAM. Il est possible d'utiliser plusieurs modules PAM qui se "passent" le mot de passe. Dans notre cas, il faut donc utiliser le même mot de passe pour la clef privée pam usb et la clef privée ssh. Ce n'est pas trés pratique, notamment quand on souhaite modifier le mot de passe.

pam usb et pam ssh ont un point commum : tous les deux utilisent une clef privée chiffrée RSA (DSA est également utilisable avec openSSH). Dans ce cas pourquoi utiliser un clef supplémentaire pour pam usb ? En effet, un des buts est d'utiliser ssh-agent, l'utilisation de la clef privée est donc indispensable. À ce stade, il faut trouver un moyen pour utiliser uniquiement pam ssh, c'est-à-dire :

Cette solution peut-être mise en place en utilisant les outils standards de GNU/Linux, c'est donc cette solution qui sera développée et pam usb ne sera pas retenu.

Première étape : montage et démontage automatique de la clef USB

Je ne suivrai pas le chemin complet qui m'a amené à la solution. En effet, j'ai essayé diverses méthodes pour arriver à ce que je voulais à cause de certaines contraintes de udev et hotplug. Pour résumer : il fallait assurer le montage et le démontage de la clef et également d'autres actions détaillée ci-après. Por cela, il faut que udev et/ou hotplug exécutent un script au branchement et débranchement de la clef. Faire exécuter un script au branchement est simple avec hotplug mais pour assurer le débranchement, il faut obligatoirement passer par udev.

udev

Au branchement d'un périphérique, udev applique les régles présentes dans /etc/urdev/rules.d/ (ce répertoire peut-être différent sur une autre distribution). La suite a été écrite grâce à http://wiki.archlinux.org/index.php/Using_udev_to_map_multiple_entries_to_a_device et http://www.reactivated.net/writing_udev_rules.html. Il est possible de changer le nom, les permissions, le groupe le owner du fichier périphérique, etc.. Il est également possible de lancer un script avec udev à partir de la version 0.58 (pas dans sarge ou ubuntu hoary)

hotplug

hotplug prend la suite de udev. Pour cela, hotplug consulte les fichiers .usermap contenus dans le répertoire /etc/hotplug.d/usb/ pour les périphériques USB. Il faut donc constituer un fichier que j'ai appelé athentification.usermap. Pour connaitre les paramètres à inclure dans ce fichier, il y a un trés bon commentaire sur un forum linuxfr. Ce fichier permet de lancer également un script au branchement comme udev.

udev + hotplug

Faut-il utiliser udev ou hotplug pour lancer le script ? Ma préférence va à hotplug car c'est la méthode la plus standard par rapport à ce qui est fait dans DEBIAN. Il faut noter que les scripts udev et hotplug n'ont pas accès aux mêmes variables d'environnement qui contiennent le nom du périphérique, son constructeur, etc. De plus il y a un bug ou une feature avec hotplug. Au branchement d'un périphérique, hotplug exécute le script qui est configuré dans le fichier .usermap mais lors du débranchement, il exécute le script contenu dans /var/run/usb/ qui se nomme par exemple %proc%bus%usb%001%002 (vous aurez remarquer le chemin vers le périphérique dans /proc en remplaçant les / par des %. Il faut donc créer ce fichier au moment du branchement du périphérique

Solution utilisée

Pour commencer, voici quelques informations sur la clef USB utilisée. C'est une clef de 256Mo partitionnée en 2. J'ai créé une partition de ~50 ko en fin de clef pour stocker la clef privée SSH.


Contenu de la clef :

$ ls -la
total 18
drwxr-xr-x  3 root    root  1024 2005-08-25 23:48 .
drwxr-xr-x  6 root    root  4096 2005-08-26 01:17 ..
-rw-------  1 sylvain root   736 2005-08-25 23:48 .id_dsa
drwx------  2 root    root 12288 2005-08-25 18:10 lost+found

Habituellement, ssh recherche la clef privée dans $HOME/.ssh, pour cela il suffit de créer un lien : ln -s /media/disk_by-uuid_xxxx-xxx-xxxxxx-xxxxx/.id_dsa id_dsa. Le nom du répertoire dans /media sera expliquée dans quelques lignes

Régle pour udev

sylvain@milhouse /home/sylvain $ cat /etc/udev/rules.d/z99-authentification.rules
BUS="usb", SYSFS{product}="product name", SYSFS{serial}="XXXXXXXXXXX", KERNEL="sd?2", GROUP="sylvain", MODE="640", RUN+="/usr/local/sbin/udev_authentification"

Quelques explications : le fichier doit être suffixé par .rules. Le préfixe z99- est présent pour que cette règle soit exécutée en dernier. Cette régle sera utilsée avec la clef USB identifiée par "product name" avec le numéro de série "XXXXXXXXX"sur la deuxième partition du périphérique (sd?2). Elle positionne le groupe du périphérique à sylvain, positionne les permisssions à 640 (user:rw group:r) et exécute le fichier /usr/local/sbin/udev_authentification. Le changement de groupe et de permission évite que d'autres utlisateurs puissent monter cette partition.

Script exécuté par udev et hotplug

Lors de l'insertion de la clef USB, c'est udev qui lance ce script et qui positionne $ACTION, lors du dénranchement de la clef, c'est hotplug qui lance le script et positionne la variable. C'est important de retenir ce point car les informations disponibles avec udev et hotplug sont différentes.

sylvain@milhouse /home/sylvain $ cat /usr/local/sbin/udev_authentification
#!/bin/bash

ID_FS_UUID_FILE=/var/run/usb/authentification
MOUNT_PATH=/media/disk_by-uuid_

if [ "$ACTION" = "add" ]
then
        # Montage du périphérique en mode read-only par mesure de sécurité :
        # pas de risque d'écriture lors du débranchement, pas de possibilité de lecture pour les autres utiisateurs
        mount -t ext2 -o ro,nosuid,noexec,nodev,noatime /dev/disk/by-uuid/${ID_FS_UUID} ${MOUNT_PATH}${ID_FS_UUID}
        
        # Le script sera exécuté par hotplug lors du débranchement, la variable $ID_FS_UUID n'est alors plus disponible
        # mais indispensable au démontage de la clef. La valeur est stockée dans un fichier
        # TODO:  trouver une autre solution
        echo "ID_FS_UUID=$ID_FS_UUID" > $ID_FS_UUID_FILE
        
        # Le username sera utile lors du débranchement de la cler et
        # donc du blocage de l'écran par l'économiseur d'écran
        # il est stocké dans le même fichier
        # TODO: trouver une autre solution, c'est vriament pas propore
        echo username=`stat -c %U ${MOUNT_PATH}${ID_FS_UUID}/.id_dsa` >> $ID_FS_UUID_FILE
fi

if [ "$ACTION" = "remove" ]
then
        # Il faut exécuter umount avec le point de montage, la clef étant débranchée, 
        # le fichier périphérique n'existe plus.
        # On retrouve le UUID dans /var/run/usb/authentification
        . $ID_FS_UUID_FILE              # charge les variables positionnées lors du branchement
        rm $ID_FS_UUID_FILE             # ménage

        # bloquage de l'écran
        # pour que ça fonctionne, il faut que la commande soit exécutée en tant qu'utilisateur
        # BUG : si plusieurs displays sur la machine, ça ne marche pas
        su $username -c 'xscreensaver-command -lock -display :0'
        # Démontage de la clef
        # À noter : la clef est déjà débranchée, le fichier périphérique n'existe plus
        umount ${MOUNT_PATH}${ID_FS_UUID}
fi

Ce script est largement commenté mais quelques explications s'imposent tout de même. Commençons pas la variable ID_FS_UUID. Elle est positionnée par udev et n'est pas disponible avec hotplug (ou alors je n'ai pas trouvé). La valeur stockée dans $ID_FS_UUID identifie une partition, on peut retouver sa valeur en tapant la commande blkid /dev/sda2. Les régles par défaut de hotplug crée un lien entre le fichier périphérique /dev/sdxx et /dev/disk/by-uuid/uuid_de_la_partition. Cette même règle crée plusieurs autres liens, dans /dev/disk/ (by-id, by-path) qui ne sont pas utilisés ici. En créant un lien symbolique entre $HOME/.ssh/id_dsa et /media/disk_bu-uuid_xxx--xxxx-xxxx-xxx, on s'assure qu'une autre clef ne pourra pas être utilisée à la place de celle prévue.

La variable $ACTION est positionée par udev et hotplug. Par contre les variables d'environnement comme $ID_FS_UUID ne sont pas disponibles avec hotplug. Quand la clef USB est branchée la variable $ACTION est positonnée à la valeur "add".

Pour le moment, la clef est montée automatiquement lors du branchement et un débranchement provoque le blocage de l'écran mais la solution ne permet pas encore de s'authentifier et de bébloquer l'écran de veille grâce à la passphrase de la clef privée SSH. Pour cela, nous aurons besoin de PAM.

Pour les distibutions qui n'utilise une ancienne version de udev (< 0.58), le mot clef RUN n'est pas utilisable dans les règles udev et le répertoire /dev/disk n'existe pas. Dans cas, le plus simple est de faire exécuter le montage par hotplug, par contre je ne sais pas comment gérer le "demontage" car le fichier /var/run/usb/xxxxx ne pointra pas vers le script sui permet de démonter la clef.

pam ssh

Pour installation pam ssh sous DEBIAN apt-get install libpam-ssh

Le module pam ssh permet de s'authentifier avec la passphrase de la clef privée SSH. Si la passphrase est correcte, le module retourne OK. PAM offre une architecture qui permet d'utiliser plusieurs modules pour s'authentifier. On pourrait choisir que la passphrase n'est pas suffisante et qu'il faut également entrer son mot de passe "classique".

Pour chaque service nécessitant une authentification, il existe un fichier dans le répertoire /etc/pam.d/. Pour tester, il est préférable de ne pas modifier tous les services qui peuvent permettre de se loguer sur la machine au risque de ne plus pouvoir se loguer ;-).

pam ssh pour l'authentification

La documentation de pam ssh montre comment utiliser son module pam et l'authentification "classique" en secours. Dans ce cas, si l'authentification par pam ssh échoue (passphrase incorrecte), le système demandera le mot de passe "classique". Cette méthode permet donc de se loguer sans la clef USB ce qui n'est pas le but recherché.

Voici le fichier /etc/pam.d/gdm où j'ai inséré l'authentification par pam ssh.

# (Replaces the `NOLOGINS_FILE' option from login.defs)
auth       requisite  pam_nologin.so

# This module parses /etc/environment (the standard for setting
# environ vars) and also allows you to use an extended config
# file /etc/security/pam_env.conf.
# (Replaces the `ENVIRON_FILE' setting from login.defs)
auth       required   pam_env.so

# Standard Un*x authentication.
@include pam-ssh-auth
#@include common-auth

# This allows certain extra groups to be granted to a user
# based on things like time of day, tty, service, and user.
# Please uncomment and edit /etc/security/group.conf if you
# wish to use this.
# (Replaces the `CONSOLE_GROUPS' option in login.defs)
# auth       optional   pam_group.so

# Uncomment and edit /etc/security/time.conf if you need to set
# time restrainst on logins.
# (Replaces the `PORTTIME_CHECKS_ENAB' option from login.defs
# as well as /etc/porttime)
# account    requisite  pam_time.so

# Uncomment and edit /etc/security/access.conf if you need to
# set access limits.
# (Replaces /etc/login.access file)
# account  required       pam_access.so

# Standard Un*x account and session
@include common-account
@include common-session
@include pam-ssh-session

# Sets up user limits according to /etc/security/limits.conf
# (Replaces the use of /etc/limits in old login)
session    required   pam_limits.so

# Prints the last login info upon succesful login
# (Replaces the `LASTLOG_ENAB' option from login.defs)
session    optional   pam_lastlog.so

# Prints the motd upon succesful login
# (Replaces the `MOTD_FILE' option in login.defs)
session    optional   pam_motd.so

# Prints the status of the user's mailbox upon succesful login
# (Replaces the `MAIL_CHECK_ENAB' option from login.defs). You
# can also enable a MAIL environment variable from here, but it
# is better handled by /etc/login.defs, since userdel also uses
# it to make sure that removing a user, also removes their mail
# spool file.
session    optional   pam_mail.so standard noenv
@include common-password
J'ai donc commenté la ligne @include common-auth, ajouté la ligne @include pam-ssh-auth et @include pam-ssh-session. pam-ssh-auth est chargé du contrôle de la passphrase, pam-ssh-session insère la clef dans l'agent ssh. Ces lignes incluent dans le fichier /etc/pam.d/pam-ssh-auth et /etc/pam.d/pam-ssh-session, c'est la méthode utilisée par debian. En modifiant ces 2 fichiers, il est facile de changer le comportement de tous les services qui les utilisent.

Acutellement, il y a un bug dans le paquet libpam-ssh de DEBIAN que j'ai signalé. Le fichier /etc/pam.d/pam-ssh-auth contient par défaut auth sufficient pam_ssh.so try_first_pass keyfiles=id_dsa,id_rsa. Avec ce fichier, si on supprime l'authentification classique common-auth, il est possible de se loguer sans passphrase ou avec une passphrase erronée. Pour corriger ce grave problème de sécurité, il faut remplacer sufficient par required.

pam ssh pour débloquer l'économisur d'écran

Comme écrit plus haut, il faut également utiliser pam pour permettre de débloquer l'économiseur d'écran. Pour cela, il faut modifier le fichier /etc/pam.d/xscreensaver

sylvain@milhouse /home/sylvain $ cat /etc/pam.d/xscreensaver
#
# /etc/pam.d/xscreensaver - PAM behavior for xscreensaver
#

#@include common-auth
@include pam-ssh-auth
@include pam-ssh-session
Il suffit de commenter la ligne concernant common-auth et d'ajouter celle concernant pam-ssh. Dans la pratique, la deuxième ligne pam-sss n'est pas utile. En effet, xscreensaver (idem pour xlock) ne fait pas appel au niveau "session" de PAM. Ce serait pourtant pratique car c'est ce niveau qui gère l'insertion de la clef dans l'agent SSH. Pour le moment, je n'ai donc pas trouvé de moyen d'insérer la clef privée SSH à ssh-agent lors de la désactivation du l'écran de veille.

encfs : répertoire home chiffré

encfs et le module PAM associé sont faciles à mettre en oeuvre en suivant la doc fournie Après juste quelques minutes d'essais et Thunderbird m'a lancé des insultes au démarrage, j'ai perdu qq mails (illisibles, vides, plusieurs textes identiques pour des mail différents). Bref, je n'ai pas continué l'expérience plus loin. Je me demande si les performances de mon PC (PIII 800) ne sont pas un peu faible chiffrer/déchifrrer de gros fichiers à la volée. En effet, j'ia rencontré des pbs avec le répertoire de Thunderbird où je garde les mails de Gulliver soient 26 Mo au total.

Bilan : qu'est-ce qui marche / marche pô