openpam_dispatch.c revision 99158
191094Sdes/*-
292289Sdes * Copyright (c) 2002 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 *
3499158Sdes * $P4: //depot/projects/openpam/lib/openpam_dispatch.c#17 $
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;
6291094Sdes
6391094Sdes	if (pamh == NULL)
6491094Sdes		return (PAM_SYSTEM_ERR);
6591094Sdes
6691094Sdes	/* prevent recursion */
6791094Sdes	if (pamh->current != NULL) {
6891094Sdes		openpam_log(PAM_LOG_ERROR, "indirect recursion");
6991094Sdes		return (PAM_ABORT);
7091094Sdes	}
7191094Sdes
7291094Sdes	/* pick a chain */
7391094Sdes	switch (primitive) {
7491094Sdes	case PAM_SM_AUTHENTICATE:
7591094Sdes	case PAM_SM_SETCRED:
7691094Sdes		chain = pamh->chains[PAM_AUTH];
7791094Sdes		break;
7891094Sdes	case PAM_SM_ACCT_MGMT:
7991094Sdes		chain = pamh->chains[PAM_ACCOUNT];
8091094Sdes		break;
8191094Sdes	case PAM_SM_OPEN_SESSION:
8291094Sdes	case PAM_SM_CLOSE_SESSION:
8391094Sdes		chain = pamh->chains[PAM_SESSION];
8491094Sdes		break;
8591094Sdes	case PAM_SM_CHAUTHTOK:
8691094Sdes		chain = pamh->chains[PAM_PASSWORD];
8791094Sdes		break;
8891094Sdes	default:
8991094Sdes		return (PAM_SYSTEM_ERR);
9091094Sdes	}
9191094Sdes
9291094Sdes	/* execute */
9391094Sdes	for (err = fail = 0; chain != NULL; chain = chain->next) {
9491097Sdes		openpam_log(PAM_LOG_DEBUG, "calling %s() in %s",
9591097Sdes		    _pam_sm_func_name[primitive], chain->module->path);
9691094Sdes		if (chain->module->func[primitive] == NULL) {
9791094Sdes			openpam_log(PAM_LOG_ERROR, "%s: no %s()",
9891094Sdes			    chain->module->path, _pam_sm_func_name[primitive]);
9991094Sdes			continue;
10091094Sdes		} else {
10191094Sdes			pamh->current = chain;
10291094Sdes			r = (chain->module->func[primitive])(pamh, flags,
10391094Sdes			    chain->optc, (const char **)chain->optv);
10491094Sdes			pamh->current = NULL;
10591094Sdes			openpam_log(PAM_LOG_DEBUG, "%s: %s(): %s",
10691094Sdes			    chain->module->path, _pam_sm_func_name[primitive],
10791094Sdes			    pam_strerror(pamh, r));
10891094Sdes		}
10991094Sdes
11091094Sdes		if (r == PAM_IGNORE)
11191094Sdes			continue;
11291094Sdes		if (r == PAM_SUCCESS) {
11391094Sdes			/*
11491684Sdes			 * For pam_setcred() and pam_chauthtok() with the
11591684Sdes			 * PAM_PRELIM_CHECK flag, treat "sufficient" as
11691094Sdes			 * "optional".
11791094Sdes			 */
11899158Sdes			if ((chain->flag == PAM_SUFFICIENT ||
11999158Sdes			    chain->flag == PAM_BINDING) && !fail &&
12091684Sdes			    primitive != PAM_SM_SETCRED &&
12199158Sdes			    !(primitive == PAM_SM_CHAUTHTOK &&
12299158Sdes				(flags & PAM_PRELIM_CHECK)))
12391094Sdes				break;
12491097Sdes			continue;
12591094Sdes		}
12691094Sdes
12791094Sdes		_openpam_check_error_code(primitive, r);
12891094Sdes
12991094Sdes		/*
13091094Sdes		 * Record the return code from the first module to
13191094Sdes		 * fail.  If a required module fails, record the
13291094Sdes		 * return code from the first required module to fail.
13391094Sdes		 */
13491094Sdes		if (err == 0)
13591094Sdes			err = r;
13699158Sdes		if ((chain->flag == PAM_REQUIRED ||
13799158Sdes		    chain->flag == PAM_BINDING) && !fail) {
13891097Sdes			openpam_log(PAM_LOG_DEBUG, "required module failed");
13991094Sdes			fail = 1;
14091094Sdes			err = r;
14191094Sdes		}
14291094Sdes
14391094Sdes		/*
14491094Sdes		 * If a requisite module fails, terminate the chain
14591094Sdes		 * immediately.
14691094Sdes		 */
14791094Sdes		if (chain->flag == PAM_REQUISITE) {
14891097Sdes			openpam_log(PAM_LOG_DEBUG, "requisite module failed");
14991094Sdes			fail = 1;
15091094Sdes			break;
15191094Sdes		}
15291094Sdes	}
15391094Sdes
15499158Sdes	if (!fail && err != PAM_NEW_AUTHTOK_REQD)
15591097Sdes		err = PAM_SUCCESS;
15691097Sdes	openpam_log(PAM_LOG_DEBUG, "returning: %s", pam_strerror(pamh, err));
15791097Sdes	return (err);
15891094Sdes}
15991094Sdes
16091094Sdes#if !defined(OPENPAM_RELAX_CHECKS)
16191094Sdesstatic void
16291094Sdes_openpam_check_error_code(int primitive, int r)
16391094Sdes{
16491094Sdes	/* common error codes */
16591097Sdes	if (r == PAM_SUCCESS ||
16691097Sdes	    r == PAM_SERVICE_ERR ||
16791094Sdes	    r == PAM_BUF_ERR ||
16891094Sdes	    r == PAM_CONV_ERR ||
16991097Sdes	    r == PAM_PERM_DENIED ||
17091097Sdes	    r == PAM_ABORT)
17191094Sdes		return;
17291094Sdes
17391094Sdes	/* specific error codes */
17491094Sdes	switch (primitive) {
17591094Sdes	case PAM_SM_AUTHENTICATE:
17691094Sdes		if (r == PAM_AUTH_ERR ||
17791094Sdes		    r == PAM_CRED_INSUFFICIENT ||
17891094Sdes		    r == PAM_AUTHINFO_UNAVAIL ||
17991094Sdes		    r == PAM_USER_UNKNOWN ||
18091094Sdes		    r == PAM_MAXTRIES)
18191094Sdes			return;
18291094Sdes		break;
18391094Sdes	case PAM_SM_SETCRED:
18491094Sdes		if (r == PAM_CRED_UNAVAIL ||
18591094Sdes		    r == PAM_CRED_EXPIRED ||
18691094Sdes		    r == PAM_USER_UNKNOWN ||
18791094Sdes		    r == PAM_CRED_ERR)
18891094Sdes			return;
18991094Sdes		break;
19091094Sdes	case PAM_SM_ACCT_MGMT:
19191094Sdes		if (r == PAM_USER_UNKNOWN ||
19291094Sdes		    r == PAM_AUTH_ERR ||
19391094Sdes		    r == PAM_NEW_AUTHTOK_REQD ||
19491094Sdes		    r == PAM_ACCT_EXPIRED)
19591094Sdes			return;
19691094Sdes		break;
19791094Sdes	case PAM_SM_OPEN_SESSION:
19891094Sdes	case PAM_SM_CLOSE_SESSION:
19991094Sdes		if (r == PAM_SESSION_ERR)
20091094Sdes			return;
20191094Sdes		break;
20291094Sdes	case PAM_SM_CHAUTHTOK:
20391094Sdes		if (r == PAM_PERM_DENIED ||
20491094Sdes		    r == PAM_AUTHTOK_ERR ||
20591094Sdes		    r == PAM_AUTHTOK_RECOVERY_ERR ||
20691094Sdes		    r == PAM_AUTHTOK_LOCK_BUSY ||
20791684Sdes		    r == PAM_AUTHTOK_DISABLE_AGING ||
20891684Sdes		    r == PAM_TRY_AGAIN)
20991094Sdes			return;
21091094Sdes		break;
21191094Sdes	}
21291094Sdes
21391094Sdes	openpam_log(PAM_LOG_ERROR, "%s(): unexpected return value %d",
21491094Sdes	    _pam_sm_func_name[primitive], r);
21591094Sdes}
21691094Sdes#endif /* !defined(OPENPAM_RELAX_CHECKS) */
21791100Sdes
21891100Sdes/*
21991100Sdes * NODOC
22091100Sdes *
22191100Sdes * Error codes:
22291100Sdes */
223