Thursday, January 26, 2012

How to detect your script is started using su...

I wrote a script that had a problem when started via su command. Actually, this is a script within /etc/profile.d so it is executed when new login shell is executed. I'll write about that problem in another post, but here I'll concetrate on how to detect su command.

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 {
    pid=$PPID # Grab the PPID of the current process

    while true
        if [ "$pid" -eq 1 ]; then
            return 1    # We reached init process, return false

        cmd=`ps --pid $pid --no-headers -o cmd`
        if [ "$cmd" != "${cmd#'su '}" -o "$cmd" != "${cmd#'/bin/su '}" ]; then
            return 0    # Found it, return true
        pid=`ps --pid $pid --no-headers -o ppid`
The function is simple actually.  It takes PPID of current shell executing script and than iteratively queries for the parrent of that process.
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:

About Me

scientist, consultant, security specialist, networking guy, system administrator, philosopher ;)

Blog Archive