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