Запустить systemctl restart ___ из корневой программы setuid, не запрашивая пароль root?

Я разрабатываю сетевое устройство, работающее под управлением Linux (CentOS 8), и для некоторой действительно базовой конфигурации на консоли VGA мы создали заблокированную учетную запись пользователя. Мы предоставили пару suid root-программ, которые выполняют такие действия, как установка IP-адреса и настройка NTP. (Все остальные настройки выполняются через Apache и некоторые веб-страницы.)

Несколько вещей, которые нам нужно сделать из этих программ: перезапустить NetworkManager.service, перезапустить chronyd.service, отключить питание и перезагрузить компьютер. И отключение питания, и перезагрузка работают отлично, но перезапуск служб не работает. Вместо этого мы получаем это:

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

Я предполагаю, что systemctl пытается быть умным в отношении аутентификации и может определить, что пользователь, от которого он был запущен, не является пользователем root, даже если он имеет привилегии root. Можно ли каким-то образом изменить идентификатор пользователя, чтобы systemctl считал, что это root, который его вызывает?

Для справки, я приложил полный исходный код к одной из корневых программ suid ниже.

#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;
}

person Timothy Miller    schedule 05.12.2019    source источник


Ответы (1)


Я понял. Мне пришлось добавить следующий код, чтобы заставить его изменить «реальный идентификатор пользователя», чтобы он совпадал с «эффективным идентификатором пользователя».

uid_t uid, euid, suid;
getresuid(&uid, &euid, &suid);
setresuid(euid, euid, suid);
person Timothy Miller    schedule 05.12.2019