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