5

I have a script, let's call it on-event.sh, that's going to be run at specific times, such as when a particular application launches or quits. The script itself will run as the current user (not root) since I'll be using BetterTouchTool to execute it.

However, on-event.sh needs to somehow trigger the execution of another script that must run as root, which I'll call run-as-root.sh. run-as-root.sh will execute some commands to load or unload specific third party launch daemons (not my own), hence the need to run as root. Since it'll all be happening in the background, it needs to happen without requiring user intervention, so it can't use sudo or AppleScript's with administrator privileges. Because of the obvious security implications, I only want it to be possible to execute run-as-root.sh as root and not any arbitrary script or terminal command.

One option I'm already aware of is I could create a launch daemon that opens a socket, waits for a connection, and then executes run-as-root.sh, and have on-event.sh connect to that socket. But that's a more heavy weight solution and I'm hoping for an easier way to do this.

How can I set it up so this is possible?

8
  • You can't have a "user" script run and in the process launch a secondary script with root privileges; that would have massive security implications. If you can make this work by listening for a connection on a socket, then that would be the path you need to take.
    – Allan
    Commented Apr 30 at 15:07
  • @Allan I do understand the restrictions around running scripts as root. I'm hoping there's some kind of existing mechanism in macOS I can use (or perhaps even a third party app) that can accomplish this more easily, similar to the method in Windows of setting up a scheduled task that will run as an administrator, and then using a shell command to execute that task on demand.
    – Bri Bri
    Commented Apr 30 at 15:10
  • There's some confusion here then. Your title asks how to have a script run another script with but with elevated privileges (you can't) and now in comments you're describing what launchd or cron do out of the box. Regardless, when either of these services kick off a script, it runs within the context it was defined; this also applies to Windows. It's not going to allow you to escalate priviliges like that at runtime.
    – Allan
    Commented Apr 30 at 15:21
  • You could use sudo to elevate privileges by adding an appropriate line to /etc/sudoers, but this comes with its own set of risks. Can you share some details about what run-as-root.sh needs to do, there might be other ways to accomplish this.
    – nohillside
    Commented Apr 30 at 15:37
  • 1
    Sorry, but can you explain why you need to load and unload these other daemons? I'm suspicious that this is an X-Y question -- you're asking for how to achieve a (very difficult) particular method as part of some larger scheme.
    – benwiggy
    Commented Apr 30 at 16:37

3 Answers 3

10

You can use sudo to allow a user to run specific commands as root, without the need for a password.

To allow user bribri to run launchctl, run sudo visudo -f /etc/sudoers.d/launchctl and add

bribri ALL = (root) NOPASSWD: /bin/launchctl

then save and exit. Afterwards, the user can run

sudo launchctl ...

without getting prompted for a password.

The caveat here is that this gives the user the rights to start/stop/load/unload any LaunchAgents/Daemons. To avoid this, a shell script could be used to wrap the launchctl call, but this brings a bunch of security risks. To at least reduce them

  • put the script into a dedicated directory,
  • make sure both the directory and the script are owned by root and chmoded to 0700,
  • keep the script as short as possible (include only things which must be run as root),
  • use absolute paths for all commands called in the script,
  • avoid to rely on any environment variables.
9
  • This could be used for granting a user permission to run a specific script as root, correct? So instead of using /bin/launchctl, it could be /path/to/run-as-root.sh, correct?
    – Bri Bri
    Commented Apr 30 at 22:14
  • 2
    @BriBri Technically yes, but running shell scripts via sudoers has some security risks. To reduce them, keep the script as short as possible, avoid the use of environment variables inherited from the user and always use full paths to commands within the script.
    – nohillside
    Commented May 1 at 4:02
  • Not sure if this works on MacOS, but on Linux entries in sudoers file can include a checksum (SHA256, for example) of the command (or script). If the user modifies the script, the checksum won't match and the execution of the substituted script won't happen. This greatly improves sudo security. For example: bribri ALL = (root) NOPASSWD: sha256:aabb1122... /bin/launchctl. If this works on MacOS you can add it to your answer.
    – VL-80
    Commented May 1 at 20:16
  • My plan is to just have the script be owned and writable by root only. The script doesn't so anything except call launchctl to load a specific launch daemon so I think this should be pretty safe.
    – Bri Bri
    Commented May 2 at 0:47
  • 1
    @BriBri if the script is in a location writable by others, nothing stops others from replacing it with something malicious.
    – nohillside
    Commented May 2 at 4:49
3

You can write a simple program which will run the run-as-root.sh script as root. For example, there is the C program shown below.

% cat run-as-root.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
    setuid(0);
    system("./run-as-root.sh");
    return 0;
}

I have command line developer tools installed, so I can enter the following to create the executable run-as-root, which will run as the root user.

% sudo rm -f run-as-root
% gcc run-as-root.c -o run-as-root
% sudo chown root run-as-root
% sudo chmod u+s run-as-root   

Below are sample on-event.sh and run-as-root.sh scripts. Also the file permissions and owners are shown.

% cat on-event.sh
echo "I am on-event.sh and my user id is $(id -u)"
./run-as-root
%
% cat run-as-root.sh
echo "I am run-as-root.sh and my user id is $(id -u)"
%
% chmod +x on-event.sh run-as-root.sh
% ls -l run-as-root on-event.sh run-as-root.sh
-rwxr-xr-x@ 1 davidanderson  staff     14 Apr 30 13:04 on-event.sh
-rwsr-xr-x  1 root           staff  49472 Apr 30 13:24 run-as-root
-rwxr-xr-x@ 1 davidanderson  staff     30 Apr 30 13:26 run-as-root.sh

Running the on-event script results in the run-as-root.sh script executing as root user. (The root user has user id of 0.)

% ./on-event.sh
I am on-event.sh and my user id is 501
I am run-as-root.sh and my user id is 0
3
  • 2
    This would let you run ANY script named run-as-root.sh with root privileges, not just the particular script that's meant to be run as root.
    – user24811
    Commented May 1 at 15:07
  • @user24811: Yes, your comment is correct. I suppose the run-as-root.c program could include a call to the stat function to check if the ./run-as-root.sh file is owned by either the OP or the root. This would prevent running ANY script named run-as-root.sh. However, my answer is just trying to show an executable created from a C program could be used to solve the OP's problem. Commented May 1 at 16:26
  • Answers posted here at Ask Different are not supposed to include writing C programs. Therefore, if someone wants to know how to make this program more secure, the person is going to have to ask such question at some Stack Exchange site that is not Ask Different. Commented May 1 at 16:36
0

If your issue is file permissions; macOS, like all its unixen siblings, supports the "setuid" and "setgid" bits on files. (And the sticky bit on directories via the same model, but that is out of scope for this answer.) Per Wikipedia's article on setuid:

The Unix and Linux access rights flags setuid and setgid (short for set user identity and set group identity) allow users to run an executable with the file system permissions of the executable's owner or group respectively and to change behaviour in directories. They are often used to allow users on a computer system to run programs with temporarily elevated privileges to perform a specific task. While the assumed user id or group id privileges provided are not always elevated, at a minimum they are specific.

A file can be chmoded 4755, and when run by any user, it will inherit the file system permissions of the user that owns the file. Note that if you chown a file, it drops any of the setuid/setgid/sticky bits, so chown first, chmod second.

If your issue is elevated execution authorization, nohillside's answer about setting up sudo appropriately is more relevant.

See also: A detailed explanation of setuid in a unix.se answer

4
  • A shell script SUID root would be a MASSIVE security problem, all you'd have to do is be able to alter the contents of the script and you could do anything you liked. I'm not sure that even works, the shell that's executing the script would be the one that needed to be SUID root,. Commented Apr 30 at 20:38
  • 1
    This is useful info unix.stackexchange.com/questions/364/… Commented Apr 30 at 20:52
  • 1
    Are you sure this still works on macOS? I couldn't get it to work with shell scripts.
    – nohillside
    Commented May 1 at 12:16
  • 2
    Setting a shell script SUID? I don't think it ever worked on macOS. Commented May 1 at 19:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .