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