This commit disables chain caching.

Chain caching is a feature of Linux-PAM, where pam_authenticate() and
pam_open_session() "freeze" the chain so that their companion
primitive (pam_setcred() and pam_close_session() respectively) will
call the exact same modules, skipping those that failed in the
previous call.

There are several reasons not to do this, the most prominent of which
is that it makes it impossible to call pam_setcred() without first
calling pam_authenticate() - which is perfectly valid according to
DCE/RFC 86.0 and XSSO, and is necessary to make 'login -f' work.

Instead of chain caching, implement something similar to the way
Solaris' libpam behaves: pam_setcred treats "sufficient" modules as if
they were "required", i.e. does not break the chain when they succeed.

PAM modules whose pam_sm_setcred() should not be called unless their
pam_sm_authenticate() succeeded can simply set a state variable using
pam_set_data() in pam_sm_authenticate(), and use pam_get_data() to
check it in pam_sm_setcred().

Sponsored by:	DARPA, NAI Labs
This commit is contained in:
Dag-Erling Smørgrav 2002-01-24 15:30:03 +00:00
parent a3e5380a47
commit e9ae7bc234
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=89738

View file

@ -4,6 +4,8 @@
* Copyright (c) 1998 Andrew G. Morgan <morgan@kernel.org>
*
* $Id: pam_dispatch.c,v 1.3 2001/02/05 06:50:41 agmorgan Exp $
*
* $FreeBSD$
*/
#include <stdlib.h>
@ -28,7 +30,7 @@
*/
static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
_pam_boolean resumed, int use_cached_chain)
_pam_boolean resumed, int ignore_sufficient)
{
int depth, impression, status, skip_depth;
@ -99,46 +101,18 @@ static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
return retval;
}
if (use_cached_chain) {
/* a former stack execution has frozen the chain */
cached_retval = *(h->cached_retval_p);
} else {
/* this stack execution is defining the frozen chain */
cached_retval = h->cached_retval = retval;
}
/* verify that the return value is a valid one */
if ((cached_retval < PAM_SUCCESS)
|| (cached_retval >= _PAM_RETURN_VALUES)) {
if ((retval < PAM_SUCCESS) || (retval >= _PAM_RETURN_VALUES)) {
retval = PAM_MUST_FAIL_CODE;
action = _PAM_ACTION_BAD;
} else {
/* We treat the current retval with some respect. It may
(for example, in the case of setcred) have a value that
needs to be propagated to the user. We want to use the
cached_retval to determine the modules to be executed
in the stacked chain, but we want to treat each
non-ignored module in the cached chain as now being
'required'. We only need to treat the,
_PAM_ACTION_IGNORE, _PAM_ACTION_IS_JUMP and
_PAM_ACTION_RESET actions specially. */
action = h->actions[cached_retval];
action = h->actions[retval];
}
D((stderr,
"use_cached_chain=%d action=%d cached_retval=%d retval=%d\n",
use_cached_chain, action, cached_retval, retval));
/* decide what to do */
switch (action) {
case _PAM_ACTION_RESET:
/* if (use_cached_chain) {
XXX - we need to consider the use_cached_chain case
do we want to trash accumulated info here..?
} */
impression = _PAM_UNDEF;
status = PAM_MUST_FAIL_CODE;
break;
@ -146,16 +120,13 @@ static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
case _PAM_ACTION_OK:
case _PAM_ACTION_DONE:
/* XXX - should we maintain cached_status and status in
the case of use_cached_chain? The same with BAD&DIE
below */
if ( impression == _PAM_UNDEF
|| (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) {
impression = _PAM_POSITIVE;
status = retval;
}
if ( impression == _PAM_POSITIVE && action == _PAM_ACTION_DONE ) {
if ( impression == _PAM_POSITIVE && action == _PAM_ACTION_DONE
&& !ignore_sufficient ) {
goto decision_made;
}
break;
@ -179,11 +150,6 @@ static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
break;
case _PAM_ACTION_IGNORE:
/* if (use_cached_chain) {
XXX - when evaluating a cached
chain, do we still want to ignore the module's
return value?
} */
break;
/* if we get here, we expect action is a positive number --
@ -192,19 +158,6 @@ static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
default:
if ( _PAM_ACTION_IS_JUMP(action) ) {
/* If we are evaluating a cached chain, we treat this
module as required (aka _PAM_ACTION_OK) as well as
executing the jump. */
if (use_cached_chain) {
if (impression == _PAM_UNDEF
|| (impression == _PAM_POSITIVE
&& status == PAM_SUCCESS) ) {
impression = _PAM_POSITIVE;
status = retval;
}
}
/* this means that we need to skip #action stacked modules */
do {
h = h->next;
@ -245,7 +198,7 @@ static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
{
struct handler *h = NULL;
int retval, use_cached_chain;
int retval, ignore_sufficient;
_pam_boolean resumed;
IF_NO_PAMH("_pam_dispatch", pamh, PAM_SYSTEM_ERR);
@ -262,7 +215,7 @@ int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
return retval;
}
use_cached_chain = 0; /* default to setting h->cached_retval */
ignore_sufficient = 0; /* default to setting h->cached_retval */
switch (choice) {
case PAM_AUTHENTICATE:
@ -270,7 +223,7 @@ int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
break;
case PAM_SETCRED:
h = pamh->handlers.conf.setcred;
use_cached_chain = 1;
ignore_sufficient = 1;
break;
case PAM_ACCOUNT:
h = pamh->handlers.conf.acct_mgmt;
@ -280,13 +233,9 @@ int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
break;
case PAM_CLOSE_SESSION:
h = pamh->handlers.conf.close_session;
use_cached_chain = 1;
break;
case PAM_CHAUTHTOK:
h = pamh->handlers.conf.chauthtok;
if (flags & PAM_UPDATE_AUTHTOK) {
use_cached_chain = 1;
}
break;
default:
_pam_system_log(LOG_ERR, "undefined fn choice; %d", choice);
@ -333,7 +282,7 @@ int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
__PAM_TO_MODULE(pamh);
/* call the list of module functions */
retval = _pam_dispatch_aux(pamh, flags, h, resumed, use_cached_chain);
retval = _pam_dispatch_aux(pamh, flags, h, resumed, ignore_sufficient);
resumed = PAM_FALSE;
__PAM_TO_APP(pamh);