openpam_dispatch.c revision 271947
1272343Sngie/*-
2272343Sngie * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
3272343Sngie * Copyright (c) 2004-2011 Dag-Erling Sm��rgrav
4272343Sngie * All rights reserved.
5272343Sngie *
6272343Sngie * This software was developed for the FreeBSD Project by ThinkSec AS and
7272343Sngie * Network Associates Laboratories, the Security Research Division of
8272343Sngie * Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
9272343Sngie * ("CBOSS"), as part of the DARPA CHATS research program.
10272343Sngie *
11272343Sngie * Redistribution and use in source and binary forms, with or without
12272343Sngie * modification, are permitted provided that the following conditions
13272343Sngie * are met:
14272343Sngie * 1. Redistributions of source code must retain the above copyright
15272343Sngie *    notice, this list of conditions and the following disclaimer.
16272343Sngie * 2. Redistributions in binary form must reproduce the above copyright
17272343Sngie *    notice, this list of conditions and the following disclaimer in the
18272343Sngie *    documentation and/or other materials provided with the distribution.
19272343Sngie * 3. The name of the author may not be used to endorse or promote
20272343Sngie *    products derived from this software without specific prior written
21272343Sngie *    permission.
22272343Sngie *
23272343Sngie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24272343Sngie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25272343Sngie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26272343Sngie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27272343Sngie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28272343Sngie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29272343Sngie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30272343Sngie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31272343Sngie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32272343Sngie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33272343Sngie * SUCH DAMAGE.
34272343Sngie *
35272343Sngie * $Id: openpam_dispatch.c 807 2014-09-09 09:41:32Z des $
36272343Sngie */
37272343Sngie
38272343Sngie#ifdef HAVE_CONFIG_H
39272343Sngie# include "config.h"
40272343Sngie#endif
41272343Sngie
42272343Sngie#include <sys/param.h>
43272343Sngie
44272343Sngie#include <security/pam_appl.h>
45272343Sngie
46272343Sngie#include "openpam_impl.h"
47272343Sngie
48272343Sngie#if !defined(OPENPAM_RELAX_CHECKS)
49272343Sngiestatic void openpam_check_error_code(int, int);
50272343Sngie#else
51272343Sngie#define openpam_check_error_code(a, b)
52272343Sngie#endif /* !defined(OPENPAM_RELAX_CHECKS) */
53272343Sngie
54272343Sngie/*
55272343Sngie * OpenPAM internal
56272343Sngie *
57272343Sngie * Execute a module chain
58272343Sngie */
59272343Sngie
60272343Sngieint
61272343Sngieopenpam_dispatch(pam_handle_t *pamh,
62272343Sngie	int primitive,
63272343Sngie	int flags)
64272343Sngie{
65272343Sngie	pam_chain_t *chain;
66272343Sngie	int err, fail, nsuccess, r;
67272343Sngie	int debug;
68272343Sngie
69272343Sngie	ENTER();
70272343Sngie	if (pamh == NULL)
71272343Sngie		RETURNC(PAM_SYSTEM_ERR);
72272343Sngie
73272343Sngie	/* prevent recursion */
74272343Sngie	if (pamh->current != NULL) {
75272343Sngie		openpam_log(PAM_LOG_ERROR,
76272343Sngie		    "%s() called while %s::%s() is in progress",
77272343Sngie		    pam_func_name[primitive],
78272343Sngie		    pamh->current->module->path,
79272343Sngie		    pam_sm_func_name[pamh->primitive]);
80272343Sngie		RETURNC(PAM_ABORT);
81272343Sngie	}
82272343Sngie
83272343Sngie	/* pick a chain */
84272343Sngie	switch (primitive) {
85272343Sngie	case PAM_SM_AUTHENTICATE:
86272343Sngie	case PAM_SM_SETCRED:
87272343Sngie		chain = pamh->chains[PAM_AUTH];
88272343Sngie		break;
89272343Sngie	case PAM_SM_ACCT_MGMT:
90272343Sngie		chain = pamh->chains[PAM_ACCOUNT];
91272343Sngie		break;
92272343Sngie	case PAM_SM_OPEN_SESSION:
93272343Sngie	case PAM_SM_CLOSE_SESSION:
94272343Sngie		chain = pamh->chains[PAM_SESSION];
95272343Sngie		break;
96272343Sngie	case PAM_SM_CHAUTHTOK:
97272343Sngie		chain = pamh->chains[PAM_PASSWORD];
98272343Sngie		break;
99272343Sngie	default:
100272343Sngie		RETURNC(PAM_SYSTEM_ERR);
101272343Sngie	}
102272343Sngie
103272343Sngie	/* execute */
104272343Sngie	err = PAM_SUCCESS;
105272343Sngie	fail = nsuccess = 0;
106272343Sngie	for (; chain != NULL; chain = chain->next) {
107272343Sngie		if (chain->module->func[primitive] == NULL) {
108272343Sngie			openpam_log(PAM_LOG_ERROR, "%s: no %s()",
109272343Sngie			    chain->module->path, pam_sm_func_name[primitive]);
110272343Sngie			r = PAM_SYSTEM_ERR;
111272343Sngie		} else {
112272343Sngie			pamh->primitive = primitive;
113272343Sngie			pamh->current = chain;
114272343Sngie			debug = (openpam_get_option(pamh, "debug") != NULL);
115272343Sngie			if (debug)
116272343Sngie				++openpam_debug;
117272343Sngie			openpam_log(PAM_LOG_LIBDEBUG, "calling %s() in %s",
118272343Sngie			    pam_sm_func_name[primitive], chain->module->path);
119272343Sngie			r = (chain->module->func[primitive])(pamh, flags,
120272343Sngie			    chain->optc, (const char **)chain->optv);
121272343Sngie			pamh->current = NULL;
122272343Sngie			openpam_log(PAM_LOG_LIBDEBUG, "%s: %s(): %s",
123272343Sngie			    chain->module->path, pam_sm_func_name[primitive],
124272343Sngie			    pam_strerror(pamh, r));
125272343Sngie			if (debug)
126272343Sngie				--openpam_debug;
127272343Sngie		}
128272343Sngie
129272343Sngie		if (r == PAM_IGNORE)
130272343Sngie			continue;
131272343Sngie		if (r == PAM_SUCCESS) {
132272343Sngie			++nsuccess;
133272343Sngie			/*
134272343Sngie			 * For pam_setcred() and pam_chauthtok() with the
135272343Sngie			 * PAM_PRELIM_CHECK flag, treat "sufficient" as
136272343Sngie			 * "optional".
137272343Sngie			 */
138272343Sngie			if ((chain->flag == PAM_SUFFICIENT ||
139272343Sngie			    chain->flag == PAM_BINDING) && !fail &&
140272343Sngie			    primitive != PAM_SM_SETCRED &&
141272343Sngie			    !(primitive == PAM_SM_CHAUTHTOK &&
142272343Sngie				(flags & PAM_PRELIM_CHECK)))
143272343Sngie				break;
144272343Sngie			continue;
145272343Sngie		}
146272343Sngie
147272343Sngie		openpam_check_error_code(primitive, r);
148272343Sngie
149272343Sngie		/*
150272343Sngie		 * Record the return code from the first module to
151272343Sngie		 * fail.  If a required module fails, record the
152272343Sngie		 * return code from the first required module to fail.
153272343Sngie		 */
154272343Sngie		if (err == PAM_SUCCESS)
155272343Sngie			err = r;
156272343Sngie		if ((chain->flag == PAM_REQUIRED ||
157272343Sngie		    chain->flag == PAM_BINDING) && !fail) {
158272343Sngie			openpam_log(PAM_LOG_LIBDEBUG, "required module failed");
159272343Sngie			fail = 1;
160272343Sngie			err = r;
161272343Sngie		}
162272343Sngie
163272343Sngie		/*
164272343Sngie		 * If a requisite module fails, terminate the chain
165272343Sngie		 * immediately.
166272343Sngie		 */
167272343Sngie		if (chain->flag == PAM_REQUISITE) {
168272343Sngie			openpam_log(PAM_LOG_LIBDEBUG, "requisite module failed");
169272343Sngie			fail = 1;
170272343Sngie			break;
171272343Sngie		}
172272343Sngie	}
173272343Sngie
174272343Sngie	if (!fail && err != PAM_NEW_AUTHTOK_REQD)
175272343Sngie		err = PAM_SUCCESS;
176272343Sngie
177272343Sngie	/*
178272343Sngie	 * Require the chain to be non-empty, and at least one module
179272343Sngie	 * in the chain to be successful, so that we don't fail open.
180272343Sngie	 */
181272343Sngie	if (err == PAM_SUCCESS && nsuccess < 1) {
182272343Sngie		openpam_log(PAM_LOG_ERROR,
183272343Sngie		    "all modules were unsuccessful for %s()",
184272343Sngie		    pam_sm_func_name[primitive]);
185272343Sngie		err = PAM_SYSTEM_ERR;
186272343Sngie	}
187272343Sngie
188272343Sngie	RETURNC(err);
189272343Sngie}
190272343Sngie
191272343Sngie#if !defined(OPENPAM_RELAX_CHECKS)
192272343Sngiestatic void
193272343Sngieopenpam_check_error_code(int primitive, int r)
194272343Sngie{
195272343Sngie	/* common error codes */
196272343Sngie	if (r == PAM_SUCCESS ||
197272343Sngie	    r == PAM_SYSTEM_ERR ||
198272343Sngie	    r == PAM_SERVICE_ERR ||
199272343Sngie	    r == PAM_BUF_ERR ||
200272343Sngie	    r == PAM_CONV_ERR ||
201272343Sngie	    r == PAM_PERM_DENIED ||
202272343Sngie	    r == PAM_ABORT)
203272343Sngie		return;
204272343Sngie
205272343Sngie	/* specific error codes */
206272343Sngie	switch (primitive) {
207272343Sngie	case PAM_SM_AUTHENTICATE:
208272343Sngie		if (r == PAM_AUTH_ERR ||
209272343Sngie		    r == PAM_CRED_INSUFFICIENT ||
210272343Sngie		    r == PAM_AUTHINFO_UNAVAIL ||
211272343Sngie		    r == PAM_USER_UNKNOWN ||
212272343Sngie		    r == PAM_MAXTRIES)
213272343Sngie			return;
214272343Sngie		break;
215272343Sngie	case PAM_SM_SETCRED:
216272343Sngie		if (r == PAM_CRED_UNAVAIL ||
217272343Sngie		    r == PAM_CRED_EXPIRED ||
218272343Sngie		    r == PAM_USER_UNKNOWN ||
219272343Sngie		    r == PAM_CRED_ERR)
220272343Sngie			return;
221272343Sngie		break;
222272343Sngie	case PAM_SM_ACCT_MGMT:
223272343Sngie		if (r == PAM_USER_UNKNOWN ||
224272343Sngie		    r == PAM_AUTH_ERR ||
225272343Sngie		    r == PAM_NEW_AUTHTOK_REQD ||
226272343Sngie		    r == PAM_ACCT_EXPIRED)
227272343Sngie			return;
228272343Sngie		break;
229272343Sngie	case PAM_SM_OPEN_SESSION:
230272343Sngie	case PAM_SM_CLOSE_SESSION:
231272343Sngie		if (r == PAM_SESSION_ERR)
232272343Sngie			return;
233272343Sngie		break;
234272343Sngie	case PAM_SM_CHAUTHTOK:
235272343Sngie		if (r == PAM_PERM_DENIED ||
236272343Sngie		    r == PAM_AUTHTOK_ERR ||
237272343Sngie		    r == PAM_AUTHTOK_RECOVERY_ERR ||
238272343Sngie		    r == PAM_AUTHTOK_LOCK_BUSY ||
239272343Sngie		    r == PAM_AUTHTOK_DISABLE_AGING ||
240272343Sngie		    r == PAM_TRY_AGAIN)
241272343Sngie			return;
242272343Sngie		break;
243272343Sngie	}
244272343Sngie
245272343Sngie	openpam_log(PAM_LOG_ERROR, "%s(): unexpected return value %d",
246272343Sngie	    pam_sm_func_name[primitive], r);
247272343Sngie}
248272343Sngie#endif /* !defined(OPENPAM_RELAX_CHECKS) */
249272343Sngie
250272343Sngie/*
251272343Sngie * NODOC
252272343Sngie *
253272343Sngie * Error codes:
254272343Sngie */
255272343Sngie