Page suivante Page précédente Table des matières
8. Ajouter le support shadow à un programme en C.
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.1dfonctionne déjà SUID root, donc ajouter le support shadow ne le rendra pas plus vulnérable.
8.1 Les fichiers d'en-tête
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).
8.2 La bibliothèque libshadow.a
Quand vous avez installé l'ensemble shadow, le fichier
libshadow.aa é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.adans le lien:
gcc programe.c -o program -lshadowCeci 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.
8.3 La structure shadow
La bibliothèque
libshadow.autilise une structure appeléespwdpour récupérer les informations contenues dans le fichier/etc/shadow. Voici la définition de la structurespwdprovenant 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_pwdpjuste 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/extrasera 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.het le code source depwauth.cpour plus d'informations.La fonction d'authentification
pwauthest toujours utilisée avant la deuxième authentification..
8.4 Les fonctions Shadow.
Le fichier
shadow.hcontient aussi la déclaration des fonctions contenues dans la bibliothèquelibshadow.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 structurespwdà partir d'un nom donné.
8.5 Exemple
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/passwdau 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.1dse 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.cavant 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 fonctiongetspnamqui placera le mot de passe dansspwd->sp_pwdp.Nous rajouterons la fonction
pwauthpour 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 fichierpasswd. Il ne fallait pas laisser ceci car utilisé avec l'optionlogin, pppd utilise le nom d'utilisateur dans/etc/passwdet le mot de passe dans/etc/shadowpour 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 deUPAP_AUTHACKdans 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_SHADOWdoit être défini, etlibshadow.adoit être ajouté au processus d'édition de liens.Editez le Makefile, et ajoutez:
LIBS = -shadowAlors, trouvez la ligne:
COMPILE_FLAGS = -I.. -D_linux_=1 -DGIDSET_TYPE=gid_tet replacez-la par:
COMPILE_FLAGS = -I.. -D_linux_=1 -DGIDSET_TYPE=gid_t -DUSE_SHADOWMaintenant, lancez make et installez.
Page suivante Page précédente Table des matières