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