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