...based on further reading, let me suggest an answer. Any additions or corrections are obviously and warmly welcome.
First of all I've found this ages old intro into PAM. Probably hasn't lost any of its sharp wit during the years.
If I take a peek under the hood, the auth module return values are documented in official linux-pam documentation.
In the list, I can see a promising retval called PAM_AUTHINFO_UNAVAIL .
Actually defined in libpam/include/security/_pam_types.h .
#define PAM_SUCCESS 0
#define PAM_AUTHINFO_UNAVAIL 9
#define _PAM_RETURN_VALUES 32
There appears to be a conflicting definition of PAM_AUTHINFO_UNAVAIL in libpam/include/security/_pam_compat.h
# define PAM_AUTHINFO_UNAVAIL 12
-- probably safe to ignore.
The source file libpam/pam_dispatch.c contains a key function called
_pam_dispatch_aux() that actually walks the stack of registered authentication modules, and acts upon their return values.
And, it turns out that it does not do much based on the "retval" directly,
possibly containing PAM_AUTHINFO_UNAVAIL
. It does respond directly to some particular "inside special use" values of retval
, but has no special handling for PAM_AUTHINFO_UNAVAIL
. retval
is not a return value of _pam_dispatch_aux()
itself: rather, retval
is just a local variable declared inside the for(;;) loop that walks the stack of registered auth modules, where the block-local retval
collects the particular module's return value.
The stackwalking loop actually makes important decisions based upon other variables: one of them derived, called action
, and the other one is a struct member called impression
"returned" by the modules by reference. So perhaps it might be influenced by the module's own code after all?
Actually it turns out that action
is taken from a "lookup table of actions",
indexed by the retval
- ahaa!
action = h->actions[cached_retval];
Is the table of actions defined by the module? But how, if the _PAM_ACTION_* macros are a #defined in libpam/pam_private.h ?
Turns out that the actions[] table is initialized in a generic way by a function called _pam_parse_conf_file()
in libpam/pam_handlers.c
,
that does a direct string comparison (matching) on keywords such as
required
,requisite
,optional
,sufficient
.
And, the whole table currently has 32 positions (see the macro definition quoted above) that get wholesale initialized to _PAM_ACTION_UNDEF before parsing the config entry for the particular module. Individual "strength keywords" then remap individual retval
s to particular desired actions.
So: it seems fairly obvious to e.g. take inspiration in this:
} else if (!strcasecmp("required", tok)) {
D(("*PAM_F_REQUIRED*"));
actions[PAM_SUCCESS] = _PAM_ACTION_OK;
actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
actions[PAM_IGNORE] = _PAM_ACTION_IGNORE;
_pam_set_default_control(actions, _PAM_ACTION_BAD);
} else if (!strcasecmp("sufficient", tok)) {
D(("*PAM_F_SUFFICIENT*"));
actions[PAM_SUCCESS] = _PAM_ACTION_DONE;
actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_DONE;
_pam_set_default_control(actions, _PAM_ACTION_IGNORE);
}
and add something like
} else if (!strcasecmp("reqd_if_avail", tok)) {
D(("*PAM_F_REQD_IF_AVAIL*"));
actions[PAM_SUCCESS] = _PAM_ACTION_OK;
actions[PAM_AUTHINFO_UNAVAIL] = _PAM_ACTION_IGNORE; // !!!!!!!!!!
actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK; // ?
actions[PAM_IGNORE] = _PAM_ACTION_IGNORE;
_pam_set_default_control(actions, _PAM_ACTION_BAD); // !!!
}
And, apparently pam_tacplus helps us on that path.
It would certainly be nice if the same effect could be achieved
by existing PAM config file syntax, without having to hack the source code.
sufficient
?sufficient
cut both ways?