1 /** Fichier auth.c
2  **
3  ** Routines de gestion des mots de passe
4  **
5  ** Copyright (c) 1991-2010 by Ollivier ROBERT
6  ** A distribuer selon les regles de la GNU GPL General Public Licence
7  ** Voir le fichier COPYING fourni.
8  **
9  **/
10 
11 #ifndef lint
12 static const char * rcsid = "@(#) $Id$";
13 #endif
14 
15 #include "config.h"     /* GNU configure */
16 
17                         /* fichier de configuration */
18 #include "conf.h"
19 
20 static int  pam_pwcheck (struct passwd *calife, char * user_to_be, \
21                          char * user_pass, int l_size);
22 static int  local_pwcheck (struct passwd *calife, char * user_to_be, \
23                            char * user_pass, int l_size);
24 static void get_user_passwd(char * user_pass, int l_size);
25 
26 /** authenticate_user
27  **
28  ** Demande et verifie le mot de passe pour l'utilisateur name
29  ** Si name == root on ne fait rien.
30  **
31  ** Parametres :    name        char *  nom de l'utilisateur
32  **                 user_to_be  char *  qui va-t-on devenir
33  **                 this_time   char *  quand ?
34  **                 tty         char *  sur quel tty
35  **
36  ** Retourne :      neant
37  **
38  ** Algo:
39  **   si WITH_PAM est définit
40  **     on essaie auth_pam()
41  **     si ça plante, on continue avec un warning
42  **     sinon retour ok
43  **   fin
44  **   vérification mdp
45  **
46  **/
47 
48 void
authenticate_user(char * who,char * user_to_be,char * when,char * tty)49 authenticate_user (char * who, char * user_to_be, char * when, char * tty)
50 {
51     size_t  l_size = 0;
52 
53     struct  passwd  * calife;
54 
55     char    got_pass = AUTH_ERR;
56     char    * user_pass = NULL;
57 
58     /*
59      * returns long usually
60      */
61 #ifdef HAVE_WORKING_SYSCONF
62     l_size = (size_t) sysconf (_SC_LINE_MAX);
63 #else
64     l_size = MAX_STRING;
65 #endif /* HAVE_WORKING_SYSCONF */
66 
67     user_pass = (char *) xalloc (l_size);
68 
69      /*
70       * become root again
71       */
72     GET_ROOT;
73     calife = getpwnam (who);       /* null or locked password */
74     RELEASE_ROOT;
75 
76     if (calife == NULL)
77         die (1, "Bad pw data at line %d", __LINE__);
78 
79 #ifdef WITH_PAM
80     got_pass = pam_pwcheck (calife, user_to_be, user_pass, l_size);
81 #endif
82 
83     if (got_pass != AUTH_OK)
84         got_pass = local_pwcheck (calife, user_to_be, user_pass, l_size);
85 
86     MESSAGE_1 ("Auth process returned %d\n", got_pass);
87 
88     if (got_pass == AUTH_NULL)
89     {
90         syslog (LOG_AUTH | LOG_ERR, "NULL CALIFE %s to %s on %s", who, \
91                 user_to_be, tty);
92         closelog ();
93         die (10, "Sorry.\n");
94     }
95 
96     if (got_pass == AUTH_ERR)
97     {
98         syslog (LOG_AUTH | LOG_ERR, "BAD CALIFE %s to %s on %s", who, user_to_be, tty);
99         closelog ();
100         die (9, "Sorry.\n");
101     }
102 
103     if (got_pass == AUTH_BADP)
104     {
105         syslog (LOG_AUTH|LOG_ERR, "GARBLED PASSWORD %s to unknown %s on%s",\
106                 who, user_to_be, tty);
107         die (8, "Bad password string.\n");
108     }
109 
110     free (user_pass);
111 }
112 
113 /** local_pwcheck
114  **
115  ** Private method to check user password against local database
116  **
117  ** Parameters:   calife     struct passwd * requesting user
118  **               user_to_be char *          target user
119  **               user_pass  chat *          user password
120  **               l_size     int             maximum size for password
121  **
122  ** Returns:      got_pass   Whether we got authenticated or not
123  **/
124 
125 static
local_pwcheck(struct passwd * calife,char * user_to_be,char * user_pass,int l_size)126 int local_pwcheck (struct passwd * calife, char * user_to_be, \
127                    char * user_pass, int l_size)
128 {
129     int   i, got_pass = AUTH_ERR;
130 
131     MESSAGE_1 ("Testing w/o PAM with got_pass = %d\n", got_pass);
132 
133     /* check arguments */
134     if (calife == NULL || calife ->pw_name == NULL || calife->pw_name[0] == '\0')
135     {
136         die(1, "Bad parameter for calife/calife->pw_name");
137 	/*NOTREACHED*/
138     }
139 
140 #if defined (HAVE_SHADOW_H) && defined (HAVE_GETSPNAM) && !defined(UNUSED_SHADOW)
141     struct  spwd  * scalife;
142 
143     GET_ROOT;
144     scalife = getspnam (calife->pw_name);       /* null or locked password */
145     RELEASE_ROOT;
146     /*
147      * Goal is to manipulate only "calife", not both so if "scalife" is
148      * valid, copy "scalife" interesting data into "calife"
149      */
150     if (scalife)
151     {
152         calife->pw_passwd =
153             (char *) xalloc (strlen (scalife->sp_pwdp) + 1);
154         strcpy (calife->pw_passwd, scalife->sp_pwdp);
155     }
156 #endif /* HAVE_SHADOW_H */
157 
158     MESSAGE ("Not using PAM.\n");
159     if ((*(calife->pw_passwd)) == '\0' || (*(calife->pw_passwd)) == '*')
160         return (AUTH_NULL);
161 
162 
163     if (getuid () != 0)
164     {
165         char    * pt_enc,
166                 * user_pass, * enc_pass, salt [10];
167 
168         user_pass = (char *) xalloc (l_size);
169         enc_pass = (char *) xalloc (l_size);
170 
171         /*
172          * cope with MD5 based crypt(3)
173          */
174         if (!strncmp (calife->pw_passwd, "$1$", 3)) /* MD5 */
175         {
176             char * pp = (char *) xalloc (strlen (calife->pw_passwd) + 1);
177             char * md5_salt;
178             char * md5_pass;
179 
180             strcpy (pp, calife->pw_passwd + 3);
181             md5_salt = strtok (pp, "$");
182             md5_pass = strtok (NULL, "$");
183 
184             if (md5_pass == NULL ||
185                 md5_salt == NULL ||
186                 (strlen (md5_salt) > 8))   /* garbled password */
187   return (AUTH_BADP);
188             MESSAGE_1 ("MD5 password found, salt=%s\n", md5_salt);
189             strcpy (salt, md5_salt);
190             free (pp);
191         }
192         else
193         {
194             strncpy (salt, calife->pw_passwd, 2);
195             salt [2] = '\0';
196         }
197 
198         for ( i = 0; i < MAX_ATTEMPTS; i ++ )
199         {
200             get_user_passwd (user_pass, l_size);
201             /*
202              * At this point, either we have a password or
203              * it has died already
204              */
205             pt_enc = (char *) crypt (user_pass, calife->pw_passwd);
206             /*
207              * Wipe out the cleartext password
208              */
209             memset (user_pass, '\0', l_size);
210 
211             /*
212              * Move from the static buffer intoa safe location of our own
213              */
214             memset (enc_pass, '\0', l_size);
215             strcpy (enc_pass, pt_enc);
216             /*
217              * assumes standard crypt(3)
218              */
219             if (!strcmp (enc_pass, calife->pw_passwd))
220             {
221                 got_pass = AUTH_OK;
222                 break;
223             }
224         } /* for */
225     } /* end if for getuid() */
226     return (got_pass);
227 }
228 
229 /** pam_pwcheck
230  **
231  ** Private method to check user password against PAM backend.
232  **
233  ** Parameters:   calife     struct passwd * target user
234  **               user_to_be char *          target user
235  **               user_pass  chat *          user password
236  **               l_size     int             maximum size for password
237  **
238  ** Returns:      whether the password was obtained through PAM or not
239  **               (including failure in PAM process)
240  **/
241 
242 static
pam_pwcheck(struct passwd * calife,char * user_to_be,char * user_pass,int l_size)243 int pam_pwcheck (struct passwd *calife, char * user_to_be, char * user_pass,\
244                  int l_size)
245 {
246     char  * who;
247     int   i, rval, got_pass = AUTH_ERR;
248 
249 #ifdef WITH_PAM
250     if (calife == NULL)
251         die(6, "calife must not be null");
252     who = calife->pw_name;
253     if (who[0] == '\0')
254         die(6, "calife->pw_name must not be null");
255     if (getuid () != 0)
256     {
257         MESSAGE ("Trying PAM\n");
258         for ( i = 0; i < MAX_ATTEMPTS; i++ )
259         {
260             get_user_passwd (user_pass, l_size);
261             /*
262 
263              * At this point, either we have a password or
264              * it has died already
265              */
266             MESSAGE ("Testing auth with PAM.\n");
267 
268             rval = auth_pam (&calife, user_pass);
269 
270             MESSAGE_1 ("PAM auth_pam returned %d\n", rval);
271             if (rval)
272             {
273                 syslog (LOG_AUTH | LOG_ERR, "PAM failed with code %d for %s", rval, who);
274                 /*
275                  * Check return value:
276                  * - 0:   auth succeeded
277                  * - >0:  auth failed.
278                  */
279                 /* Fallback to previous methods? */
280             }
281             else
282             {
283                 syslog (LOG_AUTH | LOG_INFO, "PAM auth succeeded for %s", who);
284                 got_pass = AUTH_OK;
285                 break;
286             }
287         } /* end for */
288     }
289     else
290         got_pass = AUTH_OK;
291 #endif
292     return got_pass;
293 }
294 
295 /** get_user_passwd
296  **
297  ** When not using PAM, get the user's password with getpass/getpassphrase
298  **
299  ** Parameters:    user_pass    char * password
300  **                l_size       int    maximum size
301  **
302  ** Returns:       nothing
303  **/
304 
305 static
get_user_passwd(char * user_pass,int l_size)306 void get_user_passwd(char * user_pass, int l_size)
307 {
308     char * pt_pass;
309 
310     /* XXX Solaris 10
311      * Solaris 5.10 (and maybe before) truncate getpass() entry
312      * to 8 bytes, use getpassphrase(3) instead
313      *
314      * Use getpassphrase(3) if available
315      */
316 #ifdef HAVE_GETPASSPHRASE
317         pt_pass = (char *) getpassphrase ("Password:");
318 #else
319         pt_pass = (char *) getpass ("Password:");
320 #endif /* HAVE_GETPASSPHRASE */
321     /*
322      * XXX don't assume getpass(3) will check the buffer
323      * length. Linux glibc apparently lacks such checking and
324      * will happily segfault if the previous entered password
325      * was too big.
326      * cf. <URL:http://www.securityfocus.com/archive/1/355510>
327      */
328     if (pt_pass == NULL)
329         die(1, "Corrupted or too long password");
330     memset (user_pass, '\0', l_size);
331     /*
332      * Be a bit more careful there
333      */
334     strncpy (user_pass, pt_pass, l_size);
335     user_pass[l_size - 1] = '\0';
336 }
337 
338 /*
339 
340   get_user_data
341     calife
342     scalife
343   get_user_password
344     (select getpass or getpassphrase)
345   check_passwd
346     if_pam
347       auth_pam
348     else
349       (select encryption method based on sig $nn$salt$passwd)
350     end
351  */
352