I am currently preparing a machine for a web hosting service, and I decided to use MySQL to store all our users (since the rest of our services use it already). For that, I am using libnss-mysql and pam-mysql. However, even though most of the setup is functioning, I am facing a problem when trying to change a user's password with passwd
.
At the moment, it is possible to create a user (INSERT INTO
) and log in as this user using su
. The machine does not prompt for a password, and access to the user's shell is directly given. However, once I'm logged as this user, passwd
ends with :
$ passwd myuser
passwd: Authentication token manipulation error
passwd: password unchanged
According to the MySQL logs, a query is made when passwd
is called, therefore the connection with MySQL isn't a problem. Besides, when I try calling passwd
with an unexisting user, I get an appropriate passwd: user 'doesnotexist' does not exist
. passwd
does find a user, but cannot modify its information. The auth.log
log file says :
pam_unix(passwd:chauthtok): user "myuser" does not exist in /etc/passwd
pam_mysql - option verbose is set to "1"
pam_mysql - pam_sm_chauthtok() called.
pam_mysql - pam_mysql_open_db() called.
pam_mysql - pam_mysql_open_db() returning 0.
pam_mysql - pam_sm_chauthtok() returning 0.
pam_mysql - pam_mysql_release_ctx() called.
pam_mysql - pam_mysql_destroy_ctx() called.
pam_mysql - pam_mysql_close_db() called.
When calling passwd -Sa
to get the status of all accounts, the myuser
account does appear. Besides, getent passwd
and getent shadow
both return a valid entry for myuser
.
$ passwd -Sa
...
messagebus L 06/28/2014 0 99999 7 -1
mysql L 06/28/2014 0 99999 7 -1
myuser P 01/01/1970 0 99999 7 -1
$ getent passwd myuser
myuser:x:5001:5000:First Last:/home/members/myuser:/bin/bash
$ getent shadow myuser
myuser:$6$...:0:0:99999:7:::0
However, when requesting ageing information about myuser
:
$ chage -l myuser
chage: user 'myuser' does not exist in /etc/passwd
All in all :
su
does find the user, and performs a password-less login in all cases (if I am root, otherwise I am prompted for a password, and get the token error).chage
cannot find the user ; it seems to search/etc/passwd
instead of the DB.passwd
does find the user, but returns a token error when trying to edit it.
Here are some configuration samples :
/etc/pam.d/common-account
account sufficient pam_unix.so
account required pam_mysql.so config_file=/etc/pam-mysql.conf
/etc/pam.d/common-auth
auth sufficient pam_unix.so nullok_secure
auth required pam_mysql.so config_file=/etc/pam-mysql.conf
/etc/pam.d/common-session
session sufficient pam_unix.so
session required pwam_mysql.so config_file=/etc/pam-mysql.conf
/etc/pam.d/common-passwd
password sufficient pam_unix.so obscure sha512
password required pam_mysql.so config_file=/etc/pam-mysql.conf
/etc/libnss-mysql.cfg
getpwnam SELECT username,'x',(5000+id),5000,CONCAT(firstname, lastname),CONCAT('/home/members/', username),'/bin/bash' \
FROM users \
WHERE username='%1$s' \
LIMIT 1
getpwuid SELECT username,'x',(5000+id),5000,CONCAT(firstname, lastname),CONCAT('/home/members/', username),'/bin/bash' \
FROM users \
WHERE (5000+id)='%1$u' \
LIMIT 1
getspnam SELECT username,password,0,'0','99999','7','-1','-1','0' \
FROM users \
WHERE username='%1$s' \
LIMIT 1
getpwent SELECT username,'x',(5000+id),5000,CONCAT(firstname, lastname),CONCAT('/home/members/', username),'/bin/bash' \
FROM users
getspent SELECT username,password,0,'0','99999','7','-1','-1','0' \
FROM users
getgrnam SELECT '%1$s','members',5000
getgrgid SELECT name,password,'%1$u'
getgrent SELECT 'members','members',5000
memsbygid SELECT username \
FROM users
gidsbymem SELECT 5000
- My user IDs start at 1 in the DB, but must start at 5000 for the system (5000+id).
/bin/bash
for everyone.- The
$HOME
directory formyuser
is/home/members/myuser
. - Everyone belongs to the
members
group, the GID of which is 5000.
/etc/pam-mysql.conf
users.host = localhost
users.database = mydatabase
users.db_user = root
users.db_passwd = root_password
users.table = users
users.user_column = username
users.password_column = password
users.password_crypt = 1
/etc/nsswitch.conf
passwd: compat mysql
group: compat
shadow: compat mysql
What I tried :
- Setting a password (SHA512 hash,
$6$...
) in the DB formyuser
. - Deleting the password first (
passwd -d
) : tells memyuser
cannot be found in/etc/passwd
. Same happens if I try to lock the account or force its password expiration. - Calling
passwd
from the root account, no changes. - Logging from a terminal on the machine (instead of
su
) : I get the authentication token manipulation error, and get sent back to the login prompt. Same when I try to connect with SSH (plus the password expiration warnings which are logical).
To me, it seems like the user management part (account/session ?) is correctly linked to MySQL, yet the password management part seems to rely partially on /etc/passwd
. Have I missed something in the configuration?
Best Answer
Okay, I found the solution for that one. First : I would strongly recommend anyone getting into this to study PAM modules basics. This really helps understanding the whole thing. Now, let's have a look at my
common-*
files. They all have the same structure :Now, after reading more about PAM modules, this looks ridiculous to me. Here is some documentation about the PAM control flags :
Basically, my configuration makes PAM behaves in the following way : the UNIX authentication through
/etc/passwd
and/etc/shadow
must succeed in all cases. The MySQL lookup will also be performed, yet if the UNIX authentication failed, its result won't be taken into account.With this setup, the MySQL authentication mechanism is rendered useless in all situations. A proper configuration would be :
Which tells : The MySQL authentication mechanism is sufficient. If it succeeds, no further mechanisms are to be tested. If it fails however, the UNIX authentication must succeed. Since I'll have more MySQL users than UNIX users, it is more logical to authenticate against MySQL first.
It is also important to note the difference in the roles of PAM and NSS. With my setup, the authentication was perfectly functional since the NSS configuration was correct. NSS handles basic UNIX authentication, but not account/session management, nor service-specific (SSH/FTP/...) connections. All these are handled by PAM. This separation is the reason why it was possible for
root
to connect as a MySQL user : the NSS did find the entry, and sinceroot
does not need to authenticate against anything to take a user's identity, PAM modules were never called.