Ввод пароля sudo через c - PullRequest
       8

Ввод пароля sudo через c

0 голосов
/ 02 сентября 2018

Я задавал этот вопрос раньше, но никто не дал прямого ответа. Я хотел знать, как я могу ввести пароль sudo через код c. Я пытаюсь написать скрипт, чтобы иметь возможность выполнить sudo bash и ввести требуемый пароль. Кроме того, я знаю риски жесткого кодирования паролей, но я не против в этом случае.

1 Ответ

0 голосов
/ 02 сентября 2018

Нет. Делать это таким образом - антипаттерн .

В зависимости от ситуации есть несколько вариантов на выбор:

  • Используйте gksudo (если установлена ​​переменная окружения DISPLAY) для графического приглашения.

  • Установите сценарии, которые должны выполняться в /usr/share/yourapp/scripts/ или /usr/lib/yourapp/scripts/, и соответствующую конфигурацию sudo, которая позволяет запускать их с помощью sudo без ввода пароля в /etc/sudoers.d/yourapp (или /etc/sudoers в системах без /etc/sudoers.d/)

  • В начале вашей программы проверьте, если geteuid() == 0. Если нет, то заново выполните self с помощью gksudo / sudo, чтобы получить привилегии root.

    Для нормальной работы ваша программа должна использовать только привилегии реального пользователя, выполнившего программу. Чтобы иметь возможность повысить привилегии позже, корневые привилегии «сохраняются». Итак, изначально ваша программа откажется от привилегий, например,

        uid_t  uid = getuid();
        gid_t  gid = getgid();
        if (setresgid(gid, gid, 0) == -1 ||
            setresuid(uid, uid, 0) == -1) {
            /* Failed: no root privileges! */
        }
    

    Чтобы повысить привилегии, вы используете

        if (setresgid(gid, 0, 0) == -1 ||
            setresuid(uid, 0, 0) == -1) {
            /* Failed: no root privileges! */ 
        }
    

    , который изменяет только действующий идентификатор на root (как это делают двоичные файлы setuid), или

        if (setresgid(0, 0, 0) == -1 ||
            setresuid(0, 0, 0) == -1) {
            /* Failed: no root privileges! */ 
        }
    

    , который изменяет как реальную, так и эффективную идентичность на root.

    Часто привилегии повышаются только за разветвление привилегированного дочернего раба, после чего основная программа полностью отбрасывает привилегии, используя

        if (setresgid(gid, gid, gid) == -1 ||
            setresuid(uid, uid, uid) == -1) {
            /* Failed. */
        }
    

    сохранение только пары сокетов или каналов между родителем и дочерним элементом; ребенок может затем раскошелиться и выполнить новые процессы. (Если используется сокет домена Unix, родитель может даже отправлять новые дескрипторы, которые будут использоваться для стандартных потоков новых процессов через вспомогательные сообщения.)

  • Используйте возможности файловой системы, чтобы дать вашей программе необходимые возможности, не повышая при этом всех ее привилегий.

    Например, sudo setcap CAP_NET_BIND_SERVICE=pe /usr/bin/yourapp предоставляет /usr/bin/yourapp возможность CAP_NET_BIND_SERVICE (разрешено и эффективно; не наследуется), которая позволяет вашей программе связываться с любыми неиспользуемыми портами TCP / IP и UDP / IP, включая 1-1024. См. man 7 возможностей для подробного описания возможностей.

  • Используйте доверенный бинарный файл (программу), который будет действовать как sudo для вас. Если вы установите это, например, на /usr/lib/yourapp/execute, вы можете добавить конфигурацию sudo, необходимую для ее выполнения без ввода пароля. В качестве альтернативы, вы можете сделать его setuid root или предоставить ему необходимые возможности через возможности файловой системы.

    Чтобы другие программы не могли использовать этот помощник, вы должны убедиться, что он выполняется только вашей программой. Один из способов убедиться в том, что ваша программа создаст пару сокетов домена Unix, оставив один конец открытым в вашей программе, а другой конец для помощника, например. дескриптор 3. Перед тем, как что-либо предпринять, помощник проверяет, что еще нечего получить (чтобы избежать атак по принципу «заполнения канала»), и записывает один байт для родителя. Родитель отвечает одним байтом, но со своими учетными данными в вспомогательном сообщении . Учетные данные содержат идентификатор процесса родителя. (Не используйте просто getppid(), потому что это разрешает определенные атаки; этот подход сокетов проверяет, что родитель еще жив, когда мы делаем проверку.) Наконец, используйте readlink(), чтобы прочитать псевдо /proc/PID/exe -symlink, где PID - это идентификатор родительского процесса из служебного сообщения с учетными данными. На этом этапе помощник должен отправить байт и снова получить байт с учетными данными в качестве вспомогательного сообщения, чтобы гарантировать, что родительский процесс остается тем же.

    Процесс проверки является сложным, но необходимым, чтобы не упростить использование корневых привилегий путем неправильного использования помощника. Для другого подхода, чтобы сделать именно это, посмотрите на Apache suEXEC , помощник, используемый Apache для выполнения программ CGI с определенными привилегиями пользователя.


Допустим, вы совершенно не заинтересованы в разумных действиях и настаиваете на использовании паролей. Fine; все, что я прошу, это чтобы вы не публиковали такой код или, по крайней мере, не предупреждали пользователей о том, что он совершенно небезопасен.

Это не просто грубый взлом: он самоубийственный, подобный введению пароля на ваш веб-сайт в вашей подписи электронной почты, потому что вы отправляете почту только тем друзьям, которые должны иметь доступ администратора к вашему сайту в первую очередь. место. Итак, пулемет, с триггером для волос, без безопасности, и вооруженный картечью, покрытой тетродотоксином. С красивой этикеткой с большими читаемыми детьми буквами с надписью «Пожалуйста, играй со мной! Я в безопасности!», Хранящихся в детской спальне.

Самое простое, что нужно сделать - это выполнить sudo с флагом -S, что заставит его прочитать пароль из стандартного ввода. Например, example.c :

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

int main(void)
{
    FILE  *cmd;
    int    status;

    cmd = popen("/usr/bin/sudo -S id -un 2>/dev/null", "w");
    if (!cmd) {
        fprintf(stderr, "Cannot run sudo: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    fprintf(cmd, "Password\n");
    fflush(cmd);

    status = pclose(cmd);
    if (WIFEXITED(status)) {
        if (WEXITSTATUS(status) == EXIT_SUCCESS)
            fprintf(stderr, "No errors.\n");
        else
            fprintf(stderr, "Command failed with exit status %d.\n", WEXITSTATUS(status));
    } else
    if (WIFSIGNALED(status))
        fprintf(stderr, "Command killed by signal %d.\n", WTERMSIG(status));
    else
        fprintf(stderr, "Command failed.\n");

    return EXIT_SUCCESS;
}

Канал команд открывается в режиме записи, поэтому мы можем записать в него пароль. Перенаправление 2>/dev/null скрывает запрос пароля.

Если пароль правильный, приведенный выше выводит то, что id -un выводит при запуске от имени пользователя root (т. Е. root) на стандартный вывод, а No errors. - на стандартную ошибку.

Если пароль неверный, sudo будет повторять пару раз (поэтому в течение нескольких секунд ничего не произойдет), тогда программа сообщит Command failed with exit status 1. о стандартной ошибке, потому что sudo делает это, когда пароль неверен.

...