]> arthur.barton.de Git - netatalk.git/blob - etc/uams/uams_pam.c
Merge master
[netatalk.git] / etc / uams / uams_pam.c
1 /*
2  * $Id: uams_pam.c,v 1.24 2010-03-30 10:25:49 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 #include <string.h>
19 #include <atalk/logger.h>
20
21 #ifdef HAVE_SECURITY_PAM_APPL_H
22 #include <security/pam_appl.h>
23 #endif
24 #ifdef HAVE_PAM_PAM_APPL_H
25 #include <pam/pam_appl.h>
26 #endif
27
28 #include <atalk/afp.h>
29 #include <atalk/uam.h>
30 #include <atalk/util.h>
31
32 #define PASSWDLEN 8
33
34 #ifndef MIN
35 #define MIN(a,b) ((a) < (b) ? (a) : (b))
36 #endif /* MIN */
37
38
39 /* Static variables used to communicate between the conversation function
40  * and the server_login function
41  */
42 static pam_handle_t *pamh = NULL; 
43 static const char *hostname;
44 static char *PAM_username;
45 static char *PAM_password;
46
47 /*XXX in etc/papd/file.h */
48 struct papfile;
49 extern UAM_MODULE_EXPORT void append(struct papfile *, const char *, int);
50
51 /* PAM conversation function
52  * Here we assume (for now, at least) that echo on means login name, and
53  * echo off means password.
54  */
55 static int PAM_conv (int num_msg,
56                      const struct pam_message **msg,
57                      struct pam_response **resp,
58                      void *appdata_ptr _U_) 
59 {
60   struct pam_response *reply;
61   int count;
62   
63 #define COPY_STRING(s) (s) ? strdup(s) : NULL
64   
65   if (num_msg < 1)
66     return PAM_CONV_ERR;
67
68   reply = (struct pam_response *) 
69     calloc(num_msg, sizeof(struct pam_response));
70
71   if (!reply)
72     return PAM_CONV_ERR;
73
74   for (count = 0; count < num_msg; count++) {
75     char *string = NULL;
76
77     switch (msg[count]->msg_style) {
78     case PAM_PROMPT_ECHO_ON:
79       if (!(string = COPY_STRING(PAM_username)))
80         goto pam_fail_conv;
81       break;
82     case PAM_PROMPT_ECHO_OFF:
83       if (!(string = COPY_STRING(PAM_password)))
84         goto pam_fail_conv;
85       break;
86     case PAM_TEXT_INFO:
87 #ifdef PAM_BINARY_PROMPT
88     case PAM_BINARY_PROMPT:
89 #endif /* PAM_BINARY_PROMPT */
90       /* ignore it... */
91       break;
92     case PAM_ERROR_MSG:
93     default:
94       goto pam_fail_conv;
95     }
96
97     if (string) {  
98       reply[count].resp_retcode = 0;
99       reply[count].resp = string;
100       string = NULL;
101     }
102   }
103
104   *resp = reply;
105   return PAM_SUCCESS;
106
107 pam_fail_conv:
108   for (count = 0; count < num_msg; count++) {
109     if (!reply[count].resp)
110       continue;
111     switch (msg[count]->msg_style) {
112     case PAM_PROMPT_ECHO_OFF:
113     case PAM_PROMPT_ECHO_ON:
114       free(reply[count].resp);
115       break;      
116     }
117   }
118   free(reply);
119   return PAM_CONV_ERR;
120 }
121
122 static struct pam_conv PAM_conversation = {
123   &PAM_conv,
124   NULL
125 };
126
127 static int login(void *obj, char *username, int ulen,  struct passwd **uam_pwd,
128                      char *ibuf, size_t ibuflen _U_,
129                      char *rbuf _U_, size_t *rbuflen _U_)
130 {
131     struct passwd *pwd;
132     int err, PAM_error;
133
134     if (uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME,
135                              (void *) &hostname, NULL) < 0)
136     {
137         LOG(log_info, logtype_uams, "uams_pam.c :PAM: unable to retrieve client hostname");
138         hostname = NULL;
139     }
140     
141     ibuf[ PASSWDLEN ] = '\0';
142
143     if (( pwd = uam_getname(obj, username, ulen)) == NULL ) {
144         return AFPERR_NOTAUTH;
145     }
146
147     LOG(log_info, logtype_uams, "cleartext login: %s", username);
148     PAM_username = username;
149     PAM_password = ibuf; /* Set these things up for the conv function */
150
151     err = AFPERR_NOTAUTH;
152     PAM_error = pam_start("netatalk", username, &PAM_conversation,
153                           &pamh);
154     if (PAM_error != PAM_SUCCESS)
155       goto login_err;
156
157     pam_set_item(pamh, PAM_TTY, "afpd");
158     pam_set_item(pamh, PAM_RHOST, hostname);
159     /* use PAM_DISALLOW_NULL_AUTHTOK if passwdminlen > 0 */
160     PAM_error = pam_authenticate(pamh,0);
161     if (PAM_error != PAM_SUCCESS) {
162       if (PAM_error == PAM_MAXTRIES) 
163         err = AFPERR_PWDEXPR;
164       goto login_err;
165     }      
166
167     PAM_error = pam_acct_mgmt(pamh, 0);
168     if (PAM_error != PAM_SUCCESS) {
169       if (PAM_error == PAM_NEW_AUTHTOK_REQD) /* Password change required */
170         err = AFPERR_PWDEXPR;
171 #ifdef PAM_AUTHTOKEN_REQD
172       else if (PAM_error == PAM_AUTHTOKEN_REQD) 
173         err = AFPERR_PWDCHNG;
174 #endif /* PAM_AUTHTOKEN_REQD */
175       else
176         goto login_err;
177     }
178
179 #ifndef PAM_CRED_ESTABLISH
180 #define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
181 #endif /* PAM_CRED_ESTABLISH */
182     PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
183     if (PAM_error != PAM_SUCCESS)
184       goto login_err;
185
186     PAM_error = pam_open_session(pamh, 0);
187     if (PAM_error != PAM_SUCCESS)
188       goto login_err;
189
190     *uam_pwd = pwd;
191
192     if (err == AFPERR_PWDEXPR)
193         return err;
194
195     return AFP_OK;
196
197 login_err:
198     pam_end(pamh, PAM_error);
199     pamh = NULL;
200     return err;
201 }
202
203 /* --------------------------
204    cleartxt login 
205 */
206 static int pam_login(void *obj, struct passwd **uam_pwd,
207                      char *ibuf, size_t ibuflen,
208                      char *rbuf, size_t *rbuflen)
209 {
210     char *username; 
211     size_t  len, ulen;
212
213     *rbuflen = 0;
214
215     if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) {
216         return AFPERR_MISC;
217     }
218
219     len = (unsigned char) *ibuf++;
220     if ( len > ulen ) {
221         return( AFPERR_PARAM );
222     }
223
224     memcpy(username, ibuf, len );
225     ibuf += len;
226
227     username[ len ] = '\0';
228
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, size_t ibuflen,
237                      char *rbuf, size_t *rbuflen)
238 {
239     char *username; 
240     size_t  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(void) {
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 _U_, char *username,
272                         struct passwd *pwd _U_, char *ibuf, size_t ibuflen _U_,
273                         char *rbuf _U_, size_t *rbuflen _U_)
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 static int pam_printer(char *start, char *stop, char *username, struct papfile *out)
332 {
333     int PAM_error;
334     char        *data, *p, *q;
335     char        password[PASSWDLEN + 1] = "\0";
336     static const char *loginok = "0\r";
337     struct passwd *pwd;
338
339     data = (char *)malloc(stop - start + 1);
340     if (!data) {
341         LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: malloc");
342         return(-1);
343     }
344
345     strlcpy(data, start, stop - start + 1);
346
347     /* We are looking for the following format in data:
348      * (username) (password)
349      *
350      * Let's hope username doesn't contain ") ("!
351      */
352
353     /* Parse input for username in () */
354     if ((p = strchr(data, '(' )) == NULL) {
355         LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: username not found in string");
356         free(data);
357         return(-1);
358     }
359     p++;
360     if ((q = strstr(p, ") (" )) == NULL) {
361         LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: username not found in string");
362         free(data);
363         return(-1);
364     }
365     memcpy(username, p, MIN(UAM_USERNAMELEN, q - p) );
366
367     /* Parse input for password in next () */
368     p = q + 3;
369     if ((q = strrchr(p, ')' )) == NULL) {
370         LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: password not found in string");
371         free(data);
372         return(-1);
373     }
374     memcpy(password, p, MIN(PASSWDLEN, (q - p)) );
375
376     /* Done copying username and password, clean up */
377     free(data);
378
379     if (( pwd = uam_getname(NULL, username, strlen(username))) == NULL ) {
380         LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: ( %s ) not found ",
381             username);
382         return(-1);
383     }
384
385     if (uam_checkuser(pwd) < 0) {
386         /* syslog of error happens in uam_checkuser */
387         return(-1);
388     }
389
390     PAM_username = username;
391     PAM_password = password;
392
393     PAM_error = pam_start("netatalk", username, &PAM_conversation,
394                           &pamh);
395     if (PAM_error != PAM_SUCCESS) {
396         LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s", 
397                         username, pam_strerror(pamh, PAM_error));
398         pam_end(pamh, PAM_error);
399         pamh = NULL;
400         return(-1);
401     }
402
403     pam_set_item(pamh, PAM_TTY, "papd");
404     pam_set_item(pamh, PAM_RHOST, hostname);
405     PAM_error = pam_authenticate(pamh,0);
406     if (PAM_error != PAM_SUCCESS) {
407         LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s", 
408                         username, pam_strerror(pamh, PAM_error));
409         pam_end(pamh, PAM_error);
410         pamh = NULL;
411         return(-1);
412     }      
413
414     PAM_error = pam_acct_mgmt(pamh, 0);
415     if (PAM_error != PAM_SUCCESS) {
416         LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s", 
417                         username, pam_strerror(pamh, PAM_error));
418         pam_end(pamh, PAM_error);
419         pamh = NULL;
420         return(-1);
421     }
422
423     PAM_error = pam_open_session(pamh, 0);
424     if (PAM_error != PAM_SUCCESS) {
425         LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s", 
426                         username, pam_strerror(pamh, PAM_error));
427         pam_end(pamh, PAM_error);
428         pamh = NULL;
429         return(-1);
430     }
431
432     /* Login successful, but no need to hang onto it,
433        so logout immediately */
434     append(out, loginok, strlen(loginok));
435     LOG(log_info, logtype_uams, "Login ClearTxtUAM: %s", username);
436     pam_close_session(pamh, 0);
437     pam_end(pamh, 0);
438     pamh = NULL;
439
440     return(0);
441 }
442
443
444 static int uam_setup(const char *path)
445 {
446   if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Cleartxt Passwrd", 
447                    pam_login, NULL, pam_logout, pam_login_ext) < 0)
448         return -1;
449
450   if (uam_register(UAM_SERVER_CHANGEPW, path, "Cleartxt Passwrd",
451                    pam_changepw) < 0) {
452         uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
453         return -1;
454   }
455
456   if (uam_register(UAM_SERVER_PRINTAUTH, path, "ClearTxtUAM",
457                    pam_printer) < 0) {
458         return -1;
459   }
460
461   return 0;
462 }
463
464 static void uam_cleanup(void)
465 {
466   uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
467   uam_unregister(UAM_SERVER_CHANGEPW, "Cleartxt Passwrd");
468   uam_unregister(UAM_SERVER_PRINTAUTH, "ClearTxtUAM");
469 }
470
471 UAM_MODULE_EXPORT struct uam_export uams_clrtxt = {
472   UAM_MODULE_SERVER,
473   UAM_MODULE_VERSION,
474   uam_setup, uam_cleanup
475 };
476
477 UAM_MODULE_EXPORT struct uam_export uams_pam = {
478   UAM_MODULE_SERVER,
479   UAM_MODULE_VERSION,
480   uam_setup, uam_cleanup
481 };