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  */
PAM_conv(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr _U_)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 
login(void * obj,char * username,int ulen,struct passwd ** uam_pwd,char * ibuf,size_t ibuflen _U_,char * rbuf _U_,size_t * rbuflen _U_)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 */
pam_login(void * obj,struct passwd ** uam_pwd,char * ibuf,size_t ibuflen,char * rbuf,size_t * rbuflen)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 /* ----------------------------- */
pam_login_ext(void * obj,char * uname,struct passwd ** uam_pwd,char * ibuf,size_t ibuflen,char * rbuf,size_t * rbuflen)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 */
pam_logout(void)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 */
pam_changepw(void * obj _U_,char * username,struct passwd * pwd _U_,char * ibuf,size_t ibuflen _U_,char * rbuf _U_,size_t * rbuflen _U_)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 */
pam_printer(char * start,char * stop,char * username,struct papfile * out)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 
uam_setup(void * obj,const char * path)448 static int uam_setup(void *obj, 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 
uam_cleanup(void)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 };
486