But before continuing let me clarify that this is a bit of a misnomer. Namely, the goal is to detect whether current environment is a consequence of user ID switching after login, but since this is almost exclusively done using su command, then I think I can put this title. There is also one more "problem". Namely, all user IDs currently having running processes descended from user id 0. But, we are not going so far with philosophy. :)
I started by thinking/hoping that id command could identify originating user, i.e. real user. But that was not possible since the distinction between real and effective user ids is preserved only via setuid flag on files. So, another approach has to be used. There are three possibilities, each one with its own advantages and shortcomings.
Using su without changing environment
This is achieved when you start su command without minus (-) parameter. In that case it only switches user but doesn't change environment. Looking into LOGNAME, USER and/or USERNAME variable you can determine original (i.e. invoking) user. The problem with this approach is that it is not good to transfer environment variables to a new user ID, i.e. it is safer to reset them.
Proc filesystem
It turns out that you can inspect file /proc/self/loginuid. You can test if the content of that file is the same as the result of 'id -u' command. If not, then the user has been switched. Note that it is not certain that command su itself was executed, but it is certain that some similar mechanism has been invoked. Additionally, you can not know how many time used ID has been switched because you have access only to the first and last ones.
Analyzing processes
This one is based on the fact that su, if executed, will be parent or some ancestor of the current process. The following function returns true if su is executed, or false if it isn't:
function checksu {The function is simple actually. It takes PPID of current shell executing script and than iteratively queries for the parrent of that process.
pid=$PPID # Grab the PPID of the current process
while true
do
if [ "$pid" -eq 1 ]; then
return 1 # We reached init process, return false
fi
cmd=`ps --pid $pid --no-headers -o cmd`
if [ "$cmd" != "${cmd#'su '}" -o "$cmd" != "${cmd#'/bin/su '}" ]; then
return 0 # Found it, return true
fi
pid=`ps --pid $pid --no-headers -o ppid`
done
}
If it reaches PID number 1, then it reached init (or systemd) which means it didn't find su. Otherwise it stumbles upon su command at one point (the command is extracted in line that starts with cmd=).
This function is far from perfect. The reason is that it is sensitive to the form of the su command name/invocation and thus it can be easily fooled (the second if checks if the command is or isn't su). The other problem with this function is repeated invocation of external commands (ps) that makes it inefficient even though OS itself could (and should) optimize that with caching. But it could be rewritten so that it takes ps snapshot and then processes that output.
No comments:
Post a Comment