Kök ayrıcalıklarına sahip bir dosyaya programlı olarak yazmanın en güvenli yolu nedir?


35

Çok büyük bir uygulamanın, belirli bir zamanda, kök izinleri gerektiren bir dosyaya az sayıda yazma işlemi yapması gerekir. Gerçekten bir dosya değil, Linux'a bir dosya olarak maruz kalan bir donanım arayüzüdür.

Tüm uygulamaya kök ayrıcalıkları vermekten kaçınmak için, kritik görevleri yapan bir bash betiği yazdım. Örneğin, aşağıdaki komut dosyası donanım arabiriminin 17. bağlantı noktasını çıktı olarak etkinleştirir:

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

Ancak, suidsistemimdeki bash scriptleri için devre dışı bırakıldığı için bunu başarmanın en iyi yolunun ne olduğunu merak ediyorum.

  1. Burada sunulan bazı geçici çözümleri kullanın

  2. Komut dosyasını sudoana uygulamadan arayın ve komut dosyasını çağırırken parola istememek için, kullanıcı listesini uygun şekilde düzenleyin. Sudo ayrıcalıkları vermek için biraz rahatsızım echo.

  3. Sadece bir C programı yazınız fprintfve onu suid köküne ayarlayınız. Dizeleri ve dosya adlarını sabit olarak kodlayın ve yalnızca kök dizinin düzenleyebildiğinden emin olun. Veya dizeleri bir metin dosyasından da okuyarak, hiç kimsenin dosyayı düzenleyemediğinden emin olun.

  4. Bana gelmeyen ve yukarıda sunulanlardan daha güvenli veya daha basit olan başka bir çözüm?


10
Neden programınıza kök ayrıcalıklarla başlamıyorsunuz, dosyayı açıp ayrıcalıkları bırakmıyorsunuz? Her web sunucusu ya da böyle soketler için böyle yapar. Etkili olarak, root olarak çalışmıyorsunuz, ne de gerekli bir yardımcı.
Damon,

Yanıtlar:


33

sudoErişim izni vermene gerek yok echo. Aslında, bu anlamsızdır, örneğin sudo echo foo > bar, yeniden yönlendirme, kök olarak değil, orijinal kullanıcı olarak yapılır.

Küçük betiği kullanarak sudo, NOPASSWD:SADECE bu betiğe (ve diğer benzer komut dosyalarına) erişmesi gereken kullanıcı (lar) tarafından erişime izin verin.

Bu her zaman kullanmanın en iyi / en güvenli yoludur sudo. Kök ayrıcalıklarına ihtiyaç duyan az sayıdaki komutu kendi ayrı komut dosyalarına ayırın ve güvenilmeyen veya kısmen güvenilen kullanıcının yalnızca bu komut dosyasını kök olarak çalıştırmasına izin verin.

Küçük sudoharfli komut (lar) kullanıcıdan ya (ya da girdi) kullanıcılardan (örn. Çağırdığı diğer programların kodlanmış seçenek ve argümanlara sahip olması gerekir) argüman almamalı ya da girmesi gereken tüm argümanları / girdileri doğrulamalıdır. kullanıcıdan kabul et

Doğrulamada paranoyak olun - dışlanacak "bilinen kötü" şeyleri aramak yerine, yalnızca "iyi bilinen" şeylere izin verin ve uyumsuzluk veya hata veya uzaktan şüpheli olan herhangi bir şeyi iptal edin.

Doğrulama, senaryoda mümkün olduğunca erken yapılmalıdır (tercihen root olarak başka bir şey yapmadan önce ).


Ben gerçekten ben ilk bu cevabı yazdığında bu bahsetmeliydik ancak komut bir kabuk eğer GEREKİR düzgün alıntı bütün değişkenleri. Kullanıcı tarafından sağlanan girdiyi içeren değişkenleri herhangi bir şekilde alıntılamaya özellikle dikkat edin , ancak bazı değişkenlerin güvenli olduğunu varsaymayın, QUOTE THEM ALL .

Yani potansiyel olarak (örneğin kullanıcı tarafından kontrol ortam değişkenleri içeren "$PATH", "$HOME", "$USER"vb Ve kesinlikle dahil "$QUERY_STRING"ve "HTTP_USER_AGENT"vb bir CGI komut dosyası). Aslında, sadece hepsini alıntı. Birden fazla argüman içeren bir komut satırı oluşturmak zorundaysanız, args listesini oluşturmak için bir dizi kullanın ve şunu yazın - "${myarray[@]}".

Yeterince "hepsini alıntı" dedim mi? Bunu hatırlamak. yap.


18
Senaryoda kendisi gerektiğini söylemeyi unutmuşum olunan 500. izinlerine sahip, root tarafından
Joker

13
Tanrı aşkına, en azından yazma izinlerini kaldırın. Bu gerçekten benim açımdandı. Gerisi sadece sertleşiyor.
Joker

10
Düşünceli bir şekilde yazılmış bir C programı, bir kabuk betiğinden daha küçük bir saldırı yüzeyine sahip olacaktır.
user253751

7
Muhtemelen imibis @. Ancak yazmak ve bir kabuk betiğinden daha fazla hata ayıklamak çok daha uzun sürer. Ve bir C derleyicisine sahip olmayı gerektirir (bazı üretim sunucularında, saldırganların istismarları derlemesini zorlaştırarak güvenlik riskini azaltmak için yasaklanmıştır). Ayrıca, IMO'nun bir aceminin orta seviye sysadmin veya programcıya yazdığı bir kabuk betiğinin, özellikle kullanıcı tarafından sağlanan verileri kabul etmesi ve doğrulaması gerekiyorsa, benzer beceriye sahip bir kişi tarafından yazılmış bir C programından daha az yararlanılması daha olasıdır.
Cas

3
@JonasWielicki - bence artık gerçek ve gerçek değil, fikir dünyasındayız. Sömürü veya C (veya perl veya python veya awk vb.) İstismar edilebilir hatalara daha az ya da daha çok eğilimli olduğu için geçerli argümanlar yapabilirsiniz. Ne zaman, gerçekten, programcının becerisine ve detaylara gösterilen dikkatine (ve yorgunluk, acele, dikkat vb.) Bağlıdır. Yine de, daha düşük seviyeli dillerin, daha düşük seviyeli kod satırlarında daha yüksek seviyeli dillerde yapılabilecekleri elde etmek için daha fazla kod yazma eğiliminde olduğu bir gerçektir .... yapılacak hata.
Cas

16

GPIO dosyalarındaki sahibini kontrol edin:

ls -l /sys/class/gpio/

Büyük olasılıkla, bunların gruba ait olduğunu göreceksiniz gpio:

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

Bu durumda, kullanıcılarınızı gpiosudo olmadan erişime izin vermek için gruba ekleyebilirsiniz :

sudo usermod -aG gpio myusername

Değişikliğin geçerli olması için oturumu kapatıp tekrar girmeniz gerekir.


Çalışmıyor. Gerçekten de, içindeki her şeyin grup sahibi /sys/class/gpio/gpio'dur, ancak kendimi o gruba ekledikten sonra bile, orada bir şey yazmaya çalıştığımda hala "izin verilmez" mesajı alıyorum.
vsz

1
Sorun şu ki, içindeki dosyalar /sys/class/gpio/aslında /sys/devices/platform/soc/<some temporary name>/gpiohem sahibin hem de grubun root olduğu yerlere bağlanıyor .
vsz

4
@vsz Denediniz mi chgrp gpio /sys/devices/platform/soc/*/gpio? Belki de böyle bir şey bir başlangıç ​​dosyasına koyulabilir.
jpa

evet, ama o kadar basit değil. Her zaman farklı bir şekilde üretildiklerinden, şöyle bir şey kullanmak zorunda kaldımchgrp gpio `readlink -f /sys/class/gpio/gpio18`/*
vsz

7

Bunun için bir çözüm (özellikle Linux masaüstünde kullanılır, ancak diğer durumlarda da uygulanabilir), yetkilendirmeyi yapmak için root ve polkit olarak çalışan küçük bir hizmeti etkinleştirmek için D-Bus kullanmaktır . Bu temelde polkit için tasarlanan şeydir ; onun gelen tanıtıcı belgeler :

polkit, imtiyazsız programlara (“İSTEMCİLER”) hizmet sunan imtiyazlı programlar (“MEKANİZMALAR”) tarafından kullanılması amaçlanan bir yetkilendirme API'si sunar. Sistem mimarisi ve büyük resim için polkit kılavuz sayfasına bakın.

Yardımcı programınızı uygulamak yerine, büyük, ayrıcalıklı olmayan program otobüse bir istek gönderir. Yardımcınız ya sistem önyüklemesinde başlatılan bir arka plan programı olarak çalışıyor olabilir ya da daha iyisi, systemd tarafından gerektiğinde etkinleştirilebilir . Ardından, bu yardımcı, isteğin yetkili bir yerden geldiğini doğrulamak için polkit'i kullanır. (Ya da, bu durumda, bu fazla kilolu gibi geliyorsa, başka bir sabit kodlanmış kimlik doğrulama / yetkilendirme mekanizması kullanabilirsiniz.)

D-Bus ile iletişim hakkında iyi bir temel makale buldum ve test etmedim, ancak bu, karışıma polkit eklemenin temel bir örneği gibi görünüyor .

Bu yaklaşımda hiçbir şeyin belirgin olarak işaretlenmesi gerekmez.


5

Bunu yapmanın bir yolu, sadece ihtiyaç duyulan ve başka bir şey yapmayan, C ile yazılmış bir setuid root programı oluşturmaktır. Senin durumunda, hiçbir kullanıcı girişine bakmak zorunda değil.

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

Bunu ortam değişkenleri ya da herhangi bir şey aracılığıyla alt etmenin bir yolu yok çünkü tek yaptığı bir çift sistem çağrısı yapmak.

Dezavantaj: Her sistem çağrısının dönüş değerini kontrol etmelisiniz.

Upside: hata kontrolü gerçekten çok kolay: eğer herhangi bir hata varsa, hemen perrorve kurtarın: sıfır olmayan durumdan çıkın. Bir hata varsa, ile araştırın strace. Gerçekten güzel hata mesajları vermek için bu programın kendisine ihtiyacınız yok.


2
İkincisinin yokluğuna karşı korumak için bir şeyler yazmadan önce her iki dosyayı da açmaya cazip gelebilirim. Ve ben bu programı çalıştırabilirim, bu sudoyüzden kendi kendine kurgulanması gerekmez. Bu arada, bunun <fcntl.h>için open().
Jonathan Leffler

3

Sudo için yankı yerine tişörtleri koruyun, kök izinlerini sınırlamanız gereken bir duruma yaklaşmanın yaygın bir yoludur. / Dev / null komutunun yönlendirmesi, herhangi bir çıktının sızmasını durdurmak içindir - tee istediğiniz şeyi yapar.

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

5
Eğer izin verirseniz teeile çalıştırılmak üzere sudo, HERHANGİ dosyanın üzerine olmamı sağlayan veya (örneğin, dahil eklenen /etc/passwd- sadece ekleme yeni uid = 0 hesabı). Tüm komutların çalıştırılmasına da izin verebilirsiniz sudo(BTW /etc/sudoers'HERHANGİ dosya' kümesine aittir, bu yüzden üzerine yazılabilir sudo tee)
cas

2
Görünüşe göre, bu cevabındatee açıklandığı gibi ayrı bir soru üzerine yazabilecek dosyaları sınırlayabilirsiniz . Yalnızca bazılarının kullanılan orijinal sözdizimi ile ilgili sorunları olduğundan ve bu sorunu gidermesini önerdiğinden, yorumları da okuduğunuzdan emin olun.
Alex,

1
@alex, evet, bunu sudo'da herhangi bir komutla yapabilirsiniz - izin verilen argümanları sınırlandırın. Pek çok teedosyada çalışmasına izin vermek veya neye izin vermek isterseniz, yapılandırma çok uzun ve karmaşık olabilir sudo.
Cas

0

İstediğiniz görevi gerçekleştiren bir komut dosyası yapabilirsiniz. Ardından, yalnızca OpenSSH tarafından kullanılan bir anahtar sağlayarak giriş yapabilen yeni bir kullanıcı hesabı oluşturun.

Not: Anahtar dosyası varsa, herkes bu komut dosyasını çalıştırabilir; bu nedenle, OpenSSH anahtar dosyasının, görevi gerçekleştirmesini engellemek istediğiniz herhangi biri tarafından okunamadığından emin olun.

OpenSSH yapılandırmasında (yetkili_keys dosyasında), anahtarı belirtmeden önce, bu "örnek" metinde açıklandığı şekilde bir komut (ardından boşluk) belirtin:

command="myscript" keydata optionalComment

Bu yapılandırma seçeneği, bu OpenSSH anahtarını yalnızca belirli bir komutu çalıştırmak için kısıtlayabilir. Artık izinleri vermişsiniz, ancak OpenSSH yapılandırması, kullanıcının başka komutları çalıştırmaması için yapabileceklerini sınırlamak / sınırlamak için gerçekten kullanılan çözümün bir parçası. Bu aynı zamanda bir "sudo" konfigürasyon dosyasında da karmaşık değildir, bu yüzden OpenBSD kullanıyorsanız veya OpenBSD'nin yeni "doas" ları ("olarak yapın") daha popüler olmaya başlarsa (kullandığınız işletim sisteminin gelecekteki sürümleriyle) , sudo konfigürasyonunda fazla karmaşık olmanıza gerek kalmayacak.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.