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