xref: /illumos-gate/usr/src/lib/libpam/pam_framework.c (revision bde334a8dbd66dfa70ce4d7fc9dcad6e1ae45fe4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Copyright (c) 2019, Joyent, Inc.
28  */
29 
30 #include <syslog.h>
31 #include <dlfcn.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <stdlib.h>
35 #include <strings.h>
36 #include <malloc.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 
41 #include <security/pam_appl.h>
42 #include <security/pam_modules.h>
43 #include <sys/mman.h>
44 
45 #include <libintl.h>
46 
47 #include "pam_impl.h"
48 
49 static char *pam_snames [PAM_NUM_MODULE_TYPES] = {
50 	PAM_ACCOUNT_NAME,
51 	PAM_AUTH_NAME,
52 	PAM_PASSWORD_NAME,
53 	PAM_SESSION_NAME
54 };
55 
56 static char *pam_inames [PAM_MAX_ITEMS] = {
57 /* NONE */		NULL,
58 /* PAM_SERVICE */	"service",
59 /* PAM_USER */		"user",
60 /* PAM_TTY */		"tty",
61 /* PAM_RHOST */		"rhost",
62 /* PAM_CONV */		"conv",
63 /* PAM_AUTHTOK */	"authtok",
64 /* PAM_OLDAUTHTOK */	"oldauthtok",
65 /* PAM_RUSER */		"ruser",
66 /* PAM_USER_PROMPT */	"user_prompt",
67 /* PAM_REPOSITORY */	"repository",
68 /* PAM_RESOURCE */	"resource",
69 /* PAM_AUSER */		"auser",
70 /* Undefined Items */
71 };
72 
73 /*
74  * This extra definition is needed in order to build this library
75  * on pre-64-bit-aware systems.
76  */
77 #if !defined(_LFS64_LARGEFILE)
78 #define	stat64	stat
79 #endif	/* !defined(_LFS64_LARGEFILE) */
80 
81 /* functions to dynamically load modules */
82 static int	load_modules(pam_handle_t *, int, char *, pamtab_t *);
83 static void	*open_module(pam_handle_t *, char *);
84 static int	load_function(void *, char *, int (**func)());
85 
86 /* functions to read and store the pam.conf configuration file */
87 static int	open_pam_conf(struct pam_fh **, pam_handle_t *, char *);
88 static void	close_pam_conf(struct pam_fh *);
89 static int	read_pam_conf(pam_handle_t *, char *);
90 static int	get_pam_conf_entry(struct pam_fh *, pam_handle_t *,
91     pamtab_t **);
92 static char	*read_next_token(char **);
93 static char	*nextline(struct pam_fh *, pam_handle_t *, int *);
94 static int	verify_pam_conf(pamtab_t *, char *);
95 
96 /* functions to clean up and free memory */
97 static void	clean_up(pam_handle_t *);
98 static void	free_pamconf(pamtab_t *);
99 static void	free_pam_conf_info(pam_handle_t *);
100 static void	free_env(env_list *);
101 
102 /* convenience functions for I18N/L10N communication */
103 
104 static void	free_resp(int, struct pam_response *);
105 static int	do_conv(pam_handle_t *, int, int,
106     char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE], void *,
107     struct pam_response **);
108 
109 static int	log_priority;	/* pam_trace syslog priority & facility */
110 static int	pam_debug = 0;
111 
112 static char *
113 pam_trace_iname(int item_type, char *iname_buf)
114 {
115 	char *name;
116 
117 	if (item_type <= 0 ||
118 	    item_type >= PAM_MAX_ITEMS ||
119 	    (name = pam_inames[item_type]) == NULL) {
120 		(void) sprintf(iname_buf, "%d", item_type);
121 		return (iname_buf);
122 	}
123 	return (name);
124 }
125 
126 static char *
127 pam_trace_fname(int flag)
128 {
129 	if (flag & PAM_BINDING)
130 		return (PAM_BINDING_NAME);
131 	if (flag & PAM_INCLUDE)
132 		return (PAM_INCLUDE_NAME);
133 	if (flag & PAM_OPTIONAL)
134 		return (PAM_OPTIONAL_NAME);
135 	if (flag & PAM_REQUIRED)
136 		return (PAM_REQUIRED_NAME);
137 	if (flag & PAM_REQUISITE)
138 		return (PAM_REQUISITE_NAME);
139 	if (flag & PAM_SUFFICIENT)
140 		return (PAM_SUFFICIENT_NAME);
141 	return ("bad flag name");
142 }
143 
144 static char *
145 pam_trace_cname(pam_handle_t *pamh)
146 {
147 	if (pamh->pam_conf_name[pamh->include_depth] == NULL)
148 		return ("NULL");
149 	return (pamh->pam_conf_name[pamh->include_depth]);
150 }
151 
152 #include <deflt.h>
153 #include <stdarg.h>
154 /*
155  * pam_settrace - setup configuration for pam tracing
156  *
157  * turn on PAM debug if "magic" file exists
158  * if exists (original), pam_debug = PAM_DEBUG_DEFAULT,
159  * log_priority = LOG_DEBUG(7) and log_facility = LOG_AUTH(4).
160  *
161  * if has contents, keywork=value pairs:
162  *
163  *	"log_priority=" 0-7, the pam_trace syslog priority to use
164  *		(see sys/syslog.h)
165  *	"log_facility=" 0-23, the pam_trace syslog facility to use
166  *		(see sys/syslog.h)
167  *	"debug_flags=" PAM_DEBUG_DEFAULT (0x0001), log traditional
168  *			(original) debugging.
169  *		Plus the logical or of:
170  *		    PAM_DEBUG_ITEM (0x0002), log item values and
171  *			pam_get_item.
172  *		    PAM_DEBUG_MODULE (0x0004), log module return status.
173  *		    PAM_DEBUG_CONF (0x0008), log pam.conf parsing.
174  *		    PAM_DEBUG_DATA (0x0010), get/set_data.
175  *		    PAM_DEBUG_CONV (0x0020), conversation/response.
176  *
177  *		    If compiled with DEBUG:
178  *		    PAM_DEBUG_AUTHTOK (0x8000), display AUTHTOK value if
179  *				PAM_DEBUG_ITEM is set and results from
180  *				PAM_PROMPT_ECHO_OFF responses.
181  *		    USE CAREFULLY, THIS EXPOSES THE USER'S PASSWORDS.
182  *
183  *		or set to 0 and off even if PAM_DEBUG file exists.
184  *
185  * Output has the general form:
186  * <whatever was set syslog> PAM[<pid>]: <interface>(<handle> and other info)
187  * <whatever was set syslog> PAM[<pid>]: details requested for <interface> call
188  *	Where:	<pid> is the process ID of the calling process.
189  *		<handle> is the Hex value of the pam_handle associated with the
190  *			call.
191  */
192 
193 static void
194 pam_settrace()
195 {
196 	void	*defp;
197 
198 	if ((defp = defopen_r(PAM_DEBUG)) != NULL) {
199 		char	*arg;
200 		int	code;
201 		int	facility = LOG_AUTH;
202 
203 		pam_debug = PAM_DEBUG_DEFAULT;
204 		log_priority = LOG_DEBUG;
205 
206 		(void) defcntl_r(DC_SETFLAGS, DC_CASE, defp);
207 		if ((arg = defread_r(LOG_PRIORITY, defp)) != NULL) {
208 			code = (int)strtol(arg, NULL, 10);
209 			if ((code & ~LOG_PRIMASK) == 0) {
210 				log_priority = code;
211 			}
212 		}
213 		if ((arg = defread_r(LOG_FACILITY, defp)) != NULL) {
214 			code = (int)strtol(arg, NULL, 10);
215 			if (code < LOG_NFACILITIES) {
216 				facility = code << 3;
217 			}
218 		}
219 		if ((arg = defread_r(DEBUG_FLAGS, defp)) != NULL) {
220 			pam_debug = (int)strtol(arg, NULL, 0);
221 		}
222 		defclose_r(defp);
223 
224 		log_priority |= facility;
225 	}
226 }
227 
228 /*
229  * pam_trace - logs tracing messages
230  *
231  *	flag = debug_flags from /etc/pam_debug
232  *	format and args = message to print (PAM[<pid>]: is prepended).
233  *
234  *	global log_priority = pam_trace syslog (log_priority | log_facility)
235  *		from /etc/pam_debug
236  */
237 /*PRINTFLIKE2*/
238 static void
239 pam_trace(int flag, char *format, ...)
240 {
241 	va_list args;
242 	char message[1024];
243 	int savemask;
244 
245 	if ((pam_debug & flag) == 0)
246 		return;
247 
248 	savemask = setlogmask(LOG_MASK(log_priority & LOG_PRIMASK));
249 	(void) snprintf(message, sizeof (message), "PAM[%ld]: %s",
250 	    (long)getpid(), format);
251 	va_start(args, format);
252 	(void) vsyslog(log_priority, message, args);
253 	va_end(args);
254 	(void) setlogmask(savemask);
255 }
256 
257 /*
258  * __pam_log - logs PAM syslog messages
259  *
260  *	priority = message priority
261  *	format and args = message to log
262  */
263 /*PRINTFLIKE2*/
264 void
265 __pam_log(int priority, const char *format, ...)
266 {
267 	va_list args;
268 	int savemask = setlogmask(LOG_MASK(priority & LOG_PRIMASK));
269 
270 	va_start(args, format);
271 	(void) vsyslog(priority, format, args);
272 	va_end(args);
273 	(void) setlogmask(savemask);
274 }
275 
276 
277 /*
278  *			pam_XXXXX routines
279  *
280  *	These are the entry points to the authentication switch
281  */
282 
283 /*
284  * pam_start		- initiate an authentication transaction and
285  *			  set parameter values to be used during the
286  *			  transaction
287  */
288 
289 int
290 pam_start(const char *service, const char *user,
291     const struct pam_conv *pam_conv, pam_handle_t **pamh)
292 {
293 	int	err;
294 
295 	*pamh = calloc(1, sizeof (struct pam_handle));
296 
297 	pam_settrace();
298 	pam_trace(PAM_DEBUG_DEFAULT,
299 	    "pam_start(%s,%s,%p:%p) - debug = %x",
300 	    service ? service : "NULL", user ? user : "NULL", (void *)pam_conv,
301 	    (void *)*pamh, pam_debug);
302 
303 	if (*pamh == NULL)
304 		return (PAM_BUF_ERR);
305 
306 	(*pamh)->pam_inmodule = RO_OK;		/* OK to set RO items */
307 	if ((err = pam_set_item(*pamh, PAM_SERVICE, (void *)service))
308 	    != PAM_SUCCESS) {
309 		clean_up(*pamh);
310 		*pamh = NULL;
311 		return (err);
312 	}
313 
314 	if ((err = pam_set_item(*pamh, PAM_USER, (void *)user))
315 	    != PAM_SUCCESS) {
316 		clean_up(*pamh);
317 		*pamh = NULL;
318 		return (err);
319 	}
320 
321 	if ((err = pam_set_item(*pamh, PAM_CONV, (void *)pam_conv))
322 	    != PAM_SUCCESS) {
323 		clean_up(*pamh);
324 		*pamh = NULL;
325 		return (err);
326 	}
327 
328 	(*pamh)->pam_inmodule = RW_OK;
329 	return (PAM_SUCCESS);
330 }
331 
332 /*
333  * pam_end - terminate an authentication transaction
334  */
335 
336 int
337 pam_end(pam_handle_t *pamh, int pam_status)
338 {
339 	struct pam_module_data *psd, *p;
340 	fd_list *expired;
341 	fd_list *traverse;
342 	env_list *env_expired;
343 	env_list *env_traverse;
344 
345 	pam_trace(PAM_DEBUG_DEFAULT,
346 	    "pam_end(%p): status = %s", (void *)pamh,
347 	    pam_strerror(pamh, pam_status));
348 
349 	if (pamh == NULL)
350 		return (PAM_SYSTEM_ERR);
351 
352 	/* call the cleanup routines for module specific data */
353 
354 	psd = pamh->ssd;
355 	while (psd) {
356 		if (psd->cleanup) {
357 			psd->cleanup(pamh, psd->data, pam_status);
358 		}
359 		p = psd;
360 		psd = p->next;
361 		free(p->module_data_name);
362 		free(p);
363 	}
364 	pamh->ssd = NULL;
365 
366 	/* dlclose all module fds */
367 	traverse = pamh->fd;
368 	while (traverse) {
369 		expired = traverse;
370 		traverse = traverse->next;
371 		(void) dlclose(expired->mh);
372 		free(expired);
373 	}
374 	pamh->fd = 0;
375 
376 	/* remove all environment variables */
377 	env_traverse = pamh->pam_env;
378 	while (env_traverse) {
379 		env_expired = env_traverse;
380 		env_traverse = env_traverse->next;
381 		free_env(env_expired);
382 	}
383 
384 	clean_up(pamh);
385 	return (PAM_SUCCESS);
386 }
387 
388 /*
389  * pam_set_item		- set the value of a parameter that can be
390  *			  retrieved via a call to pam_get_item()
391  */
392 
393 int
394 pam_set_item(pam_handle_t *pamh, int item_type, const void *item)
395 {
396 	struct pam_item *pip;
397 	int	size;
398 	char	iname_buf[PAM_MAX_MSG_SIZE];
399 
400 	if (((pam_debug & PAM_DEBUG_ITEM) == 0) || (pamh == NULL)) {
401 		pam_trace(PAM_DEBUG_DEFAULT,
402 		    "pam_set_item(%p:%s)", (void *)pamh,
403 		    pam_trace_iname(item_type, iname_buf));
404 	}
405 
406 	if (pamh == NULL)
407 		return (PAM_SYSTEM_ERR);
408 
409 	/* check read only items */
410 	if ((item_type == PAM_SERVICE) && (pamh->pam_inmodule != RO_OK))
411 		return (PAM_PERM_DENIED);
412 
413 	/*
414 	 * Check that item_type is within valid range
415 	 */
416 
417 	if (item_type <= 0 || item_type >= PAM_MAX_ITEMS)
418 		return (PAM_SYMBOL_ERR);
419 
420 	pip = &(pamh->ps_item[item_type]);
421 
422 	switch (item_type) {
423 	case PAM_AUTHTOK:
424 	case PAM_OLDAUTHTOK:
425 		if (pip->pi_addr != NULL)
426 			(void) memset(pip->pi_addr, 0, pip->pi_size);
427 		/*FALLTHROUGH*/
428 	case PAM_SERVICE:
429 	case PAM_USER:
430 	case PAM_TTY:
431 	case PAM_RHOST:
432 	case PAM_RUSER:
433 	case PAM_USER_PROMPT:
434 	case PAM_RESOURCE:
435 	case PAM_AUSER:
436 		if (pip->pi_addr != NULL) {
437 			free(pip->pi_addr);
438 		}
439 
440 		if (item == NULL) {
441 			pip->pi_addr = NULL;
442 			pip->pi_size = 0;
443 		} else {
444 			pip->pi_addr = strdup((char *)item);
445 			if (pip->pi_addr == NULL) {
446 				pip->pi_size = 0;
447 				return (PAM_BUF_ERR);
448 			}
449 			pip->pi_size = strlen(pip->pi_addr);
450 		}
451 		break;
452 	case PAM_CONV:
453 		if (pip->pi_addr != NULL)
454 			free(pip->pi_addr);
455 		size = sizeof (struct pam_conv);
456 		if ((pip->pi_addr = calloc(1, size)) == NULL)
457 			return (PAM_BUF_ERR);
458 		if (item != NULL)
459 			(void) memcpy(pip->pi_addr, item, (unsigned int) size);
460 		else
461 			(void) memset(pip->pi_addr, 0, size);
462 		pip->pi_size = size;
463 		break;
464 	case PAM_REPOSITORY:
465 		if (pip->pi_addr != NULL) {
466 			pam_repository_t *auth_rep;
467 
468 			auth_rep = (pam_repository_t *)pip->pi_addr;
469 			if (auth_rep->type != NULL)
470 				free(auth_rep->type);
471 			if (auth_rep->scope != NULL)
472 				free(auth_rep->scope);
473 			free(auth_rep);
474 		}
475 		if (item != NULL) {
476 			pam_repository_t *s, *d;
477 
478 			size = sizeof (struct pam_repository);
479 			pip->pi_addr = calloc(1, size);
480 			if (pip->pi_addr == NULL)
481 				return (PAM_BUF_ERR);
482 
483 			s = (struct pam_repository *)item;
484 			d = (struct pam_repository *)pip->pi_addr;
485 
486 			d->type = strdup(s->type);
487 			if (d->type == NULL)
488 				return (PAM_BUF_ERR);
489 			d->scope = malloc(s->scope_len);
490 			if (d->scope == NULL)
491 				return (PAM_BUF_ERR);
492 			(void) memcpy(d->scope, s->scope, s->scope_len);
493 			d->scope_len = s->scope_len;
494 		}
495 		pip->pi_size = size;
496 		break;
497 	default:
498 		return (PAM_SYMBOL_ERR);
499 	}
500 	switch (item_type) {
501 	case PAM_CONV:
502 		pam_trace(PAM_DEBUG_ITEM, "pam_set_item(%p:%s)=%p",
503 		    (void *)pamh,
504 		    pam_trace_iname(item_type, iname_buf),
505 		    item ? (void *)((struct pam_conv *)item)->conv :
506 		    (void *)0);
507 		break;
508 	case PAM_REPOSITORY:
509 		pam_trace(PAM_DEBUG_ITEM, "pam_set_item(%p:%s)=%s",
510 		    (void *)pamh,
511 		    pam_trace_iname(item_type, iname_buf),
512 		    item ? (((struct pam_repository *)item)->type ?
513 		    ((struct pam_repository *)item)->type : "NULL") :
514 		    "NULL");
515 		break;
516 	case PAM_AUTHTOK:
517 	case PAM_OLDAUTHTOK:
518 #ifdef	DEBUG
519 		if (pam_debug & PAM_DEBUG_AUTHTOK)
520 			pam_trace(PAM_DEBUG_ITEM,
521 			    "pam_set_item(%p:%s)=%s", (void *)pamh,
522 			    pam_trace_iname(item_type, iname_buf),
523 			    item ? (char *)item : "NULL");
524 		else
525 #endif	/* DEBUG */
526 			pam_trace(PAM_DEBUG_ITEM,
527 			    "pam_set_item(%p:%s)=%s", (void *)pamh,
528 			    pam_trace_iname(item_type, iname_buf),
529 			    item ? "********" : "NULL");
530 		break;
531 	default:
532 		pam_trace(PAM_DEBUG_ITEM, "pam_set_item(%p:%s)=%s",
533 		    (void *)pamh,
534 		    pam_trace_iname(item_type, iname_buf),
535 		    item ? (char *)item : "NULL");
536 	}
537 
538 	return (PAM_SUCCESS);
539 }
540 
541 /*
542  * pam_get_item		- read the value of a parameter specified in
543  *			  the call to pam_set_item()
544  */
545 
546 int
547 pam_get_item(const pam_handle_t *pamh, int item_type, void **item)
548 {
549 	struct pam_item *pip;
550 	char	iname_buf[PAM_MAX_MSG_SIZE];
551 
552 	if (((pam_debug & PAM_DEBUG_ITEM) == 0) || (pamh == NULL)) {
553 		pam_trace(PAM_DEBUG_ITEM, "pam_get_item(%p:%s)",
554 		    (void *)pamh, pam_trace_iname(item_type, iname_buf));
555 	}
556 
557 	if (pamh == NULL)
558 		return (PAM_SYSTEM_ERR);
559 
560 	if (item_type <= 0 || item_type >= PAM_MAX_ITEMS)
561 		return (PAM_SYMBOL_ERR);
562 
563 	if ((pamh->pam_inmodule != WO_OK) &&
564 	    ((item_type == PAM_AUTHTOK || item_type == PAM_OLDAUTHTOK))) {
565 		__pam_log(LOG_AUTH | LOG_NOTICE, "pam_get_item(%s) called from "
566 		    "a non module context",
567 		    pam_trace_iname(item_type, iname_buf));
568 		return (PAM_PERM_DENIED);
569 	}
570 
571 	pip = (struct pam_item *)&(pamh->ps_item[item_type]);
572 
573 	*item = pip->pi_addr;
574 	switch (item_type) {
575 	case PAM_CONV:
576 		pam_trace(PAM_DEBUG_ITEM, "pam_get_item(%p:%s)=%p",
577 		    (void *)pamh,
578 		    pam_trace_iname(item_type, iname_buf),
579 		    (void *)((struct pam_conv *)*item)->conv);
580 		break;
581 	case PAM_REPOSITORY:
582 		pam_trace(PAM_DEBUG_ITEM, "pam_get_item(%p:%s)=%s",
583 		    (void *)pamh,
584 		    pam_trace_iname(item_type, iname_buf),
585 		    *item ? (((struct pam_repository *)*item)->type ?
586 		    ((struct pam_repository *)*item)->type : "NULL") :
587 		    "NULL");
588 		break;
589 	case PAM_AUTHTOK:
590 	case PAM_OLDAUTHTOK:
591 #ifdef	DEBUG
592 		if (pam_debug & PAM_DEBUG_AUTHTOK)
593 			pam_trace(PAM_DEBUG_ITEM,
594 			    "pam_get_item(%p:%s)=%s", (void *)pamh,
595 			    pam_trace_iname(item_type, iname_buf),
596 			    *item ? *(char **)item : "NULL");
597 		else
598 #endif	/* DEBUG */
599 			pam_trace(PAM_DEBUG_ITEM,
600 			    "pam_get_item(%p:%s)=%s", (void *)pamh,
601 			    pam_trace_iname(item_type, iname_buf),
602 			    *item ? "********" : "NULL");
603 		break;
604 	default:
605 		pam_trace(PAM_DEBUG_ITEM, "pam_get_item(%p:%s)=%s",
606 		    (void *)pamh,
607 		    pam_trace_iname(item_type, iname_buf),
608 		    *item ? *(char **)item : "NULL");
609 	}
610 
611 	return (PAM_SUCCESS);
612 }
613 
614 /*
615  * parse_user_name         - process the user response: ignore
616  *                           '\t' or ' ' before or after a user name.
617  *                           user_input is a null terminated string.
618  *                           *ret_username will be the user name.
619  */
620 
621 static int
622 parse_user_name(char *user_input, char **ret_username)
623 {
624 	register char *ptr;
625 	register int index = 0;
626 	char username[PAM_MAX_RESP_SIZE];
627 
628 	/* Set the default value for *ret_username */
629 	*ret_username = NULL;
630 
631 	/*
632 	 * Set the initial value for username - this is a buffer holds
633 	 * the user name.
634 	 */
635 	bzero((void *)username, PAM_MAX_RESP_SIZE);
636 
637 	/*
638 	 * The user_input is guaranteed to be terminated by a null character.
639 	 */
640 	ptr = user_input;
641 
642 	/* Skip all the leading whitespaces if there are any. */
643 	while ((*ptr == ' ') || (*ptr == '\t'))
644 		ptr++;
645 
646 	if (*ptr == '\0') {
647 		/*
648 		 * We should never get here since the user_input we got
649 		 * in pam_get_user() is not all whitespaces nor just "\0".
650 		 */
651 		return (PAM_BUF_ERR);
652 	}
653 
654 	/*
655 	 * username will be the first string we get from user_input
656 	 * - we skip leading whitespaces and ignore trailing whitespaces
657 	 */
658 	while (*ptr != '\0') {
659 		if ((*ptr == ' ') || (*ptr == '\t'))
660 			break;
661 		else {
662 			username[index] = *ptr;
663 			index++;
664 			ptr++;
665 		}
666 	}
667 
668 	/* ret_username will be freed in pam_get_user(). */
669 	if ((*ret_username = malloc(index + 1)) == NULL)
670 		return (PAM_BUF_ERR);
671 	(void) strcpy(*ret_username, username);
672 	return (PAM_SUCCESS);
673 }
674 
675 /*
676  * Get the value of PAM_USER. If not set, then use the convenience function
677  * to prompt for the user. Use prompt if specified, else use PAM_USER_PROMPT
678  * if it is set, else use default.
679  */
680 #define	WHITESPACE	0
681 #define	USERNAME	1
682 
683 int
684 pam_get_user(pam_handle_t *pamh, char **user, const char *prompt_override)
685 {
686 	int	status;
687 	char	*prompt = NULL;
688 	char    *real_username;
689 	struct pam_response *ret_resp = NULL;
690 	char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
691 
692 	pam_trace(PAM_DEBUG_DEFAULT,
693 	    "pam_get_user(%p, %p, %s)", (void *)pamh, (void *)*user,
694 	    prompt_override ? prompt_override : "NULL");
695 	if (pamh == NULL)
696 		return (PAM_SYSTEM_ERR);
697 
698 	if ((status = pam_get_item(pamh, PAM_USER, (void **)user))
699 	    != PAM_SUCCESS) {
700 		return (status);
701 	}
702 
703 	/* if the user is set, return it */
704 
705 	if (*user != NULL && *user[0] != '\0') {
706 		return (PAM_SUCCESS);
707 	}
708 
709 	/*
710 	 * if the module is requesting a special prompt, use it.
711 	 * else use PAM_USER_PROMPT.
712 	 */
713 
714 	if (prompt_override != NULL) {
715 		prompt = (char *)prompt_override;
716 	} else {
717 		status = pam_get_item(pamh, PAM_USER_PROMPT, (void**)&prompt);
718 		if (status != PAM_SUCCESS) {
719 			return (status);
720 		}
721 	}
722 
723 	/* if the prompt is not set, use default */
724 
725 	if (prompt == NULL || prompt[0] == '\0') {
726 		prompt = dgettext(TEXT_DOMAIN, "Please enter user name: ");
727 	}
728 
729 	/* prompt for the user */
730 
731 	(void) strncpy(messages[0], prompt, sizeof (messages[0]));
732 
733 	for (;;) {
734 		int state = WHITESPACE;
735 
736 		status = do_conv(pamh, PAM_PROMPT_ECHO_ON, 1, messages,
737 		    NULL, &ret_resp);
738 
739 		if (status != PAM_SUCCESS) {
740 			return (status);
741 		}
742 
743 		if (ret_resp->resp && ret_resp->resp[0] != '\0') {
744 			int len = strlen(ret_resp->resp);
745 			int i;
746 
747 			for (i = 0; i < len; i++) {
748 				if ((ret_resp->resp[i] != ' ') &&
749 				    (ret_resp->resp[i] != '\t')) {
750 					state = USERNAME;
751 					break;
752 				}
753 			}
754 
755 			if (state == USERNAME)
756 				break;
757 		}
758 		/* essentially empty response, try again */
759 		free_resp(1, ret_resp);
760 		ret_resp = NULL;
761 	}
762 
763 	/* set PAM_USER */
764 	/* Parse the user input to get the user name. */
765 	status = parse_user_name(ret_resp->resp, &real_username);
766 
767 	if (status != PAM_SUCCESS) {
768 		if (real_username != NULL)
769 			free(real_username);
770 		free_resp(1, ret_resp);
771 		return (status);
772 	}
773 
774 	status = pam_set_item(pamh, PAM_USER, real_username);
775 
776 	free(real_username);
777 
778 	free_resp(1, ret_resp);
779 	if (status != PAM_SUCCESS) {
780 		return (status);
781 	}
782 
783 	/*
784 	 * finally, get PAM_USER. We have to call pam_get_item to get
785 	 * the value of user because pam_set_item mallocs the memory.
786 	 */
787 
788 	status = pam_get_item(pamh, PAM_USER, (void**)user);
789 	return (status);
790 }
791 
792 /*
793  * Set module specific data
794  */
795 
796 int
797 pam_set_data(pam_handle_t *pamh, const char *module_data_name, void *data,
798     void (*cleanup)(pam_handle_t *pamh, void *data, int pam_end_status))
799 {
800 	struct pam_module_data *psd;
801 
802 	pam_trace(PAM_DEBUG_DATA,
803 	    "pam_set_data(%p:%s:%d)=%p", (void *)pamh,
804 	    (module_data_name != NULL) ? module_data_name : "NULL",
805 	    (pamh != NULL) ? pamh->pam_inmodule : -1, data);
806 	if (pamh == NULL || (pamh->pam_inmodule != WO_OK) ||
807 	    module_data_name == NULL) {
808 		return (PAM_SYSTEM_ERR);
809 	}
810 
811 	/* check if module data already exists */
812 
813 	for (psd = pamh->ssd; psd; psd = psd->next) {
814 		if (strcmp(psd->module_data_name, module_data_name) == 0) {
815 			/* clean up original data before setting the new data */
816 			if (psd->cleanup) {
817 				psd->cleanup(pamh, psd->data, PAM_SUCCESS);
818 			}
819 			psd->data = (void *)data;
820 			psd->cleanup = cleanup;
821 			return (PAM_SUCCESS);
822 		}
823 	}
824 
825 	psd = malloc(sizeof (struct pam_module_data));
826 	if (psd == NULL)
827 		return (PAM_BUF_ERR);
828 
829 	psd->module_data_name = strdup(module_data_name);
830 	if (psd->module_data_name == NULL) {
831 		free(psd);
832 		return (PAM_BUF_ERR);
833 	}
834 
835 	psd->data = (void *)data;
836 	psd->cleanup = cleanup;
837 	psd->next = pamh->ssd;
838 	pamh->ssd = psd;
839 	return (PAM_SUCCESS);
840 }
841 
842 /*
843  * get module specific data
844  */
845 
846 int
847 pam_get_data(const pam_handle_t *pamh, const char *module_data_name,
848     const void **data)
849 {
850 	struct pam_module_data *psd;
851 
852 	if (pamh == NULL || (pamh->pam_inmodule != WO_OK) ||
853 	    module_data_name == NULL) {
854 		pam_trace(PAM_DEBUG_DATA,
855 		    "pam_get_data(%p:%s:%d)=%p", (void *)pamh,
856 		    module_data_name ? module_data_name : "NULL",
857 		    pamh->pam_inmodule, *data);
858 		return (PAM_SYSTEM_ERR);
859 	}
860 
861 	for (psd = pamh->ssd; psd; psd = psd->next) {
862 		if (strcmp(psd->module_data_name, module_data_name) == 0) {
863 			*data = psd->data;
864 			pam_trace(PAM_DEBUG_DATA,
865 			    "pam_get_data(%p:%s)=%p", (void *)pamh,
866 			    module_data_name, *data);
867 			return (PAM_SUCCESS);
868 		}
869 	}
870 	pam_trace(PAM_DEBUG_DATA,
871 	    "pam_get_data(%p:%s)=%s", (void *)pamh, module_data_name,
872 	    "PAM_NO_MODULE_DATA");
873 
874 	return (PAM_NO_MODULE_DATA);
875 }
876 
877 /*
878  * PAM equivalent to strerror()
879  */
880 /* ARGSUSED */
881 const char *
882 pam_strerror(pam_handle_t *pamh, int errnum)
883 {
884 	switch (errnum) {
885 	case PAM_SUCCESS:
886 		return (dgettext(TEXT_DOMAIN, "Success"));
887 	case PAM_OPEN_ERR:
888 		return (dgettext(TEXT_DOMAIN, "Dlopen failure"));
889 	case PAM_SYMBOL_ERR:
890 		return (dgettext(TEXT_DOMAIN, "Symbol not found"));
891 	case PAM_SERVICE_ERR:
892 		return (dgettext(TEXT_DOMAIN,
893 		    "Error in underlying service module"));
894 	case PAM_SYSTEM_ERR:
895 		return (dgettext(TEXT_DOMAIN, "System error"));
896 	case PAM_BUF_ERR:
897 		return (dgettext(TEXT_DOMAIN, "Memory buffer error"));
898 	case PAM_CONV_ERR:
899 		return (dgettext(TEXT_DOMAIN, "Conversation failure"));
900 	case PAM_PERM_DENIED:
901 		return (dgettext(TEXT_DOMAIN, "Permission denied"));
902 	case PAM_MAXTRIES:
903 		return (dgettext(TEXT_DOMAIN,
904 		    "Maximum number of attempts exceeded"));
905 	case PAM_AUTH_ERR:
906 		return (dgettext(TEXT_DOMAIN, "Authentication failed"));
907 	case PAM_NEW_AUTHTOK_REQD:
908 		return (dgettext(TEXT_DOMAIN, "Get new authentication token"));
909 	case PAM_CRED_INSUFFICIENT:
910 		return (dgettext(TEXT_DOMAIN, "Insufficient credentials"));
911 	case PAM_AUTHINFO_UNAVAIL:
912 		return (dgettext(TEXT_DOMAIN,
913 		    "Can not retrieve authentication info"));
914 	case PAM_USER_UNKNOWN:
915 		return (dgettext(TEXT_DOMAIN, "No account present for user"));
916 	case PAM_CRED_UNAVAIL:
917 		return (dgettext(TEXT_DOMAIN,
918 		    "Can not retrieve user credentials"));
919 	case PAM_CRED_EXPIRED:
920 		return (dgettext(TEXT_DOMAIN,
921 		    "User credentials have expired"));
922 	case PAM_CRED_ERR:
923 		return (dgettext(TEXT_DOMAIN,
924 		    "Failure setting user credentials"));
925 	case PAM_ACCT_EXPIRED:
926 		return (dgettext(TEXT_DOMAIN, "User account has expired"));
927 	case PAM_AUTHTOK_EXPIRED:
928 		return (dgettext(TEXT_DOMAIN, "User password has expired"));
929 	case PAM_SESSION_ERR:
930 		return (dgettext(TEXT_DOMAIN,
931 		    "Can not make/remove entry for session"));
932 	case PAM_AUTHTOK_ERR:
933 		return (dgettext(TEXT_DOMAIN,
934 		    "Authentication token manipulation error"));
935 	case PAM_AUTHTOK_RECOVERY_ERR:
936 		return (dgettext(TEXT_DOMAIN,
937 		    "Authentication token can not be recovered"));
938 	case PAM_AUTHTOK_LOCK_BUSY:
939 		return (dgettext(TEXT_DOMAIN,
940 		    "Authentication token lock busy"));
941 	case PAM_AUTHTOK_DISABLE_AGING:
942 		return (dgettext(TEXT_DOMAIN,
943 		    "Authentication token aging disabled"));
944 	case PAM_NO_MODULE_DATA:
945 		return (dgettext(TEXT_DOMAIN,
946 		    "Module specific data not found"));
947 	case PAM_IGNORE:
948 		return (dgettext(TEXT_DOMAIN, "Ignore module"));
949 	case PAM_ABORT:
950 		return (dgettext(TEXT_DOMAIN, "General PAM failure "));
951 	case PAM_TRY_AGAIN:
952 		return (dgettext(TEXT_DOMAIN,
953 		    "Unable to complete operation. Try again"));
954 	default:
955 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
956 	}
957 }
958 
959 static void *
960 sm_name(int ind)
961 {
962 	switch (ind) {
963 	case PAM_AUTHENTICATE:
964 		return (PAM_SM_AUTHENTICATE);
965 	case PAM_SETCRED:
966 		return (PAM_SM_SETCRED);
967 	case PAM_ACCT_MGMT:
968 		return (PAM_SM_ACCT_MGMT);
969 	case PAM_OPEN_SESSION:
970 		return (PAM_SM_OPEN_SESSION);
971 	case PAM_CLOSE_SESSION:
972 		return (PAM_SM_CLOSE_SESSION);
973 	case PAM_CHAUTHTOK:
974 		return (PAM_SM_CHAUTHTOK);
975 	}
976 	return (NULL);
977 }
978 
979 static int
980 (*func(pamtab_t *modulep, int ind))()
981 {
982 	void	*funcp;
983 
984 	if ((funcp = modulep->function_ptr) == NULL)
985 		return (NULL);
986 
987 	switch (ind) {
988 	case PAM_AUTHENTICATE:
989 		return (((struct auth_module *)funcp)->pam_sm_authenticate);
990 	case PAM_SETCRED:
991 		return (((struct auth_module *)funcp)->pam_sm_setcred);
992 	case PAM_ACCT_MGMT:
993 		return (((struct account_module *)funcp)->pam_sm_acct_mgmt);
994 	case PAM_OPEN_SESSION:
995 		return (((struct session_module *)funcp)->pam_sm_open_session);
996 	case PAM_CLOSE_SESSION:
997 		return (((struct session_module *)funcp)->pam_sm_close_session);
998 	case PAM_CHAUTHTOK:
999 		return (((struct password_module *)funcp)->pam_sm_chauthtok);
1000 	}
1001 	return (NULL);
1002 }
1003 
1004 /*
1005  * Run through the PAM service module stack for the given module type.
1006  */
1007 static int
1008 run_stack(pam_handle_t *pamh, int flags, int type, int def_err, int ind,
1009     char *function_name)
1010 {
1011 	int	err = PAM_SYSTEM_ERR;  /* preset */
1012 	int	optional_error = 0;
1013 	int	required_error = 0;
1014 	int	success = 0;
1015 	pamtab_t *modulep;
1016 	int	(*sm_func)();
1017 
1018 	if (pamh == NULL)
1019 		return (PAM_SYSTEM_ERR);
1020 
1021 	/* read initial entries from pam.conf */
1022 	if ((err = read_pam_conf(pamh, PAM_CONFIG)) != PAM_SUCCESS) {
1023 		return (err);
1024 	}
1025 
1026 	if ((modulep =
1027 	    pamh->pam_conf_info[pamh->include_depth][type]) == NULL) {
1028 		__pam_log(LOG_AUTH | LOG_ERR, "%s no initial module present",
1029 		    pam_trace_cname(pamh));
1030 		goto exit_return;
1031 	}
1032 
1033 	pamh->pam_inmodule = WO_OK;	/* OK to get AUTHTOK */
1034 include:
1035 	pam_trace(PAM_DEBUG_MODULE,
1036 	    "[%d:%s]:run_stack:%s(%p, %x): %s", pamh->include_depth,
1037 	    pam_trace_cname(pamh), function_name, (void *)pamh, flags,
1038 	    modulep ? modulep->module_path : "NULL");
1039 
1040 	while (modulep != NULL) {
1041 		if (modulep->pam_flag & PAM_INCLUDE) {
1042 			/* save the return location */
1043 			pamh->pam_conf_modulep[pamh->include_depth] =
1044 			    modulep->next;
1045 			pam_trace(PAM_DEBUG_MODULE,
1046 			    "setting for include[%d:%p]",
1047 			    pamh->include_depth, (void *)modulep->next);
1048 			if (pamh->include_depth++ >= PAM_MAX_INCLUDE) {
1049 				__pam_log(LOG_AUTH | LOG_ERR,
1050 				    "run_stack: includes too deep %d "
1051 				    "found trying to include %s from %s, %d "
1052 				    "allowed", pamh->include_depth,
1053 				    modulep->module_path, pamh->pam_conf_name
1054 				    [PAM_MAX_INCLUDE] == NULL ? "NULL" :
1055 				    pamh->pam_conf_name[PAM_MAX_INCLUDE],
1056 				    PAM_MAX_INCLUDE);
1057 				goto exit_return;
1058 			}
1059 			if ((err = read_pam_conf(pamh,
1060 			    modulep->module_path)) != PAM_SUCCESS) {
1061 				__pam_log(LOG_AUTH | LOG_ERR,
1062 				    "run_stack[%d:%s]: can't read included "
1063 				    "conf %s", pamh->include_depth,
1064 				    pam_trace_cname(pamh),
1065 				    modulep->module_path);
1066 				goto exit_return;
1067 			}
1068 			if ((modulep = pamh->pam_conf_info
1069 			    [pamh->include_depth][type]) == NULL) {
1070 				__pam_log(LOG_AUTH | LOG_ERR,
1071 				    "run_stack[%d:%s]: no include module "
1072 				    "present %s", pamh->include_depth,
1073 				    pam_trace_cname(pamh), function_name);
1074 				goto exit_return;
1075 			}
1076 			if (modulep->pam_flag & PAM_INCLUDE) {
1077 				/* first line another include */
1078 				goto include;
1079 			}
1080 			pam_trace(PAM_DEBUG_DEFAULT, "include[%d:%s]"
1081 			    "(%p, %s)=%s", pamh->include_depth,
1082 			    pam_trace_cname(pamh), (void *)pamh,
1083 			    function_name, modulep->module_path);
1084 			if ((err = load_modules(pamh, type, sm_name(ind),
1085 			    pamh->pam_conf_info
1086 			    [pamh->include_depth][type])) != PAM_SUCCESS) {
1087 				pam_trace(PAM_DEBUG_DEFAULT,
1088 				    "[%d:%s]:%s(%p, %x): load_modules failed",
1089 				    pamh->include_depth, pam_trace_cname(pamh),
1090 				    function_name, (void *)pamh, flags);
1091 				goto exit_return;
1092 			}
1093 			if ((modulep = pamh->pam_conf_info
1094 			    [pamh->include_depth][type]) == NULL) {
1095 				__pam_log(LOG_AUTH | LOG_ERR,
1096 				    "%s no initial module present",
1097 				    pam_trace_cname(pamh));
1098 				goto exit_return;
1099 			}
1100 		} else if ((err = load_modules(pamh, type, sm_name(ind),
1101 		    modulep)) != PAM_SUCCESS) {
1102 			pam_trace(PAM_DEBUG_DEFAULT,
1103 			    "[%d:%s]:%s(%p, %x): load_modules failed",
1104 			    pamh->include_depth, pam_trace_cname(pamh),
1105 			    function_name, (void *)pamh, flags);
1106 			goto exit_return;
1107 		}  /* PAM_INCLUDE */
1108 		sm_func = func(modulep, ind);
1109 		if (sm_func) {
1110 			err = sm_func(pamh, flags, modulep->module_argc,
1111 			    (const char **)modulep->module_argv);
1112 
1113 			pam_trace(PAM_DEBUG_MODULE,
1114 			    "[%d:%s]:%s(%p, %x): %s returned %s",
1115 			    pamh->include_depth, pam_trace_cname(pamh),
1116 			    function_name, (void *)pamh, flags,
1117 			    modulep->module_path, pam_strerror(pamh, err));
1118 
1119 			switch (err) {
1120 			case PAM_IGNORE:
1121 				/* do nothing */
1122 				break;
1123 			case PAM_SUCCESS:
1124 				if ((modulep->pam_flag & PAM_SUFFI_BIND) &&
1125 				    !required_error) {
1126 					pamh->pam_inmodule = RW_OK;
1127 					pam_trace(PAM_DEBUG_MODULE,
1128 					    "[%d:%s]:%s(%p, %x): %s: success",
1129 					    pamh->include_depth,
1130 					    pam_trace_cname(pamh),
1131 					    function_name, (void *)pamh, flags,
1132 					    (modulep->pam_flag & PAM_BINDING) ?
1133 					    PAM_BINDING_NAME :
1134 					    PAM_SUFFICIENT_NAME);
1135 					goto exit_return;
1136 				}
1137 				success = 1;
1138 				break;
1139 			case PAM_TRY_AGAIN:
1140 				/*
1141 				 * We need to return immediately, and
1142 				 * we shouldn't reset the AUTHTOK item
1143 				 * since it is not an error per-se.
1144 				 */
1145 				pamh->pam_inmodule = RW_OK;
1146 				pam_trace(PAM_DEBUG_MODULE,
1147 				    "[%d:%s]:%s(%p, %x): TRY_AGAIN: %s",
1148 				    pamh->include_depth, pam_trace_cname(pamh),
1149 				    function_name, (void *)pamh, flags,
1150 				    pam_strerror(pamh, required_error ?
1151 				    required_error : err));
1152 				err = required_error ? required_error : err;
1153 				goto exit_return;
1154 			default:
1155 				if (modulep->pam_flag & PAM_REQUISITE) {
1156 					pamh->pam_inmodule = RW_OK;
1157 					pam_trace(PAM_DEBUG_MODULE,
1158 					    "[%d:%s]:%s(%p, %x): requisite: %s",
1159 					    pamh->include_depth,
1160 					    pam_trace_cname(pamh),
1161 					    function_name, (void *)pamh, flags,
1162 					    pam_strerror(pamh,
1163 					    required_error ? required_error :
1164 					    err));
1165 					err = required_error ?
1166 					    required_error : err;
1167 					goto exit_return;
1168 				} else if (modulep->pam_flag & PAM_REQRD_BIND) {
1169 					if (!required_error)
1170 						required_error = err;
1171 				} else {
1172 					if (!optional_error)
1173 						optional_error = err;
1174 				}
1175 				pam_trace(PAM_DEBUG_DEFAULT,
1176 				    "[%d:%s]:%s(%p, %x): error %s",
1177 				    pamh->include_depth, pam_trace_cname(pamh),
1178 				    function_name, (void *)pamh, flags,
1179 				    pam_strerror(pamh, err));
1180 				break;
1181 			}
1182 		}
1183 		modulep = modulep->next;
1184 	}
1185 
1186 	pam_trace(PAM_DEBUG_MODULE, "[%d:%s]:stack_end:%s(%p, %x): %s %s: %s",
1187 	    pamh->include_depth, pam_trace_cname(pamh), function_name,
1188 	    (void *)pamh, flags, pamh->include_depth ? "included" : "final",
1189 	    required_error ? "required" : success ? "success" :
1190 	    optional_error ? "optional" : "default",
1191 	    pam_strerror(pamh, required_error ? required_error :
1192 	    success ? PAM_SUCCESS : optional_error ? optional_error : def_err));
1193 	if (pamh->include_depth > 0) {
1194 		free_pam_conf_info(pamh);
1195 		pamh->include_depth--;
1196 		/* continue at next entry */
1197 		modulep = pamh->pam_conf_modulep[pamh->include_depth];
1198 		pam_trace(PAM_DEBUG_MODULE, "looping for include[%d:%p]",
1199 		    pamh->include_depth, (void *)modulep);
1200 		goto include;
1201 	}
1202 	free_pam_conf_info(pamh);
1203 	pamh->pam_inmodule = RW_OK;
1204 	if (required_error != 0)
1205 		return (required_error);
1206 	else if (success != 0)
1207 		return (PAM_SUCCESS);
1208 	else if (optional_error != 0)
1209 		return (optional_error);
1210 	else
1211 		return (def_err);
1212 
1213 exit_return:
1214 	/*
1215 	 * All done at whatever depth we're at.
1216 	 * Go back to not having read /etc/pam.conf
1217 	 */
1218 	while (pamh->include_depth > 0) {
1219 		free_pam_conf_info(pamh);
1220 		pamh->include_depth--;
1221 	}
1222 	free_pam_conf_info(pamh);
1223 	pamh->pam_inmodule = RW_OK;
1224 	return (err);
1225 }
1226 
1227 /*
1228  * pam_authenticate - authenticate a user
1229  */
1230 
1231 int
1232 pam_authenticate(pam_handle_t *pamh, int flags)
1233 {
1234 	int	retval;
1235 
1236 	retval = run_stack(pamh, flags, PAM_AUTH_MODULE, PAM_AUTH_ERR,
1237 	    PAM_AUTHENTICATE, "pam_authenticate");
1238 
1239 	if (retval != PAM_SUCCESS)
1240 		(void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1241 	return (retval);
1242 }
1243 
1244 /*
1245  * pam_setcred - modify or retrieve user credentials
1246  */
1247 
1248 int
1249 pam_setcred(pam_handle_t *pamh, int flags)
1250 {
1251 	int	retval;
1252 
1253 	retval = run_stack(pamh, flags, PAM_AUTH_MODULE, PAM_CRED_ERR,
1254 	    PAM_SETCRED, "pam_setcred");
1255 
1256 	if (retval != PAM_SUCCESS)
1257 		(void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1258 	return (retval);
1259 }
1260 
1261 /*
1262  * pam_acct_mgmt - check password aging, account expiration
1263  */
1264 
1265 int
1266 pam_acct_mgmt(pam_handle_t *pamh, int flags)
1267 {
1268 	int	retval;
1269 
1270 	retval = run_stack(pamh, flags, PAM_ACCOUNT_MODULE, PAM_ACCT_EXPIRED,
1271 	    PAM_ACCT_MGMT, "pam_acct_mgmt");
1272 
1273 	if (retval != PAM_SUCCESS &&
1274 	    retval != PAM_NEW_AUTHTOK_REQD) {
1275 		(void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1276 	}
1277 	return (retval);
1278 }
1279 
1280 /*
1281  * pam_open_session - begin session management
1282  */
1283 
1284 int
1285 pam_open_session(pam_handle_t *pamh, int flags)
1286 {
1287 	int	retval;
1288 
1289 	retval = run_stack(pamh, flags, PAM_SESSION_MODULE, PAM_SESSION_ERR,
1290 	    PAM_OPEN_SESSION, "pam_open_session");
1291 
1292 	if (retval != PAM_SUCCESS)
1293 		(void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1294 	return (retval);
1295 }
1296 
1297 /*
1298  * pam_close_session - terminate session management
1299  */
1300 
1301 int
1302 pam_close_session(pam_handle_t *pamh, int flags)
1303 {
1304 	int	retval;
1305 
1306 	retval = run_stack(pamh, flags, PAM_SESSION_MODULE, PAM_SESSION_ERR,
1307 	    PAM_CLOSE_SESSION, "pam_close_session");
1308 
1309 	if (retval != PAM_SUCCESS)
1310 		(void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1311 	return (retval);
1312 }
1313 
1314 /*
1315  * pam_chauthtok - change user authentication token
1316  */
1317 
1318 int
1319 pam_chauthtok(pam_handle_t *pamh, int flags)
1320 {
1321 	int	retval;
1322 
1323 	/* do not let apps use PAM_PRELIM_CHECK or PAM_UPDATE_AUTHTOK */
1324 	if (flags & (PAM_PRELIM_CHECK | PAM_UPDATE_AUTHTOK)) {
1325 		pam_trace(PAM_DEBUG_DEFAULT,
1326 		    "pam_chauthtok(%p, %x): %s", (void *)pamh, flags,
1327 		    pam_strerror(pamh, PAM_SYMBOL_ERR));
1328 		return (PAM_SYMBOL_ERR);
1329 	}
1330 
1331 	/* 1st pass: PRELIM CHECK */
1332 	retval = run_stack(pamh, flags | PAM_PRELIM_CHECK, PAM_PASSWORD_MODULE,
1333 	    PAM_AUTHTOK_ERR, PAM_CHAUTHTOK, "pam_chauthtok-prelim");
1334 
1335 	if (retval == PAM_TRY_AGAIN)
1336 		return (retval);
1337 
1338 	if (retval != PAM_SUCCESS) {
1339 		(void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1340 		return (retval);
1341 	}
1342 
1343 	/* 2nd pass: UPDATE AUTHTOK */
1344 	retval = run_stack(pamh, flags | PAM_UPDATE_AUTHTOK,
1345 	    PAM_PASSWORD_MODULE, PAM_AUTHTOK_ERR, PAM_CHAUTHTOK,
1346 	    "pam_chauthtok-update");
1347 
1348 	if (retval != PAM_SUCCESS)
1349 		(void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1350 
1351 	return (retval);
1352 }
1353 
1354 /*
1355  * pam_putenv - add an environment variable to the PAM handle
1356  *	if name_value == 'NAME=VALUE'	then set variable to the value
1357  *	if name_value == 'NAME='	then set variable to an empty value
1358  *	if name_value == 'NAME'		then delete the variable
1359  */
1360 
1361 int
1362 pam_putenv(pam_handle_t *pamh, const char *name_value)
1363 {
1364 	int		error = PAM_SYSTEM_ERR;
1365 	char		*equal_sign = 0;
1366 	char		*name = NULL, *value = NULL, *tmp_value = NULL;
1367 	env_list	*traverse, *trail;
1368 
1369 	pam_trace(PAM_DEBUG_DEFAULT,
1370 	    "pam_putenv(%p, %s)", (void *)pamh,
1371 	    name_value ? name_value : "NULL");
1372 
1373 	if (pamh == NULL || name_value == NULL)
1374 		goto out;
1375 
1376 	/* see if we were passed 'NAME=VALUE', 'NAME=', or 'NAME' */
1377 	if ((equal_sign = strchr(name_value, '=')) != 0) {
1378 		if ((name = calloc(equal_sign - name_value + 1,
1379 		    sizeof (char))) == 0) {
1380 			error = PAM_BUF_ERR;
1381 			goto out;
1382 		}
1383 		(void) strncpy(name, name_value, equal_sign - name_value);
1384 		if ((value = strdup(++equal_sign)) == 0) {
1385 			error = PAM_BUF_ERR;
1386 			goto out;
1387 		}
1388 	} else {
1389 		if ((name = strdup(name_value)) == 0) {
1390 			error = PAM_BUF_ERR;
1391 			goto out;
1392 		}
1393 	}
1394 
1395 	/* check to see if we already have this variable in the PAM handle */
1396 	traverse = pamh->pam_env;
1397 	trail = traverse;
1398 	while (traverse && strncmp(traverse->name, name, strlen(name))) {
1399 		trail = traverse;
1400 		traverse = traverse->next;
1401 	}
1402 
1403 	if (traverse) {
1404 		/* found a match */
1405 		if (value == 0) {
1406 			/* remove the env variable */
1407 			if (pamh->pam_env == traverse)
1408 				pamh->pam_env = traverse->next;
1409 			else
1410 				trail->next = traverse->next;
1411 			free_env(traverse);
1412 		} else if (strlen(value) == 0) {
1413 			/* set env variable to empty value */
1414 			if ((tmp_value = strdup("")) == 0) {
1415 				error = PAM_SYSTEM_ERR;
1416 				goto out;
1417 			}
1418 			free(traverse->value);
1419 			traverse->value = tmp_value;
1420 		} else {
1421 			/* set the new value */
1422 			if ((tmp_value = strdup(value)) == 0) {
1423 				error = PAM_SYSTEM_ERR;
1424 				goto out;
1425 			}
1426 			free(traverse->value);
1427 			traverse->value = tmp_value;
1428 		}
1429 
1430 	} else if (traverse == 0 && value) {
1431 		/*
1432 		 * could not find a match in the PAM handle.
1433 		 * add the new value if there is one
1434 		 */
1435 		if ((traverse = calloc(1, sizeof (env_list))) == 0) {
1436 			error = PAM_BUF_ERR;
1437 			goto out;
1438 		}
1439 		if ((traverse->name = strdup(name)) == 0) {
1440 			free_env(traverse);
1441 			error = PAM_BUF_ERR;
1442 			goto out;
1443 		}
1444 		if ((traverse->value = strdup(value)) == 0) {
1445 			free_env(traverse);
1446 			error = PAM_BUF_ERR;
1447 			goto out;
1448 		}
1449 		if (trail == 0) {
1450 			/* new head of list */
1451 			pamh->pam_env = traverse;
1452 		} else {
1453 			/* adding to end of list */
1454 			trail->next = traverse;
1455 		}
1456 	}
1457 
1458 	error = PAM_SUCCESS;
1459 out:
1460 	if (error != PAM_SUCCESS) {
1461 		if (traverse) {
1462 			if (traverse->name)
1463 				free(traverse->name);
1464 			if (traverse->value)
1465 				free(traverse->value);
1466 			free(traverse);
1467 		}
1468 	}
1469 	if (name)
1470 		free(name);
1471 	if (value)
1472 		free(value);
1473 	return (error);
1474 }
1475 
1476 /*
1477  * pam_getenv - retrieve an environment variable from the PAM handle
1478  */
1479 char *
1480 pam_getenv(pam_handle_t *pamh, const char *name)
1481 {
1482 	int		error = PAM_SYSTEM_ERR;
1483 	env_list	*traverse;
1484 
1485 	pam_trace(PAM_DEBUG_DEFAULT,
1486 	    "pam_getenv(%p, %p)", (void *)pamh, (void *)name);
1487 
1488 	if (pamh == NULL || name == NULL)
1489 		goto out;
1490 
1491 	/* check to see if we already have this variable in the PAM handle */
1492 	traverse = pamh->pam_env;
1493 	while (traverse && strncmp(traverse->name, name, strlen(name))) {
1494 		traverse = traverse->next;
1495 	}
1496 	error = (traverse ? PAM_SUCCESS : PAM_SYSTEM_ERR);
1497 	pam_trace(PAM_DEBUG_DEFAULT,
1498 	    "pam_getenv(%p, %s)=%s", (void *)pamh, name,
1499 	    traverse ? traverse->value : "NULL");
1500 out:
1501 	return (error ? NULL : strdup(traverse->value));
1502 }
1503 
1504 /*
1505  * pam_getenvlist - retrieve all environment variables from the PAM handle
1506  *                  in a NULL terminated array. On error, return NULL.
1507  */
1508 char **
1509 pam_getenvlist(pam_handle_t *pamh)
1510 {
1511 	int		error = PAM_SYSTEM_ERR;
1512 	char		**list = 0;
1513 	int		length = 0;
1514 	env_list	*traverse;
1515 	char		*tenv;
1516 	size_t		tenv_size;
1517 
1518 	pam_trace(PAM_DEBUG_DEFAULT,
1519 	    "pam_getenvlist(%p)", (void *)pamh);
1520 
1521 	if (pamh == NULL)
1522 		goto out;
1523 
1524 	/* find out how many environment variables we have */
1525 	traverse = pamh->pam_env;
1526 	while (traverse) {
1527 		length++;
1528 		traverse = traverse->next;
1529 	}
1530 
1531 	/* allocate the array we will return to the caller */
1532 	if ((list = calloc(length + 1, sizeof (char *))) == NULL) {
1533 		error = PAM_BUF_ERR;
1534 		goto out;
1535 	}
1536 
1537 	/* add the variables one by one */
1538 	length = 0;
1539 	traverse = pamh->pam_env;
1540 	while (traverse != NULL) {
1541 		tenv_size = strlen(traverse->name) +
1542 		    strlen(traverse->value) + 2; /* name=val\0 */
1543 		if ((tenv = malloc(tenv_size)) == NULL) {
1544 			error = PAM_BUF_ERR;
1545 			goto out;
1546 		}
1547 		/*LINTED*/
1548 		(void) sprintf(tenv, "%s=%s", traverse->name, traverse->value);
1549 		list[length++] = tenv;
1550 		traverse = traverse->next;
1551 	}
1552 	list[length] = NULL;
1553 
1554 	error = PAM_SUCCESS;
1555 out:
1556 	if (error != PAM_SUCCESS) {
1557 		/* free the partially constructed list */
1558 		if (list) {
1559 			length = 0;
1560 			while (list[length] != NULL) {
1561 				free(list[length]);
1562 				length++;
1563 			}
1564 			free(list);
1565 		}
1566 	}
1567 	return (error ? NULL : list);
1568 }
1569 
1570 /*
1571  * Routines to load a requested module on demand
1572  */
1573 
1574 /*
1575  * load_modules - load the requested module.
1576  *		  if the dlopen or dlsym fail, then
1577  *		  the module is ignored.
1578  */
1579 
1580 static int
1581 load_modules(pam_handle_t *pamh, int type, char *function_name,
1582     pamtab_t *pam_entry)
1583 {
1584 	void	*mh;
1585 	struct	auth_module *authp;
1586 	struct	account_module *accountp;
1587 	struct	session_module *sessionp;
1588 	struct	password_module *passwdp;
1589 	int	loading_functions = 0; /* are we currently loading functions? */
1590 
1591 	pam_trace(PAM_DEBUG_MODULE, "load_modules[%d:%s](%p, %s)=%s:%s",
1592 	    pamh->include_depth, pam_trace_cname(pamh), (void *)pamh,
1593 	    function_name, pam_trace_fname(pam_entry->pam_flag),
1594 	    pam_entry->module_path);
1595 
1596 	while (pam_entry != NULL) {
1597 		pam_trace(PAM_DEBUG_DEFAULT,
1598 		    "while load_modules[%d:%s](%p, %s)=%s",
1599 		    pamh->include_depth, pam_trace_cname(pamh), (void *)pamh,
1600 		    function_name, pam_entry->module_path);
1601 
1602 		if (pam_entry->pam_flag & PAM_INCLUDE) {
1603 			pam_trace(PAM_DEBUG_DEFAULT,
1604 			    "done load_modules[%d:%s](%p, %s)=%s",
1605 			    pamh->include_depth, pam_trace_cname(pamh),
1606 			    (void *)pamh, function_name,
1607 			    pam_entry->module_path);
1608 			return (PAM_SUCCESS);
1609 		}
1610 		switch (type) {
1611 		case PAM_AUTH_MODULE:
1612 
1613 			/* if the function has already been loaded, return */
1614 			authp = pam_entry->function_ptr;
1615 			if (!loading_functions &&
1616 			    (((strcmp(function_name, PAM_SM_AUTHENTICATE)
1617 			    == 0) && authp && authp->pam_sm_authenticate) ||
1618 			    ((strcmp(function_name, PAM_SM_SETCRED) == 0) &&
1619 			    authp && authp->pam_sm_setcred))) {
1620 				return (PAM_SUCCESS);
1621 			}
1622 
1623 			/* function has not been loaded yet */
1624 			loading_functions = 1;
1625 			if (authp == NULL) {
1626 				authp = calloc(1, sizeof (struct auth_module));
1627 				if (authp == NULL)
1628 					return (PAM_BUF_ERR);
1629 			}
1630 
1631 			/* if open_module fails, return error */
1632 			if ((mh = open_module(pamh,
1633 			    pam_entry->module_path)) == NULL) {
1634 				__pam_log(LOG_AUTH | LOG_ERR,
1635 				    "load_modules[%d:%s]: can not open module "
1636 				    "%s", pamh->include_depth,
1637 				    pam_trace_cname(pamh),
1638 				    pam_entry->module_path);
1639 				free(authp);
1640 				return (PAM_OPEN_ERR);
1641 			}
1642 
1643 			/* load the authentication function */
1644 			if (strcmp(function_name, PAM_SM_AUTHENTICATE) == 0) {
1645 				if (load_function(mh, PAM_SM_AUTHENTICATE,
1646 				    &authp->pam_sm_authenticate)
1647 				    != PAM_SUCCESS) {
1648 					/* return error if dlsym fails */
1649 					free(authp);
1650 					return (PAM_SYMBOL_ERR);
1651 				}
1652 
1653 			/* load the setcred function */
1654 			} else if (strcmp(function_name, PAM_SM_SETCRED) == 0) {
1655 				if (load_function(mh, PAM_SM_SETCRED,
1656 				    &authp->pam_sm_setcred) != PAM_SUCCESS) {
1657 					/* return error if dlsym fails */
1658 					free(authp);
1659 					return (PAM_SYMBOL_ERR);
1660 				}
1661 			}
1662 			pam_entry->function_ptr = authp;
1663 			break;
1664 		case PAM_ACCOUNT_MODULE:
1665 			accountp = pam_entry->function_ptr;
1666 			if (!loading_functions &&
1667 			    (strcmp(function_name, PAM_SM_ACCT_MGMT) == 0) &&
1668 			    accountp && accountp->pam_sm_acct_mgmt) {
1669 				return (PAM_SUCCESS);
1670 			}
1671 
1672 			/*
1673 			 * If functions are added to the account module,
1674 			 * verify that one of the other functions hasn't
1675 			 * already loaded it.  See PAM_AUTH_MODULE code.
1676 			 */
1677 			loading_functions = 1;
1678 			accountp = calloc(1, sizeof (struct account_module));
1679 			if (accountp == NULL)
1680 				return (PAM_BUF_ERR);
1681 
1682 			/* if open_module fails, return error */
1683 			if ((mh = open_module(pamh,
1684 			    pam_entry->module_path)) == NULL) {
1685 				__pam_log(LOG_AUTH | LOG_ERR,
1686 				    "load_modules[%d:%s]: can not open module "
1687 				    "%s", pamh->include_depth,
1688 				    pam_trace_cname(pamh),
1689 				    pam_entry->module_path);
1690 				free(accountp);
1691 				return (PAM_OPEN_ERR);
1692 			}
1693 
1694 			if (load_function(mh, PAM_SM_ACCT_MGMT,
1695 			    &accountp->pam_sm_acct_mgmt) != PAM_SUCCESS) {
1696 				__pam_log(LOG_AUTH | LOG_ERR,
1697 				    "load_modules[%d:%s]: pam_sm_acct_mgmt() "
1698 				    "missing", pamh->include_depth,
1699 				    pam_trace_cname(pamh));
1700 				free(accountp);
1701 				return (PAM_SYMBOL_ERR);
1702 			}
1703 			pam_entry->function_ptr = accountp;
1704 			break;
1705 		case PAM_SESSION_MODULE:
1706 			sessionp = pam_entry->function_ptr;
1707 			if (!loading_functions &&
1708 			    (((strcmp(function_name,
1709 			    PAM_SM_OPEN_SESSION) == 0) &&
1710 			    sessionp && sessionp->pam_sm_open_session) ||
1711 			    ((strcmp(function_name,
1712 			    PAM_SM_CLOSE_SESSION) == 0) &&
1713 			    sessionp && sessionp->pam_sm_close_session))) {
1714 				return (PAM_SUCCESS);
1715 			}
1716 
1717 			loading_functions = 1;
1718 			if (sessionp == NULL) {
1719 				sessionp = calloc(1,
1720 				    sizeof (struct session_module));
1721 				if (sessionp == NULL)
1722 					return (PAM_BUF_ERR);
1723 			}
1724 
1725 			/* if open_module fails, return error */
1726 			if ((mh = open_module(pamh,
1727 			    pam_entry->module_path)) == NULL) {
1728 				__pam_log(LOG_AUTH | LOG_ERR,
1729 				    "load_modules[%d:%s]: can not open module "
1730 				    "%s", pamh->include_depth,
1731 				    pam_trace_cname(pamh),
1732 				    pam_entry->module_path);
1733 				free(sessionp);
1734 				return (PAM_OPEN_ERR);
1735 			}
1736 
1737 			if ((strcmp(function_name, PAM_SM_OPEN_SESSION) == 0) &&
1738 			    load_function(mh, PAM_SM_OPEN_SESSION,
1739 			    &sessionp->pam_sm_open_session) != PAM_SUCCESS) {
1740 				free(sessionp);
1741 				return (PAM_SYMBOL_ERR);
1742 			} else if ((strcmp(function_name,
1743 			    PAM_SM_CLOSE_SESSION) == 0) &&
1744 			    load_function(mh, PAM_SM_CLOSE_SESSION,
1745 			    &sessionp->pam_sm_close_session) != PAM_SUCCESS) {
1746 				free(sessionp);
1747 				return (PAM_SYMBOL_ERR);
1748 			}
1749 			pam_entry->function_ptr = sessionp;
1750 			break;
1751 		case PAM_PASSWORD_MODULE:
1752 			passwdp = pam_entry->function_ptr;
1753 			if (!loading_functions &&
1754 			    (strcmp(function_name, PAM_SM_CHAUTHTOK) == 0) &&
1755 			    passwdp && passwdp->pam_sm_chauthtok) {
1756 				return (PAM_SUCCESS);
1757 			}
1758 
1759 			/*
1760 			 * If functions are added to the password module,
1761 			 * verify that one of the other functions hasn't
1762 			 * already loaded it.  See PAM_AUTH_MODULE code.
1763 			 */
1764 			loading_functions = 1;
1765 			passwdp = calloc(1, sizeof (struct password_module));
1766 			if (passwdp == NULL)
1767 				return (PAM_BUF_ERR);
1768 
1769 			/* if open_module fails, continue */
1770 			if ((mh = open_module(pamh,
1771 			    pam_entry->module_path)) == NULL) {
1772 				__pam_log(LOG_AUTH | LOG_ERR,
1773 				    "load_modules[%d:%s]: can not open module "
1774 				    "%s", pamh->include_depth,
1775 				    pam_trace_cname(pamh),
1776 				    pam_entry->module_path);
1777 				free(passwdp);
1778 				return (PAM_OPEN_ERR);
1779 			}
1780 
1781 			if (load_function(mh, PAM_SM_CHAUTHTOK,
1782 			    &passwdp->pam_sm_chauthtok) != PAM_SUCCESS) {
1783 				free(passwdp);
1784 				return (PAM_SYMBOL_ERR);
1785 			}
1786 			pam_entry->function_ptr = passwdp;
1787 			break;
1788 		default:
1789 			pam_trace(PAM_DEBUG_DEFAULT,
1790 			    "load_modules[%d:%s](%p, %s): unsupported type %d",
1791 			    pamh->include_depth, pam_trace_cname(pamh),
1792 			    (void *)pamh, function_name, type);
1793 			break;
1794 		}
1795 
1796 		pam_entry = pam_entry->next;
1797 	} /* while */
1798 
1799 	pam_trace(PAM_DEBUG_MODULE, "load_modules[%d:%s](%p, %s)=done",
1800 	    pamh->include_depth, pam_trace_cname(pamh), (void *)pamh,
1801 	    function_name);
1802 
1803 	return (PAM_SUCCESS);
1804 }
1805 
1806 /*
1807  * open_module		- Open the module first checking for
1808  *			  propers modes and ownerships on the file.
1809  */
1810 
1811 static void *
1812 open_module(pam_handle_t *pamh, char *module_so)
1813 {
1814 	struct stat64	stb;
1815 	char		*errmsg;
1816 	void		*lfd;
1817 	fd_list		*module_fds = 0;
1818 	fd_list		*trail = 0;
1819 	fd_list		*traverse = 0;
1820 
1821 	/* Check the ownership and file modes */
1822 	if (stat64(module_so, &stb) < 0) {
1823 		__pam_log(LOG_AUTH | LOG_ERR,
1824 		    "open_module[%d:%s]: stat(%s) failed: %s",
1825 		    pamh->include_depth, pam_trace_cname(pamh), module_so,
1826 		    strerror(errno));
1827 		return (NULL);
1828 	}
1829 	if (stb.st_uid != (uid_t)0) {
1830 		__pam_log(LOG_AUTH | LOG_ALERT,
1831 		    "open_module[%d:%s]: Owner of the module %s is not root",
1832 		    pamh->include_depth, pam_trace_cname(pamh), module_so);
1833 		return (NULL);
1834 	}
1835 	if (stb.st_mode & S_IWGRP) {
1836 		__pam_log(LOG_AUTH | LOG_ALERT,
1837 		    "open_module[%d:%s]: module %s writable by group",
1838 		    pamh->include_depth, pam_trace_cname(pamh), module_so);
1839 		return (NULL);
1840 	}
1841 	if (stb.st_mode & S_IWOTH) {
1842 		__pam_log(LOG_AUTH | LOG_ALERT,
1843 		    "open_module[%d:%s]: module %s writable by world",
1844 		    pamh->include_depth, pam_trace_cname(pamh), module_so);
1845 		return (NULL);
1846 	}
1847 
1848 	/*
1849 	 * Perform the dlopen()
1850 	 */
1851 	lfd = (void *)dlopen(module_so, RTLD_LAZY);
1852 
1853 	if (lfd == NULL) {
1854 		errmsg = dlerror();
1855 		__pam_log(LOG_AUTH | LOG_ERR, "open_module[%d:%s]: %s "
1856 		    "failed: %s", pamh->include_depth, pam_trace_cname(pamh),
1857 		    module_so, errmsg != NULL ? errmsg : "Unknown error");
1858 		return (NULL);
1859 	} else {
1860 		/* add this fd to the pam handle */
1861 		if ((module_fds = calloc(1, sizeof (fd_list))) == 0) {
1862 			(void) dlclose(lfd);
1863 			lfd = 0;
1864 			return (NULL);
1865 		}
1866 		module_fds->mh = lfd;
1867 
1868 		if (pamh->fd == 0) {
1869 			/* adding new head of list */
1870 			pamh->fd = module_fds;
1871 		} else {
1872 			/* appending to end of list */
1873 			traverse = pamh->fd;
1874 			while (traverse) {
1875 				trail = traverse;
1876 				traverse = traverse->next;
1877 			}
1878 			trail->next = module_fds;
1879 		}
1880 	}
1881 
1882 	return (lfd);
1883 }
1884 
1885 /*
1886  * load_function - call dlsym() to resolve the function address
1887  */
1888 static int
1889 load_function(void *lfd, char *name, int (**func)())
1890 {
1891 	char *errmsg = NULL;
1892 
1893 	if (lfd == NULL)
1894 		return (PAM_SYMBOL_ERR);
1895 
1896 	*func = (int (*)())dlsym(lfd, name);
1897 	if (*func == NULL) {
1898 		errmsg = dlerror();
1899 		__pam_log(LOG_AUTH | LOG_ERR, "dlsym failed %s: error %s",
1900 		    name, errmsg != NULL ? errmsg : "Unknown error");
1901 		return (PAM_SYMBOL_ERR);
1902 	}
1903 
1904 	pam_trace(PAM_DEBUG_DEFAULT,
1905 	    "load_function: successful load of %s", name);
1906 	return (PAM_SUCCESS);
1907 }
1908 
1909 /*
1910  * Routines to read the pam.conf configuration file
1911  */
1912 
1913 /*
1914  * open_pam_conf - open the pam.conf config file
1915  */
1916 
1917 static int
1918 open_pam_conf(struct pam_fh **pam_fh, pam_handle_t *pamh, char *config)
1919 {
1920 	struct stat64	stb;
1921 	int		fd;
1922 
1923 	if ((fd = open(config, O_RDONLY)) == -1) {
1924 		__pam_log(LOG_AUTH | LOG_ALERT,
1925 		    "open_pam_conf[%d:%s]: open(%s) failed: %s",
1926 		    pamh->include_depth, pam_trace_cname(pamh), config,
1927 		    strerror(errno));
1928 		return (0);
1929 	}
1930 	/* Check the ownership and file modes */
1931 	if (fstat64(fd, &stb) < 0) {
1932 		__pam_log(LOG_AUTH | LOG_ALERT,
1933 		    "open_pam_conf[%d:%s]: stat(%s) failed: %s",
1934 		    pamh->include_depth, pam_trace_cname(pamh), config,
1935 		    strerror(errno));
1936 		(void) close(fd);
1937 		return (0);
1938 	}
1939 	if (stb.st_uid != (uid_t)0) {
1940 		__pam_log(LOG_AUTH | LOG_ALERT,
1941 		    "open_pam_conf[%d:%s]: Owner of %s is not root",
1942 		    pamh->include_depth, pam_trace_cname(pamh), config);
1943 		(void) close(fd);
1944 		return (0);
1945 	}
1946 	if (stb.st_mode & S_IWGRP) {
1947 		__pam_log(LOG_AUTH | LOG_ALERT,
1948 		    "open_pam_conf[%d:%s]: %s writable by group",
1949 		    pamh->include_depth, pam_trace_cname(pamh), config);
1950 		(void) close(fd);
1951 		return (0);
1952 	}
1953 	if (stb.st_mode & S_IWOTH) {
1954 		__pam_log(LOG_AUTH | LOG_ALERT,
1955 		    "open_pam_conf[%d:%s]: %s writable by world",
1956 		    pamh->include_depth, pam_trace_cname(pamh), config);
1957 		(void) close(fd);
1958 		return (0);
1959 	}
1960 	if ((*pam_fh = calloc(1, sizeof (struct pam_fh))) == NULL) {
1961 		(void) close(fd);
1962 		return (0);
1963 	}
1964 	(*pam_fh)->fconfig = fd;
1965 	(*pam_fh)->bufsize = (size_t)stb.st_size;
1966 	if (((*pam_fh)->data = mmap(0, (*pam_fh)->bufsize, PROT_READ,
1967 	    MAP_PRIVATE, (*pam_fh)->fconfig, 0)) == MAP_FAILED) {
1968 		(void) close(fd);
1969 		free (*pam_fh);
1970 		return (0);
1971 	}
1972 	(*pam_fh)->bufferp = (*pam_fh)->data;
1973 
1974 	return (1);
1975 }
1976 
1977 /*
1978  * close_pam_conf - close pam.conf
1979  */
1980 
1981 static void
1982 close_pam_conf(struct pam_fh *pam_fh)
1983 {
1984 	(void) munmap(pam_fh->data, pam_fh->bufsize);
1985 	(void) close(pam_fh->fconfig);
1986 	free(pam_fh);
1987 }
1988 
1989 /*
1990  * read_pam_conf - read in each entry in pam.conf and store info
1991  *		   under the pam handle.
1992  */
1993 
1994 static int
1995 read_pam_conf(pam_handle_t *pamh, char *config)
1996 {
1997 	struct pam_fh	*pam_fh;
1998 	pamtab_t	*pamentp;
1999 	pamtab_t	*tpament;
2000 	char		*service;
2001 	int		error;
2002 	int		i = pamh->include_depth;	/* include depth */
2003 	/*
2004 	 * service types:
2005 	 * error (-1), "auth" (0), "account" (1), "session" (2), "password" (3)
2006 	 */
2007 	int service_found[PAM_NUM_MODULE_TYPES+1] = {0, 0, 0, 0, 0};
2008 
2009 	(void) pam_get_item(pamh, PAM_SERVICE, (void **)&service);
2010 	if (service == NULL || *service == '\0') {
2011 		__pam_log(LOG_AUTH | LOG_ERR, "No service name");
2012 		return (PAM_SYSTEM_ERR);
2013 	}
2014 
2015 	pamh->pam_conf_name[i] = strdup(config);
2016 	pam_trace(PAM_DEBUG_CONF, "read_pam_conf[%d:%s](%p) open(%s)",
2017 	    i, pam_trace_cname(pamh), (void *)pamh, config);
2018 	if (open_pam_conf(&pam_fh, pamh, config) == 0) {
2019 		return (PAM_SYSTEM_ERR);
2020 	}
2021 
2022 	while ((error =
2023 	    get_pam_conf_entry(pam_fh, pamh, &pamentp)) == PAM_SUCCESS &&
2024 	    pamentp) {
2025 
2026 		/* See if entry is this service and valid */
2027 		if (verify_pam_conf(pamentp, service)) {
2028 			pam_trace(PAM_DEBUG_CONF,
2029 			    "read_pam_conf[%d:%s](%p): bad entry error %s",
2030 			    i, pam_trace_cname(pamh), (void *)pamh, service);
2031 
2032 			error = PAM_SYSTEM_ERR;
2033 			free_pamconf(pamentp);
2034 			goto out;
2035 		}
2036 		if (strcasecmp(pamentp->pam_service, service) == 0) {
2037 			pam_trace(PAM_DEBUG_CONF,
2038 			    "read_pam_conf[%d:%s](%p): processing %s",
2039 			    i, pam_trace_cname(pamh), (void *)pamh, service);
2040 			/* process first service entry */
2041 			if (service_found[pamentp->pam_type + 1] == 0) {
2042 				/* purge "other" entries */
2043 				while ((tpament = pamh->pam_conf_info[i]
2044 				    [pamentp->pam_type]) != NULL) {
2045 					pam_trace(PAM_DEBUG_CONF,
2046 					    "read_pam_conf(%p): purging "
2047 					    "\"other\"[%d:%s][%s]",
2048 					    (void *)pamh, i,
2049 					    pam_trace_cname(pamh),
2050 					    pam_snames[pamentp->pam_type]);
2051 					pamh->pam_conf_info[i]
2052 					    [pamentp->pam_type] = tpament->next;
2053 					free_pamconf(tpament);
2054 				}
2055 				/* add first service entry */
2056 				pam_trace(PAM_DEBUG_CONF,
2057 				    "read_pam_conf(%p): adding 1st "
2058 				    "%s[%d:%s][%s]",
2059 				    (void *)pamh, service, i,
2060 				    pam_trace_cname(pamh),
2061 				    pam_snames[pamentp->pam_type]);
2062 				pamh->pam_conf_info[i][pamentp->pam_type] =
2063 				    pamentp;
2064 				service_found[pamentp->pam_type + 1] = 1;
2065 			} else {
2066 				/* append more service entries */
2067 				pam_trace(PAM_DEBUG_CONF,
2068 				    "read_pam_conf(%p): adding more "
2069 				    "%s[%d:%s][%s]",
2070 				    (void *)pamh, service, i,
2071 				    pam_trace_cname(pamh),
2072 				    pam_snames[pamentp->pam_type]);
2073 				tpament =
2074 				    pamh->pam_conf_info[i][pamentp->pam_type];
2075 				while (tpament->next != NULL) {
2076 					tpament = tpament->next;
2077 				}
2078 				tpament->next = pamentp;
2079 			}
2080 		} else if (service_found[pamentp->pam_type + 1] == 0) {
2081 			/* See if "other" entry available and valid */
2082 			if (verify_pam_conf(pamentp, "other")) {
2083 				pam_trace(PAM_DEBUG_CONF,
2084 				    "read_pam_conf(%p): bad entry error %s "
2085 				    "\"other\"[%d:%s]",
2086 				    (void *)pamh, service, i,
2087 				    pam_trace_cname(pamh));
2088 				error = PAM_SYSTEM_ERR;
2089 				free_pamconf(pamentp);
2090 				goto out;
2091 			}
2092 			if (strcasecmp(pamentp->pam_service, "other") == 0) {
2093 				pam_trace(PAM_DEBUG_CONF,
2094 				    "read_pam_conf(%p): processing "
2095 				    "\"other\"[%d:%s]", (void *)pamh, i,
2096 				    pam_trace_cname(pamh));
2097 				if ((tpament = pamh->pam_conf_info[i]
2098 				    [pamentp->pam_type]) == NULL) {
2099 					/* add first "other" entry */
2100 					pam_trace(PAM_DEBUG_CONF,
2101 					    "read_pam_conf(%p): adding 1st "
2102 					    "other[%d:%s][%s]", (void *)pamh, i,
2103 					    pam_trace_cname(pamh),
2104 					    pam_snames[pamentp->pam_type]);
2105 					pamh->pam_conf_info[i]
2106 					    [pamentp->pam_type] = pamentp;
2107 				} else {
2108 					/* append more "other" entries */
2109 					pam_trace(PAM_DEBUG_CONF,
2110 					    "read_pam_conf(%p): adding more "
2111 					    "other[%d:%s][%s]", (void *)pamh, i,
2112 					    pam_trace_cname(pamh),
2113 					    pam_snames[pamentp->pam_type]);
2114 					while (tpament->next != NULL) {
2115 						tpament = tpament->next;
2116 					}
2117 					tpament->next = pamentp;
2118 				}
2119 			} else {
2120 				/* irrelevant entry */
2121 				free_pamconf(pamentp);
2122 			}
2123 		} else {
2124 			/* irrelevant entry */
2125 			free_pamconf(pamentp);
2126 		}
2127 	}
2128 out:
2129 	(void) close_pam_conf(pam_fh);
2130 	if (error != PAM_SUCCESS)
2131 		free_pam_conf_info(pamh);
2132 	return (error);
2133 }
2134 
2135 /*
2136  * get_pam_conf_entry - get a pam.conf entry
2137  */
2138 
2139 static int
2140 get_pam_conf_entry(struct pam_fh *pam_fh, pam_handle_t *pamh, pamtab_t **pam)
2141 {
2142 	char		*cp, *arg;
2143 	int		argc;
2144 	char		*tmp, *tmp_free;
2145 	int		i;
2146 	char		*current_line = NULL;
2147 	int		error = PAM_SYSTEM_ERR;	/* preset to error */
2148 	int		err;
2149 
2150 	/* get the next line from pam.conf */
2151 	if ((cp = nextline(pam_fh, pamh, &err)) == NULL) {
2152 		/* no more lines in pam.conf ==> return */
2153 		error = PAM_SUCCESS;
2154 		*pam = NULL;
2155 		goto out;
2156 	}
2157 
2158 	if ((*pam = calloc(1, sizeof (pamtab_t))) == NULL) {
2159 		__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2160 		goto out;
2161 	}
2162 
2163 	/* copy full line for error reporting */
2164 	if ((current_line = strdup(cp)) == NULL) {
2165 		__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2166 		goto out;
2167 	}
2168 
2169 	pam_trace(PAM_DEBUG_CONF,
2170 	    "pam.conf[%s] entry:\t%s", pam_trace_cname(pamh), current_line);
2171 
2172 	/* get service name (e.g. login, su, passwd) */
2173 	if ((arg = read_next_token(&cp)) == 0) {
2174 		__pam_log(LOG_AUTH | LOG_CRIT,
2175 		    "illegal pam.conf[%s] entry: %s: missing SERVICE NAME",
2176 		    pam_trace_cname(pamh), current_line);
2177 		goto out;
2178 	}
2179 	if (((*pam)->pam_service = strdup(arg)) == 0) {
2180 		__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2181 		goto out;
2182 	}
2183 
2184 	/* get module type (e.g. authentication, acct mgmt) */
2185 	if ((arg = read_next_token(&cp)) == 0) {
2186 		__pam_log(LOG_AUTH | LOG_CRIT,
2187 		    "illegal pam.conf[%s] entry: %s: missing MODULE TYPE",
2188 		    pam_trace_cname(pamh), current_line);
2189 		(*pam)->pam_type = -1;	/* 0 is a valid value */
2190 		goto getflag;
2191 	}
2192 	if (strcasecmp(arg, PAM_AUTH_NAME) == 0) {
2193 		(*pam)->pam_type = PAM_AUTH_MODULE;
2194 	} else if (strcasecmp(arg, PAM_ACCOUNT_NAME) == 0) {
2195 		(*pam)->pam_type = PAM_ACCOUNT_MODULE;
2196 	} else if (strcasecmp(arg, PAM_SESSION_NAME) == 0) {
2197 		(*pam)->pam_type = PAM_SESSION_MODULE;
2198 	} else if (strcasecmp(arg, PAM_PASSWORD_NAME) == 0) {
2199 		(*pam)->pam_type = PAM_PASSWORD_MODULE;
2200 	} else {
2201 		/* error */
2202 		__pam_log(LOG_AUTH | LOG_CRIT,
2203 		    "illegal pam.conf[%s] entry: %s: invalid module "
2204 		    "type: %s", pam_trace_cname(pamh), current_line, arg);
2205 		(*pam)->pam_type = -1;	/* 0 is a valid value */
2206 	}
2207 
2208 getflag:
2209 	/* get pam flag (e.g., requisite, required, sufficient, optional) */
2210 	if ((arg = read_next_token(&cp)) == 0) {
2211 		__pam_log(LOG_AUTH | LOG_CRIT,
2212 		    "illegal pam.conf[%s] entry: %s: missing CONTROL FLAG",
2213 		    pam_trace_cname(pamh), current_line);
2214 		goto getpath;
2215 	}
2216 	if (strcasecmp(arg, PAM_BINDING_NAME) == 0) {
2217 		(*pam)->pam_flag = PAM_BINDING;
2218 	} else if (strcasecmp(arg, PAM_INCLUDE_NAME) == 0) {
2219 		(*pam)->pam_flag = PAM_INCLUDE;
2220 	} else if (strcasecmp(arg, PAM_OPTIONAL_NAME) == 0) {
2221 		(*pam)->pam_flag = PAM_OPTIONAL;
2222 	} else if (strcasecmp(arg, PAM_REQUIRED_NAME) == 0) {
2223 		(*pam)->pam_flag = PAM_REQUIRED;
2224 	} else if (strcasecmp(arg, PAM_REQUISITE_NAME) == 0) {
2225 		(*pam)->pam_flag = PAM_REQUISITE;
2226 	} else if (strcasecmp(arg, PAM_SUFFICIENT_NAME) == 0) {
2227 		(*pam)->pam_flag = PAM_SUFFICIENT;
2228 	} else {
2229 		/* error */
2230 		__pam_log(LOG_AUTH | LOG_CRIT,
2231 		    "illegal pam.conf[%s] entry: %s",
2232 		    pam_trace_cname(pamh), current_line);
2233 		__pam_log(LOG_AUTH | LOG_CRIT,
2234 		    "\tinvalid control flag: %s", arg);
2235 	}
2236 
2237 getpath:
2238 	/* get module path (e.g. /usr/lib/security/pam_unix_auth.so.1) */
2239 	if ((arg = read_next_token(&cp)) == 0) {
2240 		__pam_log(LOG_AUTH | LOG_CRIT,
2241 		    "illegal pam.conf[%s] entry: %s: missing MODULE PATH",
2242 		    pam_trace_cname(pamh), current_line);
2243 		error = PAM_SUCCESS;	/* success */
2244 		goto out;
2245 	}
2246 	if (arg[0] != '/') {
2247 		size_t len;
2248 		/*
2249 		 * If module path does not start with "/", then
2250 		 * prepend PAM_LIB_DIR (/usr/lib/security/).
2251 		 */
2252 		/* sizeof (PAM_LIB_DIR) has room for '\0' */
2253 		len = sizeof (PAM_LIB_DIR) + sizeof (PAM_ISA_DIR) + strlen(arg);
2254 		if (((*pam)->module_path = malloc(len)) == NULL) {
2255 			__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2256 			goto out;
2257 		}
2258 		if ((*pam)->pam_flag & PAM_INCLUDE) {
2259 			(void) snprintf((*pam)->module_path, len, "%s%s",
2260 			    PAM_LIB_DIR, arg);
2261 		} else {
2262 			(void) snprintf((*pam)->module_path, len, "%s%s%s",
2263 			    PAM_LIB_DIR, PAM_ISA_DIR, arg);
2264 		}
2265 	} else {
2266 		/* Full path provided for module */
2267 		char *isa;
2268 
2269 		/* Check for Instruction Set Architecture indicator */
2270 		if ((isa = strstr(arg, PAM_ISA)) != NULL) {
2271 			size_t len;
2272 			len = strlen(arg) - (sizeof (PAM_ISA)-1) +
2273 			    sizeof (PAM_ISA_DIR);
2274 
2275 			/* substitute the architecture dependent path */
2276 			if (((*pam)->module_path = malloc(len)) == NULL) {
2277 				__pam_log(LOG_AUTH | LOG_ERR,
2278 				    "strdup: out of memory");
2279 				goto out;
2280 			}
2281 			*isa = '\000';
2282 			isa += strlen(PAM_ISA);
2283 			(void) snprintf((*pam)->module_path, len, "%s%s%s",
2284 			    arg, PAM_ISA_DIR, isa);
2285 		} else if (((*pam)->module_path = strdup(arg)) == 0) {
2286 			__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2287 			goto out;
2288 		}
2289 	}
2290 
2291 	/* count the number of module-specific options first */
2292 	argc = 0;
2293 	if ((tmp = strdup(cp)) == NULL) {
2294 		__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2295 		goto out;
2296 	}
2297 	tmp_free = tmp;
2298 	for (arg = read_next_token(&tmp); arg; arg = read_next_token(&tmp))
2299 		argc++;
2300 	free(tmp_free);
2301 
2302 	/* allocate array for the module-specific options */
2303 	if (argc > 0) {
2304 		if (((*pam)->module_argv =
2305 		    calloc(argc+1, sizeof (char *))) == 0) {
2306 			__pam_log(LOG_AUTH | LOG_ERR, "calloc: out of memory");
2307 			goto out;
2308 		}
2309 		i = 0;
2310 		for (arg = read_next_token(&cp); arg;
2311 		    arg = read_next_token(&cp)) {
2312 			(*pam)->module_argv[i] = strdup(arg);
2313 			if ((*pam)->module_argv[i] == NULL) {
2314 				__pam_log(LOG_AUTH | LOG_ERR, "strdup failed");
2315 				goto out;
2316 			}
2317 			i++;
2318 		}
2319 		(*pam)->module_argv[argc] = NULL;
2320 	}
2321 	(*pam)->module_argc = argc;
2322 
2323 	error = PAM_SUCCESS;	/* success */
2324 	(*pam)->pam_err = err;	/* was the line truncated */
2325 
2326 out:
2327 	if (current_line)
2328 		free(current_line);
2329 	if (error != PAM_SUCCESS) {
2330 		/* on error free this */
2331 		if (*pam)
2332 			free_pamconf(*pam);
2333 	}
2334 	return (error);
2335 }
2336 
2337 
2338 /*
2339  * read_next_token - skip tab and space characters and return the next token
2340  */
2341 
2342 static char *
2343 read_next_token(char **cpp)
2344 {
2345 	register char *cp = *cpp;
2346 	char *start;
2347 
2348 	if (cp == (char *)0) {
2349 		*cpp = (char *)0;
2350 		return ((char *)0);
2351 	}
2352 	while (*cp == ' ' || *cp == '\t')
2353 		cp++;
2354 	if (*cp == '\0') {
2355 		*cpp = (char *)0;
2356 		return ((char *)0);
2357 	}
2358 	start = cp;
2359 	while (*cp && *cp != ' ' && *cp != '\t')
2360 		cp++;
2361 	if (*cp != '\0')
2362 		*cp++ = '\0';
2363 	*cpp = cp;
2364 	return (start);
2365 }
2366 
2367 static char *
2368 pam_conf_strnchr(char *sp, int c, intptr_t count)
2369 {
2370 	while (count) {
2371 		if (*sp == (char)c)
2372 			return ((char *)sp);
2373 		else {
2374 			sp++;
2375 			count--;
2376 		}
2377 	};
2378 	return (NULL);
2379 }
2380 
2381 /*
2382  * nextline - skip all blank lines and comments
2383  */
2384 
2385 static char *
2386 nextline(struct pam_fh *pam_fh, pam_handle_t *pamh, int *err)
2387 {
2388 	char	*ll;
2389 	int	find_a_line = 0;
2390 	char	*data = pam_fh->data;
2391 	char	*bufferp = pam_fh->bufferp;
2392 	char	*bufferendp = &data[pam_fh->bufsize];
2393 	size_t	input_len;
2394 
2395 	/*
2396 	 * Skip the blank line, comment line
2397 	 */
2398 	while (!find_a_line) {
2399 		/* if we are at the end of the buffer, there is no next line */
2400 		if (bufferp == bufferendp)
2401 			return (NULL);
2402 
2403 		/* skip blank line */
2404 		while (*bufferp == '\n') {
2405 			/*
2406 			 * If we are at the end of the buffer, there is
2407 			 * no next line.
2408 			 */
2409 			if (++bufferp == bufferendp) {
2410 				return (NULL);
2411 			}
2412 			/* else we check *bufferp again */
2413 		}
2414 
2415 		/* skip comment line */
2416 		while (*bufferp == '#') {
2417 			if ((ll = pam_conf_strnchr(bufferp, '\n',
2418 			    bufferendp - bufferp)) != NULL) {
2419 				bufferp = ll;
2420 			} else {
2421 				/*
2422 				 * this comment line the last line.
2423 				 * no next line
2424 				 */
2425 				return (NULL);
2426 			}
2427 
2428 			/*
2429 			 * If we are at the end of the buffer, there is
2430 			 * no next line.
2431 			 */
2432 			if (bufferp == bufferendp) {
2433 				return (NULL);
2434 			}
2435 		}
2436 
2437 		if ((*bufferp != '\n') && (*bufferp != '#')) {
2438 			find_a_line = 1;
2439 		}
2440 	}
2441 
2442 	*err = PAM_SUCCESS;
2443 	/* now we find one line */
2444 	if ((ll = pam_conf_strnchr(bufferp, '\n', bufferendp - bufferp))
2445 	    != NULL) {
2446 		if ((input_len = ll - bufferp) >= sizeof (pam_fh->line)) {
2447 			__pam_log(LOG_AUTH | LOG_ERR,
2448 			    "nextline[%d:%s]: pam.conf line too long %.256s",
2449 			    pamh->include_depth, pam_trace_cname(pamh),
2450 			    bufferp);
2451 			input_len = sizeof (pam_fh->line) - 1;
2452 			*err = PAM_SERVICE_ERR;
2453 		}
2454 		(void) strncpy(pam_fh->line, bufferp, input_len);
2455 		pam_fh->line[input_len] = '\0';
2456 		pam_fh->bufferp = ll++;
2457 	} else {
2458 		ll = bufferendp;
2459 		if ((input_len = ll - bufferp) >= sizeof (pam_fh->line)) {
2460 			__pam_log(LOG_AUTH | LOG_ERR,
2461 			    "nextline[%d:%s]: pam.conf line too long %.256s",
2462 			    pamh->include_depth, pam_trace_cname(pamh),
2463 			    bufferp);
2464 			input_len = sizeof (pam_fh->line) - 1;
2465 			*err = PAM_SERVICE_ERR;
2466 		}
2467 		(void) strncpy(pam_fh->line, bufferp, input_len);
2468 		pam_fh->line[input_len] = '\0';
2469 		pam_fh->bufferp = ll;
2470 	}
2471 
2472 	return (pam_fh->line);
2473 }
2474 
2475 /*
2476  * verify_pam_conf - verify that the pam_conf entry is filled in.
2477  *
2478  *	True = Error if there is no service.
2479  *	True = Error if there is a service and it matches the requested service
2480  *		but, the type, flag, line overflow, or path is in error.
2481  */
2482 
2483 static int
2484 verify_pam_conf(pamtab_t *pam, char *service)
2485 {
2486 	return ((pam->pam_service == (char *)NULL) ||
2487 	    ((strcasecmp(pam->pam_service, service) == 0) &&
2488 	    ((pam->pam_type == -1) ||
2489 	    (pam->pam_flag == 0) ||
2490 	    (pam->pam_err != PAM_SUCCESS) ||
2491 	    (pam->module_path == (char *)NULL))));
2492 }
2493 
2494 /*
2495  * Routines to free allocated storage
2496  */
2497 
2498 /*
2499  * clean_up -  free allocated storage in the pam handle
2500  */
2501 
2502 static void
2503 clean_up(pam_handle_t *pamh)
2504 {
2505 	int i;
2506 	pam_repository_t *auth_rep;
2507 
2508 	if (pamh) {
2509 		while (pamh->include_depth >= 0) {
2510 			free_pam_conf_info(pamh);
2511 			pamh->include_depth--;
2512 		}
2513 
2514 		/* Cleanup PAM_REPOSITORY structure */
2515 		auth_rep = pamh->ps_item[PAM_REPOSITORY].pi_addr;
2516 		if (auth_rep != NULL) {
2517 			if (auth_rep->type != NULL)
2518 				free(auth_rep->type);
2519 			if (auth_rep->scope != NULL)
2520 				free(auth_rep->scope);
2521 		}
2522 
2523 		for (i = 0; i < PAM_MAX_ITEMS; i++) {
2524 			if (pamh->ps_item[i].pi_addr != NULL) {
2525 				if (i == PAM_AUTHTOK || i == PAM_OLDAUTHTOK) {
2526 					(void) memset(pamh->ps_item[i].pi_addr,
2527 					    0, pamh->ps_item[i].pi_size);
2528 				}
2529 				free(pamh->ps_item[i].pi_addr);
2530 			}
2531 		}
2532 		free(pamh);
2533 	}
2534 }
2535 
2536 /*
2537  * free_pamconf - free memory used to store pam.conf entry
2538  */
2539 
2540 static void
2541 free_pamconf(pamtab_t *cp)
2542 {
2543 	int i;
2544 
2545 	if (cp) {
2546 		if (cp->pam_service)
2547 			free(cp->pam_service);
2548 		if (cp->module_path)
2549 			free(cp->module_path);
2550 		for (i = 0; i < cp->module_argc; i++) {
2551 			if (cp->module_argv[i])
2552 				free(cp->module_argv[i]);
2553 		}
2554 		if (cp->module_argc > 0)
2555 			free(cp->module_argv);
2556 		if (cp->function_ptr)
2557 			free(cp->function_ptr);
2558 
2559 		free(cp);
2560 	}
2561 }
2562 
2563 /*
2564  * free_pam_conf_info - free memory used to store all pam.conf info
2565  *			under the pam handle
2566  */
2567 
2568 static void
2569 free_pam_conf_info(pam_handle_t *pamh)
2570 {
2571 	pamtab_t *pamentp;
2572 	pamtab_t *pament_trail;
2573 	int i = pamh->include_depth;
2574 	int j;
2575 
2576 	for (j = 0; j < PAM_NUM_MODULE_TYPES; j++) {
2577 		pamentp = pamh->pam_conf_info[i][j];
2578 		pamh->pam_conf_info[i][j] = NULL;
2579 		pament_trail = pamentp;
2580 		while (pamentp) {
2581 			pamentp = pamentp->next;
2582 			free_pamconf(pament_trail);
2583 			pament_trail = pamentp;
2584 		}
2585 	}
2586 	if (pamh->pam_conf_name[i] != NULL) {
2587 		free(pamh->pam_conf_name[i]);
2588 		pamh->pam_conf_name[i] = NULL;
2589 	}
2590 }
2591 
2592 static void
2593 free_env(env_list *pam_env)
2594 {
2595 	if (pam_env) {
2596 		if (pam_env->name)
2597 			free(pam_env->name);
2598 		if (pam_env->value)
2599 			free(pam_env->value);
2600 		free(pam_env);
2601 	}
2602 }
2603 
2604 /*
2605  *	Internal convenience functions for Solaris PAM service modules.
2606  */
2607 
2608 #include <libintl.h>
2609 #include <nl_types.h>
2610 #include <synch.h>
2611 #include <locale.h>
2612 #include <thread.h>
2613 
2614 typedef struct pam_msg_data {
2615 	nl_catd fd;
2616 } pam_msg_data_t;
2617 
2618 /*
2619  * free_resp():
2620  *	free storage for responses used in the call back "pam_conv" functions
2621  */
2622 
2623 void
2624 free_resp(int num_msg, struct pam_response *resp)
2625 {
2626 	int			i;
2627 	struct pam_response	*r;
2628 
2629 	if (resp) {
2630 		r = resp;
2631 		for (i = 0; i < num_msg; i++, r++) {
2632 			if (r->resp) {
2633 				/* clear before freeing -- may be a password */
2634 				bzero(r->resp, strlen(r->resp));
2635 				free(r->resp);
2636 				r->resp = NULL;
2637 			}
2638 		}
2639 		free(resp);
2640 	}
2641 }
2642 
2643 static int
2644 do_conv(pam_handle_t *pamh, int msg_style, int num_msg,
2645     char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE], void *conv_apdp,
2646     struct pam_response *ret_respp[])
2647 {
2648 	struct pam_message	*msg;
2649 	struct pam_message	*m;
2650 	int			i;
2651 	int			k;
2652 	int			retcode;
2653 	struct pam_conv		*pam_convp;
2654 
2655 	if ((retcode = pam_get_item(pamh, PAM_CONV,
2656 	    (void **)&pam_convp)) != PAM_SUCCESS) {
2657 		return (retcode);
2658 	}
2659 
2660 	/*
2661 	 * When pam_set_item() is called to set PAM_CONV and the
2662 	 * item is NULL, memset(pip->pi_addr, 0, size) is called.
2663 	 * So at this point, we should check whether pam_convp->conv
2664 	 * is NULL or not.
2665 	 */
2666 	if ((pam_convp == NULL) || (pam_convp->conv == NULL))
2667 		return (PAM_SYSTEM_ERR);
2668 
2669 	i = 0;
2670 	k = num_msg;
2671 
2672 	msg = calloc(num_msg, sizeof (struct pam_message));
2673 	if (msg == NULL) {
2674 		return (PAM_BUF_ERR);
2675 	}
2676 	m = msg;
2677 
2678 	while (k--) {
2679 		/*
2680 		 * fill out the message structure to display prompt message
2681 		 */
2682 		m->msg_style = msg_style;
2683 		m->msg = messages[i];
2684 		pam_trace(PAM_DEBUG_CONV,
2685 		    "pam_conv_msg(%p:%d[%d]=%s)",
2686 		    (void *)pamh, msg_style, i, messages[i]);
2687 		m++;
2688 		i++;
2689 	}
2690 
2691 	/*
2692 	 * The UNIX pam modules always calls __pam_get_authtok() and
2693 	 * __pam_display_msg() with a NULL pointer as the conv_apdp.
2694 	 * In case the conv_apdp is NULL and the pam_convp->appdata_ptr
2695 	 * is not NULL, we should pass the pam_convp->appdata_ptr
2696 	 * to the conversation function.
2697 	 */
2698 	if (conv_apdp == NULL && pam_convp->appdata_ptr != NULL)
2699 		conv_apdp = pam_convp->appdata_ptr;
2700 
2701 	/*
2702 	 * Call conv function to display the prompt.
2703 	 */
2704 	retcode = (pam_convp->conv)(num_msg, &msg, ret_respp, conv_apdp);
2705 	pam_trace(PAM_DEBUG_CONV,
2706 	    "pam_conv_resp(%p pam_conv = %s) ret_respp = %p",
2707 	    (void *)pamh, pam_strerror(pamh, retcode), (void *)ret_respp);
2708 	if (*ret_respp == NULL) {
2709 		pam_trace(PAM_DEBUG_CONV,
2710 		    "pam_conv_resp(%p No response requested)", (void *)pamh);
2711 	} else if ((pam_debug & (PAM_DEBUG_CONV | PAM_DEBUG_AUTHTOK)) != 0) {
2712 		struct pam_response *r = *ret_respp;
2713 
2714 		for (i = 0; i < num_msg; i++, r++) {
2715 			if (r->resp == NULL) {
2716 				pam_trace(PAM_DEBUG_CONV,
2717 				    "pam_conv_resp(%p:"
2718 				    "[%d] NULL response string)",
2719 				    (void *)pamh, i);
2720 			} else {
2721 				if (msg_style == PAM_PROMPT_ECHO_OFF) {
2722 #ifdef	DEBUG
2723 					pam_trace(PAM_DEBUG_AUTHTOK,
2724 					    "pam_conv_resp(%p:[%d]=%s, "
2725 					    "code=%d)",
2726 					    (void *)pamh, i, r->resp,
2727 					    r->resp_retcode);
2728 #endif	/* DEBUG */
2729 					pam_trace(PAM_DEBUG_CONV,
2730 					    "pam_conv_resp(%p:[%d] len=%lu, "
2731 					    "code=%d)",
2732 					    (void *)pamh, i,
2733 					    (ulong_t)strlen(r->resp),
2734 					    r->resp_retcode);
2735 				} else {
2736 					pam_trace(PAM_DEBUG_CONV,
2737 					    "pam_conv_resp(%p:[%d]=%s, "
2738 					    "code=%d)",
2739 					    (void *)pamh, i, r->resp,
2740 					    r->resp_retcode);
2741 				}
2742 			}
2743 		}
2744 	}
2745 
2746 	if (msg)
2747 		free(msg);
2748 	return (retcode);
2749 }
2750 
2751 /*
2752  * __pam_display_msg():
2753  *	display message by calling the call back functions
2754  *	provided by the application through "pam_conv" structure
2755  */
2756 
2757 int
2758 __pam_display_msg(pam_handle_t *pamh, int msg_style, int num_msg,
2759     char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE], void *conv_apdp)
2760 {
2761 	struct pam_response	*ret_respp = NULL;
2762 	int ret;
2763 
2764 	ret = do_conv(pamh, msg_style, num_msg, messages,
2765 	    conv_apdp, &ret_respp);
2766 
2767 	if (ret_respp != NULL)
2768 		free_resp(num_msg, ret_respp);
2769 
2770 	return (ret);
2771 }
2772 
2773 /*
2774  * __pam_get_authtok()
2775  *	retrieves a password of at most PASS_MAX length from the pam
2776  *	handle (pam_get_item) or from the input stream (do_conv).
2777  *
2778  * This function allocates memory for the new authtok.
2779  * Applications calling this function are responsible for
2780  * freeing this memory.
2781  *
2782  * If "source" is
2783  *	PAM_HANDLE
2784  * and "type" is:
2785  *	PAM_AUTHTOK - password is taken from pam handle (PAM_AUTHTOK)
2786  *	PAM_OLDAUTHTOK - password is taken from pam handle (PAM_OLDAUTHTOK)
2787  *
2788  * If "source" is
2789  *	PAM_PROMPT
2790  * and "type" is:
2791  *	0:		Prompt for new passwd, do not even attempt
2792  *			to store it in the pam handle.
2793  *	PAM_AUTHTOK:	Prompt for new passwd, store in pam handle as
2794  *			PAM_AUTHTOK item if this value is not already set.
2795  *	PAM_OLDAUTHTOK:	Prompt for new passwd, store in pam handle as
2796  *			PAM_OLDAUTHTOK item if this value is not
2797  *			already set.
2798  */
2799 int
2800 __pam_get_authtok(pam_handle_t *pamh, int source, int type, char *prompt,
2801     char **authtok)
2802 {
2803 	int error = PAM_SYSTEM_ERR;
2804 	char *new_password = NULL;
2805 	struct pam_response *ret_resp = NULL;
2806 	char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
2807 
2808 	if ((*authtok = calloc(PASS_MAX+1, sizeof (char))) == NULL)
2809 		return (PAM_BUF_ERR);
2810 
2811 	if (prompt == NULL)
2812 		prompt = dgettext(TEXT_DOMAIN, "password: ");
2813 
2814 	switch (source) {
2815 	case PAM_HANDLE:
2816 
2817 		/* get password from pam handle item list */
2818 
2819 		switch (type) {
2820 		case PAM_AUTHTOK:
2821 		case PAM_OLDAUTHTOK:
2822 
2823 			if ((error = pam_get_item(pamh, type,
2824 			    (void **)&new_password)) != PAM_SUCCESS)
2825 				goto err_ret;
2826 
2827 			if (new_password == NULL || new_password[0] == '\0') {
2828 				free(*authtok);
2829 				*authtok = NULL;
2830 			} else {
2831 				(void) strlcpy(*authtok, new_password,
2832 				    PASS_MAX+1);
2833 			}
2834 			break;
2835 		default:
2836 			__pam_log(LOG_AUTH | LOG_ERR,
2837 			    "__pam_get_authtok() invalid type: %d", type);
2838 			error = PAM_SYMBOL_ERR;
2839 			goto err_ret;
2840 		}
2841 		break;
2842 	case PAM_PROMPT:
2843 
2844 		/*
2845 		 * Prompt for new password and save in pam handle item list
2846 		 * if the that item is not already set.
2847 		 */
2848 
2849 		(void) strncpy(messages[0], prompt, sizeof (messages[0]));
2850 		if ((error = do_conv(pamh, PAM_PROMPT_ECHO_OFF, 1, messages,
2851 		    NULL, &ret_resp)) != PAM_SUCCESS)
2852 			goto err_ret;
2853 
2854 		if (ret_resp->resp == NULL) {
2855 			/* getpass didn't return anything */
2856 			error = PAM_SYSTEM_ERR;
2857 			goto err_ret;
2858 		}
2859 
2860 		/* save the new password if this item was NULL */
2861 		if (type) {
2862 			if ((error = pam_get_item(pamh, type,
2863 			    (void **)&new_password)) != PAM_SUCCESS) {
2864 				free_resp(1, ret_resp);
2865 				goto err_ret;
2866 			}
2867 			if (new_password == NULL)
2868 				(void) pam_set_item(pamh, type, ret_resp->resp);
2869 		}
2870 
2871 		(void) strlcpy(*authtok, ret_resp->resp, PASS_MAX+1);
2872 		free_resp(1, ret_resp);
2873 		break;
2874 	default:
2875 		__pam_log(LOG_AUTH | LOG_ERR,
2876 		    "__pam_get_authtok() invalid source: %d", source);
2877 		error = PAM_SYMBOL_ERR;
2878 		goto err_ret;
2879 	}
2880 
2881 	return (PAM_SUCCESS);
2882 
2883 err_ret:
2884 	bzero(*authtok, PASS_MAX+1);
2885 	free(*authtok);
2886 	*authtok = NULL;
2887 	return (error);
2888 }
2889