Ajouter le support shadow à un programme C est assez facile. Le
seul problème est que le programme doit être lancé par root (ou SUID root)
pour qu'il puisse accéder au fichier /etc/shadow
.
Ceci présente un réel problème, il faut faire très attention lors de la création de programmes SUID. Par exemple, il ne faut pas qu'un programme SUID root puisse permettre un accès au shell.
La meilleure solution pour qu'un programme puisse accéder aux mots de passe
encodés sans être SUID root, est de lancer
ce programme SUID shadow à la place. C'est le cas par exemple du programme
xlock
.
Dans l'exemple donné précédemment, pppd-1.2.1d
fonctionne déjà
SUID root, donc ajouter le support shadow ne le rendra pas plus vulnérable.
Les fichiers d'en-tête doivent être stockés dans le répertoire
/usr/include/shadow
. Le fichier /usr/include/shadow.h
,
doit être un lien symbolique vers /usr/include/shadow/shadow.h
.
Pour ajouter le support shadow à un programme, vous devez inclure les fichiers de header:
#include <shadow/shadow.h> #include <shadow/pwauth.h>
La meilleure solution est d'utiliser des directives de compilation pour compiler conditionnellement le code shadow (Il y aura un exemple par la suite).
Quand vous avez installé l'ensemble shadow, le fichier
libshadow.a
a été créé et installé dans le répertoire /usr/lib
.
Lorsque vous compilez un programme avec le support shadow, vous devez
préciser à l'éditeur de liens d'inclure la bibliothèque libshadow.a
dans le lien:
gcc programe.c -o program -lshadow
Ceci dit, et vous le verrez par la suite dans notre exemple, la plupart des
programmes plus ou moins gros utilisent un fichier Makefile
, qui en général,
utilise une variable appelée LIBS=... que vous pourrez modifier.
La bibliothèque libshadow.a
utilise une structure appelée
spwd
pour récupérer les informations contenues dans le fichier
/etc/shadow
. Voici la définition de la structure spwd
provenant de /usr/include/shadow/shadow.h
:
struct spwd { char *sp_namp; /* nom de login */ char *sp_pwdp; /* mot de passe encode */ sptime sp_lstchg; /* date de la derniere modification */ sptime sp_min; /* nombre de jours minimum entre les modifs */ sptime spmax; /* nombre de jours maximum entre les modifs*/ sptime sp_warn; /* nombre de jours de warning avant l'expiration du mot de passe */ sptime sp_inact; /* nombre de jours d'utilisation du compte apres l'expiration. */ sptime sp_expire; /* nombre de jours a partir du 01/01/70 jusqu'a l'expiration du compte */ unsigned long sp_flag; /* reserve pour une utilisation future */ };
L'ensemble shadow peut placer des données dans le champ sp_pwdp
juste après le mot de passe encodé, le champ password pourrait contenir:
username:Npge08pfz4wuk;@/sbin/extra:9479:0:10000::::
Cela signifie qu'en plus du mot de passe, le programme /sbin/extra
sera
appelé pour procéder à une authentification supplémentaire. Le programme appelé
recevra comme argument, le nom d'utilisateur et un switch qui indiquera pourquoi il
est appelé. Regardez le fichier /usr/include/shadow/pwauth.h
et le
code source de pwauth.c
pour plus d'informations.
La fonction d'authentification pwauth
est toujours utilisée
avant la deuxième authentification..
Le fichier shadow.h
contient aussi la déclaration des
fonctions contenues dans la bibliothèque libshadow.a
:
extern void setspent __P ((void)); extern void endspent __P ((void)); extern struct spwd *sgetspent __P ((__const char *__string)); extern struct spwd *fgetspent __P ((FILE *__fp)); extern struct spwd *getspent __P ((void)); extern struct spwd *getspnam __P ((__const char *__name)); extern int putspent __P ((__const struct spwd *__sp, FILE *__fp));
La fonction que nous allons étudier est getspnam
, elle récupère une
structure spwd
à partir d'un nom donné.
Voici un exemple d'ajout du support shadow à un programme qui en nécessite mais pour qui ce support n'existe pas par défaut.
Nous allons nous baser sur l'exemple du serveur pppd-1.2.1d
(
Serveur Point-to-Point protocol) configuré avec l'option
login:
il va chercher les mots de passe pour son authentification PAP dans le fichier
/etc/passwd
au lieu des fichiers PAP ou CHAP. Vous n'avez pas besoin
d'ajouter ce code à pppd-2.2.0
, c'est déjà fait.
Bien que cette possibilité de pppd ne soit pas très utilisée,
elle ne fonctionnera plus dès lors que vous aurez installé l'ensemble
shadow: les mots de passe ne sont plus stockés dans /etc/passwd
.
La partie du code source d'authentification des utilisateurs avec
pppd-1.2.1d
se trouve dans le fichier
/usr/src/pppd-1.2.1d/pppd/auth.c
.
Le code qui suit doit être ajouté au début du fichier, là où sont toutes les
autres directives #include
.
#ifdef HAS_SHADOW #include <shadow.h> #include <shadow/pwauth.h> #endif
Maintenant, il faut modifier le code actuel. Nous sommes toujours avec le
fichier auth.c
.
La fonction auth.c
avant les modifications:
/* * login - Controle le nom d'utilisateur et le mot de passe par rapport * a ceux stockes sur le systeme. * Accepte la connection si l'utilisateur est OK. * * retourne: * UPAP_AUTHNAK: Connection refusee. * UPAP_AUTHACK: Connection Acceptee. * Dans un cas comme dans l'autre, msg pointe sur le message approprie. */ static int login(user, passwd, msg, msglen) char *user; char *passwd; char **msg; int *msglen; { struct passwd *pw; char *epasswd; char *tty; if ((pw = getpwnam(user)) == NULL) { return (UPAP_AUTHNAK); } /* * XXX Si il n'y a pas de mots de passe, accepte la connection. */ if (pw->pw_passwd == '\0') { return (UPAP_AUTHACK); } epasswd = crypt(passwd, pw->pw_passwd); if (strcmp(epasswd, pw->pw_passwd)) { return (UPAP_AUTHNAK); } syslog(LOG_INFO, "user %s logged in", user); /* * Ecris une entree wtmp pour cet utilisateur. */ tty = strrchr(devname, '/'); if (tty == NULL) tty = devname; else tty++; logwtmp(tty, user, ""); /* Ajoute une entree wtmp de connection */ logged_in = TRUE; return (UPAP_AUTHACK); }
Le mot de passe de l'utilisateur est placé dans pw->pw_passwd
,
donc, nous devons ajouter la fonction getspnam
qui placera le mot
de passe dans spwd->sp_pwdp
.
Nous rajouterons la fonction pwauth
pour l'authentification
actuelle. Une seconde authentification sera effectuée si le fichier
shadow est configuré pour.
Voici la fonction auth.c apres les modifications pour le support de shadow:
/* * login - Controle le nom d'utilisateur et le mot de passe par rapport * a ceux stockes sur le systeme. * Accepte la connection si l'utilisateur est OK. * * Cette fonction a ete modifiee pour etre compatible avec les mots de * passe Shadow Linux si USE_SHADOW a ete defini * * retourne: * UPAP_AUTHNAK: Connection refusee. * UPAP_AUTHACK: Connection Acceptee. * Dans un cas comme dans l'autre, msg pointe sur le message approprie. */ static int login(user, passwd, msg, msglen) char *user; char *passwd; char **msg; int *msglen; { struct passwd *pw; char *epasswd; char *tty; #ifdef USE_SHADOW struct spwd *spwd; struct spwd *getspnam(); #endif if ((pw = getpwnam(user)) == NULL) { return (UPAP_AUTHNAK); } #ifdef USE_SHADOW if ((spwd = getspnam(user)) == NULL) { pw->pw_passwd = ""; } else { pw->pw_passwd = spwd->sp_pwdp; } #endif /* * XXX Si il n'y a pas de mots de passe, accepte la connection. */ if (pw->pw_passwd == '\0') { return (UPAP_AUTHNAK); } #ifdef HAS_SHADOW if ((pw->pw_passwd && pw->pw_passwd[0] == '@' && pw_auth (pw->pw_passwd+1, pw->pw_name, PW_LOGIN, NULL)) || !valid (passwd, pw)) { return (UPAP_AUTHNAK); } #else epasswd = crypt(passwd, pw->pw_passwd); if (strcmp(epasswd, pw->pw_passwd)) { return (UPAP_AUTHNAK); } #endif syslog(LOG_INFO, "user %s logged in", user); /* * Ecris une entree wtmp pour cet utilisateur. */ tty = strrchr(devname, '/'); if (tty == NULL) tty = devname; else tty++; logwtmp(tty, user, ""); /* Ajoute une entree wtmp de connection */ logged_in = TRUE; return (UPAP_AUTHACK); }
En examinant précisément le code, vous verrez que d'autres modifications ont
été effectuées. La version originale autorisait l'accès (en retournant
UPAP_AUTHACK
) quand il n'y avait
pas de mots de passe dans le fichier passwd
. Il ne fallait
pas laisser ceci car utilisé avec l'option login
, pppd
utilise le nom d'utilisateur dans /etc/passwd
et le mot de passe dans
/etc/shadow
pour son authentification PAP.
Donc si nous avions gardé la version originale, n'importe qui aurait pu établir une connexion ppp avec un mot de passe vide.
Nous avons arrangé ça en retournant UPAP_AUTHNAK
à la place de
UPAP_AUTHACK
dans le cap ou le champ mot de passe est vide.
A savoir que pppd-2.2.0 possède le même problème.
Nous devons modifier le Makefile pour que deux choses soient
prises en compte:
USE_SHADOW
doit être défini, et libshadow.a
doit être ajouté
au processus d'édition de liens.
Editez le Makefile, et ajoutez:
LIBS = -shadow
Alors, trouvez la ligne:
COMPILE_FLAGS = -I.. -D_linux_=1 -DGIDSET_TYPE=gid_t
et replacez-la par:
COMPILE_FLAGS = -I.. -D_linux_=1 -DGIDSET_TYPE=gid_t -DUSE_SHADOW
Maintenant, lancez make et installez.