Привилегии доступа к каналу для программы setuid

Я расширяю некоторое программное обеспечение (автор которого я не являюсь), работающее под GNU/Linux (Ubuntu 14.04) и состоящее из manager процесса и нескольких worker процессов. Менеджер может запустить воркера с помощью командной строки, которую я могу указать в файле конфигурации.

После запуска воркера менеджер связывается с ним с помощью пайпа. Из соображений безопасности мы решили позволить рабочим процессам работать под другим пользователем, а не под менеджером (назовем их manager-user и worker-user). Это достигается путем написания небольшого скрипта-оболочки, который переключает пользователя с su и запускает нового рабочего. После этого менеджер может общаться через канал с рабочим процессом. Этот подход работает уже много месяцев.

В качестве альтернативы su мы рассмотрели возможность использования бита setuid для запуска рабочих процессов. Итак, мы написали оболочку C, которую может вызвать менеджер для запуска рабочего процесса. Если мы настроим обертку так, чтобы она принадлежала manager-user, рабочий процесс запускается правильно (но, конечно, с неправильными привилегиями). Если мы настроим оболочку так, чтобы она принадлежала worker-user, и установим бит setuid, то рабочие процессы запустятся, но затем завершатся, потому что они не могут подключиться к менеджеру.

Итак, мой вопрос: как запуск исполняемого файла setuid влияет на разрешение каналов, созданных как родительским, так и дочерним процессом? Может быть рабочие процессы, запущенные через setuid-обертку, не имеют разрешения на открытие пайпов менеджера (или наоборот)? Если это так, как мы можем изменить эти разрешения?

У меня мало опыта использования setuid, поэтому приветствуется любая информация/объяснение.


person Giorgio    schedule 29.07.2015    source источник


Ответы (2)


Unix pipe может быть именованным или безымянным. Именованный канал реализован в виде файла, который имеет стандартные биты разрешений пользователя, группы и мира.

Безымянный канал также имеет разрешения, но они ограничены euid, egid и umask на момент создания канала.

Таким образом, если ваш worker-user является setuid для другого пользователя с другими групповыми разрешениями, если только egid не является общим с главным процессом, он не сможет использовать разрешения пользователя или группы для доступа к каналу, созданному родительским процессом.

Конечно, при определенных значениях umask мировые разрешения безымянного канала позволят процессам взаимодействовать через канал, но любой процесс сможет читать/записывать этот канал. Быть безымянным более безопасно, чем именованный канал, но предоставление доступа к миру любому каналу не является хорошей практикой безопасности.

Возможным решением для этого варианта использования (желания, чтобы два взаимодействующих процесса выполнялись под разными пользователями) было бы, чтобы процессы manager-user и worker-user находились в одной группе, а umask очищались от group-perm во время создания канала, чтобы оба процесса могли читать и записывать безымянный канал.

So, if

  1. manager-user принадлежит группе team,
  2. worker-user также принадлежит группе team,
  3. у обоих процессов сброшены биты группы umask (нет значений 1x .. 7x)
  4. до создания безымянного канала

Тогда manager-user должен иметь возможность писать в безымянный канал, а worker-user должен иметь возможность читать его (или наоборот, в зависимости от того, как используется канал), даже если они работают как отдельные пользователи.

Подробную информацию о битах разрешений см. на странице man в разделе chmod.

person aks    schedule 29.07.2015
comment
Единственное, что я хотел бы добавить, это то, что значение umask также применяется к именованным каналам, созданным процессами. Таким образом, если установлено значение 022 и канал не принадлежит рабочему процессу, рабочий процесс не сможет писать. - person Jason; 29.07.2015
comment
Да, это правда. С другой стороны, именованный канал может легче управляться процессом после его создания — с помощью chmoding, поэтому я не сосредоточился на этом сравнительно простом пути. - person aks; 29.07.2015
comment
Большое спасибо за подсказки! Я попробую их завтра. - person Giorgio; 30.07.2015

Используйте анонимный канал с помощью функции pipe(), например (пример взят из немецкая Википедия):

# check the link above for #includes and const definitons

int main(void) {
    int fd[2], n, i;
    pid_t pid;
    char line[MAX_CHARS];

    // Create the pipe
    if (pipe(fd) < 0)
        fprintf(stderr, "Failed to create pipe()");

    // Fork child
    if ((pid = fork()) > 0) {
        // Parent process

        close(fd[0]);
        fprintf(stdout, "Parent : ");
        fgets(line, MAX_CHARS, stdin);
        write(fd[1], line, strlen(line));

        if (waitpid(pid, NULL, 0) < 0)
            fprintf(stderr, "Error: waitpid()");
    }

    else {
        // Child process
        close(fd[1]);
        n = read(fd[0], line, MAX_CHARS);

        for (i = 0; i < n; i++)
            line[i] = toupper(line[i]);
        fprintf(stderr, "Child : ");

        write(STDOUT_FILENO, line, n);
    }
    exit(0);
}

Приведенная выше программа создает однонаправленный канал, в котором родительский процесс удерживает конец чтения, а дочерний процесс — конец записи.

person hek2mgl    schedule 29.07.2015
comment
Вместо линии все еще присутствует zeile (у этого комментария, вероятно, короткий срок годности). MAX_CHARS тоже нужно определить, и я думаю, вы оставили необходимые #includes в качестве упражнения для читателя. - person sjnarv; 29.07.2015
comment
Спасибо! Я должен был использовать sed! :) Вы можете найти #includes на странице Википедии, на которую я дал ссылку. Мне нравится простой пример, даже если это немецкая Википедия. - person hek2mgl; 29.07.2015