xref: /freebsd/crypto/openssh/auth-pam.c (revision ee2ea5ceafed78a5bd9810beb9e3ca927180c226)
1 /*
2  * Copyright (c) 2000 Damien Miller.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24 
25 #include "includes.h"
26 
27 #ifdef USE_PAM
28 #include <security/pam_appl.h>
29 #include "ssh.h"
30 #include "xmalloc.h"
31 #include "log.h"
32 #include "servconf.h"
33 #include "readpass.h"
34 #include "canohost.h"
35 
36 RCSID("$FreeBSD$");
37 
38 #define NEW_AUTHTOK_MSG \
39 	"Warning: Your password has expired, please change it now"
40 
41 #define SSHD_PAM_SERVICE "sshd"
42 #define PAM_STRERROR(a, b) pam_strerror((a), (b))
43 
44 /* Callbacks */
45 static int do_pam_conversation(int num_msg, const struct pam_message **msg,
46 	  struct pam_response **resp, void *appdata_ptr);
47 void do_pam_cleanup_proc(void *context);
48 void pam_msg_cat(const char *msg);
49 
50 /* module-local variables */
51 static struct pam_conv conv = {
52 	do_pam_conversation,
53 	NULL
54 };
55 static pam_handle_t *pamh = NULL;
56 static const char *pampasswd = NULL;
57 static char *pam_msg = NULL;
58 extern ServerOptions options;
59 
60 /* states for do_pam_conversation() */
61 typedef enum { INITIAL_LOGIN, OTHER } pamstates;
62 static pamstates pamstate = INITIAL_LOGIN;
63 /* remember whether pam_acct_mgmt() returned PAM_NEWAUTHTOK_REQD */
64 static int password_change_required = 0;
65 /* remember whether the last pam_authenticate() succeeded or not */
66 static int was_authenticated = 0;
67 
68 /* Remember what has been initialised */
69 static int session_opened = 0;
70 static int creds_set = 0;
71 
72 /*
73  * accessor which allows us to switch conversation structs according to
74  * the authentication method being used
75  */
76 void do_pam_set_conv(struct pam_conv *conv)
77 {
78 	pam_set_item(pamh, PAM_CONV, conv);
79 }
80 
81 /*
82  * PAM conversation function.
83  * There are two states this can run in.
84  *
85  * INITIAL_LOGIN mode simply feeds the password from the client into
86  * PAM in response to PAM_PROMPT_ECHO_OFF, and collects output
87  * messages with pam_msg_cat().  This is used during initial
88  * authentication to bypass the normal PAM password prompt.
89  *
90  * OTHER mode handles PAM_PROMPT_ECHO_OFF with read_passphrase(prompt, 1)
91  * and outputs messages to stderr. This mode is used if pam_chauthtok()
92  * is called to update expired passwords.
93  */
94 static int do_pam_conversation(int num_msg, const struct pam_message **msg,
95 	struct pam_response **resp, void *appdata_ptr)
96 {
97 	struct pam_response *reply;
98 	int count;
99 	char buf[1024];
100 
101 	/* PAM will free this later */
102 	reply = malloc(num_msg * sizeof(*reply));
103 	if (reply == NULL)
104 		return PAM_CONV_ERR;
105 
106 	for (count = 0; count < num_msg; count++) {
107 		switch ((*msg)[count].msg_style) {
108 			case PAM_PROMPT_ECHO_ON:
109 				if (pamstate == INITIAL_LOGIN) {
110 					free(reply);
111 					return PAM_CONV_ERR;
112 				} else {
113 					fputs((*msg)[count].msg, stderr);
114 					fgets(buf, sizeof(buf), stdin);
115 					reply[count].resp = xstrdup(buf);
116 					reply[count].resp_retcode = PAM_SUCCESS;
117 					break;
118 				}
119 			case PAM_PROMPT_ECHO_OFF:
120 				if (pamstate == INITIAL_LOGIN) {
121 					if (pampasswd == NULL) {
122 						free(reply);
123 						return PAM_CONV_ERR;
124 					}
125 					reply[count].resp = xstrdup(pampasswd);
126 				} else {
127 					reply[count].resp =
128 						xstrdup(read_passphrase((*msg)[count].msg, 1));
129 				}
130 				reply[count].resp_retcode = PAM_SUCCESS;
131 				break;
132 			case PAM_ERROR_MSG:
133 			case PAM_TEXT_INFO:
134 				if ((*msg)[count].msg != NULL) {
135 					if (pamstate == INITIAL_LOGIN)
136 						pam_msg_cat((*msg)[count].msg);
137 					else {
138 						fputs((*msg)[count].msg, stderr);
139 						fputs("\n", stderr);
140 					}
141 				}
142 				reply[count].resp = xstrdup("");
143 				reply[count].resp_retcode = PAM_SUCCESS;
144 				break;
145 			default:
146 				free(reply);
147 				return PAM_CONV_ERR;
148 		}
149 	}
150 
151 	*resp = reply;
152 
153 	return PAM_SUCCESS;
154 }
155 
156 /* Called at exit to cleanly shutdown PAM */
157 void do_pam_cleanup_proc(void *context)
158 {
159 	int pam_retval;
160 
161 	if (pamh != NULL && session_opened) {
162 		pam_retval = pam_close_session(pamh, 0);
163 		if (pam_retval != PAM_SUCCESS) {
164 			log("Cannot close PAM session[%d]: %.200s",
165 				pam_retval, PAM_STRERROR(pamh, pam_retval));
166 		}
167 	}
168 
169 	if (pamh != NULL && creds_set) {
170 		pam_retval = pam_setcred(pamh, PAM_DELETE_CRED);
171 		if (pam_retval != PAM_SUCCESS) {
172 			debug("Cannot delete credentials[%d]: %.200s",
173 				pam_retval, PAM_STRERROR(pamh, pam_retval));
174 		}
175 	}
176 
177 	if (pamh != NULL) {
178 		pam_retval = pam_end(pamh, pam_retval);
179 		if (pam_retval != PAM_SUCCESS) {
180 			log("Cannot release PAM authentication[%d]: %.200s",
181 				pam_retval, PAM_STRERROR(pamh, pam_retval));
182 		}
183 	}
184 }
185 
186 /* Attempt password authentation using PAM */
187 int auth_pam_password(Authctxt *authctxt, const char *password)
188 {
189 	struct passwd *pw = authctxt->pw;
190 	int pam_retval;
191 
192 	do_pam_set_conv(&conv);
193 
194 	/* deny if no user. */
195 	if (pw == NULL)
196 		return 0;
197 	if (pw->pw_uid == 0 && options.permit_root_login == 2)
198 		return 0;
199 	if (*password == '\0' && options.permit_empty_passwd == 0)
200 		return 0;
201 
202 	pampasswd = password;
203 
204 	pamstate = INITIAL_LOGIN;
205 	pam_retval = pam_authenticate(pamh, 0);
206 	was_authenticated = (pam_retval == PAM_SUCCESS);
207 	if (pam_retval == PAM_SUCCESS) {
208 		debug("PAM Password authentication accepted for user \"%.100s\"",
209 			pw->pw_name);
210 		return 1;
211 	} else {
212 		debug("PAM Password authentication for \"%.100s\" failed[%d]: %s",
213 			pw->pw_name, pam_retval, PAM_STRERROR(pamh, pam_retval));
214 		return 0;
215 	}
216 }
217 
218 /* Do account management using PAM */
219 int do_pam_account(char *username, char *remote_user)
220 {
221 	int pam_retval;
222 
223 	do_pam_set_conv(&conv);
224 
225 	debug("PAM setting rhost to \"%.200s\"",
226 	    get_canonical_hostname(options.verify_reverse_mapping));
227 	pam_retval = pam_set_item(pamh, PAM_RHOST,
228 		get_canonical_hostname(options.verify_reverse_mapping));
229 	if (pam_retval != PAM_SUCCESS) {
230 		fatal("PAM set rhost failed[%d]: %.200s",
231 			pam_retval, PAM_STRERROR(pamh, pam_retval));
232 	}
233 
234 	if (remote_user != NULL) {
235 		debug("PAM setting ruser to \"%.200s\"", remote_user);
236 		pam_retval = pam_set_item(pamh, PAM_RUSER, remote_user);
237 		if (pam_retval != PAM_SUCCESS) {
238 			fatal("PAM set ruser failed[%d]: %.200s",
239 				pam_retval, PAM_STRERROR(pamh, pam_retval));
240 		}
241 	}
242 
243 	pam_retval = pam_acct_mgmt(pamh, 0);
244 	switch (pam_retval) {
245 		case PAM_SUCCESS:
246 			/* This is what we want */
247 			break;
248 		case PAM_NEW_AUTHTOK_REQD:
249 			pam_msg_cat(NEW_AUTHTOK_MSG);
250 			/* flag that password change is necessary */
251 			password_change_required = 1;
252 			break;
253 		default:
254 			log("PAM rejected by account configuration[%d]: %.200s",
255 				pam_retval, PAM_STRERROR(pamh, pam_retval));
256 			return(0);
257 	}
258 
259 	return(1);
260 }
261 
262 /* Do PAM-specific session initialisation */
263 void do_pam_session(char *username, const char *ttyname)
264 {
265 	int pam_retval;
266 
267 	do_pam_set_conv(&conv);
268 
269 	if (ttyname != NULL) {
270 		debug("PAM setting tty to \"%.200s\"", ttyname);
271 		pam_retval = pam_set_item(pamh, PAM_TTY, ttyname);
272 		if (pam_retval != PAM_SUCCESS) {
273 			fatal("PAM set tty failed[%d]: %.200s",
274 				pam_retval, PAM_STRERROR(pamh, pam_retval));
275 		}
276 	}
277 
278 	debug("do_pam_session: euid %u, uid %u", geteuid(), getuid());
279 	pam_retval = pam_open_session(pamh, 0);
280 	if (pam_retval != PAM_SUCCESS) {
281 		fatal("PAM session setup failed[%d]: %.200s",
282 			pam_retval, PAM_STRERROR(pamh, pam_retval));
283 	}
284 
285 	session_opened = 1;
286 }
287 
288 /* Set PAM credentials */
289 void do_pam_setcred(void)
290 {
291 	int pam_retval;
292 
293 	do_pam_set_conv(&conv);
294 
295 	debug("PAM establishing creds");
296 	pam_retval = pam_setcred(pamh, PAM_ESTABLISH_CRED);
297 	if (pam_retval != PAM_SUCCESS) {
298 		if (was_authenticated)
299 			fatal("PAM setcred failed[%d]: %.200s",
300 			      pam_retval, PAM_STRERROR(pamh, pam_retval));
301 		else
302 			debug("PAM setcred failed[%d]: %.200s",
303 			      pam_retval, PAM_STRERROR(pamh, pam_retval));
304 	} else
305 		creds_set = 1;
306 }
307 
308 /* accessor function for file scope static variable */
309 int pam_password_change_required(void)
310 {
311 	return password_change_required;
312 }
313 
314 /*
315  * Have user change authentication token if pam_acct_mgmt() indicated
316  * it was expired.  This needs to be called after an interactive
317  * session is established and the user's pty is connected to
318  * stdin/stout/stderr.
319  */
320 void do_pam_chauthtok(void)
321 {
322 	int pam_retval;
323 
324 	do_pam_set_conv(&conv);
325 
326 	if (password_change_required) {
327 		pamstate = OTHER;
328 		/*
329 		 * XXX: should we really loop forever?
330 		 */
331 		do {
332 			pam_retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
333 			if (pam_retval != PAM_SUCCESS) {
334 				log("PAM pam_chauthtok failed[%d]: %.200s",
335 					pam_retval, PAM_STRERROR(pamh, pam_retval));
336 			}
337 		} while (pam_retval != PAM_SUCCESS);
338 	}
339 }
340 
341 /* Cleanly shutdown PAM */
342 void finish_pam(void)
343 {
344 	do_pam_cleanup_proc(NULL);
345 	fatal_remove_cleanup(&do_pam_cleanup_proc, NULL);
346 }
347 
348 /* Start PAM authentication for specified account */
349 void start_pam(struct passwd *pw)
350 {
351 	int pam_retval;
352 
353 	debug("Starting up PAM with username \"%.200s\"", pw->pw_name);
354 
355 	pam_retval = pam_start(SSHD_PAM_SERVICE, pw->pw_name, &conv, &pamh);
356 
357 	if (pam_retval != PAM_SUCCESS) {
358 		fatal("PAM initialisation failed[%d]: %.200s",
359 			pam_retval, PAM_STRERROR(pamh, pam_retval));
360 	}
361 
362 #ifdef PAM_TTY_KLUDGE
363 	/*
364 	 * Some PAM modules (e.g. pam_time) require a TTY to operate,
365 	 * and will fail in various stupid ways if they don't get one.
366 	 * sshd doesn't set the tty until too late in the auth process and may
367 	 * not even need one (for tty-less connections)
368 	 * Kludge: Set a fake PAM_TTY
369 	 */
370 	pam_retval = pam_set_item(pamh, PAM_TTY, "ssh");
371 	if (pam_retval != PAM_SUCCESS) {
372 		fatal("PAM set tty failed[%d]: %.200s",
373 			pam_retval, PAM_STRERROR(pamh, pam_retval));
374 	}
375 #endif /* PAM_TTY_KLUDGE */
376 
377 	fatal_add_cleanup(&do_pam_cleanup_proc, NULL);
378 }
379 
380 /* Return list of PAM enviornment strings */
381 char **fetch_pam_environment(void)
382 {
383 #ifdef HAVE_PAM_GETENVLIST
384 	return(pam_getenvlist(pamh));
385 #else /* HAVE_PAM_GETENVLIST */
386 	return(NULL);
387 #endif /* HAVE_PAM_GETENVLIST */
388 }
389 
390 /* Print any messages that have been generated during authentication */
391 /* or account checking to stderr */
392 void print_pam_messages(void)
393 {
394 	if (pam_msg != NULL)
395 		fputs(pam_msg, stderr);
396 }
397 
398 /* Append a message to the PAM message buffer */
399 void pam_msg_cat(const char *msg)
400 {
401 	char *p;
402 	size_t new_msg_len;
403 	size_t pam_msg_len;
404 
405 	new_msg_len = strlen(msg);
406 
407 	if (pam_msg) {
408 		pam_msg_len = strlen(pam_msg);
409 		pam_msg = xrealloc(pam_msg, new_msg_len + pam_msg_len + 2);
410 		p = pam_msg + pam_msg_len;
411 	} else {
412 		pam_msg = p = xmalloc(new_msg_len + 2);
413 	}
414 
415 	memcpy(p, msg, new_msg_len);
416 	p[new_msg_len] = '\n';
417 	p[new_msg_len + 1] = '\0';
418 }
419 
420 struct inverted_pam_userdata {
421     /*
422      * Pipe for telling whether we are doing conversation or sending
423      * authentication results.
424      */
425     int statefd[2];
426     int challengefd[2];
427     int responsefd[2];
428 
429     /* Whether we have sent off our challenge */
430     int state;
431 };
432 
433 #define STATE_CONV	1
434 #define STATE_AUTH_OK	2
435 #define STATE_AUTH_FAIL	3
436 
437 int
438 ssh_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp,
439 	 void *userdata) {
440 	int i;
441 	FILE *reader;
442 	char buf[1024];
443 	struct pam_response *reply = NULL;
444 	char state_to_write = STATE_CONV; /* One char to write */
445 	struct inverted_pam_userdata *ud = userdata;
446 	char *response = NULL;
447 
448 	/* The stdio functions are more convenient for the read half */
449 	reader = fdopen(ud->responsefd[0], "rb");
450 	if (reader == NULL)
451 		goto protocol_failure;
452 
453 	reply = malloc(num_msg * sizeof(struct pam_response));
454 	if (reply == NULL)
455 		return PAM_CONV_ERR;
456 
457 	if (write(ud->statefd[1], &state_to_write, 1) != 1)
458 		goto protocol_failure;
459 
460 	/*
461 	 * Re-package our data and send it off to our better half (the actual SSH
462 	 * process)
463 	 */
464 	if (write(ud->challengefd[1], buf,
465 		  sprintf(buf, "%d\n", num_msg)) == -1)
466 		goto protocol_failure;
467 	for (i = 0; i < num_msg; i++) {
468 		if (write(ud->challengefd[1], buf,
469 			  sprintf(buf, "%d\n", msg[i]->msg_style)) == -1)
470 			goto protocol_failure;
471 		if (write(ud->challengefd[1], buf,
472 			  sprintf(buf, "%d\n", strlen(msg[i]->msg))) == -1)
473 			goto protocol_failure;
474 		if (write(ud->challengefd[1], msg[i]->msg,
475 			  strlen(msg[i]->msg)) == -1)
476 			goto protocol_failure;
477 	}
478 	/*
479 	 * Read back responses.  These may not be as nice as we want, as the SSH
480 	 * protocol isn't exactly a perfect fit with PAM.
481 	 */
482 
483 	for (i = 0; i < num_msg; i++) {
484 		char buf[1024];
485 		char *endptr;
486 		size_t len;	/* Length of the response */
487 
488 		switch (msg[i]->msg_style) {
489 		case PAM_PROMPT_ECHO_OFF:
490 		case PAM_PROMPT_ECHO_ON:
491 			if (fgets(buf, sizeof(buf), reader) == NULL)
492 				goto protocol_failure;
493 			len = (size_t)strtoul(buf, &endptr, 10);
494 			/* The length is supposed to stand on a line by itself */
495 			if (endptr == NULL || *endptr != '\n')
496 				goto protocol_failure;
497 			response = malloc(len+1);
498 			if (response == NULL)
499 				goto protocol_failure;
500 			if (fread(response, len, 1, reader) != 1)
501 				goto protocol_failure;
502 			response[len] = '\0';
503 			reply[i].resp = response;
504 			response = NULL;
505 			break;
506 		default:
507 			reply[i].resp = NULL;
508 			break;
509 		}
510 	}
511 	*resp = reply;
512 	return PAM_SUCCESS;
513  protocol_failure:
514 	free(reply);
515 	return PAM_CONV_ERR;
516 }
517 
518 void
519 ipam_free_cookie(struct inverted_pam_cookie *cookie) {
520 	struct inverted_pam_userdata *ud;
521 	int i;
522 
523 	if (cookie == NULL)
524 		return;
525 	ud = cookie->userdata;
526 	cookie->userdata = NULL;
527 	/* Free userdata if allocated */
528 	if (ud) {
529 		/* Close any opened file descriptors */
530 		if (ud->statefd[0] != -1)
531 			close(ud->statefd[0]);
532 		if (ud->statefd[1] != -1)
533 			close(ud->statefd[1]);
534 		if (ud->challengefd[0] != -1)
535 			close(ud->challengefd[0]);
536 		if (ud->challengefd[1] != -1)
537 			close(ud->challengefd[1]);
538 		if (ud->responsefd[0] != -1)
539 			close(ud->responsefd[0]);
540 		if (ud->responsefd[1] != -1)
541 			close(ud->responsefd[1]);
542 		free(ud);
543 		ud = NULL;
544 	}
545 	/* Now free the normal cookie */
546 	if (cookie->pid != 0 && cookie->pid != -1) {
547 		int status;
548 
549 		/* XXX Use different signal? */
550 		kill(cookie->pid, SIGKILL);
551 		waitpid(cookie->pid, &status, 0);
552 	}
553 	for (i = 0; i < cookie->num_msg; i++) {
554 		if (cookie->resp && cookie->resp[i]) {
555 			free(cookie->resp[i]->resp);
556 			free(cookie->resp[i]);
557 		}
558 		if (cookie->msg && cookie->msg[i]) {
559 			free((void *)cookie->msg[i]->msg);
560 			free(cookie->msg[i]);
561 		}
562 	}
563 	free(cookie->msg);
564 	free(cookie->resp);
565 	free(cookie);
566 }
567 
568 /*
569  * Do first half of PAM authentication - this comes to the point where
570  * you get a message to send to the user.
571  */
572 struct inverted_pam_cookie *
573 ipam_start_auth(const char *service, const char *username) {
574 	struct inverted_pam_cookie *cookie;
575 	struct inverted_pam_userdata *ud;
576 	static struct pam_conv conv = {
577 		ssh_conv,
578 		NULL
579 	};
580 	const char *rhost;
581 
582 	cookie = malloc(sizeof(*cookie));
583 	if (cookie == NULL)
584 		return NULL;
585 	cookie->state = 0;
586 	/* Set up the cookie so ipam_freecookie can be used on it */
587 	cookie->num_msg = 0;
588 	cookie->msg = NULL;
589 	cookie->resp = NULL;
590 	cookie->pid = -1;
591 
592 	ud = calloc(sizeof(*ud), 1);
593 	if (ud == NULL) {
594 		free(cookie);
595 		return NULL;
596 	}
597 	cookie->userdata = ud;
598 	ud->statefd[0] = ud->statefd[1] = -1;
599 	ud->challengefd[0] = ud->challengefd[1] = -1;
600 	ud->responsefd[0] = ud->responsefd[1] = -1;
601 
602 	if (pipe(ud->statefd) != 0) {
603 		ud->statefd[0] = ud->statefd[1] = -1;
604 		ipam_free_cookie(cookie);
605 		return NULL;
606 	}
607 	if (pipe(ud->challengefd) != 0) {
608 		ud->challengefd[0] = ud->challengefd[1] = -1;
609 		ipam_free_cookie(cookie);
610 		return NULL;
611 	}
612 	if (pipe(ud->responsefd) != 0) {
613 		ud->responsefd[0] = ud->responsefd[1] = -1;
614 		ipam_free_cookie(cookie);
615 		return NULL;
616 	}
617 	rhost = get_canonical_hostname(options.verify_reverse_mapping);
618 	cookie->pid = fork();
619 	if (cookie->pid == -1) {
620 		ipam_free_cookie(cookie);
621 		return NULL;
622 	} else if (cookie->pid != 0) {
623 		int num_msgs;	/* Number of messages from PAM */
624 		char *endptr;
625 		char buf[1024];
626 		FILE *reader;
627 		size_t num_msg;
628 		int i;
629 		char state;	/* Which state did the connection just enter? */
630 
631 		/* We are the parent - wait for a call to the communications
632 		   function to turn up, or the challenge to be finished */
633 		if (read(ud->statefd[0], &state, 1) != 1) {
634 			ipam_free_cookie(cookie);
635 			return NULL;
636 		}
637 		cookie->state = state;
638 		switch (state) {
639 		case STATE_CONV:
640 			/* We are running the conversation function */
641 			/* The stdio functions are more convenient for read */
642 			reader = fdopen(ud->challengefd[0], "r");
643 			if (reader == NULL) {
644 				ipam_free_cookie(cookie);
645 				return NULL;
646 			}
647 			if (fgets(buf, 4, reader) == NULL) {
648 				fclose(reader);
649 				ipam_free_cookie(cookie);
650 				return NULL;
651 			}
652 			num_msg = (size_t)strtoul(buf, &endptr, 10);
653 			/* The length is supposed to stand on a line by itself */
654 			if (endptr == NULL || *endptr != '\n') {
655 				fclose(reader);
656 				ipam_free_cookie(cookie);
657 				return NULL;
658 			}
659 			cookie->msg =
660 				malloc(sizeof(struct pam_message *) * num_msg);
661 			cookie->resp =
662 				malloc(sizeof(struct pam_response *) * num_msg);
663 			if (cookie->msg == NULL || cookie->resp == NULL) {
664 				fclose(reader);
665 				ipam_free_cookie(cookie);
666 				return NULL;
667 			}
668 			for (i = 0; i < num_msg; i++) {
669 				cookie->msg[i] =
670 					malloc(sizeof(struct pam_message));
671 				cookie->resp[i] =
672 					malloc(sizeof(struct pam_response));
673 				if (cookie->msg[i] == NULL ||
674 				    cookie->resp[i] == NULL) {
675 					for (;;) {
676 						free(cookie->msg[i]);
677 						free(cookie->resp[i]);
678 						if (i == 0)
679 							break;
680 						i--;
681 					}
682 					fclose(reader);
683 					ipam_free_cookie(cookie);
684 					return NULL;
685 				}
686 				cookie->msg[i]->msg = NULL;
687 				cookie->resp[i]->resp = NULL;
688 				cookie->resp[i]->resp_retcode = 0;
689 			}
690 			/* Set up so the above will be freed on failure */
691 			cookie->num_msg = num_msg;
692 			/*
693 			 * We have a an allocated response and message for
694 			 * each of the entries in the PAM structure - transfer
695 			 * the data sent to the conversation function over.
696 			 */
697 			for (i = 0; i < num_msg; i++) {
698 				size_t len;
699 
700 				if (fgets(buf, sizeof(buf), reader) == NULL) {
701 					fclose(reader);
702 					ipam_free_cookie(cookie);
703 					return NULL;
704 				}
705 				cookie->msg[i]->msg_style =
706 					(size_t)strtoul(buf, &endptr, 10);
707 				if (endptr == NULL || *endptr != '\n') {
708 					fclose(reader);
709 					ipam_free_cookie(cookie);
710 					return NULL;
711 				}
712 				if (fgets(buf, sizeof(buf), reader) == NULL) {
713 					fclose(reader);
714 					ipam_free_cookie(cookie);
715 					return NULL;
716 				}
717 				len = (size_t)strtoul(buf, &endptr, 10);
718 				if (endptr == NULL || *endptr != '\n') {
719 					fclose(reader);
720 					ipam_free_cookie(cookie);
721 					return NULL;
722 				}
723 				cookie->msg[i]->msg = malloc(len + 1);
724 				if (cookie->msg[i]->msg == NULL) {
725 					fclose(reader);
726 					ipam_free_cookie(cookie);
727 					return NULL;
728 				}
729 				if (fread((char *)cookie->msg[i]->msg, len, 1, reader) !=
730 				    1) {
731 					fclose(reader);
732 					ipam_free_cookie(cookie);
733 					return NULL;
734 				}
735 				*(char *)&(cookie->msg[i]->msg[len]) = '\0';
736 			}
737 			break;
738 		case STATE_AUTH_OK:
739 		case STATE_AUTH_FAIL:
740 			break;
741 		default:
742 			/* Internal failure, somehow */
743 			fclose(reader);
744 			ipam_free_cookie(cookie);
745 			return NULL;
746 		}
747 		return cookie;
748 	} else {
749 		/* We are the child */
750 		pam_handle_t *pamh=NULL;
751 		int retval;
752 		char state;
753 
754 		conv.appdata_ptr = ud;
755 		retval = pam_start(service, username, &conv, &pamh);
756 		fprintf(stderr, "pam_start returned %d\n", retval);
757 		if (retval == PAM_SUCCESS)
758 			retval = pam_set_item(pamh, PAM_RHOST, rhost);
759 		/* Is user really user? */
760 		if (retval == PAM_SUCCESS)
761 			retval = pam_authenticate(pamh, 0);
762 		/* permitted access? */
763 		if (retval == PAM_SUCCESS)
764 			retval = pam_acct_mgmt(pamh, 0);
765 		/* This is where we have been authorized or not. */
766 
767 		/* Be conservative - flag as auth failure if we can't close */
768 		/*
769 		 * XXX This is based on example code from Linux-PAM -
770 		 * but can it really be correct to pam_end if
771 		 * pam_start failed?
772 		 */
773 		if (pam_end(pamh, retval) != PAM_SUCCESS)
774 			retval = PAM_AUTH_ERR;
775 
776 		/* Message to parent */
777 		state = retval == PAM_SUCCESS ? STATE_AUTH_OK : STATE_AUTH_FAIL;
778 		if (write(ud->statefd[1], &state, 1) != 1) {
779 			_exit(1);
780 		}
781 		/* FDs will be closed, so further communication will stop */
782 		_exit(0);
783 	}
784 }
785 
786 /*
787  * Do second half of PAM authentication - cookie should now be filled
788  * in with the response to the challenge.
789  */
790 
791 int
792 ipam_complete_auth(struct inverted_pam_cookie *cookie) {
793     int i;
794     char buf[1024];
795     struct inverted_pam_userdata *ud = cookie->userdata;
796     char state;
797 
798     /* Send over our responses */
799     for (i = 0; i < cookie->num_msg; i++) {
800 	if (cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_ON &&
801 	    cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
802 	    continue;
803 	if (write(ud->responsefd[1], buf,
804 		  sprintf(buf, "%d\n", strlen(cookie->resp[i]->resp))) == -1) {
805 	    ipam_free_cookie(cookie);
806 	    return 0;
807 	}
808 	if (write(ud->responsefd[1], cookie->resp[i]->resp,
809 		  strlen(cookie->resp[i]->resp)) == -1) {
810 	    ipam_free_cookie(cookie);
811 	    return 0;
812 	}
813     }
814     /* Find out what state we are changing to */
815     if (read(ud->statefd[0], &state, 1) != 1) {
816 	ipam_free_cookie(cookie);
817 	return 0;
818     }
819 
820     return state == STATE_AUTH_OK ? 1 : 0;
821 }
822 
823 #endif /* USE_PAM */
824