***Title - Creating a secondary password on a pam enabled linux system ***Author - Richard Marshall ***Why In this situation the reason behind the need for multiple passwords for the root user was to accommodate the demands of the systems team who required access via direct root login to a box I manage. The system has recently been deemed a "production" server and as such access by the systems team is required in case the box goes down and I am not around to fix it. However since the production root password is not something I am aloud to have there was a problem. The solution was to either only have sudo access which I deemed not acceptable or to figure out a way for both sides to have their way. Systems can login with their production password and I can login with my root password and everyone is happy. ***How My first thought was to change the references to /etc/shadow to something like /etc/shadow2. I ran pam_unix.so through strings piped to grep and searched for the path name which found a result so that was promising. Obviously I needed the source code for pam which was as easy as a simple apt-get command. However after doing a search/replace on the source and a quick compile I did some straces and found it was still opening /etc/shadow. Next was to actually do some real digging. Continuing the thought of changing the file read during the password lookups I started trying to figure out where /etc/shadow is actually read. Pretty shortly I found that the reads to that file aren't happening within pam directly but in the standard library. Making changes there wasn't really something I wanted to do since I wanted an easy way to turn this on and off and having to build a modified version of libc just for my version of pam_unix didn't sound like fun. The next idea was to find the point where the password hash found in /etc/shadow is compared to the password entered by the user. Tracing out that path was rather quick when running pam with debugging enabled. ./configure --enable-debug Running su with debugging enabled helped point me in the right direction to determine the best place to setup the secondary password check. A quick modification of /etc/pam.d/common-auth was needed to add my version of pam_unix.so as an option for authentication. Added the following line. auth sufficient pam_linux.so nullok_secure richard@box:~$ su - root [pam_unix_auth.c:pam_sm_authenticate(112)] called. [support.c:_set_ctrl(60)] called. [support.c:_set_ctrl(87)] pam_unix arg: nullok_secure [support.c:_set_ctrl(139)] done. [support.c:_unix_blankpasswd(510)] called [support.c:_unix_read_password(718)] called Password: ***** [pam_unix_auth.c:pam_sm_authenticate(176)] user=root, password=[*****] [support.c:_unix_verify_password(560)] called [support.c:_unix_verify_password(564)] setting delay [support.c:_unix_verify_password(571)] locating user's record [passverify.c:verify_pwd_hash(72)] called [passverify.c:verify_pwd_hash(120)] comparing state of pp[*****], hash[*****] [passverify.c:verify_pwd_hash(133)] done [7]. [support.c:_unix_verify_password(697)] done [7]. [pam_unix_auth.c:pam_sm_authenticate(182)] done. [Authentication failed] richard@box:~$ The debug output also had the added benefit of displaying the result of salting and hashing of the user provided password so I could build the hash value for the secondary password without having to write any code or reset the existing root password to see what would be put in the shadow file. I started looking in _unix_verify_password to see if that would be a good place. However after a look there turned out this is not where the comparison between the hash stored in /etc/shadow and the password entered by the user takes place. _unix_verify_password is used to make sure that a valid password was input by the user. Next obvious choice was verify_pwd_hash which turned out to be perfect. Near the end of the function the following if statement is where the magic happens. if (pp && strcmp(pp, hash) == 0) { retval = PAM_SUCCESS; } else { retval = PAM_AUTH_ERR; } The previous code compares the string pp which is the hashed user provided password against the string hash which is the value stored in /etc/shadow. My first thought was to replace the value in hash with the hashed secondary password however this would add complications with making sure that entering the real password still works. So the best thing to do was adding an else if to that previous code block. The password check became. if (pp && strcmp(pp, hash) == 0) { retval = PAM_SUCCESS; } else if (pp && strcmp(pp,"") == 0) { retval = PAM_SUCCESS; } else { retval = PAM_AUTH_ERR; } This allows the normal processing to work and a second check to be done against the secondary hash if the first check fails. I felt that doing it this way instead of adding an or clause to the first expression was a cleaner separation of what is the valid login and what is the secondary login. After compiling and installing it was time to test the modification. Ran su - root, entered the secondary password and viola hello root! The last thing to do was reconfigure without --enable-debug to get rid of those pesky debugging messages that would tip someone off that something is not as it should be. ***Improvements The main thing that would be good to improve upon would be to not hard code the secondary password hash into the library. It would not be very difficult to read the hash from a file somewhere which would allow the password to be changed without having to recompile the whole thing. Also a utility to change the password in a passwd like fashion could be written to make updating the secondary password easier. ***Final Thoughts Obviously this is a very easily detectable way of providing multiple passwords for a single user so while it works it's not very stealthy. However it gives me what I want which is for two people with two different passwords to login as the same user. A patch file is available for debian lennys pam-1.0.1 here: http://pub.easytospell.net/pam_1.0.1.patch