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