openpam_dispatch.c revision 115619
191094Sdes/*-
2115619Sdes * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
391094Sdes * All rights reserved.
491094Sdes *
591094Sdes * This software was developed for the FreeBSD Project by ThinkSec AS and
699158Sdes * Network Associates Laboratories, the Security Research Division of
799158Sdes * Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
899158Sdes * ("CBOSS"), as part of the DARPA CHATS research program.
991094Sdes *
1091094Sdes * Redistribution and use in source and binary forms, with or without
1191094Sdes * modification, are permitted provided that the following conditions
1291094Sdes * are met:
1391094Sdes * 1. Redistributions of source code must retain the above copyright
1491094Sdes *    notice, this list of conditions and the following disclaimer.
1591094Sdes * 2. Redistributions in binary form must reproduce the above copyright
1691094Sdes *    notice, this list of conditions and the following disclaimer in the
1791094Sdes *    documentation and/or other materials provided with the distribution.
1891094Sdes * 3. The name of the author may not be used to endorse or promote
1991094Sdes *    products derived from this software without specific prior written
2091094Sdes *    permission.
2191094Sdes *
2291094Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2391094Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2491094Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2591094Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2691094Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2791094Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2891094Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2991094Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3091094Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3191094Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3291094Sdes * SUCH DAMAGE.
3391094Sdes *
34115619Sdes * $P4: //depot/projects/openpam/lib/openpam_dispatch.c#21 $
3591094Sdes */
3691094Sdes
3791094Sdes#include <sys/param.h>
3891094Sdes
3991094Sdes#include <security/pam_appl.h>
4091094Sdes
4191094Sdes#include "openpam_impl.h"
4291094Sdes
4391094Sdes#if !defined(OPENPAM_RELAX_CHECKS)
4491094Sdesstatic void _openpam_check_error_code(int, int);
4591094Sdes#else
4691094Sdes#define _openpam_check_error_code(a, b)
4791094Sdes#endif /* !defined(OPENPAM_RELAX_CHECKS) */
4891094Sdes
4991094Sdes/*
5091100Sdes * OpenPAM internal
5191100Sdes *
5291094Sdes * Execute a module chain
5391094Sdes */
5491094Sdes
5591094Sdesint
5691094Sdesopenpam_dispatch(pam_handle_t *pamh,
5791094Sdes	int primitive,
5891094Sdes	int flags)
5991094Sdes{
6091094Sdes	pam_chain_t *chain;
6191094Sdes	int err, fail, r;
62114536Sdes#ifdef DEBUG
63114536Sdes	int debug;
64114536Sdes#endif
6591094Sdes
66107937Sdes	ENTER();
6791094Sdes	if (pamh == NULL)
68107937Sdes		RETURNC(PAM_SYSTEM_ERR);
6991094Sdes
7091094Sdes	/* prevent recursion */
7191094Sdes	if (pamh->current != NULL) {
72107937Sdes		openpam_log(PAM_LOG_ERROR,
73107937Sdes		    "%s() called while %s::%s() is in progress",
74107937Sdes		    _pam_func_name[primitive],
75107937Sdes		    pamh->current->module->path,
76107937Sdes		    _pam_sm_func_name[pamh->primitive]);
77107937Sdes		RETURNC(PAM_ABORT);
7891094Sdes	}
7991094Sdes
8091094Sdes	/* pick a chain */
8191094Sdes	switch (primitive) {
8291094Sdes	case PAM_SM_AUTHENTICATE:
8391094Sdes	case PAM_SM_SETCRED:
8491094Sdes		chain = pamh->chains[PAM_AUTH];
8591094Sdes		break;
8691094Sdes	case PAM_SM_ACCT_MGMT:
8791094Sdes		chain = pamh->chains[PAM_ACCOUNT];
8891094Sdes		break;
8991094Sdes	case PAM_SM_OPEN_SESSION:
9091094Sdes	case PAM_SM_CLOSE_SESSION:
9191094Sdes		chain = pamh->chains[PAM_SESSION];
9291094Sdes		break;
9391094Sdes	case PAM_SM_CHAUTHTOK:
9491094Sdes		chain = pamh->chains[PAM_PASSWORD];
9591094Sdes		break;
9691094Sdes	default:
97107937Sdes		RETURNC(PAM_SYSTEM_ERR);
9891094Sdes	}
9991094Sdes
10091094Sdes	/* execute */
10191094Sdes	for (err = fail = 0; chain != NULL; chain = chain->next) {
10291094Sdes		if (chain->module->func[primitive] == NULL) {
10391094Sdes			openpam_log(PAM_LOG_ERROR, "%s: no %s()",
10491094Sdes			    chain->module->path, _pam_sm_func_name[primitive]);
10591094Sdes			continue;
10691094Sdes		} else {
107107937Sdes			pamh->primitive = primitive;
10891094Sdes			pamh->current = chain;
109114536Sdes#ifdef DEBUG
110114536Sdes			debug = (openpam_get_option(pamh, "debug") != NULL);
111114536Sdes			if (debug)
112114536Sdes				++_openpam_debug;
113114536Sdes			openpam_log(PAM_LOG_DEBUG, "calling %s() in %s",
114114536Sdes			    _pam_sm_func_name[primitive], chain->module->path);
115114536Sdes#endif
11691094Sdes			r = (chain->module->func[primitive])(pamh, flags,
11791094Sdes			    chain->optc, (const char **)chain->optv);
11891094Sdes			pamh->current = NULL;
119114536Sdes#ifdef DEBUG
12091094Sdes			openpam_log(PAM_LOG_DEBUG, "%s: %s(): %s",
12191094Sdes			    chain->module->path, _pam_sm_func_name[primitive],
12291094Sdes			    pam_strerror(pamh, r));
123114536Sdes			if (debug)
124114536Sdes				--_openpam_debug;
125114536Sdes#endif
12691094Sdes		}
12791094Sdes
12891094Sdes		if (r == PAM_IGNORE)
12991094Sdes			continue;
13091094Sdes		if (r == PAM_SUCCESS) {
13191094Sdes			/*
13291684Sdes			 * For pam_setcred() and pam_chauthtok() with the
13391684Sdes			 * PAM_PRELIM_CHECK flag, treat "sufficient" as
13491094Sdes			 * "optional".
13591094Sdes			 */
13699158Sdes			if ((chain->flag == PAM_SUFFICIENT ||
13799158Sdes			    chain->flag == PAM_BINDING) && !fail &&
13891684Sdes			    primitive != PAM_SM_SETCRED &&
13999158Sdes			    !(primitive == PAM_SM_CHAUTHTOK &&
14099158Sdes				(flags & PAM_PRELIM_CHECK)))
14191094Sdes				break;
14291097Sdes			continue;
14391094Sdes		}
14491094Sdes
14591094Sdes		_openpam_check_error_code(primitive, r);
14691094Sdes
14791094Sdes		/*
14891094Sdes		 * Record the return code from the first module to
14991094Sdes		 * fail.  If a required module fails, record the
15091094Sdes		 * return code from the first required module to fail.
15191094Sdes		 */
15291094Sdes		if (err == 0)
15391094Sdes			err = r;
15499158Sdes		if ((chain->flag == PAM_REQUIRED ||
15599158Sdes		    chain->flag == PAM_BINDING) && !fail) {
15691097Sdes			openpam_log(PAM_LOG_DEBUG, "required module failed");
15791094Sdes			fail = 1;
15891094Sdes			err = r;
15991094Sdes		}
16091094Sdes
16191094Sdes		/*
16291094Sdes		 * If a requisite module fails, terminate the chain
16391094Sdes		 * immediately.
16491094Sdes		 */
16591094Sdes		if (chain->flag == PAM_REQUISITE) {
16691097Sdes			openpam_log(PAM_LOG_DEBUG, "requisite module failed");
16791094Sdes			fail = 1;
16891094Sdes			break;
16991094Sdes		}
17091094Sdes	}
17191094Sdes
17299158Sdes	if (!fail && err != PAM_NEW_AUTHTOK_REQD)
17391097Sdes		err = PAM_SUCCESS;
174107937Sdes	RETURNC(err);
17591094Sdes}
17691094Sdes
17791094Sdes#if !defined(OPENPAM_RELAX_CHECKS)
17891094Sdesstatic void
17991094Sdes_openpam_check_error_code(int primitive, int r)
18091094Sdes{
18191094Sdes	/* common error codes */
18291097Sdes	if (r == PAM_SUCCESS ||
18391097Sdes	    r == PAM_SERVICE_ERR ||
18491094Sdes	    r == PAM_BUF_ERR ||
18591094Sdes	    r == PAM_CONV_ERR ||
18691097Sdes	    r == PAM_PERM_DENIED ||
18791097Sdes	    r == PAM_ABORT)
18891094Sdes		return;
18991094Sdes
19091094Sdes	/* specific error codes */
19191094Sdes	switch (primitive) {
19291094Sdes	case PAM_SM_AUTHENTICATE:
19391094Sdes		if (r == PAM_AUTH_ERR ||
19491094Sdes		    r == PAM_CRED_INSUFFICIENT ||
19591094Sdes		    r == PAM_AUTHINFO_UNAVAIL ||
19691094Sdes		    r == PAM_USER_UNKNOWN ||
19791094Sdes		    r == PAM_MAXTRIES)
19891094Sdes			return;
19991094Sdes		break;
20091094Sdes	case PAM_SM_SETCRED:
20191094Sdes		if (r == PAM_CRED_UNAVAIL ||
20291094Sdes		    r == PAM_CRED_EXPIRED ||
20391094Sdes		    r == PAM_USER_UNKNOWN ||
20491094Sdes		    r == PAM_CRED_ERR)
20591094Sdes			return;
20691094Sdes		break;
20791094Sdes	case PAM_SM_ACCT_MGMT:
20891094Sdes		if (r == PAM_USER_UNKNOWN ||
20991094Sdes		    r == PAM_AUTH_ERR ||
21091094Sdes		    r == PAM_NEW_AUTHTOK_REQD ||
21191094Sdes		    r == PAM_ACCT_EXPIRED)
21291094Sdes			return;
21391094Sdes		break;
21491094Sdes	case PAM_SM_OPEN_SESSION:
21591094Sdes	case PAM_SM_CLOSE_SESSION:
21691094Sdes		if (r == PAM_SESSION_ERR)
21791094Sdes			return;
21891094Sdes		break;
21991094Sdes	case PAM_SM_CHAUTHTOK:
22091094Sdes		if (r == PAM_PERM_DENIED ||
22191094Sdes		    r == PAM_AUTHTOK_ERR ||
22291094Sdes		    r == PAM_AUTHTOK_RECOVERY_ERR ||
22391094Sdes		    r == PAM_AUTHTOK_LOCK_BUSY ||
22491684Sdes		    r == PAM_AUTHTOK_DISABLE_AGING ||
22591684Sdes		    r == PAM_TRY_AGAIN)
22691094Sdes			return;
22791094Sdes		break;
22891094Sdes	}
22991094Sdes
23091094Sdes	openpam_log(PAM_LOG_ERROR, "%s(): unexpected return value %d",
23191094Sdes	    _pam_sm_func_name[primitive], r);
23291094Sdes}
23391094Sdes#endif /* !defined(OPENPAM_RELAX_CHECKS) */
23491100Sdes
23591100Sdes/*
23691100Sdes * NODOC
23791100Sdes *
23891100Sdes * Error codes:
23991100Sdes */
240