Solution with PAM, policy configured in sudoers
Create /usr/local/bin/use-own-password with the right ownership and permissions. The following shell code will do it:
sudo sh -e -c 'cat > /usr/local/bin/use-own-password << "EOF"#!/bin/sh -e# From https://superuser.com/a/1869100/432690case "$1" in no-op) exit 0 ;; verify) IFS= read -r password || true user="${password%%:*}" [ "$user" = "$password" ] || [ "$user" = "" ] && exit 1 password="${password#*:}" printf "%s\n" "$password" | 2>/dev/null sudo -u "$user" \ SUDO_ASKPASS=/usr/local/bin/use-own-password sudo -S -u "$PAM_USER" \ /usr/local/bin/use-own-password no-op ;;esacEOFchmod 755 /usr/local/bin/use-own-password'Yes, the file shall be executable for everyone. The script by itself is totally harmless. Its ability to log in one user as another comes from its interaction with PAM, when it is run from the inside of PAM.
Next sudoedit /etc/pam.d/sddm and place the following lines before any existing auth … line:
# Allowing sudoers, see https://superuser.com/a/1869100/432690auth sufficient pam_exec.so expose_authtok quiet /usr/local/bin/use-own-password verifyThen run sudo visudo and configure sudoers:
The solution will not work with
requiretty. When in doubt, useDefaults !requiretty.The solution nests
sudos. The outersudowill be run by root to run the inner sudo as the user whose credentials you want to use. It's crucial that the outersudodoes not ask for password. Fortunately this is the case: root can usesudofreely and I don't think there is even a way to break this.If and only if userA is a sudoer allowed to run
/usr/local/bin/use-own-password no-opas userB then our script will authorize logging into sddm as userB with userA's credentials supplied asuserA:passwordAin the password field. Now it's your job to set up sudoers, so only right users can run the script as right users. Basic lines in thesudoersfile will be like:userA ALL=(userB) PASSWD: /usr/local/bin/use-own-password no-opUsing
NOPASSWDfor this will allow anyone to log into sddm as userB by just claiming they are userA (and using any password). The point is you are not yet logged in, in particular not logged in as userA and there is no mean to verify you are userA except userA's password. Sddm will just passuserA:whateverfrom the password field to our script and in case ofNOPASSWDthis will always succeed.Keep in mind that the last match is used. E.g. you may want to use the above example line for
use-own-passwordand genericuserA ALL=(userB) NOPASSWD: ALLfor everything else; then you shall place the example line after the generic line, otherwiseNOPASSWDfrom the generic line will apply to sddm. I think you shall place all lines allowinguse-own-passwordat the very end ofsudoers(even after@includedirand so).Also keep in mind you don't need specific lines mentioning
use-own-passwordfor the solution to work. A generic lineuserC ALL=(userD) ALLwill allow userC to log into sddm as userD. If you want to prevent specifically this then you need to turn the lastALLinto!/usr/local/bin/use-own-password.Note there are options (
rootpw,targetpw,runaspw) that makesudoask for password other than the one of invoking user. They will interfere, if used.
If you configure everything correctly, you will be able to log into sddm as userB by choosing userB as the user and supplying userA:passwordA as the password. The colon is verbatim, it is a separator.
How it works
Our use-own-password has two modes of operation, they are chosen by the first argument.
First PAM runs the script in the verify mode, supplying the typed password (hopefully userA:passwordA) via stdin. The script extracts the authenticating user's name and their actual password from the password string.
Then the script uses sudo to run another sudo as the authenticating user, just to run another instance of itself as the target user. The other instance is in the no-op mode, where it is equivalent to true, its only purpose is to check if the authenticating user is allowed (by sudoers) to run the script as the target user: the script will succeed if so, sudo will fail in the first place if not.
The inner sudo reads password from its stdin. It is the password extracted from userA:passwordA.
Notes
- A colon in authenticating user's password will not break things. Only the first colon in
userA:passwordAis a separator. Usernames cannot contain:, so there is no ambiguity which colon is the separator. - A typed password with a colon will always be interpreted as
userX:passwordXfirst. It may happen that the target user's password contains:; this will not break things because if our script fails then/etc/pam.d/sddmwill continue as if nothing happened and it will eventually log the target user in (if the whole password is indeed right). - It may happen that somebody's password is exactly
userA:passwordA, wherepasswordAis userA's password and this user is a sudoer with access to our solution. If so, then the original user, while supplying their password, will inadvertently log in using our method and userA's credentials. In many cases there will be no difference, as he or she would log in anyway. Only if/etc/pam.d/sddmwould normally disallow the original user or do something extra (e.g. require two-factor authentication) then the user will notice. - It's possible to use this solution along with the one that uses a master password. Simply there will be two extra entries in
/etc/pam.d/sddminstead of one. This means in the context of your question you, as the admin, can have granular control over other users (throughsudoers), while using a convenient master password for your own purpose. - To revert:
- remove our line from
/etc/pam.d/sddm; rm /usr/local/bin/use-own-password- clean up
/etc/sudoers.
- remove our line from