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