RHEL 7 and pam_pwhistory - old password can still be re-used

Latest response

The intent is to prevent a user from re-using old passwords. We elect to remember 4 previous passwords in our requirements. We are trying to implement this on RHEL 7.2.1511.

Previously, this should be done via pam_unix.so, but we now have pam_pwhistory.so, so I base my attempts around it.

First, we examine /etc/pam.d/passwd to see what is used for the service:

auth       include      system-auth
account    include      system-auth
password   substack     system-auth
-password   optional    pam_gnome_keyring.so use_authtok
password   substack     postlogin

Surprisingly, it's not password-auth, but rather system-auth that's used (probably because password-auth is used for remote authentication (e.g. sshd), whereas system-auth is geared more towards local authentication -- just my guess).

So, we look at the system-auth: ls -l /etc/pam.d/system-auth results in:

lrwxrwxrwx. 1 root root 16 Aug  2 16:34 /etc/pam.d/system-auth -> system-auth-ac

As system-auth-ac is overwritten automatically by authconfig, it's recommended to create a different file and link it to your system-auth. We choose to call ours "system-auth-paid". Here are its contents:

auth include system-auth-ac

account include system-auth-ac

password include system-auth-ac

session include system-auth-ac

Next, we modify it to use our specs:

auth include system-auth-ac

account include system-auth-ac

password substack system-auth-ac
password required pam_pwhistory.so debug use_authtok remember=4

session include system-auth-ac

Only two things changed here:
1) I included password directives from "system-auth-ac" as a substack, rather than plain include (had to do this after some testing).
2) I added a new line for pam_pwhistory.so with three parameters (debug, use_authtok, remember=4).

Now I try to test this with a non-elevated user.
I had already changed the password once, so I have two values for passwords: CURRENT_PASSWORD and OLD_PASSWORD.

 passwd    # here, I am logged in as a non-elevated user called 'testuser'
Changing password for user testuser.
Changing password for testuser.
(current) UNIX password: CURRENT_PASSWORD
New password: OLD_PASSWORD
Retype new password: OLD_PASSWORD
Password has been already used. Choose another.
passwd: Have exhausted maximum number of retries for service

This would appear to be exactly what we want. The problem is that now OLD_PASSWORD is actually set as my password, despite the system telling me that it was rejected (?!)

At the same time, I am tailing /var/log/messages to see what "debug" directive from pam_pwhistory.so spits out.

Snippet from /var/log/messages

Aug  3 12:27:48 localhost passwd: pam_unix(passwd:chauthtok): password changed for testuser
Aug  3 12:27:48 localhost passwd: pam_pwhistory(passwd:chauthtok): pam_sm_chauthtok entered
Aug  3 12:27:48 localhost passwd: pam_pwhistory(passwd:chauthtok): got new auth token
Aug  3 12:27:48 localhost passwd: pam_pwhistory(passwd:chauthtok): check against old password file
Aug  3 12:27:48 localhost pwhistory_helper[18109]: New password already used   # <--- this is good
Aug  3 12:27:48 localhost passwd: pam_pwhistory(passwd:chauthtok): Aborted, too many tries 
Aug  3 12:27:48 localhost passwd: gkr-pam: no password set, and use_authtok was specified   # <--- ???

1) Right now, although OLD_PASSWORD appears to have been rejected, it's actually my new password!
2) Looking at /var/log/messages last line, I thought I should remove use_authtok. But if I do so, then I obviate previous directives (e.g. from pam_pwquality.so) which are very useful and should be passed onto this stage.
3) Also, if I do go ahead and remove use_authtok, so that my line looks like this:

 password required pam_pwhistory.so debug remember=4

I still have the same problem. So, I chose to leave use_authtok in.

Finally, if I change "required" directive to "requisite", so that it looks like this:

password requisite pam_pwhistory.so debug use_authtok remember=4

I still have the same problem.

Looking for any ideas/help. Greatly appreciated.


Small update: I was confused by this line in /var/log/messages:

Aug  3 12:27:48 localhost passwd: gkr-pam: no password set, and use_authtok was specified

It turns out that this is a gnome-keyring message. So something funky is going on with gnome-keyring and passwd binary, in conjunction to password-auth file. I will explore further.

gnome-keyring repoted some problems in /var/log/audit/audit.log

First, I placed SELinux in Permissive mode to see if it would help. It didn't, original problem persisted.

I returned SELinux to Enforcing state.

Then I still ran 'sealert -a /var/log/audit/audit.log' with this result:

[13:45:24][root@mfda-rhel7 ~]# sealert -a /var/log/audit/audit.log
PuTTY X11 proxy: Unsupported authorisation protocol
 94% done'list' object has no attribute 'split'
100% done
found 1 alerts in /var/log/audit/audit.log

SELinux is preventing /usr/bin/gnome-keyring-daemon from create access on the directory keyring.

*****  Plugin catchall (100. confidence) suggests   **************************

If you believe that gnome-keyring-daemon should be allowed create access on the keyring directory by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
allow this access for now by executing:
# grep gnome-keyring-d /var/log/audit/audit.log | audit2allow -M mypol
# semodule -i mypol.pp

Additional Information:
Source Context                unconfined_u:unconfined_r:passwd_t:s0-s0:c0.c1023
Target Context                unconfined_u:object_r:user_tmp_t:s0
Target Objects                keyring [ dir ]
Source                        gnome-keyring-d
Source Path                   /usr/bin/gnome-keyring-daemon
Port                          <Unknown>
Host                          <Unknown>
Source RPM Packages           gnome-keyring-3.14.0-1.el7.x86_64
Target RPM Packages
Policy RPM                    selinux-policy-3.13.1-60.el7_2.3.noarch
Selinux Enabled               True
Policy Type                   targeted
Enforcing Mode                Enforcing
Host Name                     mfda-rhel7
Platform                      Linux mfda-rhel7 3.10.0-327.18.2.el7.x86_64 #1 SMP
                              Thu May 12 11:03:55 UTC 2016 x86_64 x86_64
Alert Count                   33
First Seen                    2016-08-03 11:00:10 EDT
Last Seen                     2016-08-03 11:42:02 EDT
Local ID                      1d35cb1a-4ec7-4255-a109-551afd4d74a6

Raw Audit Messages
type=AVC msg=audit(1470238922.796:10032): avc:  denied  { create } for  pid=16807 comm="gnome-keyring-d" name="keyring" scontext=unconfined_u:unconfined_r:passwd_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:user_tmp_t:s0 tclass=dir

type=SYSCALL msg=audit(1470238922.796:10032): arch=x86_64 syscall=mkdir success=no exit=EACCES a0=7f1cc9d310f0 a1=1c0 a2=7f1cc9d31106 a3=7fff2202b7a0 items=0 ppid=16795 pid=16807 auid=1000 uid=1000 gid=1000 euid=1000 suid=1000 fsuid=1000 egid=1000 sgid=1000 fsgid=1000 tty=pts1 ses=1210 comm=gnome-keyring-d exe=/usr/bin/gnome-keyring-daemon subj=unconfined_u:unconfined_r:passwd_t:s0-s0:c0.c1023 key=(null)

Hash: gnome-keyring-d,passwd_t,user_tmp_t,dir,create

I then ran audit2allow, as specified in log above and applied the policy.

Again, no changes.

I wonder if there is a bug in gkr-pam?

So, there's a solution, but it's a dirty one.

At issue is how we include authconfig-generated "system-auth-ac" file into my custom file "system-auth-paid".

To remind you, I created a file called "system-auth-paid" and it contains these values:

auth include system-auth-ac

account include system-auth-ac

password substack system-auth-ac
password requisite pam_pwhistory.so debug use_authtok remember=4 retry=3

session include system-auth-ac

I then made a symbolic link called "system-auth", which points to "system-auth-paid" (this is the best practice, according to RHEL security guide; it's advised because authconfig will overwrite system-auth-ac so you want to have your overrides in a separate file).

However, this inclusion did not work.

So instead, I copied the contents of system-auth-ac directly into system-auth-paid custom file and changed the ordering slightly. I embedded my directive smack in the middle of "password" block of system-auth-ac.

My custom system-auth-paid now looks like this:

# This is a copy+paste from system-auth-ac
auth        required      pam_env.so
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 1000 quiet_success
auth        required      pam_deny.so

account     required      pam_unix.so
account     sufficient    pam_localuser.so
account     sufficient    pam_succeed_if.so uid < 1000 quiet
account     required      pam_permit.so

password    requisite     pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
password requisite pam_pwhistory.so debug use_authtok remember=4 retry=3      # <--- I inserted this
password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
-session     optional      pam_systemd.so
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session     required      pam_unix.so

This works as expected: old password is remembered and rejected. I must select a new password and cycle through 4 previous ones.

Since I had to insert my directive in the middle of the stack, I see no way to work around this by in-lining the system-auth-ac and then putting my instruction after it. It looks like I must modify the file directly.

However, if I modify it, I lose any changes from authconfig to system-auth-ac in the future.

So, what's the sanctioned way of going about this?

There is apparently no way. This feature was requested, but rejected for no reason: https://bugzilla.redhat.com/show_bug.cgi?id=1271804

the "reason" , is that this is Red Hat, and... Red Hat never, EVER, actually fix anything.