xref: /freebsd/crypto/openssh/auth-pam.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
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.reverse_mapping_check));
227 	pam_retval = pam_set_item(pamh, PAM_RHOST,
228 		get_canonical_hostname(options.reverse_mapping_check));
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 
581 	cookie = malloc(sizeof(*cookie));
582 	if (cookie == NULL)
583 		return NULL;
584 	cookie->state = 0;
585 	/* Set up the cookie so ipam_freecookie can be used on it */
586 	cookie->num_msg = 0;
587 	cookie->msg = NULL;
588 	cookie->resp = NULL;
589 	cookie->pid = -1;
590 
591 	ud = calloc(sizeof(*ud), 1);
592 	if (ud == NULL) {
593 		free(cookie);
594 		return NULL;
595 	}
596 	cookie->userdata = ud;
597 	ud->statefd[0] = ud->statefd[1] = -1;
598 	ud->challengefd[0] = ud->challengefd[1] = -1;
599 	ud->responsefd[0] = ud->responsefd[1] = -1;
600 
601 	if (pipe(ud->statefd) != 0) {
602 		ud->statefd[0] = ud->statefd[1] = -1;
603 		ipam_free_cookie(cookie);
604 		return NULL;
605 	}
606 	if (pipe(ud->challengefd) != 0) {
607 		ud->challengefd[0] = ud->challengefd[1] = -1;
608 		ipam_free_cookie(cookie);
609 		return NULL;
610 	}
611 	if (pipe(ud->responsefd) != 0) {
612 		ud->responsefd[0] = ud->responsefd[1] = -1;
613 		ipam_free_cookie(cookie);
614 		return NULL;
615 	}
616 	cookie->pid = fork();
617 	if (cookie->pid == -1) {
618 		ipam_free_cookie(cookie);
619 		return NULL;
620 	} else if (cookie->pid != 0) {
621 		int num_msgs;	/* Number of messages from PAM */
622 		char *endptr;
623 		char buf[1024];
624 		FILE *reader;
625 		size_t num_msg;
626 		int i;
627 		char state;	/* Which state did the connection just enter? */
628 
629 		/* We are the parent - wait for a call to the communications
630 		   function to turn up, or the challenge to be finished */
631 		if (read(ud->statefd[0], &state, 1) != 1) {
632 			ipam_free_cookie(cookie);
633 			return NULL;
634 		}
635 		cookie->state = state;
636 		switch (state) {
637 		case STATE_CONV:
638 			/* We are running the conversation function */
639 			/* The stdio functions are more convenient for read */
640 			reader = fdopen(ud->challengefd[0], "r");
641 			if (reader == NULL) {
642 				ipam_free_cookie(cookie);
643 				return NULL;
644 			}
645 			if (fgets(buf, 4, reader) == NULL) {
646 				fclose(reader);
647 				ipam_free_cookie(cookie);
648 				return NULL;
649 			}
650 			num_msg = (size_t)strtoul(buf, &endptr, 10);
651 			/* The length is supposed to stand on a line by itself */
652 			if (endptr == NULL || *endptr != '\n') {
653 				fclose(reader);
654 				ipam_free_cookie(cookie);
655 				return NULL;
656 			}
657 			cookie->msg =
658 				malloc(sizeof(struct pam_message *) * num_msg);
659 			cookie->resp =
660 				malloc(sizeof(struct pam_response *) * num_msg);
661 			if (cookie->msg == NULL || cookie->resp == NULL) {
662 				fclose(reader);
663 				ipam_free_cookie(cookie);
664 				return NULL;
665 			}
666 			for (i = 0; i < num_msg; i++) {
667 				cookie->msg[i] =
668 					malloc(sizeof(struct pam_message));
669 				cookie->resp[i] =
670 					malloc(sizeof(struct pam_response));
671 				if (cookie->msg[i] == NULL ||
672 				    cookie->resp[i] == NULL) {
673 					for (;;) {
674 						free(cookie->msg[i]);
675 						free(cookie->resp[i]);
676 						if (i == 0)
677 							break;
678 						i--;
679 					}
680 					fclose(reader);
681 					ipam_free_cookie(cookie);
682 					return NULL;
683 				}
684 				cookie->msg[i]->msg = NULL;
685 				cookie->resp[i]->resp = NULL;
686 				cookie->resp[i]->resp_retcode = 0;
687 			}
688 			/* Set up so the above will be freed on failure */
689 			cookie->num_msg = num_msg;
690 			/*
691 			 * We have a an allocated response and message for
692 			 * each of the entries in the PAM structure - transfer
693 			 * the data sent to the conversation function over.
694 			 */
695 			for (i = 0; i < num_msg; i++) {
696 				size_t len;
697 
698 				if (fgets(buf, sizeof(buf), reader) == NULL) {
699 					fclose(reader);
700 					ipam_free_cookie(cookie);
701 					return NULL;
702 				}
703 				cookie->msg[i]->msg_style =
704 					(size_t)strtoul(buf, &endptr, 10);
705 				if (endptr == NULL || *endptr != '\n') {
706 					fclose(reader);
707 					ipam_free_cookie(cookie);
708 					return NULL;
709 				}
710 				if (fgets(buf, sizeof(buf), reader) == NULL) {
711 					fclose(reader);
712 					ipam_free_cookie(cookie);
713 					return NULL;
714 				}
715 				len = (size_t)strtoul(buf, &endptr, 10);
716 				if (endptr == NULL || *endptr != '\n') {
717 					fclose(reader);
718 					ipam_free_cookie(cookie);
719 					return NULL;
720 				}
721 				cookie->msg[i]->msg = malloc(len + 1);
722 				if (cookie->msg[i]->msg == NULL) {
723 					fclose(reader);
724 					ipam_free_cookie(cookie);
725 					return NULL;
726 				}
727 				if (fread((char *)cookie->msg[i]->msg, len, 1, reader) !=
728 				    1) {
729 					fclose(reader);
730 					ipam_free_cookie(cookie);
731 					return NULL;
732 				}
733 				*(char *)&(cookie->msg[i]->msg[len]) = '\0';
734 			}
735 			break;
736 		case STATE_AUTH_OK:
737 		case STATE_AUTH_FAIL:
738 			break;
739 		default:
740 			/* Internal failure, somehow */
741 			fclose(reader);
742 			ipam_free_cookie(cookie);
743 			return NULL;
744 		}
745 		return cookie;
746 	} else {
747 		/* We are the child */
748 		pam_handle_t *pamh=NULL;
749 		int retval;
750 		char state;
751 
752 		conv.appdata_ptr = ud;
753 		retval = pam_start(service, username, &conv, &pamh);
754 		/* Is user really user? */
755 		if (retval == PAM_SUCCESS)
756 			retval = pam_authenticate(pamh, 0);
757 		/* permitted access? */
758 		if (retval == PAM_SUCCESS)
759 			retval = pam_acct_mgmt(pamh, 0);
760 		/* This is where we have been authorized or not. */
761 
762 		/* Be conservative - flag as auth failure if we can't close */
763 		/*
764 		 * XXX This is based on example code from Linux-PAM -
765 		 * but can it really be correct to pam_end if
766 		 * pam_start failed?
767 		 */
768 		if (pam_end(pamh, retval) != PAM_SUCCESS)
769 			retval = PAM_AUTH_ERR;
770 
771 		/* Message to parent */
772 		state = retval == PAM_SUCCESS ? STATE_AUTH_OK : STATE_AUTH_FAIL;
773 		if (write(ud->statefd[1], &state, 1) != 1) {
774 			_exit(1);
775 		}
776 		/* FDs will be closed, so further communication will stop */
777 		_exit(0);
778 	}
779 }
780 
781 /*
782  * Do second half of PAM authentication - cookie should now be filled
783  * in with the response to the challenge.
784  */
785 
786 int
787 ipam_complete_auth(struct inverted_pam_cookie *cookie) {
788     int i;
789     char buf[1024];
790     struct inverted_pam_userdata *ud = cookie->userdata;
791     char state;
792 
793     /* Send over our responses */
794     for (i = 0; i < cookie->num_msg; i++) {
795 	if (cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_ON &&
796 	    cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
797 	    continue;
798 	if (write(ud->responsefd[1], buf,
799 		  sprintf(buf, "%d\n", strlen(cookie->resp[i]->resp))) == -1) {
800 	    ipam_free_cookie(cookie);
801 	    return 0;
802 	}
803 	if (write(ud->responsefd[1], cookie->resp[i]->resp,
804 		  strlen(cookie->resp[i]->resp)) == -1) {
805 	    ipam_free_cookie(cookie);
806 	    return 0;
807 	}
808     }
809     /* Find out what state we are changing to */
810     if (read(ud->statefd[0], &state, 1) != 1) {
811 	ipam_free_cookie(cookie);
812 	return 0;
813     }
814 
815     return state == STATE_AUTH_OK ? 1 : 0;
816 }
817 
818 #endif /* USE_PAM */
819