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