openpam_dispatch.c revision 91684
180709Sjake/*-
280709Sjake * Copyright (c) 2002 Networks Associates Technologies, Inc.
380709Sjake * All rights reserved.
480709Sjake *
580709Sjake * This software was developed for the FreeBSD Project by ThinkSec AS and
680709Sjake * NAI Labs, the Security Research Division of Network Associates, Inc.
780709Sjake * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
880709Sjake * DARPA CHATS research program.
980709Sjake *
1080709Sjake * Redistribution and use in source and binary forms, with or without
1180709Sjake * modification, are permitted provided that the following conditions
1280709Sjake * are met:
1380709Sjake * 1. Redistributions of source code must retain the above copyright
1481337Sobrien *    notice, this list of conditions and the following disclaimer.
1580709Sjake * 2. Redistributions in binary form must reproduce the above copyright
1680709Sjake *    notice, this list of conditions and the following disclaimer in the
1781337Sobrien *    documentation and/or other materials provided with the distribution.
1880709Sjake * 3. The name of the author may not be used to endorse or promote
1980709Sjake *    products derived from this software without specific prior written
2080709Sjake *    permission.
2180709Sjake *
2280709Sjake * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2380709Sjake * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2480709Sjake * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2580709Sjake * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2680709Sjake * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2780709Sjake * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2880709Sjake * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2980709Sjake * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3080709Sjake * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3180709Sjake * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3280709Sjake * SUCH DAMAGE.
3382910Sjake *
3480709Sjake * $P4: //depot/projects/openpam/lib/openpam_dispatch.c#13 $
3580709Sjake */
3680709Sjake
3784186Sjake#include <sys/param.h>
3880709Sjake
3980709Sjake#include <security/pam_appl.h>
4081381Sjake
4181381Sjake#include "openpam_impl.h"
4281381Sjake
4381381Sjake#if !defined(OPENPAM_RELAX_CHECKS)
4480709Sjakestatic void _openpam_check_error_code(int, int);
4580709Sjake#else
4680709Sjake#define _openpam_check_error_code(a, b)
4780709Sjake#endif /* !defined(OPENPAM_RELAX_CHECKS) */
4881135Stmm
4980709Sjake/*
5080709Sjake * OpenPAM internal
5181614Sjake *
5280709Sjake * Execute a module chain
5380709Sjake */
5480709Sjake
5582910Sjakeint
5680709Sjakeopenpam_dispatch(pam_handle_t *pamh,
5780709Sjake	int primitive,
5880709Sjake	int flags)
5980709Sjake{
6082010Sjake	pam_chain_t *chain;
6180709Sjake	int err, fail, r;
6283756Sjake
6383756Sjake	if (pamh == NULL)
6480709Sjake		return (PAM_SYSTEM_ERR);
6580709Sjake
6680709Sjake	/* prevent recursion */
6783366Sjulian	if (pamh->current != NULL) {
6884186Sjake		openpam_log(PAM_LOG_ERROR, "indirect recursion");
6983366Sjulian		return (PAM_ABORT);
7080709Sjake	}
7180709Sjake
7282910Sjake	/* pick a chain */
7380709Sjake	switch (primitive) {
7481135Stmm	case PAM_SM_AUTHENTICATE:
7581135Stmm	case PAM_SM_SETCRED:
7681135Stmm		chain = pamh->chains[PAM_AUTH];
7781135Stmm		break;
7881381Sjake	case PAM_SM_ACCT_MGMT:
7981381Sjake		chain = pamh->chains[PAM_ACCOUNT];
8081381Sjake		break;
8181381Sjake	case PAM_SM_OPEN_SESSION:
8280709Sjake	case PAM_SM_CLOSE_SESSION:
8381381Sjake		chain = pamh->chains[PAM_SESSION];
8481381Sjake		break;
8581381Sjake	case PAM_SM_CHAUTHTOK:
8680709Sjake		chain = pamh->chains[PAM_PASSWORD];
8785244Sjake		break;
8880709Sjake	default:
8980709Sjake		return (PAM_SYSTEM_ERR);
9080709Sjake	}
9180709Sjake
9282910Sjake	/* execute */
9385244Sjake	for (err = fail = 0; chain != NULL; chain = chain->next) {
9484186Sjake		openpam_log(PAM_LOG_DEBUG, "calling %s() in %s",
9584186Sjake		    _pam_sm_func_name[primitive], chain->module->path);
9684186Sjake		if (chain->module->func[primitive] == NULL) {
9782910Sjake			openpam_log(PAM_LOG_ERROR, "%s: no %s()",
9885244Sjake			    chain->module->path, _pam_sm_func_name[primitive]);
9985244Sjake			continue;
10085244Sjake		} else {
10182910Sjake			pamh->current = chain;
10282910Sjake			r = (chain->module->func[primitive])(pamh, flags,
10382910Sjake			    chain->optc, (const char **)chain->optv);
10482910Sjake			pamh->current = NULL;
10582910Sjake			openpam_log(PAM_LOG_DEBUG, "%s: %s(): %s",
10682910Sjake			    chain->module->path, _pam_sm_func_name[primitive],
10782910Sjake			    pam_strerror(pamh, r));
10882910Sjake		}
10982910Sjake
11080709Sjake		if (r == PAM_IGNORE)
11180709Sjake			continue;
11281381Sjake		if (r == PAM_SUCCESS) {
11380709Sjake			/*
11481381Sjake			 * For pam_setcred() and pam_chauthtok() with the
11580709Sjake			 * PAM_PRELIM_CHECK flag, treat "sufficient" as
11680709Sjake			 * "optional".
11780709Sjake			 *
11880709Sjake			 * Note that Solaris libpam does not terminate
11981614Sjake			 * the chain here if a required module has
12081381Sjake			 * previously failed.  I'm not sure why.
12180709Sjake			 */
12280709Sjake			if (chain->flag == PAM_SUFFICIENT &&
12380709Sjake			    primitive != PAM_SM_SETCRED &&
12480709Sjake			    (primitive != PAM_SM_CHAUTHTOK ||
12580709Sjake				!(flags & PAM_PRELIM_CHECK)))
12680709Sjake				break;
12780709Sjake			continue;
12884186Sjake		}
12984186Sjake
13083366Sjulian		_openpam_check_error_code(primitive, r);
13180709Sjake
13282910Sjake		/*
13380709Sjake		 * Record the return code from the first module to
13484186Sjake		 * fail.  If a required module fails, record the
13584186Sjake		 * return code from the first required module to fail.
13684186Sjake		 */
13781614Sjake		if (err == 0)
13881614Sjake			err = r;
13981614Sjake		if (chain->flag == PAM_REQUIRED && !fail) {
14081614Sjake			openpam_log(PAM_LOG_DEBUG, "required module failed");
14181614Sjake			fail = 1;
14281614Sjake			err = r;
14381614Sjake		}
14481614Sjake
14581614Sjake		/*
14681614Sjake		 * If a requisite module fails, terminate the chain
14781614Sjake		 * immediately.
14881614Sjake		 */
14981614Sjake		if (chain->flag == PAM_REQUISITE) {
15081614Sjake			openpam_log(PAM_LOG_DEBUG, "requisite module failed");
15181614Sjake			fail = 1;
15281614Sjake			break;
15385244Sjake		}
15485244Sjake	}
15580709Sjake
15680709Sjake	if (!fail)
15780709Sjake		err = PAM_SUCCESS;
15880709Sjake	openpam_log(PAM_LOG_DEBUG, "returning: %s", pam_strerror(pamh, err));
15983366Sjulian	return (err);
16083366Sjulian}
16182910Sjake
16282910Sjake#if !defined(OPENPAM_RELAX_CHECKS)
16382910Sjakestatic void
16480709Sjake_openpam_check_error_code(int primitive, int r)
16580709Sjake{
16683366Sjulian	/* common error codes */
16783366Sjulian	if (r == PAM_SUCCESS ||
16883366Sjulian	    r == PAM_SERVICE_ERR ||
16983366Sjulian	    r == PAM_BUF_ERR ||
17083366Sjulian	    r == PAM_CONV_ERR ||
17183366Sjulian	    r == PAM_PERM_DENIED ||
17283366Sjulian	    r == PAM_ABORT)
17383366Sjulian		return;
17484186Sjake
17581135Stmm	/* specific error codes */
17680709Sjake	switch (primitive) {
17780709Sjake	case PAM_SM_AUTHENTICATE:
17882010Sjake		if (r == PAM_AUTH_ERR ||
17980709Sjake		    r == PAM_CRED_INSUFFICIENT ||
18082010Sjake		    r == PAM_AUTHINFO_UNAVAIL ||
18182910Sjake		    r == PAM_USER_UNKNOWN ||
18282910Sjake		    r == PAM_MAXTRIES)
18382910Sjake			return;
18480709Sjake		break;
18581381Sjake	case PAM_SM_SETCRED:
18681381Sjake		if (r == PAM_CRED_UNAVAIL ||
18781381Sjake		    r == PAM_CRED_EXPIRED ||
18881381Sjake		    r == PAM_USER_UNKNOWN ||
18981135Stmm		    r == PAM_CRED_ERR)
19081135Stmm			return;
19181135Stmm		break;
19281135Stmm	case PAM_SM_ACCT_MGMT:
19381135Stmm		if (r == PAM_USER_UNKNOWN ||
19481135Stmm		    r == PAM_AUTH_ERR ||
19581135Stmm		    r == PAM_NEW_AUTHTOK_REQD ||
19680709Sjake		    r == PAM_ACCT_EXPIRED)
19780709Sjake			return;
19880709Sjake		break;
19982910Sjake	case PAM_SM_OPEN_SESSION:
20082010Sjake	case PAM_SM_CLOSE_SESSION:
20180709Sjake		if (r == PAM_SESSION_ERR)
20280709Sjake			return;
20380709Sjake		break;
20480709Sjake	case PAM_SM_CHAUTHTOK:
20580709Sjake		if (r == PAM_PERM_DENIED ||
20680709Sjake		    r == PAM_AUTHTOK_ERR ||
20780709Sjake		    r == PAM_AUTHTOK_RECOVERY_ERR ||
20880709Sjake		    r == PAM_AUTHTOK_LOCK_BUSY ||
20980709Sjake		    r == PAM_AUTHTOK_DISABLE_AGING ||
21080709Sjake		    r == PAM_TRY_AGAIN)
21180709Sjake			return;
21280709Sjake		break;
21380709Sjake	}
21480709Sjake
21580709Sjake	openpam_log(PAM_LOG_ERROR, "%s(): unexpected return value %d",
21680709Sjake	    _pam_sm_func_name[primitive], r);
21780709Sjake}
21880709Sjake#endif /* !defined(OPENPAM_RELAX_CHECKS) */
21980709Sjake
22080709Sjake/*
22180709Sjake * NODOC
22280709Sjake *
22380709Sjake * Error codes:
22480709Sjake */
22582910Sjake