, 2 min read

Changing euid (effective user ID)

Sometimes you have to create applications in an environment where you are not able to su to root, or use sudo. Nevertheless you are working with two or more user IDs to make your application work. For example, your business application is running under user ID 1555 (let's call this user u1555), while your web-server is running under user ID 1000 (let's call this user u1000). You want to switch from u1000 to u1555 without explicitly providing the password of u1555.

One simple solution is to use ssh. You populate .ssh/authorized_keys of u1555 with the public key of u1000. As user u1000 you run

ssh u1555@localhost

This will not only change the effective user ID but also the real user ID.

Another solution is depicted below, not employing ssh.

The following simple C program, called swtt, allows you to switch to user ID 1555 and run a single command under this new effective user ID.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

int main (int argc, char *argv[]) {
        uid_t uid =getuid();
        if (uid != 1000) return 0;

        if (argc <= 1) return 0;

        if (setuid(1555) < 0) {
                printf("%s: cannot change euid - %s\n",
                        argv[0], strerror(errno));
                return 1;
        }

        execv(argv[1],argv+1);

        return 0;
}

Above program assumes that your source is uid=1000, and that you want to switch to euid=1555.

Compile as user u1555 using

cc -Wall swtt.c -o swtt

change permissions, still being user u1555, i.e., let others, who execute the program, get the user ID of the calling program with

chmod u+s swtt

and run as user u1000 with

swtt /usr/bin/id

This will show you that the effective user ID (euid) has been changed.

It is now easy to enhance above program and check the arguments for execl() before execution. For example you can use

if (strcmp(argv[1],"/bin/kill") != 0) return 1;

in lineĀ 18. So you can specifically limit the execution of swtt to a restricted set of commands, i.e., a tailored sudo.

There is one stumbling block with this program, however. If you call bash from swtt it won't work, i.e., it won't change the effective user ID -- bash ignores the effective user ID when called the first time. It will work with ksh and zsh.

It is tempting to use system() instead of execl(). Below are some remarks from the manual page for system():

Do not use system() from a program with set-user-ID or set-group-ID privileges, because strange values for some environment variables might be used to subvert system integrity. Use the exec(3) family of functions instead, but not execlp(3) or execvp(3). system() will not, in fact, work properly from programs with set-user-ID or set-group-ID privileges on systems on which /bin/sh is bash version 2, since bash 2 drops privileges on startup. (Debian uses a modified bash which does not do this when invoked as sh.)