Run "systemctl restart ___" from setuid root program without asking for root password?

343 Views Asked by At

I am developing for a network appliance that runs Linux (CentOS 8), and for some really basic config at the VGA console, we've set up a locked-down user account. We've provided a couple of suid root programs that do things like set up the IP address and configure NTP. (All other setup is done via Apache and some web pages.)

A few things we need to be able to do from these programs are restart NetworkManager.service, restart chronyd.service, poweroff, and reboot. Both poweroff and reboot work great, but restarting the services doesn't. Instead, we get this:

Restarting ntp service
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ====
Authentication is required to restart 'chronyd.service'.
Authenticating as: root
Password: 

I'm guessing that systemctl is trying to be smart about authentication and is able to detect that the user it was run from is not root, even though it has root privileges. Is there some way I can change the user ID so that systemctl things it's root that's calling it?

For reference, I've attached the full source to one of the suid root programs below.

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

void print_usage()
{
    printf("Usage: safeguard <option>\n");
    printf("Options:\n");
    printf("  poweroff\n");
    printf("  reboot\n");
    printf("  ntp       [restarts NTP service]\n");
    printf("  net       [restarts NetworkManager service]\n");
}

int main(int argc, char *argv[])
{
    if (argc < 2) {
        print_usage();
        return 0;
    }

    if (0==strcmp(argv[1], "poweroff")) {
        printf("Executing poweroff\n");
        system("poweroff");
        return 0;
    } else if (0==strcmp(argv[1], "reboot")) {
        printf("Executing reboot\n");
        system("reboot");
        return 0;
    } else if (0==strcmp(argv[1], "ntp")) {
        printf("Restarting ntp service\n");
        system("systemctl restart chronyd.service");
        return 0;
    } else if (0==strcmp(argv[1], "net")) {
        printf("Restarting NetworkManager service\n");
        system("systemctl restart NetworkManager.service");
        return 0;
    }

    print_usage();
    return 0;
}
1

There are 1 best solutions below

0
Timothy Miller On

I figured it out. I had to add the following code to get it to change the "real user id" to be the same as the "effective user id."

uid_t uid, euid, suid;
getresuid(&uid, &euid, &suid);
setresuid(euid, euid, suid);