]> arthur.barton.de Git - netatalk.git/blob - etc/uams/uams_pam.c
add expired password handling. Thanks go to Leland Wallace for giving the right pointers.
[netatalk.git] / etc / uams / uams_pam.c
1 /*
2  * $Id: uams_pam.c,v 1.15.2.1.2.3 2003-10-30 00:21:47 bfernhomberg Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
6  * All Rights Reserved.  See COPYRIGHT.
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif /* HAVE_CONFIG_H */
12
13 #ifndef ATACC
14 #include <stdio.h>
15 #include <stdlib.h>
16 #ifdef HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif /* HAVE_UNISTD_H */
19
20 /* STDC check */
21 #if STDC_HEADERS
22 #include <string.h>
23 #else /* STDC_HEADERS */
24 #ifndef HAVE_STRCHR
25 #define strchr index
26 #define strrchr index
27 #endif /* HAVE_STRCHR */
28 char *strchr (), *strrchr ();
29 #ifndef HAVE_MEMCPY
30 #define memcpy(d,s,n) bcopy ((s), (d), (n))
31 #define memmove(d,s,n) bcopy ((s), (d), (n))
32 #endif /* ! HAVE_MEMCPY */
33 #endif /* STDC_HEADERS */
34
35 #include <atalk/logger.h>
36
37 #include <security/pam_appl.h>
38
39 #include <atalk/afp.h>
40 #include <atalk/uam.h>
41
42 #define PASSWDLEN 8
43
44 #ifndef MIN
45 #define MIN(a,b) ((a) < (b) ? (a) : (b))
46 #endif /* MIN */
47
48
49 /* Static variables used to communicate between the conversation function
50  * and the server_login function
51  */
52 static pam_handle_t *pamh = NULL; 
53 static char *hostname;
54 static char *PAM_username;
55 static char *PAM_password;
56
57 extern void append(void *, const char *, int);
58
59 /* PAM conversation function
60  * Here we assume (for now, at least) that echo on means login name, and
61  * echo off means password.
62  */
63 static int PAM_conv (int num_msg,
64                      const struct pam_message **msg,
65                      struct pam_response **resp,
66                      void *appdata_ptr) 
67 {
68   struct pam_response *reply;
69   int count;
70   
71 #define COPY_STRING(s) (s) ? strdup(s) : NULL
72   
73   if (num_msg < 1)
74     return PAM_CONV_ERR;
75
76   reply = (struct pam_response *) 
77     calloc(num_msg, sizeof(struct pam_response));
78
79   if (!reply)
80     return PAM_CONV_ERR;
81
82   for (count = 0; count < num_msg; count++) {
83     char *string = NULL;
84
85     switch (msg[count]->msg_style) {
86     case PAM_PROMPT_ECHO_ON:
87       if (!(string = COPY_STRING(PAM_username)))
88         goto pam_fail_conv;
89       break;
90     case PAM_PROMPT_ECHO_OFF:
91       if (!(string = COPY_STRING(PAM_password)))
92         goto pam_fail_conv;
93       break;
94     case PAM_TEXT_INFO:
95 #ifdef PAM_BINARY_PROMPT
96     case PAM_BINARY_PROMPT:
97 #endif /* PAM_BINARY_PROMPT */
98       /* ignore it... */
99       break;
100     case PAM_ERROR_MSG:
101     default:
102       goto pam_fail_conv;
103     }
104
105     if (string) {  
106       reply[count].resp_retcode = 0;
107       reply[count].resp = string;
108       string = NULL;
109     }
110   }
111
112   *resp = reply;
113   return PAM_SUCCESS;
114
115 pam_fail_conv:
116   for (count = 0; count < num_msg; count++) {
117     if (!reply[count].resp)
118       continue;
119     switch (msg[count]->msg_style) {
120     case PAM_PROMPT_ECHO_OFF:
121     case PAM_PROMPT_ECHO_ON:
122       free(reply[count].resp);
123       break;      
124     }
125   }
126   free(reply);
127   return PAM_CONV_ERR;
128 }
129
130 static struct pam_conv PAM_conversation = {
131   &PAM_conv,
132   NULL
133 };
134
135 static int login(void *obj, char *username, int ulen,  struct passwd **uam_pwd,
136                      char *ibuf, int ibuflen,
137                      char *rbuf, int *rbuflen)
138 {
139     struct passwd *pwd;
140     int err, PAM_error;
141
142     if (uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME,
143                              (void *) &hostname, NULL) < 0)
144     {
145         LOG(log_info, logtype_uams, "uams_pam.c :PAM: unable to retrieve client hostname");
146         hostname = NULL;
147     }
148     
149     ibuf[ PASSWDLEN ] = '\0';
150
151     if (( pwd = uam_getname(obj, username, ulen)) == NULL ) {
152         return AFPERR_PARAM;
153     }
154
155     LOG(log_info, logtype_uams, "cleartext login: %s", username);
156     PAM_username = username;
157     PAM_password = ibuf; /* Set these things up for the conv function */
158
159     err = AFPERR_NOTAUTH;
160     PAM_error = pam_start("netatalk", username, &PAM_conversation,
161                           &pamh);
162     if (PAM_error != PAM_SUCCESS)
163       goto login_err;
164
165     pam_set_item(pamh, PAM_TTY, "afpd");
166     pam_set_item(pamh, PAM_RHOST, hostname);
167     /* use PAM_DISALLOW_NULL_AUTHTOK if passwdminlen > 0 */
168     PAM_error = pam_authenticate(pamh,0);
169     if (PAM_error != PAM_SUCCESS) {
170       if (PAM_error == PAM_MAXTRIES) 
171         err = AFPERR_PWDEXPR;
172       goto login_err;
173     }      
174
175     PAM_error = pam_acct_mgmt(pamh, 0);
176     if (PAM_error != PAM_SUCCESS) {
177       if (PAM_error == PAM_NEW_AUTHTOK_REQD) /* Password change required */
178         err = AFPERR_PWDEXPR;
179 #ifdef PAM_AUTHTOKEN_REQD
180       else if (PAM_error == PAM_AUTHTOKEN_REQD) 
181         err = AFPERR_PWDCHNG;
182 #endif /* PAM_AUTHTOKEN_REQD */
183       else
184         goto login_err;
185     }
186
187 #ifndef PAM_CRED_ESTABLISH
188 #define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
189 #endif /* PAM_CRED_ESTABLISH */
190     PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
191     if (PAM_error != PAM_SUCCESS)
192       goto login_err;
193
194     PAM_error = pam_open_session(pamh, 0);
195     if (PAM_error != PAM_SUCCESS)
196       goto login_err;
197
198     *uam_pwd = pwd;
199
200     if (err == AFPERR_PWDEXPR)
201         return err;
202
203     return AFP_OK;
204
205 login_err:
206     pam_end(pamh, PAM_error);
207     pamh = NULL;
208     return err;
209 }
210
211 /* --------------------------
212    cleartxt login 
213 */
214 static int pam_login(void *obj, struct passwd **uam_pwd,
215                      char *ibuf, int ibuflen,
216                      char *rbuf, int *rbuflen)
217 {
218     char *username; 
219     int  len, ulen;
220
221     *rbuflen = 0;
222
223     if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) {
224         return AFPERR_MISC;
225     }
226
227     len = (unsigned char) *ibuf++;
228     if ( len > ulen ) {
229         return( AFPERR_PARAM );
230     }
231
232     memcpy(username, ibuf, len );
233     ibuf += len;
234
235     username[ len ] = '\0';
236
237     if ((unsigned long) ibuf & 1)  /* pad character */
238       ++ibuf;
239     return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
240 }
241
242 /* ----------------------------- */
243 static int pam_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
244                      char *ibuf, int ibuflen,
245                      char *rbuf, int *rbuflen)
246 {
247     char *username; 
248     int  len, ulen;
249     u_int16_t  temp16;
250
251     *rbuflen = 0;
252
253     if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0)
254       return AFPERR_MISC;
255
256     if (*uname != 3)
257         return AFPERR_PARAM;
258     uname++;
259     memcpy(&temp16, uname, sizeof(temp16));
260     len = ntohs(temp16);
261
262     if (!len || len > ulen ) {
263         return( AFPERR_PARAM );
264     }
265     memcpy(username, uname +2, len );
266     username[ len ] = '\0';
267     
268     return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
269 }
270
271 /* logout */
272 static void pam_logout() {
273     pam_close_session(pamh, 0);
274     pam_end(pamh, 0);
275     pamh = NULL;
276 }
277
278 /* change passwd */
279 static int pam_changepw(void *obj, char *username,
280                         struct passwd *pwd, char *ibuf, int ibuflen,
281                         char *rbuf, int *rbuflen)
282 {
283     char pw[PASSWDLEN + 1];
284     pam_handle_t *lpamh;
285     uid_t uid;
286     int PAM_error;
287
288     /* old password */
289     memcpy(pw, ibuf, PASSWDLEN);
290     memset(ibuf, 0, PASSWDLEN);
291     pw[PASSWDLEN] = '\0';
292
293     /* let's do a quick check for the same password */
294     if (memcmp(pw, ibuf + PASSWDLEN, PASSWDLEN) == 0)
295       return AFPERR_PWDSAME;
296
297     /* Set these things up for the conv function */
298     PAM_username = username;
299     PAM_password = pw; 
300
301     PAM_error = pam_start("netatalk", username, &PAM_conversation,
302                           &lpamh);
303     if (PAM_error != PAM_SUCCESS) 
304       return AFPERR_PARAM;
305     pam_set_item(lpamh, PAM_TTY, "afpd");
306     pam_set_item(lpamh, PAM_RHOST, hostname);
307
308     /* we might need to do this as root */
309     uid = geteuid();
310     seteuid(0);
311     PAM_error = pam_authenticate(lpamh,0);
312     if (PAM_error != PAM_SUCCESS) {
313       seteuid(uid);
314       pam_end(lpamh, PAM_error);
315       return AFPERR_NOTAUTH;
316     }
317
318     /* new password */
319     ibuf += PASSWDLEN;
320     PAM_password = ibuf;
321     ibuf[PASSWDLEN] = '\0';
322     
323     /* this really does need to be done as root */
324     PAM_error = pam_chauthtok(lpamh, 0);
325     seteuid(uid); /* un-root ourselves. */
326     memset(pw, 0, PASSWDLEN);
327     memset(ibuf, 0, PASSWDLEN);
328     if (PAM_error != PAM_SUCCESS) {
329       pam_end(lpamh, PAM_error);
330       return AFPERR_ACCESS;
331     }
332
333     pam_end(lpamh, 0);
334     return AFP_OK;
335 }
336
337
338 /* Printer ClearTxtUAM login */
339 int pam_printer(start, stop, username, out)
340         char    *start, *stop, *username;
341         struct papfile  *out;
342 {
343     int PAM_error;
344     char        *data, *p, *q;
345     char        password[PASSWDLEN + 1] = "\0";
346     static const char *loginok = "0\r";
347     struct passwd *pwd;
348
349     data = (char *)malloc(stop - start + 2);
350     if (!data) {
351         LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: malloc");
352         return(-1);
353     }
354
355     strncpy(data, start, stop - start + 1);
356     data[stop - start + 2] = 0;
357
358     /* We are looking for the following format in data:
359      * (username) (password)
360      *
361      * Let's hope username doesn't contain ") ("!
362      */
363
364     /* Parse input for username in () */
365     if ((p = strchr(data, '(' )) == NULL) {
366         LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: username not found in string");
367         free(data);
368         return(-1);
369     }
370     p++;
371     if ((q = strstr(p, ") (" )) == NULL) {
372         LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: username not found in string");
373         free(data);
374         return(-1);
375     }
376     strncpy(username, p, MIN(UAM_USERNAMELEN, q - p) );
377     username[ UAM_USERNAMELEN +1] = '\0';
378
379     /* Parse input for password in next () */
380     p = q + 3;
381     if ((q = strrchr(p, ')' )) == NULL) {
382         LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: password not found in string");
383         free(data);
384         return(-1);
385     }
386     strncpy(password, p, MIN(PASSWDLEN, (q - p)) );
387     password[ PASSWDLEN + 1] = '\0';
388
389     /* Done copying username and password, clean up */
390     free(data);
391
392     if (( pwd = uam_getname(NULL, username, strlen(username))) == NULL ) {
393         LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: ( %s ) not found ",
394             username);
395         return(-1);
396     }
397
398     if (uam_checkuser(pwd) < 0) {
399         /* syslog of error happens in uam_checkuser */
400         return(-1);
401     }
402
403     PAM_username = username;
404     PAM_password = password;
405
406     PAM_error = pam_start("netatalk", username, &PAM_conversation,
407                           &pamh);
408     if (PAM_error != PAM_SUCCESS) {
409         LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s", 
410                         username, pam_strerror(pamh, PAM_error));
411         pam_end(pamh, PAM_error);
412         pamh = NULL;
413         return(-1);
414     }
415
416     pam_set_item(pamh, PAM_TTY, "papd");
417     pam_set_item(pamh, PAM_RHOST, hostname);
418     PAM_error = pam_authenticate(pamh,0);
419     if (PAM_error != PAM_SUCCESS) {
420         LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s", 
421                         username, pam_strerror(pamh, PAM_error));
422         pam_end(pamh, PAM_error);
423         pamh = NULL;
424         return(-1);
425     }      
426
427     PAM_error = pam_acct_mgmt(pamh, 0);
428     if (PAM_error != PAM_SUCCESS) {
429         LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s", 
430                         username, pam_strerror(pamh, PAM_error));
431         pam_end(pamh, PAM_error);
432         pamh = NULL;
433         return(-1);
434     }
435
436     PAM_error = pam_open_session(pamh, 0);
437     if (PAM_error != PAM_SUCCESS) {
438         LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s", 
439                         username, pam_strerror(pamh, PAM_error));
440         pam_end(pamh, PAM_error);
441         pamh = NULL;
442         return(-1);
443     }
444
445     /* Login successful, but no need to hang onto it,
446        so logout immediately */
447     append(out, loginok, strlen(loginok));
448     LOG(log_info, logtype_uams, "Login ClearTxtUAM: %s", username);
449     pam_close_session(pamh, 0);
450     pam_end(pamh, 0);
451     pamh = NULL;
452
453     return(0);
454 }
455
456
457 static int uam_setup(const char *path)
458 {
459   if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Cleartxt Passwrd", 
460                    pam_login, NULL, pam_logout, pam_login_ext) < 0)
461         return -1;
462
463   if (uam_register(UAM_SERVER_CHANGEPW, path, "Cleartxt Passwrd",
464                    pam_changepw) < 0) {
465         uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
466         return -1;
467   }
468
469   if (uam_register(UAM_SERVER_PRINTAUTH, path, "ClearTxtUAM",
470                    pam_printer) < 0) {
471         return -1;
472   }
473
474   return 0;
475 }
476
477 static void uam_cleanup(void)
478 {
479   uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
480   uam_unregister(UAM_SERVER_CHANGEPW, "Cleartxt Passwrd");
481   uam_unregister(UAM_SERVER_PRINTAUTH, "ClearTxtUAM");
482 }
483
484 UAM_MODULE_EXPORT struct uam_export uams_clrtxt = {
485   UAM_MODULE_SERVER,
486   UAM_MODULE_VERSION,
487   uam_setup, uam_cleanup
488 };
489
490 UAM_MODULE_EXPORT struct uam_export uams_pam = {
491   UAM_MODULE_SERVER,
492   UAM_MODULE_VERSION,
493   uam_setup, uam_cleanup
494 };
495 #endif