I am testing PAM scenarios on RedHat 7.5
The pam module pam_succeed_if.so looks like the most basic level of conditional testing that PAM has to offer, and it is not meeting my needs.
You can only create tests on the user, uid, gid, shell, home, ruser, rhost, tty, and service fields.
In my situation, I do want to test based off the 'rhost' field, however after putting the module into debug, I saw that the rhost field was not set.
My goal is to only run a PAM module in /etc/pam.d/sudo if the user is logged in locally to the machine. If we can detect that the user is logged in through SSH, then I want to skip the module. I had actually come up with 3 different ideas that I thought would work, but all ended up failing.
I'll share a couple of the solutions that ended up failing
Conditional entry using pam_exec.so
I wanted to add the following pam entry on top of the pam module that I want to conditionally skip:
auth [success=ok default=1] pam_exec.so /etc/security/deny-ssh-user.sh
The contents of /etc/security/deny-ssh-user.sh
#!/bin/bash
# Returns 1 if the user is logged in through SSH
# Returns 0 if the user is not logged in through SSH
SSH_SESSION=false
if [ -n "${SSH_CLIENT}" ] || [ -n "${SSH_TTY}" ] || [ -n "${SSH_CONNECTION}" ]; then
SSH_SESSION=true
else
case $(ps -o comm= -p $PPID) in
sshd|*/sshd) SSH_SESSION=true;;
esac
fi
if "${SSH_SESSION}"; then
exit 1
else
exit 0
fi
I've gone through the source code for pam_exec.so at https://github.com/linux-pam/linux-pam/blob/master/modules/pam_exec/pam_exec.c and astonishingly, it will ALWAYS return PAM_SUCCESS, regardless of the exit code of the script. And I can't get the script to cause the pam_exec module to return PAM_SERVICE_ERR, PAM_SYSTEM_ERR, or PAM_IGNORE.
Conditional entry using pam_access.so
Again, I add the following pam entry on top of the pam module that I want to conditionally skip
auth [success=ok perm_denied=1] pam_access.so accessfile=/etc/security/ssh-sudo-access.conf noaudit
The contents of /etc/security/ssh-sudo-access.conf
+:ALL:localhost 127.0.0.1
-:ALL:ALL
Wow, super clean right? It will return success if you are logged in locally, and deny everything else. Well no. It turns out when the pam_access.so module is put into debug, it has no knowledge of remote hosts, only the terminal that is being used. So the pam_access can't actually block access by remote hosts.
It has been an infuriating day of literally reading source code just to find out what black magic I have to cast just so I can skip a PAM module.
Best Answer
Here is a solution that works for me. My
/etc/pam.d/sudo
:And
/tmp/test-pam
:I get this behavior:
The first line added to the default
pam.d/sudo
callspam_exec
and, if it succeeds, skips the next entry. The second line just denies access unconditionally.In
/tmp/test-pam
I calllast
to get the IP address associated with the TTY pam was invoked from.${PAM_TTY#/dev/}
removes/dev/
from the front of the value, becauselast
doesn't recognize the full device path. The-i
flag makeslast
show either the IP address or the placeholder0.0.0.0
if there is no IP address; by default it shows an info string which is much harder to check. This is also why I usedlast
instead ofwho
orw
; those don't have a similar option. The-p now
option isn't strictly necessary, as we'll seeawk
is only checking the first line of output, but it restrictslast
to show only users who are presently logged in.The
awk
command just checks the first line, and if the third field isn't0.0.0.0
it exits with an error. Since this is the last command in/tmp/test-pam
, awk's exit code becomes the exit code for the script.On my system, none of the tests you were trying in your
deny-ssh-user.sh
would work. If you putenv > /tmp/test-pam.log
at the top of your script, you'll see that the environment has been stripped, so none of your SSH_FOO variables will be set. And $PPID could point to any number of processes. For example, runperl -e 'system("sudo cat /etc/passwd")'
and see that $PPID refers to theperl
process.This is Arch Linux, kernel
4.16.11-1-ARCH
, in case it matters. I don't think it should, though.