openpam_dispatch.c revision 91684
1239674Srwatson/*-
2239674Srwatson * Copyright (c) 2002 Networks Associates Technologies, Inc.
3239674Srwatson * All rights reserved.
4239674Srwatson *
5239674Srwatson * This software was developed for the FreeBSD Project by ThinkSec AS and
6239674Srwatson * NAI Labs, the Security Research Division of Network Associates, Inc.
7239674Srwatson * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8239674Srwatson * DARPA CHATS research program.
9239674Srwatson *
10239674Srwatson * Redistribution and use in source and binary forms, with or without
11239674Srwatson * modification, are permitted provided that the following conditions
12239674Srwatson * are met:
13239674Srwatson * 1. Redistributions of source code must retain the above copyright
14239674Srwatson *    notice, this list of conditions and the following disclaimer.
15239674Srwatson * 2. Redistributions in binary form must reproduce the above copyright
16239674Srwatson *    notice, this list of conditions and the following disclaimer in the
17239674Srwatson *    documentation and/or other materials provided with the distribution.
18239674Srwatson * 3. The name of the author may not be used to endorse or promote
19239674Srwatson *    products derived from this software without specific prior written
20239674Srwatson *    permission.
21239674Srwatson *
22239674Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23239674Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24239674Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25239674Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26239674Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27239674Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28239674Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29239674Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30239674Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31239674Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32239674Srwatson * SUCH DAMAGE.
33239674Srwatson *
34239674Srwatson * $P4: //depot/projects/openpam/lib/openpam_dispatch.c#13 $
35239674Srwatson */
36239674Srwatson
37239674Srwatson#include <sys/param.h>
38239674Srwatson
39239674Srwatson#include <security/pam_appl.h>
40239674Srwatson
41239674Srwatson#include "openpam_impl.h"
42239674Srwatson
43239674Srwatson#if !defined(OPENPAM_RELAX_CHECKS)
44239674Srwatsonstatic void _openpam_check_error_code(int, int);
45239674Srwatson#else
46239674Srwatson#define _openpam_check_error_code(a, b)
47239674Srwatson#endif /* !defined(OPENPAM_RELAX_CHECKS) */
48239674Srwatson
49239674Srwatson/*
50239674Srwatson * OpenPAM internal
51239674Srwatson *
52239674Srwatson * Execute a module chain
53239674Srwatson */
54239674Srwatson
55239674Srwatsonint
56239674Srwatsonopenpam_dispatch(pam_handle_t *pamh,
57239674Srwatson	int primitive,
58239674Srwatson	int flags)
59256114Sjmg{
60256114Sjmg	pam_chain_t *chain;
61256114Sjmg	int err, fail, r;
62239674Srwatson
63256114Sjmg	if (pamh == NULL)
64239674Srwatson		return (PAM_SYSTEM_ERR);
65239674Srwatson
66239674Srwatson	/* prevent recursion */
67239674Srwatson	if (pamh->current != NULL) {
68239674Srwatson		openpam_log(PAM_LOG_ERROR, "indirect recursion");
69239674Srwatson		return (PAM_ABORT);
70239674Srwatson	}
71239674Srwatson
72239674Srwatson	/* pick a chain */
73239674Srwatson	switch (primitive) {
74239674Srwatson	case PAM_SM_AUTHENTICATE:
75239674Srwatson	case PAM_SM_SETCRED:
76239674Srwatson		chain = pamh->chains[PAM_AUTH];
77239674Srwatson		break;
78239674Srwatson	case PAM_SM_ACCT_MGMT:
79239674Srwatson		chain = pamh->chains[PAM_ACCOUNT];
80239674Srwatson		break;
81239674Srwatson	case PAM_SM_OPEN_SESSION:
82239677Sjoel	case PAM_SM_CLOSE_SESSION:
83239674Srwatson		chain = pamh->chains[PAM_SESSION];
84239674Srwatson		break;
85239674Srwatson	case PAM_SM_CHAUTHTOK:
86239674Srwatson		chain = pamh->chains[PAM_PASSWORD];
87239674Srwatson		break;
88239674Srwatson	default:
89239674Srwatson		return (PAM_SYSTEM_ERR);
90239674Srwatson	}
91239674Srwatson
92239674Srwatson	/* execute */
93239674Srwatson	for (err = fail = 0; chain != NULL; chain = chain->next) {
94239674Srwatson		openpam_log(PAM_LOG_DEBUG, "calling %s() in %s",
95239674Srwatson		    _pam_sm_func_name[primitive], chain->module->path);
96239674Srwatson		if (chain->module->func[primitive] == NULL) {
97239674Srwatson			openpam_log(PAM_LOG_ERROR, "%s: no %s()",
98239674Srwatson			    chain->module->path, _pam_sm_func_name[primitive]);
99239674Srwatson			continue;
100239674Srwatson		} else {
101239674Srwatson			pamh->current = chain;
102239674Srwatson			r = (chain->module->func[primitive])(pamh, flags,
103239674Srwatson			    chain->optc, (const char **)chain->optv);
104239674Srwatson			pamh->current = NULL;
105239674Srwatson			openpam_log(PAM_LOG_DEBUG, "%s: %s(): %s",
106239674Srwatson			    chain->module->path, _pam_sm_func_name[primitive],
107239674Srwatson			    pam_strerror(pamh, r));
108239674Srwatson		}
109239674Srwatson
110239674Srwatson		if (r == PAM_IGNORE)
111239674Srwatson			continue;
112239674Srwatson		if (r == PAM_SUCCESS) {
113239674Srwatson			/*
114239674Srwatson			 * For pam_setcred() and pam_chauthtok() with the
115239674Srwatson			 * PAM_PRELIM_CHECK flag, treat "sufficient" as
116239674Srwatson			 * "optional".
117239674Srwatson			 *
118239674Srwatson			 * Note that Solaris libpam does not terminate
119239674Srwatson			 * the chain here if a required module has
120239674Srwatson			 * previously failed.  I'm not sure why.
121239674Srwatson			 */
122239674Srwatson			if (chain->flag == PAM_SUFFICIENT &&
123239674Srwatson			    primitive != PAM_SM_SETCRED &&
124239674Srwatson			    (primitive != PAM_SM_CHAUTHTOK ||
125239674Srwatson				!(flags & PAM_PRELIM_CHECK)))
126239674Srwatson				break;
127239674Srwatson			continue;
128239674Srwatson		}
129239674Srwatson
130239674Srwatson		_openpam_check_error_code(primitive, r);
131239674Srwatson
132239674Srwatson		/*
133239674Srwatson		 * Record the return code from the first module to
134239674Srwatson		 * fail.  If a required module fails, record the
135239674Srwatson		 * return code from the first required module to fail.
136239674Srwatson		 */
137239674Srwatson		if (err == 0)
138239674Srwatson			err = r;
139239674Srwatson		if (chain->flag == PAM_REQUIRED && !fail) {
140239674Srwatson			openpam_log(PAM_LOG_DEBUG, "required module failed");
141239674Srwatson			fail = 1;
142239674Srwatson			err = r;
143239674Srwatson		}
144239674Srwatson
145239674Srwatson		/*
146239674Srwatson		 * If a requisite module fails, terminate the chain
147239674Srwatson		 * immediately.
148239674Srwatson		 */
149239674Srwatson		if (chain->flag == PAM_REQUISITE) {
150239674Srwatson			openpam_log(PAM_LOG_DEBUG, "requisite module failed");
151239674Srwatson			fail = 1;
152239674Srwatson			break;
153239674Srwatson		}
154239674Srwatson	}
155239674Srwatson
156	if (!fail)
157		err = PAM_SUCCESS;
158	openpam_log(PAM_LOG_DEBUG, "returning: %s", pam_strerror(pamh, err));
159	return (err);
160}
161
162#if !defined(OPENPAM_RELAX_CHECKS)
163static void
164_openpam_check_error_code(int primitive, int r)
165{
166	/* common error codes */
167	if (r == PAM_SUCCESS ||
168	    r == PAM_SERVICE_ERR ||
169	    r == PAM_BUF_ERR ||
170	    r == PAM_CONV_ERR ||
171	    r == PAM_PERM_DENIED ||
172	    r == PAM_ABORT)
173		return;
174
175	/* specific error codes */
176	switch (primitive) {
177	case PAM_SM_AUTHENTICATE:
178		if (r == PAM_AUTH_ERR ||
179		    r == PAM_CRED_INSUFFICIENT ||
180		    r == PAM_AUTHINFO_UNAVAIL ||
181		    r == PAM_USER_UNKNOWN ||
182		    r == PAM_MAXTRIES)
183			return;
184		break;
185	case PAM_SM_SETCRED:
186		if (r == PAM_CRED_UNAVAIL ||
187		    r == PAM_CRED_EXPIRED ||
188		    r == PAM_USER_UNKNOWN ||
189		    r == PAM_CRED_ERR)
190			return;
191		break;
192	case PAM_SM_ACCT_MGMT:
193		if (r == PAM_USER_UNKNOWN ||
194		    r == PAM_AUTH_ERR ||
195		    r == PAM_NEW_AUTHTOK_REQD ||
196		    r == PAM_ACCT_EXPIRED)
197			return;
198		break;
199	case PAM_SM_OPEN_SESSION:
200	case PAM_SM_CLOSE_SESSION:
201		if (r == PAM_SESSION_ERR)
202			return;
203		break;
204	case PAM_SM_CHAUTHTOK:
205		if (r == PAM_PERM_DENIED ||
206		    r == PAM_AUTHTOK_ERR ||
207		    r == PAM_AUTHTOK_RECOVERY_ERR ||
208		    r == PAM_AUTHTOK_LOCK_BUSY ||
209		    r == PAM_AUTHTOK_DISABLE_AGING ||
210		    r == PAM_TRY_AGAIN)
211			return;
212		break;
213	}
214
215	openpam_log(PAM_LOG_ERROR, "%s(): unexpected return value %d",
216	    _pam_sm_func_name[primitive], r);
217}
218#endif /* !defined(OPENPAM_RELAX_CHECKS) */
219
220/*
221 * NODOC
222 *
223 * Error codes:
224 */
225