I've been reading everything I can find on PAM (e.g. http://wpollock.com/AUnix2/PAM-Help.htm), however I'm still confused about how precisely the advanced options are supposed to work. For example, every reference says this:
Each of the four control-flag keywords (required, requisite, sufficient, and
optional) have an equivalent expression in terms of the [...] syntax:
required
[success=ok new_authtok_reqd=ok ignore=ignore default=bad]
My understanding is that modules can return a variety of tokens, and that the action associated with each token is described in the configuration file. Based on that understanding, what does
new_authtok_reqd=ok
have to do with the required control flag? What's the meaning/purpose of
ignore=ignore ?
This alone:
success=ok
is what I would think matches the required behavior, but does
default=bad
mean that if the module returns any other action token, the module fails? Is the action=value token success=ok overruled by default=bad, or vice versa? Which takes precendence? It's not clear from anything I've read.
More generally, suppose I have something like
[success=done default=die]
What happens if the module returns success and one other token?
Finally, I can't find the answer to this question, either: can every value ok, done, bad, die, ignore, reset, N
be associated with any action? What would it even mean to say
[default=done] ?
PAM modules have over 30 different return values that are mapped to either pass or fail of the whole PAM stack as stated by the configuration.
It is noteworthy that a PAM module may behave different depending on the context (auth, account, password, session) that it is called in.
The pairs of value=action in square brackets describe which action to take for each possible return value of the PAM module.
A good explanation of values and actions can be found by carefully reading the above mentioned page to the end.
success=ok
the module has returned success, this will be honored in the evaluation of the whole PAM stack, if no preceding module has failed, consider pass for the whole stack up to this point.new_authtok_reqd=ok
a new authentication token is required. E.g. in session context this might make the user change her/his password.ignore=ignore
the PAM module wants its result to be ignored, so we ignore it.default=bad
all other results make the PAM stack fail (but don't stop processing subsequent modules)PAM modules return only a single value.
This means: any token not mentioned here (i.e. every possible token) ends processing of the PAM stack and returns the result so far.
First of all, ua2b provided the critical answer which was causing my confusion:
PAM modules can only return a single status value per invocation
Since modules can only return one status code, it's clear that the action for any bracketed set of value/action pairs is unambiguous. I thought that a module could return several status codes at once (based on reading various documents), hence my confusion. It boggles my mind that no reference I could find, including the official documentation, is capable of stating this clearly. Instead, one finds comments like this :
"One or more status codes are returned by each PAM-API routine."
This to me sounds like a module can return several status codes. It was only after reading through the PAM module developer's guide that I realized that status codes are returned as a single integer which maps to the possible status code values as described here (Yes, the same reference!)
My next question had to do with equating the older style control flag keyword required with this square bracket syntax:
This equivalence is repeated all over the place, for example here.
It turns out that this is complete and utter nonsense. The status codes returned by any particular module can differ, and differ again depending on the management group type (e.g. auth vs. account).
Take for example, the pam_securetty module. This module only provides the auth type, and has the following possible status code return values:
The
ignore=ignore
value/action mapping consequently appears to be useless, as this module doesn't ever return ignore. In this case, you might want to consider usinguser_unknown=ignore
if in fact you do want to ignore the outcome if the user can't be found.