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 2006 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  */
1548 char **
1549 pam_getenvlist(pam_handle_t *pamh)
1550 {
1551 	int		error = PAM_SYSTEM_ERR;
1552 	char		**list = 0;
1553 	int		length = 0;
1554 	env_list	*traverse;
1555 	char		env_buf[1024];
1556 
1557 	pam_trace(PAM_DEBUG_DEFAULT,
1558 	    "pam_getenvlist(%p)", (void *)pamh);
1559 
1560 	if (pamh == NULL)
1561 		goto out;
1562 
1563 	/* find out how many environment variables we have */
1564 	traverse = pamh->pam_env;
1565 	while (traverse) {
1566 		length++;
1567 		traverse = traverse->next;
1568 	}
1569 
1570 	/* allocate the array we will return to the caller */
1571 	if ((list = (char **)calloc(length + 1, sizeof (char *))) == 0) {
1572 		error = PAM_BUF_ERR;
1573 		goto out;
1574 	}
1575 
1576 	/* add the variables one by one */
1577 	length = 0;
1578 	traverse = pamh->pam_env;
1579 	while (traverse) {
1580 		(void) snprintf(env_buf, sizeof (env_buf), "%s=%s",
1581 		    traverse->name, traverse->value);
1582 		if ((list[length] = strdup(env_buf)) == 0) {
1583 			error = PAM_BUF_ERR;
1584 			goto out;
1585 		}
1586 		length++;
1587 		traverse = traverse->next;
1588 	}
1589 
1590 	/* null terminate the list */
1591 	list[length] = 0;
1592 
1593 	error = PAM_SUCCESS;
1594 out:
1595 	if (error != PAM_SUCCESS) {
1596 		/* free the partially constructed list */
1597 		if (list) {
1598 			length = 0;
1599 			while (list[length] != NULL) {
1600 				free(list[length]);
1601 				length++;
1602 			}
1603 			free(list);
1604 		}
1605 	}
1606 	return (error ? NULL : list);
1607 }
1608 
1609 /*
1610  * Routines to load a requested module on demand
1611  */
1612 
1613 /*
1614  * load_modules - load the requested module.
1615  *		  if the dlopen or dlsym fail, then
1616  *		  the module is ignored.
1617  */
1618 
1619 static int
1620 load_modules(pam_handle_t *pamh, int type, char *function_name,
1621     pamtab_t *pam_entry)
1622 {
1623 	void	*mh;
1624 	struct	auth_module *authp;
1625 	struct	account_module *accountp;
1626 	struct	session_module *sessionp;
1627 	struct	password_module *passwdp;
1628 	int	loading_functions = 0; /* are we currently loading functions? */
1629 
1630 	pam_trace(PAM_DEBUG_MODULE, "load_modules[%d:%s](%p, %s)=%s:%s",
1631 		pamh->include_depth, pam_trace_cname(pamh), (void *)pamh,
1632 		function_name, pam_trace_fname(pam_entry->pam_flag),
1633 		pam_entry->module_path);
1634 
1635 	while (pam_entry != NULL) {
1636 		pam_trace(PAM_DEBUG_DEFAULT,
1637 		    "while load_modules[%d:%s](%p, %s)=%s",
1638 		    pamh->include_depth, pam_trace_cname(pamh), (void *)pamh,
1639 		    function_name, pam_entry->module_path);
1640 
1641 		if (pam_entry->pam_flag & PAM_INCLUDE) {
1642 			pam_trace(PAM_DEBUG_DEFAULT,
1643 			    "done load_modules[%d:%s](%p, %s)=%s",
1644 			    pamh->include_depth, pam_trace_cname(pamh),
1645 			    (void *)pamh, function_name,
1646 			    pam_entry->module_path);
1647 			return (PAM_SUCCESS);
1648 		}
1649 		switch (type) {
1650 		case PAM_AUTH_MODULE:
1651 
1652 			/* if the function has already been loaded, return */
1653 			authp = pam_entry->function_ptr;
1654 			if (!loading_functions &&
1655 				(((strcmp(function_name, PAM_SM_AUTHENTICATE)
1656 				== 0) &&
1657 				authp && authp->pam_sm_authenticate) ||
1658 				((strcmp(function_name, PAM_SM_SETCRED) == 0) &&
1659 				authp && authp->pam_sm_setcred))) {
1660 				return (PAM_SUCCESS);
1661 			}
1662 
1663 			/* function has not been loaded yet */
1664 			loading_functions = 1;
1665 			if (authp == NULL) {
1666 				authp = (struct auth_module *)calloc(1,
1667 				    sizeof (struct auth_module));
1668 				if (authp == NULL)
1669 					return (PAM_BUF_ERR);
1670 			}
1671 
1672 			/* if open_module fails, return error */
1673 			if ((mh = open_module(pamh,
1674 			    pam_entry->module_path)) == NULL) {
1675 				__pam_log(LOG_AUTH | LOG_ERR,
1676 				    "load_modules[%d:%s]: can not open module "
1677 				    "%s", pamh->include_depth,
1678 				    pam_trace_cname(pamh),
1679 				    pam_entry->module_path);
1680 				free(authp);
1681 				return (PAM_OPEN_ERR);
1682 			}
1683 
1684 			/* load the authentication function */
1685 			if (strcmp(function_name, PAM_SM_AUTHENTICATE) == 0) {
1686 				if (load_function(mh, PAM_SM_AUTHENTICATE,
1687 				    &authp->pam_sm_authenticate)
1688 				    != PAM_SUCCESS) {
1689 					/* return error if dlsym fails */
1690 					free(authp);
1691 					return (PAM_SYMBOL_ERR);
1692 				}
1693 
1694 			/* load the setcred function */
1695 			} else if (strcmp(function_name, PAM_SM_SETCRED) == 0) {
1696 				if (load_function(mh, PAM_SM_SETCRED,
1697 				    &authp->pam_sm_setcred) != PAM_SUCCESS) {
1698 					/* return error if dlsym fails */
1699 					free(authp);
1700 					return (PAM_SYMBOL_ERR);
1701 				}
1702 			}
1703 			pam_entry->function_ptr = authp;
1704 			break;
1705 		case PAM_ACCOUNT_MODULE:
1706 			accountp = pam_entry->function_ptr;
1707 			if (!loading_functions &&
1708 			    (strcmp(function_name, PAM_SM_ACCT_MGMT) == 0) &&
1709 			    accountp && accountp->pam_sm_acct_mgmt) {
1710 				return (PAM_SUCCESS);
1711 			}
1712 
1713 			/*
1714 			 * If functions are added to the account module,
1715 			 * verify that one of the other functions hasn't
1716 			 * already loaded it.  See PAM_AUTH_MODULE code.
1717 			 */
1718 			loading_functions = 1;
1719 			accountp = (struct account_module *)calloc(1,
1720 			    sizeof (struct account_module));
1721 			if (accountp == NULL)
1722 				return (PAM_BUF_ERR);
1723 
1724 			/* if open_module fails, return error */
1725 			if ((mh = open_module(pamh,
1726 			    pam_entry->module_path)) == NULL) {
1727 				__pam_log(LOG_AUTH | LOG_ERR,
1728 				    "load_modules[%d:%s]: can not open module "
1729 				    "%s", pamh->include_depth,
1730 				    pam_trace_cname(pamh),
1731 				    pam_entry->module_path);
1732 				free(accountp);
1733 				return (PAM_OPEN_ERR);
1734 			}
1735 
1736 			if (load_function(mh, PAM_SM_ACCT_MGMT,
1737 			    &accountp->pam_sm_acct_mgmt) != PAM_SUCCESS) {
1738 				__pam_log(LOG_AUTH | LOG_ERR,
1739 				    "load_modules[%d:%s]: pam_sm_acct_mgmt() "
1740 				    "missing", pamh->include_depth,
1741 				    pam_trace_cname(pamh));
1742 				free(accountp);
1743 				return (PAM_SYMBOL_ERR);
1744 			}
1745 			pam_entry->function_ptr = accountp;
1746 			break;
1747 		case PAM_SESSION_MODULE:
1748 			sessionp = pam_entry->function_ptr;
1749 			if (!loading_functions &&
1750 			    (((strcmp(function_name,
1751 			    PAM_SM_OPEN_SESSION) == 0) &&
1752 			    sessionp && sessionp->pam_sm_open_session) ||
1753 			    ((strcmp(function_name,
1754 			    PAM_SM_CLOSE_SESSION) == 0) &&
1755 			    sessionp && sessionp->pam_sm_close_session))) {
1756 				return (PAM_SUCCESS);
1757 			}
1758 
1759 			loading_functions = 1;
1760 			if (sessionp == NULL) {
1761 				sessionp = (struct session_module *)
1762 				    calloc(1, sizeof (struct session_module));
1763 				if (sessionp == NULL)
1764 					return (PAM_BUF_ERR);
1765 			}
1766 
1767 			/* if open_module fails, return error */
1768 			if ((mh = open_module(pamh,
1769 			    pam_entry->module_path)) == NULL) {
1770 				__pam_log(LOG_AUTH | LOG_ERR,
1771 				    "load_modules[%d:%s]: can not open module "
1772 				    "%s", pamh->include_depth,
1773 				    pam_trace_cname(pamh),
1774 				    pam_entry->module_path);
1775 				free(sessionp);
1776 				return (PAM_OPEN_ERR);
1777 			}
1778 
1779 			if ((strcmp(function_name, PAM_SM_OPEN_SESSION) == 0) &&
1780 			    load_function(mh, PAM_SM_OPEN_SESSION,
1781 				&sessionp->pam_sm_open_session)
1782 				!= PAM_SUCCESS) {
1783 				free(sessionp);
1784 				return (PAM_SYMBOL_ERR);
1785 			} else if ((strcmp(function_name,
1786 					PAM_SM_CLOSE_SESSION) == 0) &&
1787 				    load_function(mh, PAM_SM_CLOSE_SESSION,
1788 					&sessionp->pam_sm_close_session)
1789 					!= PAM_SUCCESS) {
1790 				free(sessionp);
1791 				return (PAM_SYMBOL_ERR);
1792 			}
1793 			pam_entry->function_ptr = sessionp;
1794 			break;
1795 		case PAM_PASSWORD_MODULE:
1796 			passwdp = pam_entry->function_ptr;
1797 			if (!loading_functions &&
1798 			    (strcmp(function_name, PAM_SM_CHAUTHTOK) == 0) &&
1799 			    passwdp && passwdp->pam_sm_chauthtok) {
1800 				return (PAM_SUCCESS);
1801 			}
1802 
1803 			/*
1804 			 * If functions are added to the password module,
1805 			 * verify that one of the other functions hasn't
1806 			 * already loaded it.  See PAM_AUTH_MODULE code.
1807 			 */
1808 			loading_functions = 1;
1809 			passwdp = (struct password_module *)
1810 				calloc(1, sizeof (struct password_module));
1811 			if (passwdp == NULL)
1812 				return (PAM_BUF_ERR);
1813 
1814 			/* if open_module fails, continue */
1815 			if ((mh = open_module(pamh,
1816 			    pam_entry->module_path)) == NULL) {
1817 				__pam_log(LOG_AUTH | LOG_ERR,
1818 				    "load_modules[%d:%s]: can not open module "
1819 				    "%s", pamh->include_depth,
1820 				    pam_trace_cname(pamh),
1821 				    pam_entry->module_path);
1822 				free(passwdp);
1823 				return (PAM_OPEN_ERR);
1824 			}
1825 
1826 			if (load_function(mh, PAM_SM_CHAUTHTOK,
1827 			    &passwdp->pam_sm_chauthtok) != PAM_SUCCESS) {
1828 				free(passwdp);
1829 				return (PAM_SYMBOL_ERR);
1830 			}
1831 			pam_entry->function_ptr = passwdp;
1832 			break;
1833 		default:
1834 			pam_trace(PAM_DEBUG_DEFAULT,
1835 			    "load_modules[%d:%s](%p, %s): unsupported type %d",
1836 			    pamh->include_depth, pam_trace_cname(pamh),
1837 			    (void *)pamh, function_name, type);
1838 			break;
1839 		}
1840 
1841 		pam_entry = pam_entry->next;
1842 	} /* while */
1843 
1844 	pam_trace(PAM_DEBUG_MODULE, "load_modules[%d:%s](%p, %s)=done",
1845 	    pamh->include_depth, pam_trace_cname(pamh), (void *)pamh,
1846 	    function_name);
1847 
1848 	return (PAM_SUCCESS);
1849 }
1850 
1851 /*
1852  * open_module		- Open the module first checking for
1853  *			  propers modes and ownerships on the file.
1854  */
1855 
1856 static void *
1857 open_module(pam_handle_t *pamh, char *module_so)
1858 {
1859 	struct stat64	stb;
1860 	char		*errmsg;
1861 	void		*lfd;
1862 	fd_list		*module_fds = 0;
1863 	fd_list		*trail = 0;
1864 	fd_list		*traverse = 0;
1865 
1866 	/* Check the ownership and file modes */
1867 	if (stat64(module_so, &stb) < 0) {
1868 		__pam_log(LOG_AUTH | LOG_ERR,
1869 		    "open_module[%d:%s]: stat(%s) failed: %s",
1870 		    pamh->include_depth, pam_trace_cname(pamh), module_so,
1871 		    strerror(errno));
1872 		return (NULL);
1873 	}
1874 	if (stb.st_uid != (uid_t)0) {
1875 		__pam_log(LOG_AUTH | LOG_ALERT,
1876 		    "open_module[%d:%s]: Owner of the module %s is not root",
1877 		    pamh->include_depth, pam_trace_cname(pamh), module_so);
1878 		return (NULL);
1879 	}
1880 	if (stb.st_mode & S_IWGRP) {
1881 		__pam_log(LOG_AUTH | LOG_ALERT,
1882 		    "open_module[%d:%s]: module %s writable by group",
1883 		    pamh->include_depth, pam_trace_cname(pamh), module_so);
1884 		return (NULL);
1885 	}
1886 	if (stb.st_mode & S_IWOTH) {
1887 		__pam_log(LOG_AUTH | LOG_ALERT,
1888 		    "open_module[%d:%s]: module %s writable by world",
1889 		    pamh->include_depth, pam_trace_cname(pamh), module_so);
1890 		return (NULL);
1891 	}
1892 
1893 	/*
1894 	 * Perform the dlopen()
1895 	 */
1896 	lfd = (void *)dlopen(module_so, RTLD_LAZY);
1897 
1898 	if (lfd == NULL) {
1899 		errmsg = dlerror();
1900 		__pam_log(LOG_AUTH | LOG_ERR, "open_module[%d:%s]: %s "
1901 		    "failed: %s", pamh->include_depth, pam_trace_cname(pamh),
1902 		    module_so, errmsg != NULL ? errmsg : "Unknown error");
1903 		return (NULL);
1904 	} else {
1905 		/* add this fd to the pam handle */
1906 		if ((module_fds = (fd_list *)calloc(1, sizeof (fd_list)))
1907 		    == 0) {
1908 			(void) dlclose(lfd);
1909 			lfd = 0;
1910 			return (NULL);
1911 		}
1912 		module_fds->mh = lfd;
1913 
1914 		if (pamh->fd == 0) {
1915 			/* adding new head of list */
1916 			pamh->fd = module_fds;
1917 		} else {
1918 			/* appending to end of list */
1919 			traverse = pamh->fd;
1920 			while (traverse) {
1921 				trail = traverse;
1922 				traverse = traverse->next;
1923 			}
1924 			trail->next = module_fds;
1925 		}
1926 	}
1927 
1928 	return (lfd);
1929 }
1930 
1931 /*
1932  * load_function - call dlsym() to resolve the function address
1933  */
1934 static int
1935 load_function(void *lfd, char *name, int (**func)())
1936 {
1937 	char *errmsg = NULL;
1938 
1939 	if (lfd == NULL)
1940 		return (PAM_SYMBOL_ERR);
1941 
1942 	*func = (int (*)())dlsym(lfd, name);
1943 	if (*func == NULL) {
1944 		errmsg = dlerror();
1945 		__pam_log(LOG_AUTH | LOG_ERR, "dlsym failed %s: error %s",
1946 		    name, errmsg != NULL ? errmsg : "Unknown error");
1947 		return (PAM_SYMBOL_ERR);
1948 	}
1949 
1950 	pam_trace(PAM_DEBUG_DEFAULT,
1951 	    "load_function: successful load of %s", name);
1952 	return (PAM_SUCCESS);
1953 }
1954 
1955 /*
1956  * Routines to read the pam.conf configuration file
1957  */
1958 
1959 /*
1960  * open_pam_conf - open the pam.conf config file
1961  */
1962 
1963 static int
1964 open_pam_conf(struct pam_fh **pam_fh, pam_handle_t *pamh, char *config)
1965 {
1966 	struct stat64	stb;
1967 	int		fd;
1968 
1969 	if ((fd = open(config, O_RDONLY)) == -1) {
1970 		__pam_log(LOG_AUTH | LOG_ALERT,
1971 		    "open_pam_conf[%d:%s]: open(%s) failed: %s",
1972 		    pamh->include_depth, pam_trace_cname(pamh), config,
1973 		    strerror(errno));
1974 		return (0);
1975 	}
1976 	/* Check the ownership and file modes */
1977 	if (fstat64(fd, &stb) < 0) {
1978 		__pam_log(LOG_AUTH | LOG_ALERT,
1979 		    "open_pam_conf[%d:%s]: stat(%s) failed: %s",
1980 		    pamh->include_depth, pam_trace_cname(pamh), config,
1981 		    strerror(errno));
1982 		(void) close(fd);
1983 		return (0);
1984 	}
1985 	if (stb.st_uid != (uid_t)0) {
1986 		__pam_log(LOG_AUTH | LOG_ALERT,
1987 		    "open_pam_conf[%d:%s]: Owner of %s is not root",
1988 		    pamh->include_depth, pam_trace_cname(pamh), config);
1989 		(void) close(fd);
1990 		return (0);
1991 	}
1992 	if (stb.st_mode & S_IWGRP) {
1993 		__pam_log(LOG_AUTH | LOG_ALERT,
1994 		    "open_pam_conf[%d:%s]: %s writable by group",
1995 		    pamh->include_depth, pam_trace_cname(pamh), config);
1996 		(void) close(fd);
1997 		return (0);
1998 	}
1999 	if (stb.st_mode & S_IWOTH) {
2000 		__pam_log(LOG_AUTH | LOG_ALERT,
2001 		    "open_pam_conf[%d:%s]: %s writable by world",
2002 		    pamh->include_depth, pam_trace_cname(pamh), config);
2003 		(void) close(fd);
2004 		return (0);
2005 	}
2006 	if ((*pam_fh = calloc(1, sizeof (struct pam_fh))) == NULL) {
2007 		(void) close(fd);
2008 		return (0);
2009 	}
2010 	(*pam_fh)->fconfig = fd;
2011 	(*pam_fh)->bufsize = (size_t)stb.st_size;
2012 	if (((*pam_fh)->data = mmap(0, (*pam_fh)->bufsize, PROT_READ,
2013 	    MAP_PRIVATE, (*pam_fh)->fconfig, 0)) == MAP_FAILED) {
2014 		(void) close(fd);
2015 		free (*pam_fh);
2016 		return (0);
2017 	}
2018 	(*pam_fh)->bufferp = (*pam_fh)->data;
2019 
2020 	return (1);
2021 }
2022 
2023 /*
2024  * close_pam_conf - close pam.conf
2025  */
2026 
2027 static void
2028 close_pam_conf(struct pam_fh *pam_fh)
2029 {
2030 	(void) munmap(pam_fh->data, pam_fh->bufsize);
2031 	(void) close(pam_fh->fconfig);
2032 	free(pam_fh);
2033 }
2034 
2035 /*
2036  * read_pam_conf - read in each entry in pam.conf and store info
2037  *		   under the pam handle.
2038  */
2039 
2040 static int
2041 read_pam_conf(pam_handle_t *pamh, char *config)
2042 {
2043 	struct pam_fh	*pam_fh;
2044 	pamtab_t	*pamentp;
2045 	pamtab_t	*tpament;
2046 	char		*service;
2047 	int		error;
2048 	int		i = pamh->include_depth;	/* include depth */
2049 	/*
2050 	 * service types:
2051 	 * error (-1), "auth" (0), "account" (1), "session" (2), "password" (3)
2052 	 */
2053 	int service_found[PAM_NUM_MODULE_TYPES+1] = {0, 0, 0, 0, 0};
2054 
2055 	(void) pam_get_item(pamh, PAM_SERVICE, (void **)&service);
2056 	if (service == NULL || *service == '\0') {
2057 		__pam_log(LOG_AUTH | LOG_ERR, "No service name");
2058 		return (PAM_SYSTEM_ERR);
2059 	}
2060 
2061 	pamh->pam_conf_name[i] = strdup(config);
2062 	pam_trace(PAM_DEBUG_CONF, "read_pam_conf[%d:%s](%p) open(%s)",
2063 	    i, pam_trace_cname(pamh), (void *)pamh, config);
2064 	if (open_pam_conf(&pam_fh, pamh, config) == 0) {
2065 		return (PAM_SYSTEM_ERR);
2066 	}
2067 
2068 	while ((error =
2069 	    get_pam_conf_entry(pam_fh, pamh, &pamentp)) == PAM_SUCCESS &&
2070 	    pamentp) {
2071 
2072 		/* See if entry is this service and valid */
2073 		if (verify_pam_conf(pamentp, service)) {
2074 			pam_trace(PAM_DEBUG_CONF,
2075 			    "read_pam_conf[%d:%s](%p): bad entry error %s",
2076 			    i, pam_trace_cname(pamh), (void *)pamh, service);
2077 
2078 			error = PAM_SYSTEM_ERR;
2079 			free_pamconf(pamentp);
2080 			goto out;
2081 		}
2082 		if (strcasecmp(pamentp->pam_service, service) == 0) {
2083 			pam_trace(PAM_DEBUG_CONF,
2084 			    "read_pam_conf[%d:%s](%p): processing %s",
2085 			    i, pam_trace_cname(pamh), (void *)pamh, service);
2086 			/* process first service entry */
2087 			if (service_found[pamentp->pam_type + 1] == 0) {
2088 				/* purge "other" entries */
2089 				while ((tpament = pamh->pam_conf_info[i]
2090 				    [pamentp->pam_type]) != NULL) {
2091 					pam_trace(PAM_DEBUG_CONF,
2092 					    "read_pam_conf(%p): purging "
2093 					    "\"other\"[%d:%s][%s]",
2094 					    (void *)pamh, i,
2095 					    pam_trace_cname(pamh),
2096 					    pam_snames[pamentp->pam_type]);
2097 					pamh->pam_conf_info[i]
2098 					    [pamentp->pam_type] = tpament->next;
2099 					free_pamconf(tpament);
2100 				}
2101 				/* add first service entry */
2102 				pam_trace(PAM_DEBUG_CONF,
2103 				    "read_pam_conf(%p): adding 1st "
2104 				    "%s[%d:%s][%s]",
2105 				    (void *)pamh, service, i,
2106 				    pam_trace_cname(pamh),
2107 				    pam_snames[pamentp->pam_type]);
2108 				pamh->pam_conf_info[i][pamentp->pam_type] =
2109 				    pamentp;
2110 				service_found[pamentp->pam_type + 1] = 1;
2111 			} else {
2112 				/* append more service entries */
2113 				pam_trace(PAM_DEBUG_CONF,
2114 				    "read_pam_conf(%p): adding more "
2115 				    "%s[%d:%s][%s]",
2116 				    (void *)pamh, service, i,
2117 				    pam_trace_cname(pamh),
2118 				    pam_snames[pamentp->pam_type]);
2119 				tpament =
2120 				    pamh->pam_conf_info[i][pamentp->pam_type];
2121 				while (tpament->next != NULL) {
2122 					tpament = tpament->next;
2123 				}
2124 				tpament->next = pamentp;
2125 			}
2126 		} else if (service_found[pamentp->pam_type + 1] == 0) {
2127 			/* See if "other" entry available and valid */
2128 			if (verify_pam_conf(pamentp, "other")) {
2129 				pam_trace(PAM_DEBUG_CONF,
2130 				    "read_pam_conf(%p): bad entry error %s "
2131 				    "\"other\"[%d:%s]",
2132 				    (void *)pamh, service, i,
2133 				    pam_trace_cname(pamh));
2134 				error = PAM_SYSTEM_ERR;
2135 				free_pamconf(pamentp);
2136 				goto out;
2137 			}
2138 			if (strcasecmp(pamentp->pam_service, "other") == 0) {
2139 				pam_trace(PAM_DEBUG_CONF,
2140 				    "read_pam_conf(%p): processing "
2141 				    "\"other\"[%d:%s]", (void *)pamh, i,
2142 				    pam_trace_cname(pamh));
2143 				if ((tpament = pamh->pam_conf_info[i]
2144 				    [pamentp->pam_type]) == NULL) {
2145 					/* add first "other" entry */
2146 					pam_trace(PAM_DEBUG_CONF,
2147 					    "read_pam_conf(%p): adding 1st "
2148 					    "other[%d:%s][%s]", (void *)pamh, i,
2149 					    pam_trace_cname(pamh),
2150 					    pam_snames[pamentp->pam_type]);
2151 					pamh->pam_conf_info[i]
2152 					    [pamentp->pam_type] = pamentp;
2153 				} else {
2154 					/* append more "other" entries */
2155 					pam_trace(PAM_DEBUG_CONF,
2156 					    "read_pam_conf(%p): adding more "
2157 					    "other[%d:%s][%s]", (void *)pamh, i,
2158 					    pam_trace_cname(pamh),
2159 					    pam_snames[pamentp->pam_type]);
2160 					while (tpament->next != NULL) {
2161 						tpament = tpament->next;
2162 					}
2163 					tpament->next = pamentp;
2164 				}
2165 			} else {
2166 				/* irrelevent entry */
2167 				free_pamconf(pamentp);
2168 			}
2169 		} else {
2170 			/* irrelevent entry */
2171 			free_pamconf(pamentp);
2172 		}
2173 	}
2174 out:
2175 	(void) close_pam_conf(pam_fh);
2176 	if (error != PAM_SUCCESS)
2177 		free_pam_conf_info(pamh);
2178 	return (error);
2179 }
2180 
2181 /*
2182  * get_pam_conf_entry - get a pam.conf entry
2183  */
2184 
2185 static int
2186 get_pam_conf_entry(struct pam_fh *pam_fh, pam_handle_t *pamh, pamtab_t **pam)
2187 {
2188 	char		*cp, *arg;
2189 	int		argc;
2190 	char		*tmp, *tmp_free;
2191 	int		i;
2192 	char		*current_line = NULL;
2193 	int		error = PAM_SYSTEM_ERR;	/* preset to error */
2194 	int		err;
2195 
2196 	/* get the next line from pam.conf */
2197 	if ((cp = nextline(pam_fh, pamh, &err)) == NULL) {
2198 		/* no more lines in pam.conf ==> return */
2199 		error = PAM_SUCCESS;
2200 		*pam = NULL;
2201 		goto out;
2202 	}
2203 
2204 	if ((*pam = (pamtab_t *)calloc(1, sizeof (pamtab_t))) == NULL) {
2205 		__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2206 		goto out;
2207 	}
2208 
2209 	/* copy full line for error reporting */
2210 	if ((current_line = strdup(cp)) == NULL) {
2211 		__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2212 		goto out;
2213 	}
2214 
2215 	pam_trace(PAM_DEBUG_CONF,
2216 	    "pam.conf[%s] entry:\t%s", pam_trace_cname(pamh), current_line);
2217 
2218 	/* get service name (e.g. login, su, passwd) */
2219 	if ((arg = read_next_token(&cp)) == 0) {
2220 		__pam_log(LOG_AUTH | LOG_CRIT,
2221 		    "illegal pam.conf[%s] entry: %s: missing SERVICE NAME",
2222 		    pam_trace_cname(pamh), current_line);
2223 		goto out;
2224 	}
2225 	if (((*pam)->pam_service = strdup(arg)) == 0) {
2226 		__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2227 		goto out;
2228 	}
2229 
2230 	/* get module type (e.g. authentication, acct mgmt) */
2231 	if ((arg = read_next_token(&cp)) == 0) {
2232 		__pam_log(LOG_AUTH | LOG_CRIT,
2233 		    "illegal pam.conf[%s] entry: %s: missing MODULE TYPE",
2234 		    pam_trace_cname(pamh), current_line);
2235 		(*pam)->pam_type = -1;	/* 0 is a valid value */
2236 		goto getflag;
2237 	}
2238 	if (strcasecmp(arg, PAM_AUTH_NAME) == 0) {
2239 		(*pam)->pam_type = PAM_AUTH_MODULE;
2240 	} else if (strcasecmp(arg, PAM_ACCOUNT_NAME) == 0) {
2241 		(*pam)->pam_type = PAM_ACCOUNT_MODULE;
2242 	} else if (strcasecmp(arg, PAM_SESSION_NAME) == 0) {
2243 		(*pam)->pam_type = PAM_SESSION_MODULE;
2244 	} else if (strcasecmp(arg, PAM_PASSWORD_NAME) == 0) {
2245 		(*pam)->pam_type = PAM_PASSWORD_MODULE;
2246 	} else {
2247 		/* error */
2248 		__pam_log(LOG_AUTH | LOG_CRIT,
2249 		    "illegal pam.conf[%s] entry: %s: invalid module "
2250 		    "type: %s", pam_trace_cname(pamh), current_line, arg);
2251 		(*pam)->pam_type = -1;	/* 0 is a valid value */
2252 	}
2253 
2254 getflag:
2255 	/* get pam flag (e.g., requisite, required, sufficient, optional) */
2256 	if ((arg = read_next_token(&cp)) == 0) {
2257 		__pam_log(LOG_AUTH | LOG_CRIT,
2258 		    "illegal pam.conf[%s] entry: %s: missing CONTROL FLAG",
2259 		    pam_trace_cname(pamh), current_line);
2260 		goto getpath;
2261 	}
2262 	if (strcasecmp(arg, PAM_BINDING_NAME) == 0) {
2263 		(*pam)->pam_flag = PAM_BINDING;
2264 	} else if (strcasecmp(arg, PAM_INCLUDE_NAME) == 0) {
2265 		(*pam)->pam_flag = PAM_INCLUDE;
2266 	} else if (strcasecmp(arg, PAM_OPTIONAL_NAME) == 0) {
2267 		(*pam)->pam_flag = PAM_OPTIONAL;
2268 	} else if (strcasecmp(arg, PAM_REQUIRED_NAME) == 0) {
2269 		(*pam)->pam_flag = PAM_REQUIRED;
2270 	} else if (strcasecmp(arg, PAM_REQUISITE_NAME) == 0) {
2271 		(*pam)->pam_flag = PAM_REQUISITE;
2272 	} else if (strcasecmp(arg, PAM_SUFFICIENT_NAME) == 0) {
2273 		(*pam)->pam_flag = PAM_SUFFICIENT;
2274 	} else {
2275 		/* error */
2276 		__pam_log(LOG_AUTH | LOG_CRIT,
2277 		    "illegal pam.conf[%s] entry: %s",
2278 		    pam_trace_cname(pamh), current_line);
2279 		__pam_log(LOG_AUTH | LOG_CRIT,
2280 		    "\tinvalid control flag: %s", arg);
2281 	}
2282 
2283 getpath:
2284 	/* get module path (e.g. /usr/lib/security/pam_unix_auth.so.1) */
2285 	if ((arg = read_next_token(&cp)) == 0) {
2286 		__pam_log(LOG_AUTH | LOG_CRIT,
2287 		    "illegal pam.conf[%s] entry: %s: missing MODULE PATH",
2288 		    pam_trace_cname(pamh), current_line);
2289 		error = PAM_SUCCESS;	/* success */
2290 		goto out;
2291 	}
2292 	if (arg[0] != '/') {
2293 		size_t len;
2294 		/*
2295 		 * If module path does not start with "/", then
2296 		 * prepend PAM_LIB_DIR (/usr/lib/security/).
2297 		 */
2298 		/* sizeof (PAM_LIB_DIR) has room for '\0' */
2299 		len = sizeof (PAM_LIB_DIR) + sizeof (PAM_ISA_DIR) + strlen(arg);
2300 		if (((*pam)->module_path = malloc(len)) == NULL) {
2301 			__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2302 			goto out;
2303 		}
2304 		if ((*pam)->pam_flag & PAM_INCLUDE) {
2305 			(void) snprintf((*pam)->module_path, len, "%s%s",
2306 			    PAM_LIB_DIR, arg);
2307 		} else {
2308 			(void) snprintf((*pam)->module_path, len, "%s%s%s",
2309 			    PAM_LIB_DIR, PAM_ISA_DIR, arg);
2310 		}
2311 	} else {
2312 		/* Full path provided for module */
2313 		char *isa;
2314 
2315 		/* Check for Instruction Set Architecture indicator */
2316 		if ((isa = strstr(arg, PAM_ISA)) != NULL) {
2317 			size_t len;
2318 			len = strlen(arg) - (sizeof (PAM_ISA)-1) +
2319 			    sizeof (PAM_ISA_DIR);
2320 
2321 			/* substitute the architecture dependent path */
2322 			if (((*pam)->module_path = malloc(len)) == NULL) {
2323 				__pam_log(LOG_AUTH | LOG_ERR,
2324 				    "strdup: out of memory");
2325 				goto out;
2326 			}
2327 			*isa = '\000';
2328 			isa += strlen(PAM_ISA);
2329 			(void) snprintf((*pam)->module_path, len, "%s%s%s",
2330 			    arg, PAM_ISA_DIR, isa);
2331 		} else if (((*pam)->module_path = strdup(arg)) == 0) {
2332 			__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2333 			goto out;
2334 		}
2335 	}
2336 
2337 	/* count the number of module-specific options first */
2338 	argc = 0;
2339 	if ((tmp = strdup(cp)) == NULL) {
2340 		__pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2341 		goto out;
2342 	}
2343 	tmp_free = tmp;
2344 	for (arg = read_next_token(&tmp); arg; arg = read_next_token(&tmp))
2345 		argc++;
2346 	free(tmp_free);
2347 
2348 	/* allocate array for the module-specific options */
2349 	if (argc > 0) {
2350 		if (((*pam)->module_argv = (char **)
2351 			calloc(argc+1, sizeof (char *))) == 0) {
2352 			__pam_log(LOG_AUTH | LOG_ERR, "calloc: out of memory");
2353 			goto out;
2354 		}
2355 		i = 0;
2356 		for (arg = read_next_token(&cp); arg;
2357 			arg = read_next_token(&cp)) {
2358 			(*pam)->module_argv[i] = strdup(arg);
2359 			if ((*pam)->module_argv[i] == NULL) {
2360 				__pam_log(LOG_AUTH | LOG_ERR, "strdup failed");
2361 				goto out;
2362 			}
2363 			i++;
2364 		}
2365 		(*pam)->module_argv[argc] = NULL;
2366 	}
2367 	(*pam)->module_argc = argc;
2368 
2369 	error = PAM_SUCCESS;	/* success */
2370 	(*pam)->pam_err = err;	/* was the line truncated */
2371 
2372 out:
2373 	if (current_line)
2374 		free(current_line);
2375 	if (error != PAM_SUCCESS) {
2376 		/* on error free this */
2377 		if (*pam)
2378 			free_pamconf(*pam);
2379 	}
2380 	return (error);
2381 }
2382 
2383 
2384 /*
2385  * read_next_token - skip tab and space characters and return the next token
2386  */
2387 
2388 static char *
2389 read_next_token(char **cpp)
2390 {
2391 	register char *cp = *cpp;
2392 	char *start;
2393 
2394 	if (cp == (char *)0) {
2395 		*cpp = (char *)0;
2396 		return ((char *)0);
2397 	}
2398 	while (*cp == ' ' || *cp == '\t')
2399 		cp++;
2400 	if (*cp == '\0') {
2401 		*cpp = (char *)0;
2402 		return ((char *)0);
2403 	}
2404 	start = cp;
2405 	while (*cp && *cp != ' ' && *cp != '\t')
2406 		cp++;
2407 	if (*cp != '\0')
2408 		*cp++ = '\0';
2409 	*cpp = cp;
2410 	return (start);
2411 }
2412 
2413 static char *
2414 pam_conf_strnchr(char *sp, int c, intptr_t count)
2415 {
2416 	while (count) {
2417 		if (*sp == (char)c)
2418 			return ((char *)sp);
2419 		else {
2420 			sp++;
2421 			count--;
2422 		}
2423 	};
2424 	return (NULL);
2425 }
2426 
2427 /*
2428  * nextline - skip all blank lines and comments
2429  */
2430 
2431 static char *
2432 nextline(struct pam_fh *pam_fh, pam_handle_t *pamh, int *err)
2433 {
2434 	char	*ll;
2435 	int	find_a_line = 0;
2436 	char	*data = pam_fh->data;
2437 	char	*bufferp = pam_fh->bufferp;
2438 	char	*bufferendp = &data[pam_fh->bufsize];
2439 	size_t	input_len;
2440 
2441 	/*
2442 	 * Skip the blank line, comment line
2443 	 */
2444 	while (!find_a_line) {
2445 		/* if we are at the end of the buffer, there is no next line */
2446 		if (bufferp == bufferendp)
2447 			return (NULL);
2448 
2449 		/* skip blank line */
2450 		while (*bufferp == '\n') {
2451 			/*
2452 			 * If we are at the end of the buffer, there is
2453 			 * no next line.
2454 			 */
2455 			if (++bufferp == bufferendp) {
2456 				return (NULL);
2457 			}
2458 			/* else we check *bufferp again */
2459 		}
2460 
2461 		/* skip comment line */
2462 		while (*bufferp == '#') {
2463 			if ((ll = pam_conf_strnchr(bufferp, '\n',
2464 				bufferendp - bufferp)) != NULL) {
2465 				bufferp = ll;
2466 			} else {
2467 				/*
2468 				 * this comment line the last line.
2469 				 * no next line
2470 				 */
2471 				return (NULL);
2472 			}
2473 
2474 			/*
2475 			 * If we are at the end of the buffer, there is
2476 			 * no next line.
2477 			 */
2478 			if (bufferp == bufferendp) {
2479 				return (NULL);
2480 			}
2481 		}
2482 
2483 		if ((*bufferp != '\n') && (*bufferp != '#')) {
2484 			find_a_line = 1;
2485 		}
2486 	}
2487 
2488 	*err = PAM_SUCCESS;
2489 	/* now we find one line */
2490 	if ((ll = pam_conf_strnchr(bufferp, '\n', bufferendp - bufferp))
2491 	    != NULL) {
2492 		if ((input_len = ll - bufferp) >= sizeof (pam_fh->line)) {
2493 			__pam_log(LOG_AUTH | LOG_ERR,
2494 			    "nextline[%d:%s]: pam.conf line too long %.256s",
2495 			    pamh->include_depth, pam_trace_cname(pamh),
2496 			    bufferp);
2497 			input_len = sizeof (pam_fh->line) - 1;
2498 			*err = PAM_SERVICE_ERR;
2499 		}
2500 		(void) strncpy(pam_fh->line, bufferp, input_len);
2501 		pam_fh->line[input_len] = '\0';
2502 		pam_fh->bufferp = ll++;
2503 	} else {
2504 		ll = bufferendp;
2505 		if ((input_len = ll - bufferp) >= sizeof (pam_fh->line)) {
2506 			__pam_log(LOG_AUTH | LOG_ERR,
2507 			    "nextline[%d:%s]: pam.conf line too long %.256s",
2508 			    pamh->include_depth, pam_trace_cname(pamh),
2509 			    bufferp);
2510 			input_len = sizeof (pam_fh->line) - 1;
2511 			*err = PAM_SERVICE_ERR;
2512 		}
2513 		(void) strncpy(pam_fh->line, bufferp, input_len);
2514 		pam_fh->line[input_len] = '\0';
2515 		pam_fh->bufferp = ll;
2516 	}
2517 
2518 	return (pam_fh->line);
2519 }
2520 
2521 /*
2522  * verify_pam_conf - verify that the pam_conf entry is filled in.
2523  *
2524  *	True = Error if there is no service.
2525  *	True = Error if there is a service and it matches the requested service
2526  *		but, the type, flag, line overflow, or path is in error.
2527  */
2528 
2529 static int
2530 verify_pam_conf(pamtab_t *pam, char *service)
2531 {
2532 	return ((pam->pam_service == (char *)NULL) ||
2533 	    ((strcasecmp(pam->pam_service, service) == 0) &&
2534 	    ((pam->pam_type == -1) ||
2535 	    (pam->pam_flag == 0) ||
2536 	    (pam->pam_err != PAM_SUCCESS) ||
2537 	    (pam->module_path == (char *)NULL))));
2538 }
2539 
2540 /*
2541  * Routines to free allocated storage
2542  */
2543 
2544 /*
2545  * clean_up -  free allocated storage in the pam handle
2546  */
2547 
2548 static void
2549 clean_up(pam_handle_t *pamh)
2550 {
2551 	int i;
2552 	pam_repository_t *auth_rep;
2553 
2554 	if (pamh) {
2555 		/* Cleanup Sun proprietary tag information */
2556 		if (pamh->pam_client_message_version_number)
2557 			free(pamh->pam_client_message_version_number);
2558 
2559 		while (pamh->include_depth >= 0) {
2560 			free_pam_conf_info(pamh);
2561 			pamh->include_depth--;
2562 		}
2563 
2564 		/* Cleanup PAM_REPOSITORY structure */
2565 		auth_rep = pamh->ps_item[PAM_REPOSITORY].pi_addr;
2566 		if (auth_rep != NULL) {
2567 			if (auth_rep->type != NULL)
2568 				free(auth_rep->type);
2569 			if (auth_rep->scope != NULL)
2570 				free(auth_rep->scope);
2571 		}
2572 
2573 		for (i = 0; i < PAM_MAX_ITEMS; i++) {
2574 			if (pamh->ps_item[i].pi_addr != NULL) {
2575 				if (i == PAM_AUTHTOK || i == PAM_OLDAUTHTOK) {
2576 					(void) memset(pamh->ps_item[i].pi_addr,
2577 					    0, pamh->ps_item[i].pi_size);
2578 				}
2579 				free(pamh->ps_item[i].pi_addr);
2580 			}
2581 		}
2582 		free(pamh);
2583 	}
2584 }
2585 
2586 /*
2587  * free_pamconf - free memory used to store pam.conf entry
2588  */
2589 
2590 static void
2591 free_pamconf(pamtab_t *cp)
2592 {
2593 	int i;
2594 
2595 	if (cp) {
2596 		if (cp->pam_service)
2597 			free(cp->pam_service);
2598 		if (cp->module_path)
2599 			free(cp->module_path);
2600 		for (i = 0; i < cp->module_argc; i++) {
2601 			if (cp->module_argv[i])
2602 				free(cp->module_argv[i]);
2603 		}
2604 		if (cp->module_argc > 0)
2605 			free(cp->module_argv);
2606 		if (cp->function_ptr)
2607 			free(cp->function_ptr);
2608 
2609 		free(cp);
2610 	}
2611 }
2612 
2613 /*
2614  * free_pam_conf_info - free memory used to store all pam.conf info
2615  *			under the pam handle
2616  */
2617 
2618 static void
2619 free_pam_conf_info(pam_handle_t *pamh)
2620 {
2621 	pamtab_t *pamentp;
2622 	pamtab_t *pament_trail;
2623 	int i = pamh->include_depth;
2624 	int j;
2625 
2626 	for (j = 0; j < PAM_NUM_MODULE_TYPES; j++) {
2627 		pamentp = pamh->pam_conf_info[i][j];
2628 		pamh->pam_conf_info[i][j] = NULL;
2629 		pament_trail = pamentp;
2630 		while (pamentp) {
2631 			pamentp = pamentp->next;
2632 			free_pamconf(pament_trail);
2633 			pament_trail = pamentp;
2634 		}
2635 	}
2636 	if (pamh->pam_conf_name[i] != NULL) {
2637 		free(pamh->pam_conf_name[i]);
2638 		pamh->pam_conf_name[i] = NULL;
2639 	}
2640 }
2641 
2642 static void
2643 free_env(env_list *pam_env)
2644 {
2645 	if (pam_env) {
2646 		if (pam_env->name)
2647 			free(pam_env->name);
2648 		if (pam_env->value)
2649 			free(pam_env->value);
2650 		free(pam_env);
2651 	}
2652 }
2653 
2654 /*
2655  *	Internal convenience functions for Solaris PAM service modules.
2656  */
2657 
2658 #include <libintl.h>
2659 #include <nl_types.h>
2660 #include <synch.h>
2661 #include <locale.h>
2662 #include <thread.h>
2663 
2664 typedef struct pam_msg_data {
2665 	nl_catd fd;
2666 } pam_msg_data_t;
2667 
2668 /*
2669  * free_resp():
2670  *	free storage for responses used in the call back "pam_conv" functions
2671  */
2672 
2673 void
2674 free_resp(int num_msg, struct pam_response *resp)
2675 {
2676 	int			i;
2677 	struct pam_response	*r;
2678 
2679 	if (resp) {
2680 		r = resp;
2681 		for (i = 0; i < num_msg; i++, r++) {
2682 			if (r->resp) {
2683 				/* clear before freeing -- may be a password */
2684 				bzero(r->resp, strlen(r->resp));
2685 				free(r->resp);
2686 				r->resp = NULL;
2687 			}
2688 		}
2689 		free(resp);
2690 	}
2691 }
2692 
2693 static int
2694 do_conv(pam_handle_t *pamh, int msg_style, int num_msg,
2695     char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE], void *conv_apdp,
2696     struct pam_response *ret_respp[])
2697 {
2698 	struct pam_message	*msg;
2699 	struct pam_message	*m;
2700 	int			i;
2701 	int			k;
2702 	int			retcode;
2703 	struct pam_conv		*pam_convp;
2704 
2705 	if ((retcode = pam_get_item(pamh, PAM_CONV,
2706 	    (void **)&pam_convp)) != PAM_SUCCESS) {
2707 		return (retcode);
2708 	}
2709 
2710 	/*
2711 	 * When pam_set_item() is called to set PAM_CONV and the
2712 	 * item is NULL, memset(pip->pi_addr, 0, size) is called.
2713 	 * So at this point, we should check whether pam_convp->conv
2714 	 * is NULL or not.
2715 	 */
2716 	if ((pam_convp == NULL) || (pam_convp->conv == NULL))
2717 		return (PAM_SYSTEM_ERR);
2718 
2719 	i = 0;
2720 	k = num_msg;
2721 
2722 	msg = (struct pam_message *)calloc(num_msg,
2723 	    sizeof (struct pam_message));
2724 	if (msg == NULL) {
2725 		return (PAM_BUF_ERR);
2726 	}
2727 	m = msg;
2728 
2729 	while (k--) {
2730 		/*
2731 		 * fill out the message structure to display prompt message
2732 		 */
2733 		m->msg_style = msg_style;
2734 		m->msg = messages[i];
2735 		pam_trace(PAM_DEBUG_CONV,
2736 		    "pam_conv_msg(%p:%d[%d]=%s)",
2737 		    (void *)pamh, msg_style, i, messages[i]);
2738 		m++;
2739 		i++;
2740 	}
2741 
2742 	/*
2743 	 * The UNIX pam modules always calls __pam_get_authtok() and
2744 	 * __pam_display_msg() with a NULL pointer as the conv_apdp.
2745 	 * In case the conv_apdp is NULL and the pam_convp->appdata_ptr
2746 	 * is not NULL, we should pass the pam_convp->appdata_ptr
2747 	 * to the conversation function.
2748 	 */
2749 	if (conv_apdp == NULL && pam_convp->appdata_ptr != NULL)
2750 		conv_apdp = pam_convp->appdata_ptr;
2751 
2752 	/*
2753 	 * Call conv function to display the prompt.
2754 	 */
2755 	retcode = (pam_convp->conv)(num_msg, &msg, ret_respp, conv_apdp);
2756 	pam_trace(PAM_DEBUG_CONV,
2757 	    "pam_conv_resp(%p pam_conv = %s) ret_respp = %p",
2758 	    (void *)pamh, pam_strerror(pamh, retcode), (void *)ret_respp);
2759 	if (*ret_respp == NULL) {
2760 		pam_trace(PAM_DEBUG_CONV,
2761 		    "pam_conv_resp(%p No response requested)", (void *)pamh);
2762 	} else if ((pam_debug & (PAM_DEBUG_CONV | PAM_DEBUG_AUTHTOK)) != 0) {
2763 		struct pam_response *r = *ret_respp;
2764 
2765 		for (i = 0; i < num_msg; i++, r++) {
2766 			if (r->resp == NULL) {
2767 				pam_trace(PAM_DEBUG_CONV,
2768 				    "pam_conv_resp(%p:"
2769 				    "[%d] NULL response string)",
2770 				    (void *)pamh, i);
2771 			} else {
2772 				if (msg_style == PAM_PROMPT_ECHO_OFF) {
2773 #ifdef	DEBUG
2774 					pam_trace(PAM_DEBUG_AUTHTOK,
2775 					    "pam_conv_resp(%p:[%d]=%s, "
2776 					    "code=%d)",
2777 					    (void *)pamh, i, r->resp,
2778 					    r->resp_retcode);
2779 #endif	/* DEBUG */
2780 					pam_trace(PAM_DEBUG_CONV,
2781 					    "pam_conv_resp(%p:[%d] len=%lu, "
2782 					    "code=%d)",
2783 					    (void *)pamh, i,
2784 					    (ulong_t)strlen(r->resp),
2785 					    r->resp_retcode);
2786 				} else {
2787 					pam_trace(PAM_DEBUG_CONV,
2788 					    "pam_conv_resp(%p:[%d]=%s, "
2789 					    "code=%d)",
2790 					    (void *)pamh, i, r->resp,
2791 					    r->resp_retcode);
2792 				}
2793 			}
2794 		}
2795 	}
2796 
2797 	if (msg)
2798 		free(msg);
2799 	return (retcode);
2800 }
2801 
2802 /*
2803  * __pam_display_msg():
2804  *	display message by calling the call back functions
2805  *	provided by the application through "pam_conv" structure
2806  */
2807 
2808 int
2809 __pam_display_msg(pam_handle_t *pamh, int msg_style, int num_msg,
2810     char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE], void *conv_apdp)
2811 {
2812 	struct pam_response	*ret_respp = NULL;
2813 
2814 	return (do_conv(pamh, msg_style, num_msg, messages,
2815 	    conv_apdp, &ret_respp));
2816 }
2817 
2818 /*
2819  * __pam_get_authtok()
2820  *	retrieves a password of at most PASS_MAX length from the pam
2821  *	handle (pam_get_item) or from the input stream (do_conv).
2822  *
2823  * This function allocates memory for the new authtok.
2824  * Applications calling this function are responsible for
2825  * freeing this memory.
2826  *
2827  * If "source" is
2828  *	PAM_HANDLE
2829  * and "type" is:
2830  *	PAM_AUTHTOK - password is taken from pam handle (PAM_AUTHTOK)
2831  *	PAM_OLDAUTHTOK - password is taken from pam handle (PAM_OLDAUTHTOK)
2832  *
2833  * If "source" is
2834  *	PAM_PROMPT
2835  * and "type" is:
2836  *	0:		Prompt for new passwd, do not even attempt
2837  *			to store it in the pam handle.
2838  *	PAM_AUTHTOK:	Prompt for new passwd, store in pam handle as
2839  *			PAM_AUTHTOK item if this value is not already set.
2840  *	PAM_OLDAUTHTOK:	Prompt for new passwd, store in pam handle as
2841  *			PAM_OLDAUTHTOK item if this value is not
2842  *			already set.
2843  */
2844 int
2845 __pam_get_authtok(pam_handle_t *pamh, int source, int type, char *prompt,
2846     char **authtok)
2847 {
2848 	int error = PAM_SYSTEM_ERR;
2849 	char *new_password = NULL;
2850 	struct pam_response *ret_resp = NULL;
2851 	char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
2852 
2853 	if ((*authtok = calloc(PASS_MAX+1, sizeof (char))) == NULL)
2854 		return (PAM_BUF_ERR);
2855 
2856 	if (prompt == NULL)
2857 		prompt = dgettext(TEXT_DOMAIN, "password: ");
2858 
2859 	switch (source) {
2860 	case PAM_HANDLE:
2861 
2862 		/* get password from pam handle item list */
2863 
2864 		switch (type) {
2865 		case PAM_AUTHTOK:
2866 		case PAM_OLDAUTHTOK:
2867 
2868 			if ((error = pam_get_item(pamh, type,
2869 			    (void **)&new_password)) != PAM_SUCCESS)
2870 				goto err_ret;
2871 
2872 			if (new_password == NULL || new_password[0] == '\0') {
2873 				free(*authtok);
2874 				*authtok = NULL;
2875 			} else {
2876 				(void) strlcpy(*authtok, new_password,
2877 				    PASS_MAX+1);
2878 			}
2879 			break;
2880 		default:
2881 			__pam_log(LOG_AUTH | LOG_ERR,
2882 			    "__pam_get_authtok() invalid type: %d", type);
2883 			error = PAM_SYMBOL_ERR;
2884 			goto err_ret;
2885 		}
2886 		break;
2887 	case PAM_PROMPT:
2888 
2889 		/*
2890 		 * Prompt for new password and save in pam handle item list
2891 		 * if the that item is not already set.
2892 		 */
2893 
2894 		(void) strncpy(messages[0], prompt, sizeof (messages[0]));
2895 		if ((error = do_conv(pamh, PAM_PROMPT_ECHO_OFF, 1, messages,
2896 		    NULL, &ret_resp)) != PAM_SUCCESS)
2897 			goto err_ret;
2898 
2899 		if (ret_resp->resp == NULL) {
2900 			/* getpass didn't return anything */
2901 			error = PAM_SYSTEM_ERR;
2902 			goto err_ret;
2903 		}
2904 
2905 		/* save the new password if this item was NULL */
2906 		if (type) {
2907 			if ((error = pam_get_item(pamh, type,
2908 			    (void **)&new_password)) != PAM_SUCCESS) {
2909 				free_resp(1, ret_resp);
2910 				goto err_ret;
2911 			}
2912 			if (new_password == NULL)
2913 				(void) pam_set_item(pamh, type, ret_resp->resp);
2914 		}
2915 
2916 		(void) strlcpy(*authtok, ret_resp->resp, PASS_MAX+1);
2917 		free_resp(1, ret_resp);
2918 		break;
2919 	default:
2920 		__pam_log(LOG_AUTH | LOG_ERR,
2921 		    "__pam_get_authtok() invalid source: %d", source);
2922 		error = PAM_SYMBOL_ERR;
2923 		goto err_ret;
2924 	}
2925 
2926 	return (PAM_SUCCESS);
2927 
2928 err_ret:
2929 	bzero(*authtok, PASS_MAX+1);
2930 	free(*authtok);
2931 	*authtok = NULL;
2932 	return (error);
2933 }
2934