Making a program always run as root in OS X

Solution 1:

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.

Solution 2:

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.