Password hardening using PAM

Latest response

Hi

We have a requirement to validate/enforce password policy by the group to which the user belongs.

The obvious way to do this seems to be to use PAM but I can not find anything that illustrates the syntax required.

In detail we have split users into three groups each one having a specific length and complexity requirement. We have looked into the requirement and come up with what we thought was the correct syntax but we get the standard Authentication token manipulation error.

The syntax we tried was:

password [success=4 default=ignore] pam_succeed_if.so user ingroup wheel
password [success=8 default=ignore] pam_succeed_if.so user ingroup generic
password requisite pam_cracklib.so try_first_pass retry=3 minlen=12 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password required pam_deny.so

password requisite pam_cracklib.so try_first_pass retry=3 minlen=16 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password sufficient pam_unix.so sha512 shadow nullok try_first_pass # use_authtok
password required pam_deny.so

password requisite pam_cracklib.so try_first_pass retry=3 minlen=24 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password sufficient pam_unix.so sha512 shadow nullok try_first_pass # use_authtok
password required pam_deny.so

I'm guessing its something quite simple but I have been unable to find it :(

Any suggestions would be greatly appreciated.

Greg

Responses

Remember that PAM is stacked: statement-ordering matters. You'd probably need to have something more like:

password [default=1 success=ignore] pam_succeed_if.so user ingroup wheel
password requisite pam_cracklib.so try_first_pass retry=3 minlen=12 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password [default=1 success=ignore] pam_succeed_if.so user ingroup generic
password requisite pam_cracklib.so try_first_pass retry=3 minlen=16 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=

In general, you want your pam_succeed and pam_cracklib lines to be ordinally-paired (makes the logic-tracking easier to follow). Using the above should cause the cracklib to apply the minlen=12 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 only if a user's primary group is "wheel" and to apply the minlen=16 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 only if the user's primary group is "generic".

Hi Tom

Thanks for the suggestion. However what we were attempting to do was to apply minlen=16 to the wheel group, minlen=24 to the generic group and the default (minlen=12) to everybody else so I slightly modified your suggestion as follows:

password [success=4 default=ignore] pam_succeed_if.so user ingroup wheel
password requisite pam_cracklib.so try_first_pass retry=3 minlen=16 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password [success=8 default=ignore] pam_succeed_if.so user ingroup generic
password requisite pam_cracklib.so try_first_pass retry=3 minlen=24 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password requisite pam_cracklib.so try_first_pass retry=3 minlen=12 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password required pam_deny.so

but as soon as we try to change a wheel group users password we get passwd: Authentication token manipulation error.

I apologise but I am a complete novice where PAM is concerned and we have nobody on staff here who can help.

Greg

Remember that the success= parameter sets how many rules to skip if the stanza matches. In the modified configuration you're presenting

password [success=4 default=ignore] pam_succeed_if.so user ingroup wheel

if a user is in the "wheel" group, the match-behavior tell s pam, "skip the next four rules" - meaning that the next rule it processes will be:

password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok

Which probably isn't the behavior you're intending?

Similarly, with your:

password [success=8 default=ignore] pam_succeed_if.so user ingroup generic

Line, if a user is a member of the generic group, PAM skip the over the next eight rules. If there isn't a ninth rule after this stanza, you'll get an error.

Also, because PAM evaluates top to bottom, it operates on a first-match behavior. Thus, if your user is in both the "wheel" and the "generic" group (and your "wheel" rule doesn't cause you to skip straight past the "generic" rule), PAM will attempt to apply both succeed_if skip-actions ...which, if there isn't a ninth rule to evaluate, should result in an error.

Hi Tom

Once again thanks for the help and we have now made at least some progress

the configuration now looks like this:
password [success=2 default=ignore] pam_succeed_if.so user ingroup wheel
password [success=2 default=ignore] pam_succeed_if.so user ingroup generic
password [success=2 default=ignore] requisite pam_cracklib.so try_first_pass retry=3 minlen=12 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password [success=1 default=ignore] requisite pam_cracklib.so try_first_pass retry=3 minlen=16 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password requisite pam_cracklib.so try_first_pass retry=3 minlen=24 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=

which, as I understand it, will skip 2 lines if the users group is wheel ending up on the minlen=16 line and 2 lines if the users group is generic ending on the minlen=24 line otherwise the minlen=12 line will be used.

However the system now insists that all passwords are 24 characters

Greg

The threaded view of this forum isn't the best for reading config-files. It's especially hard to read if you don't use code-tags. Pulling from your immediately-prior response:

password [success=1 default=ignore] requisite pam_cracklib.so try_first_pass retry=3 minlen=16 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password requisite pam_cracklib.so try_first_pass retry=3 minlen=24 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=

In looking at the pam_cracklib man page, I'm not sure that it will honor your [success=1 default=ignore] statement ...which means that, even if you have "SUCCESS", it will continue on to the next match-statement ...resulting all logins still being subject to your default password policy.

Two possible ways around this:
* Pair your pam_succeed/pam_cracklib stanzas (and set your success/default tokens to [default=1 success=ignore])
* Use outer-inner pairings to prevent fall-through (would probably help if I could illustrate this, but this forum doesn't really allow for that)

The former means that your pam_cracklib lines will only get evaluated if the pam_succeed doesn't result in a skip (you may need to invert (negate) your ingroup match-criteria. Down side to this approach is that your users will be subjected to multiple group evaluations rather than single-group evaluations.

Note: been a while since I've tried to implement something like this and I don't currently have access to a test system to verify my statements. Basically going from the frustration-filled, half-decade old memories of what I went through to get things going the way I wanted. Should not that we ultimately ended up throwing it out because the skip-numberins are a very fragile way of enforcing behaviors (if someone came along and modifed your PAM config without being cognizant of module-ordering, all hell would break loose).

Hi Tom

Point taken. Many thanks for your help.

Greg

FWIW, what we ultimately ended up doing was offloading all (interactive) user management to a centralized directory system. It was a lot easier to apply per-group password (and other security) policies within the context of that directory service than than it was to set up and maintain PAM configs (though, keeping PAM configs in check is also doable via an enterprise CM solution like Puppet or Salt).

Hi Tom

I fully agree with you but I don't have that option :( However the good news is that its now working and for information purposes here is the config:

password [success=1 default=ignore] pam_succeed_if.so user notingroup wheel
password requisite pam_cracklib.so try_first_pass retry=3 minlen=16 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password [success=1 default=ignore] pam_succeed_if.so user notingroup generic
password requisite pam_cracklib.so try_first_pass retry=3 minlen=24 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password requisite pam_cracklib.so try_first_pass retry=3 minlen=12 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 maxrepeat=1 type=
password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password    required      pam_deny.so

I just hope some bright spark does not introduce another category.

Once again thanks for all your help.

Greg

Awesome. Glad it's working for you. Hopefully, I led you in the correct direction rather than further from your goal. =)