Making a program always run as root in OS X

4

3

I'm trying to make a C program run always as root, no matter who is calling it. Basically, I want it to invoke "mkdir /test" as an example. So I created the C program as follows:

#include <stdio.h>

int main()
{
system("mkdir /test");
printf("bye...\n");
return 0;
}

Now, I just compiled it: gcc test.c -o test

And now I tried to set the permissions:

chmod +s test

However running it as a normal user, I get a permission denied error. So, it executes the file but not with root's permissions. I also tried setting the permissions as:

chmod a+s test
chmod o+s test

But I always get the same problem.

Anyone can help me with this? By the way, the file test.c is being created by root and it's also being compiled as root.

bash-3.2# ls -al | grep test 
-rwxr-xr-x   1 root   staff       8796  5 Ago 19:07 test
bash-3.2# chmod +s test
bash-3.2# ls -al | grep test 
-rwsr-sr-x   1 root   staff       8796  5 Ago 19:07 test
bash-3.2# whoami
root
bash-3.2#

Thanks in advance! Cheerz!

yaroze

Posted 2012-08-05T18:05:04.410

Reputation: 63

Answers

10

There are two things to know here:

  • The sticky bit, which could be used on files and directories, but won't do what you want. From sticky(8): The sticky bit has no effect on executable files.

  • The setuid flag, which would allow a program to be run with its owners permissions. What seems to be a restriction in OS X, and apparently is not documented, is that the setuid bit on an executable has an effect only if the executable is in a directory that is owned by root (and not open for writing by others), etc, up to the root directory. Otherwise it is ignored for security reasons though.

Anyway, you can modify the sudoers file in such a way that it won't require a password for one command. Remember that you have to use visudo to edit it. If you manage to get the syntax wrong while editing the file, you won't be able to run sudo at all anymore.

sudo visudo

Then, press I, and at the bottom insert:

username    ALL= NOPASSWD: /path/to/command

Here, you obviously need to change the username for the user who is supposed to run the command without having to type a password. Also, change the path to your executable. Note that at this point, the executable can be owned by root and have execute permissions only for root as well.

Press Esc, then write :wq, then Enter.

Now, the user username can run the command with sudo /path/to/command and doesn't need to enter a password to do that.

slhck

Posted 2012-08-05T18:05:04.410

Reputation: 182 472

4The setuid bit is not ignored on OS X, how do you think a program like the sudo you mention would be able to work if not thanks to it being setuid root? What seems to be a restriction in OS X, and apparently is not documented, is that the setuid bit on an executable has an effect only if the executable is in a directory that is owned by root (and not open for writing by others), etc, up to the root directory. – tml – 2015-05-22T12:53:33.930

The sticky bit has nothing to do with setuid, which is why I thought it strange when the asker brought it up. – Ignacio Vazquez-Abrams – 2012-08-05T18:27:53.007

Cool! I didn't know that. Yes it seems way safer that way. – yaroze – 2012-08-05T18:29:02.113

@IgnacioVazquez-Abrams, you're completely right. Thanks for pointing that out. I already edited the question. – yaroze – 2012-08-05T18:30:58.443

If the program requires root permissions to function, you can always check your current user ID, and if non-zero, try to run sudo $0 (or equivalent). If that works, great; otherwise print an error message and exit. Since this isn't exactly expected behavior, you shouldn't do this for something you want to distribute. – Daniel Beck – 2012-08-05T20:56:39.727

0

I'm working under Linux, but since OSX is just BSD I assume it follows the same (general) rules as any other Unix.

Within your program, you need to call setuid (UID), which returns 0 on success, to make it run as that UID and you want to run as root. Root's UID is probably 0, but if you're especially paranoid, use getpwnam (const char *szUserName) to get the UID.

So, something like this:

#include <stdio.h> // needed for printf

#include <sys/types.h>
#include <pwd.h>
#include <unistd.h>

void change_to_user (const char *szUserName)
{
  struct passwd *pw;

  pw = getpwnam(szUserName);
  if (pw != NULL)
  {
    uid_t uid = pw->pw_uid; // we now have the UID of the username

    printf ("UID of user %s is %d\n", szUserName, (int)uid);
    if (setuid (uid) != 0)
    {
      perror ("setuid");
    }
    else
    {
      // this will fail if you try to change to root without the SUID
      // bit set.  This executive needs to be owned by root (probably
      // group owned by root as well), and set the SUID bit with:
      //   suid a+s {executable}
      printf ("UID is now %d\n", (int)uid);
    }
  }
  else
  {
    perror ("getpwnam");
  }
}


int main (int argc, char **argv)
{
  int iIter;

  if (argc == 1)
  {
    printf ("Give me a user name\n");
    return 1;
  }

  for (iIter = 1 ; iIter < argc ; iIter++)
  {
    change_to_user (argv[iIter]);
  }
  return 0;
}

You can setuid to ANY username, so maybe you're running a cgi script, and you want to run as your username on your own account instead of "www-data" or whatever - you can use it there. I do this, which is why I can answer your question in detail.

Richard Wicks

Posted 2012-08-05T18:05:04.410

Reputation: 9