191094Sdes/*-
2115619Sdes * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
3228690Sdes * Copyright (c) 2004-2011 Dag-Erling Sm��rgrav
491094Sdes * All rights reserved.
591094Sdes *
691094Sdes * This software was developed for the FreeBSD Project by ThinkSec AS and
799158Sdes * Network Associates Laboratories, the Security Research Division of
899158Sdes * Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
999158Sdes * ("CBOSS"), as part of the DARPA CHATS research program.
1091094Sdes *
1191094Sdes * Redistribution and use in source and binary forms, with or without
1291094Sdes * modification, are permitted provided that the following conditions
1391094Sdes * are met:
1491094Sdes * 1. Redistributions of source code must retain the above copyright
1591094Sdes *    notice, this list of conditions and the following disclaimer.
1691094Sdes * 2. Redistributions in binary form must reproduce the above copyright
1791094Sdes *    notice, this list of conditions and the following disclaimer in the
1891094Sdes *    documentation and/or other materials provided with the distribution.
1991094Sdes * 3. The name of the author may not be used to endorse or promote
2091094Sdes *    products derived from this software without specific prior written
2191094Sdes *    permission.
2291094Sdes *
2391094Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2491094Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2591094Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2691094Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2791094Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2891094Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2991094Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3091094Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3191094Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3291094Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3391094Sdes * SUCH DAMAGE.
3491094Sdes *
35271947Sdes * $Id: openpam_dispatch.c 807 2014-09-09 09:41:32Z des $
3691094Sdes */
3791094Sdes
38228690Sdes#ifdef HAVE_CONFIG_H
39228690Sdes# include "config.h"
40228690Sdes#endif
41228690Sdes
4291094Sdes#include <sys/param.h>
4391094Sdes
4491094Sdes#include <security/pam_appl.h>
4591094Sdes
4691094Sdes#include "openpam_impl.h"
4791094Sdes
4891094Sdes#if !defined(OPENPAM_RELAX_CHECKS)
49228690Sdesstatic void openpam_check_error_code(int, int);
5091094Sdes#else
51228690Sdes#define openpam_check_error_code(a, b)
5291094Sdes#endif /* !defined(OPENPAM_RELAX_CHECKS) */
5391094Sdes
5491094Sdes/*
5591100Sdes * OpenPAM internal
5691100Sdes *
5791094Sdes * Execute a module chain
5891094Sdes */
5991094Sdes
6091094Sdesint
6191094Sdesopenpam_dispatch(pam_handle_t *pamh,
6291094Sdes	int primitive,
6391094Sdes	int flags)
6491094Sdes{
6591094Sdes	pam_chain_t *chain;
66271947Sdes	int err, fail, nsuccess, r;
67114536Sdes	int debug;
6891094Sdes
69107937Sdes	ENTER();
7091094Sdes	if (pamh == NULL)
71107937Sdes		RETURNC(PAM_SYSTEM_ERR);
7291094Sdes
7391094Sdes	/* prevent recursion */
7491094Sdes	if (pamh->current != NULL) {
75107937Sdes		openpam_log(PAM_LOG_ERROR,
76107937Sdes		    "%s() called while %s::%s() is in progress",
77228690Sdes		    pam_func_name[primitive],
78107937Sdes		    pamh->current->module->path,
79228690Sdes		    pam_sm_func_name[pamh->primitive]);
80107937Sdes		RETURNC(PAM_ABORT);
8191094Sdes	}
8291094Sdes
8391094Sdes	/* pick a chain */
8491094Sdes	switch (primitive) {
8591094Sdes	case PAM_SM_AUTHENTICATE:
8691094Sdes	case PAM_SM_SETCRED:
8791094Sdes		chain = pamh->chains[PAM_AUTH];
8891094Sdes		break;
8991094Sdes	case PAM_SM_ACCT_MGMT:
9091094Sdes		chain = pamh->chains[PAM_ACCOUNT];
9191094Sdes		break;
9291094Sdes	case PAM_SM_OPEN_SESSION:
9391094Sdes	case PAM_SM_CLOSE_SESSION:
9491094Sdes		chain = pamh->chains[PAM_SESSION];
9591094Sdes		break;
9691094Sdes	case PAM_SM_CHAUTHTOK:
9791094Sdes		chain = pamh->chains[PAM_PASSWORD];
9891094Sdes		break;
9991094Sdes	default:
100107937Sdes		RETURNC(PAM_SYSTEM_ERR);
10191094Sdes	}
10291094Sdes
10391094Sdes	/* execute */
104271947Sdes	err = PAM_SUCCESS;
105271947Sdes	fail = nsuccess = 0;
106271947Sdes	for (; chain != NULL; chain = chain->next) {
10791094Sdes		if (chain->module->func[primitive] == NULL) {
10891094Sdes			openpam_log(PAM_LOG_ERROR, "%s: no %s()",
109228690Sdes			    chain->module->path, pam_sm_func_name[primitive]);
110228690Sdes			r = PAM_SYSTEM_ERR;
11191094Sdes		} else {
112107937Sdes			pamh->primitive = primitive;
11391094Sdes			pamh->current = chain;
114114536Sdes			debug = (openpam_get_option(pamh, "debug") != NULL);
115114536Sdes			if (debug)
116228690Sdes				++openpam_debug;
117255376Sdes			openpam_log(PAM_LOG_LIBDEBUG, "calling %s() in %s",
118228690Sdes			    pam_sm_func_name[primitive], chain->module->path);
11991094Sdes			r = (chain->module->func[primitive])(pamh, flags,
120294192Sdes			    chain->optc, (const char **)(intptr_t)chain->optv);
12191094Sdes			pamh->current = NULL;
122255376Sdes			openpam_log(PAM_LOG_LIBDEBUG, "%s: %s(): %s",
123228690Sdes			    chain->module->path, pam_sm_func_name[primitive],
12491094Sdes			    pam_strerror(pamh, r));
125114536Sdes			if (debug)
126228690Sdes				--openpam_debug;
12791094Sdes		}
12891094Sdes
12991094Sdes		if (r == PAM_IGNORE)
13091094Sdes			continue;
131271947Sdes		if (r == PAM_SUCCESS) {
132271947Sdes			++nsuccess;
13391094Sdes			/*
13491684Sdes			 * For pam_setcred() and pam_chauthtok() with the
13591684Sdes			 * PAM_PRELIM_CHECK flag, treat "sufficient" as
13691094Sdes			 * "optional".
13791094Sdes			 */
13899158Sdes			if ((chain->flag == PAM_SUFFICIENT ||
13999158Sdes			    chain->flag == PAM_BINDING) && !fail &&
14091684Sdes			    primitive != PAM_SM_SETCRED &&
14199158Sdes			    !(primitive == PAM_SM_CHAUTHTOK &&
14299158Sdes				(flags & PAM_PRELIM_CHECK)))
14391094Sdes				break;
14491097Sdes			continue;
14591094Sdes		}
14691094Sdes
147228690Sdes		openpam_check_error_code(primitive, r);
14891094Sdes
14991094Sdes		/*
15091094Sdes		 * Record the return code from the first module to
15191094Sdes		 * fail.  If a required module fails, record the
15291094Sdes		 * return code from the first required module to fail.
15391094Sdes		 */
154271947Sdes		if (err == PAM_SUCCESS)
15591094Sdes			err = r;
15699158Sdes		if ((chain->flag == PAM_REQUIRED ||
15799158Sdes		    chain->flag == PAM_BINDING) && !fail) {
158255376Sdes			openpam_log(PAM_LOG_LIBDEBUG, "required module failed");
15991094Sdes			fail = 1;
16091094Sdes			err = r;
16191094Sdes		}
16291094Sdes
16391094Sdes		/*
16491094Sdes		 * If a requisite module fails, terminate the chain
16591094Sdes		 * immediately.
16691094Sdes		 */
16791094Sdes		if (chain->flag == PAM_REQUISITE) {
168255376Sdes			openpam_log(PAM_LOG_LIBDEBUG, "requisite module failed");
16991094Sdes			fail = 1;
17091094Sdes			break;
17191094Sdes		}
17291094Sdes	}
17391094Sdes
17499158Sdes	if (!fail && err != PAM_NEW_AUTHTOK_REQD)
17591097Sdes		err = PAM_SUCCESS;
176271947Sdes
177271947Sdes	/*
178271947Sdes	 * Require the chain to be non-empty, and at least one module
179271947Sdes	 * in the chain to be successful, so that we don't fail open.
180271947Sdes	 */
181271947Sdes	if (err == PAM_SUCCESS && nsuccess < 1) {
182271947Sdes		openpam_log(PAM_LOG_ERROR,
183271947Sdes		    "all modules were unsuccessful for %s()",
184271947Sdes		    pam_sm_func_name[primitive]);
185271947Sdes		err = PAM_SYSTEM_ERR;
186271947Sdes	}
187271947Sdes
188107937Sdes	RETURNC(err);
18991094Sdes}
19091094Sdes
19191094Sdes#if !defined(OPENPAM_RELAX_CHECKS)
19291094Sdesstatic void
193228690Sdesopenpam_check_error_code(int primitive, int r)
19491094Sdes{
19591094Sdes	/* common error codes */
19691097Sdes	if (r == PAM_SUCCESS ||
197255376Sdes	    r == PAM_SYSTEM_ERR ||
19891097Sdes	    r == PAM_SERVICE_ERR ||
19991094Sdes	    r == PAM_BUF_ERR ||
20091094Sdes	    r == PAM_CONV_ERR ||
20191097Sdes	    r == PAM_PERM_DENIED ||
20291097Sdes	    r == PAM_ABORT)
20391094Sdes		return;
20491094Sdes
20591094Sdes	/* specific error codes */
20691094Sdes	switch (primitive) {
20791094Sdes	case PAM_SM_AUTHENTICATE:
20891094Sdes		if (r == PAM_AUTH_ERR ||
20991094Sdes		    r == PAM_CRED_INSUFFICIENT ||
21091094Sdes		    r == PAM_AUTHINFO_UNAVAIL ||
21191094Sdes		    r == PAM_USER_UNKNOWN ||
21291094Sdes		    r == PAM_MAXTRIES)
21391094Sdes			return;
21491094Sdes		break;
21591094Sdes	case PAM_SM_SETCRED:
21691094Sdes		if (r == PAM_CRED_UNAVAIL ||
21791094Sdes		    r == PAM_CRED_EXPIRED ||
21891094Sdes		    r == PAM_USER_UNKNOWN ||
21991094Sdes		    r == PAM_CRED_ERR)
22091094Sdes			return;
22191094Sdes		break;
22291094Sdes	case PAM_SM_ACCT_MGMT:
22391094Sdes		if (r == PAM_USER_UNKNOWN ||
22491094Sdes		    r == PAM_AUTH_ERR ||
22591094Sdes		    r == PAM_NEW_AUTHTOK_REQD ||
22691094Sdes		    r == PAM_ACCT_EXPIRED)
22791094Sdes			return;
22891094Sdes		break;
22991094Sdes	case PAM_SM_OPEN_SESSION:
23091094Sdes	case PAM_SM_CLOSE_SESSION:
23191094Sdes		if (r == PAM_SESSION_ERR)
23291094Sdes			return;
23391094Sdes		break;
23491094Sdes	case PAM_SM_CHAUTHTOK:
23591094Sdes		if (r == PAM_PERM_DENIED ||
23691094Sdes		    r == PAM_AUTHTOK_ERR ||
23791094Sdes		    r == PAM_AUTHTOK_RECOVERY_ERR ||
23891094Sdes		    r == PAM_AUTHTOK_LOCK_BUSY ||
23991684Sdes		    r == PAM_AUTHTOK_DISABLE_AGING ||
24091684Sdes		    r == PAM_TRY_AGAIN)
24191094Sdes			return;
24291094Sdes		break;
24391094Sdes	}
24491094Sdes
24591094Sdes	openpam_log(PAM_LOG_ERROR, "%s(): unexpected return value %d",
246228690Sdes	    pam_sm_func_name[primitive], r);
24791094Sdes}
24891094Sdes#endif /* !defined(OPENPAM_RELAX_CHECKS) */
24991100Sdes
25091100Sdes/*
25191100Sdes * NODOC
25291100Sdes *
25391100Sdes * Error codes:
25491100Sdes */
255