Який найбезпечніший спосіб програматичного запису у файл із root правами?


35

Величезному додатку потрібно за один конкретний час виконати невелику кількість записів у файл, який потребує кореневих прав. Це насправді не файл, а апаратний інтерфейс, який піддається Linux як файл.

Щоб уникнути надання кореневих привілеїв всій програмі, я написав сценарій bash, який виконує найважливіші завдання. Наприклад, наступний сценарій включить порт 17 апаратного інтерфейсу як вихід:

echo "17" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio17/direction

Однак, оскільки suidв моїй системі відключено для bash-скриптів, мені цікаво, який найкращий спосіб досягти цього.

  1. Скористайтеся деяким вирішенням, представленим тут

  2. Зателефонуйте за скриптом із sudoосновної програми та відредагуйте список судорів відповідно, щоб уникнути необхідності пароля під час виклику сценарію. Мені трохи незручно надавати привілеї судо echo.

  3. Просто напишіть програму C з fprintf, і встановіть її на suid root. Увімкніть рядки та назви файлів і переконайтесь, що лише root може редагувати їх. Або читайте рядки з текстового файлу, аналогічно переконуючись, що ніхто не може редагувати файл.

  4. Якесь інше рішення, яке мені не прийшло в голову і є більш безпечним чи простим, ніж представлене вище?


10
Чому ви просто не запускаєте програму з привілеями root, не відкриєте файл та не скидаєте привілеї? Ось так робить кожен веб-сервер або подібний для сокетів. Ефективно, ви не працюєте як корінь, а також не потрібний помічник.
Деймон

Відповіді:


33

Вам не потрібно надати sudoдоступ echo. Насправді це безглуздо, оскільки, наприклад sudo echo foo > bar, перенаправлення виконується як оригінальний користувач, а не як root.

Зателефонуйте до невеликого сценарію sudo, дозволивши NOPASSWD:доступ до ТОЛЬКО цього сценарію (та будь-яких інших подібних сценаріїв) користувачами, які потребують доступу до нього.

Це завжди найкращий / найбезпечніший спосіб використання sudo. Виділіть невелику кількість команд, для яких потрібні привілеї root, у власні окремі сценарії (и), і дозвольте користувачеві, якому не довіряють або частково довіряють, запускати цей сценарій як корінь.

sudoСценарій з невеликими можливостями або не повинен брати аргументи (або вводити) від користувача (тобто будь-які інші програми, які він викликає, повинні мати жорстко закодовані параметри та аргументи), або він повинен дуже обережно перевірити будь-які аргументи / введення, які він повинен прийняти від користувача.

Будьте параноїком у валідації - а не шукайте «відомих поганих» речей, щоб їх виключити, дозвольте лише «відомі добрі» речі і перервіть будь-яку невідповідність чи помилку чи щось навіть віддалено підозріле.

Перевірка повинна відбуватися якомога раніше в сценарії (бажано, перш ніж це робити щось інше як root).


Я справді повинен був згадати це, коли я вперше написав цю відповідь, але якщо ваш скрипт є сценарієм оболонки, він ОБОВ'ЯЗКОВО належним чином цитувати всі змінні. Будьте особливо обережні , щоб процитувати змінні , що містять вхід , переданому користувачем в будь-який спосіб, але не припускають деякі змінні є безпечними, взяв у лапки ЇХ ВСЕ .

Це включає в себе змінні оточення , потенційно контрольованих користувачем (наприклад "$PATH", "$HOME", і "$USER"т.д. І , звичайно , в тому числі "$QUERY_STRING"і "HTTP_USER_AGENT"так далі в сценарії CGI). Насправді просто цитуйте їх усіх. Якщо вам доведеться побудувати командний рядок з декількох аргументів, використовуйте масив для створення списку аргументів і цитуйте це - "${myarray[@]}".

Я досить часто говорив "цитувати їх усіх"? Запам'ятай це. Зроби це.


18
Ви забули згадати, що сам сценарій повинен належати root, з дозволами 500.
Wildcard

13
Принаймні видаліть дозволи на запис, на користь. Це було справді моє значення. Решта - лише загартовування.
Wildcard

10
Продумано написана програма C матиме меншу поверхню атаки, ніж сценарій оболонки.
користувач253751

7
@immibis, можливо. Але для написання та налагодження буде потрібно набагато більше часу, ніж сценарій оболонки. І вимагає наявності компілятора C (який заборонений на деяких виробничих серверах для зменшення ризику безпеки, ускладнюючи зловмисникам компіляцію подвигів). Крім того, сценарій оболонки IMO, написаний новачком на системний адміністратор середнього рівня або програміст, є менш ймовірним для експлуатації, ніж програма C, написана ким-небудь подібним майстерністю, особливо якщо вона має приймати та перевіряти дані, що надаються користувачем.
cas

3
@JonasWielicki - я думаю, що зараз ми добре і справді перебуваємо в царині думок, а не фактів. Ви можете зробити вагомі аргументи для оболонки або C (або perl, python, awk тощо), будучи більш-менш схильною до помилок, що використовуються. Коли, дійсно, це залежить головним чином від майстерності та уваги програміста до деталей (і втоми, поспіху, обережності тощо). Факт, однак, що мови нижчого рівня, як правило, вимагають писати більше коду, щоб досягти того, що можна зробити набагато менше рядків коду на мовах вищого рівня .... і кожен LoC - це ще одна можливість для помилка, яку слід зробити.
cas

16

Перевірте власника файлів gpio:

ls -l /sys/class/gpio/

Швидше за все, ви дізнаєтесь, що вони належать групі gpio:

-rwxrwx--- 1 root     gpio     4096 Mar  8 10:50 export
...

У такому випадку ви можете просто додати свого користувача до gpioгрупи, щоб надати доступ без sudo:

sudo usermod -aG gpio myusername

Вам потрібно буде вийти з системи та увійти знову після цього, щоб зміни набрали чинності.


Це не працює. Дійсно, власником групи є все /sys/class/gpio/gpio, але навіть після того, як додати себе до цієї групи, я все одно отримую "дозвіл відмовлений", коли намагаюся написати що-небудь там.
vsz

1
Проблема полягає в тому, що файли в /sys/class/gpio/насправді просто посилання на те, /sys/devices/platform/soc/<some temporary name>/gpioде і власник, і група є коренем.
vsz

4
@vsz Ви пробували chgrp gpio /sys/devices/platform/soc/*/gpio? Можливо, щось подібне можна було б помістити у файл запуску.
jpa

так, але це не так просто. Оскільки вони завжди генеруються по-іншому, мені довелося використовувати щось на кшталтchgrp gpio `readlink -f /sys/class/gpio/gpio18`/*
vs

7

Одне рішення для цього (використовується особливо на робочому столі Linux, але також застосовується в інших випадках) - використовувати D-Bus для активації невеликого сервісу, що працює як root та polkit, щоб зробити авторизацію. Це принципово те, для чого був розроблений polkit ; з вступної документації :

polkit надає API авторизації, призначений для використання пільговими програмами ("МЕХАНІЗМИ"), що пропонують обслуговування непривілейованих програм ("КЛІЄНТИ"). Дивіться сторінку посібника з polkit щодо архітектури системи та великої картини.

Замість того, щоб виконувати свою помічну програму, велика непривілейована програма надсилатиме запит у автобус. Ваш помічник може працювати або як демон, запущений під час завантаження системи, або, ще краще, бути активований у міру необхідності systemd . Потім цей помічник використовує polkit, щоб перевірити, що запит надходить з дозволеного місця. (Або в цьому випадку, якщо це відчувається як надмірність, ви можете використовувати інший жорстко закодований механізм аутентифікації / авторизації.)

Я знайшов хорошу основну статтю про спілкування через D-Bus , і, хоча я ще не перевіряв її, це, здається, є основним прикладом додавання polkit до суміші .

У такому підході нічого не потрібно відзначати встановленим.


5

Один із способів зробити це - створити встановлену кореневу програму, написану на C, яка виконує лише те, що потрібно, і нічого більше. У вашому випадку взагалі не потрібно дивитись на будь-які дані користувача.

#include <unistd.h>
#include <string.h>
#include <stdio.h>  // for perror(3)
// #include ...  more stuff for open(2)

static void write_str_to_file(const char*fn, const char*str) {
    int fd = open(fn, O_WRONLY)
    if (-1 == fd) {
        perror("opening device file");  // make this a CPP macro instead of function so you can use string concat to get the filename into the error msg
        exit(1);
    }
    int err = write(fd, str, strlen(str));
    // ... error check
    err = close(fd);
    // ... error check
}

int main(int argc, char**argv) {
    write_string_to_file("/sys/class/gpio/export", "17");
    write_string_to_file("/sys/class/gpio/gpio17/direction", "out");
    return 0;
}

Немає способу підривати це через змінні середовища чи що-небудь, тому що все, що він робить, - це зробити пару системних викликів.

Нижня сторона: ви повинні перевірити значення повернення кожного системного дзвінка.

Вгору: перевірка помилок дійсно проста: якщо якась помилка взагалі є, просто perrorі виправдайте: вихід з ненульовим статусом. Якщо є помилка, перевірте, за допомогою strace. Вам не потрібна сама ця програма, щоб надсилати дійсно приємні повідомлення про помилки.


2
Я міг би спокуситись відкрити обидва файли, перш ніж написати щось, щоб захистити відсутності другого. І я можу запустити цю програму через, sudoщоб вона сама не потребувала налаштування. До речі, саме <fcntl.h>для open().
Джонатан Леффлер

3

Благословіть трійник замість відлуння для судо - один із розповсюджених способів наближення до ситуації, коли вам потрібно обмежити кореневу хімію. Переадресація на / dev / null полягає у припиненні витікання будь-якого виходу - трійник робить те, що ви хочете.

echo "17" | sudo tee /sys/class/gpio/export > /dev/null
echo "out" | sudo tee /sys/class/gpio/gpio17/direction > /dev/null

5
Якщо ви дозволяєте teeзапускати sudo, ви дозволяєте будь-якому файлу перезаписати або /etc/passwdдодати його (зокрема, наприклад, просто додайте новий обліковий запис uid = 0). Ви також можете дозволити запускати всі команди sudo(BTW /etc/sudoersналежить до набору "БУДЬ-ЯКІ файли", тому їх можна перезаписати sudo tee)
cas

2
Мабуть, ви можете обмежити файли, до яких teeможна записати, як описано в цій відповіді окремим запитанням. Просто переконайтеся, що ви також читали коментарі, оскільки деякі люди мали проблеми з використаним оригінальним синтаксисом і пропонують виправити цю проблему.
Олексій

1
@alex, так, ви можете це зробити з будь-якою командою в sudo - обмежте допустимі аргументи. Конфігурація може бути дуже довгою і складною, якщо ви хочете дозволити teeабо працювати з багатьма файлами sudo.
cas

0

Ви можете зробити сценарій, який виконує бажане завдання. Потім створіть новий обліковий запис користувача, який може увійти, лише ввівши ключ, який використовує OpenSSH.

Примітка. Будь-хто зможе запустити цей скрипт, якщо у нього є файл ключів, тому переконайтеся, що файл ключа OpenSSH не читається будь-ким, кому ви хочете запобігти виконанню завдання.

У конфігурації OpenSSH (у файлі санкціонованих_ ключів) перед тим, як вказати ключ, вкажіть команду (далі пробіл), як описано в цьому "прикладі" тексту:

command="myscript" keydata optionalComment

Цей параметр конфігурації може обмежувати цей ключ OpenSSH лише виконанням певної команди. Тепер у вас є sudo надання дозволів, але конфігурація OpenSSH є частиною рішення, яке реально використовується для обмеження / обмеження того, що може зробити цей користувач, щоб користувач не виконував інші команди. Це також не має такого складного файлу конфігурації "sudo", тому якщо ви використовуєте OpenBSD або якщо нові "doas" ("do as") OpenBSD починають ставати все більш популярними (з майбутніми версіями будь-якої операційної системи, яку ви використовуєте) , вам не потрібно буде кидати виклик великою складністю в конфігурації sudo.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.