Apa cara teraman untuk menulis secara terprogram ke file dengan hak akses root?

35

Suatu aplikasi yang sangat besar membutuhkan, pada satu waktu tertentu, untuk melakukan sejumlah kecil penulisan ke file yang memerlukan izin root. Ini bukan benar-benar file tetapi antarmuka perangkat keras yang terkena Linux sebagai file.

Untuk menghindari memberikan hak akses root ke seluruh aplikasi, saya menulis skrip bash yang melakukan tugas-tugas penting. Misalnya, skrip berikut akan mengaktifkan port 17 dari antarmuka perangkat keras sebagai output:

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

Namun, seperti suidyang dinonaktifkan untuk skrip bash di sistem saya, saya bertanya-tanya apa cara terbaik untuk mencapai ini.

  1. Gunakan beberapa solusi yang disajikan di sini

  2. Panggil skrip dengan sudodari aplikasi utama, dan edit daftar sudoers sesuai, untuk menghindari memerlukan kata sandi saat memanggil skrip. Saya sedikit tidak nyaman untuk memberikan hak istimewa sudo echo.

  3. Cukup tulis program C, dengan fprintf, dan atur ke suid root. Hardcode string dan nama file dan pastikan hanya root yang bisa mengeditnya. Atau baca string dari file teks, juga pastikan tidak ada yang bisa mengedit file.

  4. Beberapa solusi lain yang tidak terpikir oleh saya dan lebih aman atau lebih sederhana daripada yang disajikan di atas?

vsz
sumber
10
Mengapa Anda tidak memulai saja program Anda dengan root privilege, buka file, dan drop privilege? Begitulah cara setiap server web melakukannya untuk soket. Secara efektif, Anda tidak berjalan sebagai root, juga tidak perlu bantuan.
Damon

Jawaban:

33

Anda tidak perlu memberikan sudoakses ke echo. Sebenarnya, itu tidak ada gunanya karena, misalnya dengan sudo echo foo > bar, pengalihan dilakukan sebagai pengguna asli, bukan sebagai root.

Panggil skrip kecil dengan sudo, memungkinkan NOPASSWD:akses ke HANYA skrip itu (dan skrip sejenis lainnya) oleh pengguna yang membutuhkan akses ke skrip tersebut.

Ini selalu merupakan cara terbaik / teraman untuk digunakan sudo. Isolasikan sejumlah kecil perintah yang memerlukan hak akses root ke skrip terpisah mereka sendiri dan izinkan pengguna yang tidak tepercaya atau sebagian tepercaya hanya menjalankan skrip itu sebagai root.

sudoScript yang kecil sebaiknya tidak mengambil argumen (atau input) dari pengguna (mis. Program lain mana pun yang dipanggil harus memiliki opsi dan kode yang sulit) atau harus dengan sangat hati-hati memvalidasi argumen / input yang harus dibuat. terima dari pengguna.

Jadilah paranoid dalam validasi - daripada mencari hal-hal yang 'diketahui buruk' untuk dikecualikan, hanya izinkan hal-hal yang 'diketahui baik' dan batalkan segala ketidaksesuaian atau kesalahan atau apa pun yang bahkan mencurigakan.

Validasi harus dilakukan sedini mungkin dalam skrip (sebaiknya sebelum melakukan apa pun sebagai root).


Saya benar - benar harus menyebutkan ini ketika saya pertama kali menulis jawaban ini, tetapi jika skrip Anda adalah skrip shell, HARUS mengutip semua variabel dengan benar. Berhati-hatilah untuk mengutip variabel yang berisi input yang disediakan oleh pengguna dengan cara apa pun , tetapi jangan menganggap beberapa variabel aman, KUTIPAN SEMUA .

Itu termasuk variabel lingkungan berpotensi dikendalikan oleh pengguna (misalnya "$PATH", "$HOME", "$USER"dll Dan pasti termasuk "$QUERY_STRING"dan "HTTP_USER_AGENT"dll dalam naskah CGI). Bahkan, kutip saja semuanya. Jika Anda harus membuat baris perintah dengan banyak argumen, gunakan array untuk membuat daftar argumen dan kutip itu - "${myarray[@]}".

Sudahkah saya mengatakan "mengutip semuanya" cukup sering? ingat itu. lakukan.

cas
sumber
18
Anda lupa menyebutkan bahwa skrip itu sendiri harus dimiliki oleh root, dengan izin 500.
Wildcard
13
Setidaknya hapus izin menulis, demi kebaikan. Itu benar-benar poin saya. Sisanya hanya pengerasan.
Wildcard
10
Program C yang ditulis dengan cermat akan memiliki permukaan serangan yang lebih kecil daripada skrip shell.
user253751
7
@ Imibis, mungkin. Tetapi akan lebih lama untuk menulis dan men-debug daripada skrip shell. Dan memerlukan memiliki kompiler C (yang dilarang pada beberapa server produksi untuk mengurangi risiko keamanan dengan mempersulit penyerang untuk mengkompilasi eksploitasi). Juga, IMO skrip shell yang ditulis oleh sysadmin atau programmer tingkat pemula hingga menengah cenderung tidak dapat dieksploitasi daripada program C yang ditulis oleh seseorang yang memiliki keterampilan serupa - terutama jika harus menerima dan memvalidasi data yang disediakan pengguna.
cas
3
@JonasWielicki - saya pikir kita sekarang baik dan benar-benar ke ranah pendapat daripada fakta. Anda dapat membuat argumen yang valid untuk shell atau C (atau perl atau python atau awk dll) yang lebih atau kurang rentan terhadap kesalahan yang dapat dieksploitasi. Kapan, sebenarnya, sebagian besar tergantung pada keterampilan programmer dan perhatian terhadap detail (dan kelelahan, tergesa-gesa, hati-hati, dll). Namun faktanya, bahasa tingkat yang lebih rendah cenderung memerlukan lebih banyak kode untuk ditulis untuk mencapai apa yang dapat dilakukan dalam garis kode yang jauh lebih sedikit dalam bahasa tingkat yang lebih tinggi .... dan setiap LoC adalah peluang lain untuk kesalahan yang harus dilakukan.
cas
16

Periksa pemilik pada file gpio:

ls -l /sys/class/gpio/

Kemungkinan besar, Anda akan mengetahui bahwa mereka dimiliki oleh grup gpio:

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

Dalam hal ini, Anda cukup menambahkan pengguna Anda ke gpiogrup untuk memberikan akses tanpa sudo:

sudo usermod -aG gpio myusername

Anda harus keluar dan masuk kembali setelah itu agar perubahan berlaku.

jpa
sumber
Itu tidak bekerja. Memang, pemilik grup dari semua yang ada di /sys/class/gpio/gpio, tetapi bahkan setelah menambahkan diri saya ke grup itu, saya masih mendapatkan "izin ditolak" setiap kali saya mencoba menulis apa pun di sana.
vsz
1
Masalahnya adalah, bahwa file-file di dalam /sys/class/gpio/sebenarnya hanya symlinks ke /sys/devices/platform/soc/<some temporary name>/gpiotempat pemilik dan grup adalah root.
vsz
4
@ vsz Sudahkah Anda mencoba chgrp gpio /sys/devices/platform/soc/*/gpio? Mungkin sesuatu seperti itu bisa dimasukkan ke dalam file startup.
jpa
ya, tapi itu tidak sesederhana itu. Karena mereka selalu dihasilkan dengan cara yang berbeda, saya harus menggunakan sesuatu sepertichgrp gpio `readlink -f /sys/class/gpio/gpio18`/*
vsz
7

Salah satu solusi untuk ini (digunakan terutama pada desktop Linux tetapi juga berlaku dalam kasus lain) adalah menggunakan D-Bus untuk mengaktifkan layanan kecil yang berjalan sebagai root dan polkit untuk melakukan otorisasi. Ini pada dasarnya untuk apa polkit dirancang ; dari dokumentasi pengantar :

polkit menyediakan API otorisasi yang dimaksudkan untuk digunakan oleh program istimewa ("MEKANISME") yang menawarkan layanan untuk program yang tidak terjangkau ("KLIEN"). Lihat halaman manual polkit untuk arsitektur sistem dan gambaran besar.

Alih-alih menjalankan program pembantu Anda, program besar yang tidak terjangkau akan mengirim permintaan ke bus. Helper Anda dapat berjalan saat daemon dimulai saat boot sistem, atau, lebih baik, diaktifkan sesuai kebutuhan oleh systemd . Kemudian, pembantu itu akan menggunakan polkit untuk memverifikasi bahwa permintaan datang dari tempat yang resmi. (Atau, dalam hal ini, jika itu terasa berlebihan, Anda dapat menggunakan beberapa mekanisme otentikasi / otorisasi hard-coded lainnya.)

Saya menemukan artikel dasar yang bagus tentang berkomunikasi melalui D-Bus , dan sementara saya belum mengujinya, ini tampaknya menjadi contoh dasar menambahkan polkit ke dalam campuran .

Dalam pendekatan ini, tidak ada yang perlu ditandai setuid.

mattdm
sumber
5

Salah satu cara untuk melakukan ini adalah membuat program setuid-root yang ditulis dalam C yang hanya melakukan apa yang dibutuhkan dan tidak lebih. Dalam kasus Anda, itu tidak harus melihat input pengguna sama sekali.

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

Tidak ada cara untuk menumbangkan ini melalui variabel lingkungan atau apa pun, karena yang dilakukannya hanyalah membuat panggilan sistem pasangan.

Kelemahan: Anda harus memeriksa nilai balik dari setiap panggilan sistem.

Terbalik: pengecekan kesalahan sangat mudah: jika ada kesalahan sama sekali, adil perrordan bail out: keluar dengan status tidak nol. Jika ada kesalahan, selidiki dengan strace. Anda tidak perlu program ini sendiri untuk memberikan pesan kesalahan yang sangat bagus.

Peter Cordes
sumber
2
Saya mungkin tergoda untuk membuka kedua file sebelum menulis apa pun untuk melindungi terhadap tidak adanya yang kedua. Dan saya mungkin menjalankan program ini melalui sudosehingga tidak perlu setuid. Kebetulan, itu <fcntl.h>untuk open().
Jonathan Leffler
3

Memberkati tee alih-alih gema untuk sudo adalah salah satu cara umum mendekati situasi di mana Anda perlu membatasi perm root. Redirect ke / dev / null adalah untuk menghentikan output yang bocor - tee melakukan apa yang Anda inginkan.

echo "17" | sudo tee /sys/class/gpio/export > /dev/null
echo "out" | sudo tee /sys/class/gpio/gpio17/direction > /dev/null
Miles Gillham
sumber
5
Jika Anda mengizinkan teeuntuk dijalankan sudo, Anda mengizinkan file APA SAJA untuk ditimpa atau ditambahkan ke (termasuk, misalnya, /etc/passwd- tambahkan saja uid = 0 akun baru). Anda juga dapat mengizinkan semua perintah untuk dijalankan sudo(BTW /etc/sudoerstermasuk dalam set 'file APA PUN sehingga dapat ditimpa sudo tee)
cas
2
Rupanya, Anda dapat membatasi file yang teedapat menulis, seperti yang dijelaskan dalam jawaban ini pada pertanyaan terpisah. Pastikan Anda membaca komentar juga, karena beberapa orang memiliki masalah dengan sintaks asli yang digunakan dan menyarankan perbaikan untuk masalah itu.
Alex
1
@ alex, ya, Anda bisa melakukannya dengan perintah apa pun di sudo - batasi argumen yang diizinkan. Konfigurasi dapat menjadi sangat panjang dan rumit jika Anda ingin mengizinkan teeatau apa pun untuk beroperasi pada banyak file dengan sudo.
cas
0

Anda dapat membuat skrip yang melakukan tugas yang Anda inginkan. Kemudian, buat akun pengguna baru yang hanya bisa masuk dengan memasok kunci yang digunakan oleh OpenSSH.

Catatan: Siapa saja dapat menjalankan skrip itu jika memiliki file kunci, jadi pastikan file kunci OpenSSH tidak dapat dibaca oleh siapa pun yang ingin Anda hindari melakukan tugas tersebut.

Dalam konfigurasi OpenSSH (dalam file Authorized_key), sebelum menentukan kunci, tentukan perintah (diikuti oleh spasi), seperti yang dijelaskan dalam teks "contoh" ini:

command="myscript" keydata optionalComment

Opsi konfigurasi ini dapat membatasi kunci OpenSSH untuk hanya menjalankan perintah tertentu. Sekarang Anda telah memberikan sudo pemberian izin, tetapi konfigurasi OpenSSH adalah bagian dari solusi yang benar-benar digunakan untuk membatasi / membatasi apa yang dapat dilakukan pengguna, sehingga pengguna tidak menjalankan perintah lain. Ini juga tidak memiliki berkas konfigurasi "sudo" yang rumit, jadi jika Anda menggunakan OpenBSD atau jika "doas" ("do as") OpenBSD mulai menjadi lebih populer (dengan versi mendatang dari sistem operasi apa pun yang Anda gunakan) , Anda tidak perlu ditantang oleh banyak kerumitan dalam konfigurasi sudo.

TOOGAM
sumber