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