Kabuk komutlarını C içinden çağırmak iyi bir fikir midir?


50

udevadm info -q path -n /dev/ttyUSB2C programından çağırmak istediğim bir unix shell komutu ( ) var. Muhtemelen bir hafta süren bir mücadele ile kendim yeniden uygulayabilirdim, ama bunu yapmak istemiyorum.

Sadece aramam benim için iyi bir uygulama mı kabul edilir popen("my_command", "r");, yoksa kabul edilemez güvenlik sorunları doğuracak ve uyumluluk sorunları iletecek mi? Böyle bir şey yapmak bana yanlış geliyor, ama neden kötü olacağına parmağımı sokamam.


8
Dikkate alınması gereken bir şey, enjeksiyon saldırıları.
MetaFight

8
Genelde kullanıcı girişi verdiğiniz durumu kabuk komut argümanları olarak düşünüyordum.
MetaFight

8
Birisi udevadmprogramınızla aynı dizine kötü amaçlı bir çalıştırılabilir dosya koyabilir ve ayrıcalıkları artırmak için kullanabilir mi? Unix güvenliği hakkında fazla bir şey bilmiyorum, ancak programınız çalıştırılacak setuid rootmı?
Andrew Piliser

7
@AndrewPiliser Geçerli dizin açık değilse PATH, o zaman hayır - birlikte çalıştırılması gerekir ./udevadm. IIRC Windows olsa da aynı dizinde çalıştırılabilir dosyaları çalıştıracaktı.
Izkata

4
Mümkün olduğunda, kabuktan geçmeden doğrudan istediğiniz programı çağırınız.
CodesInChaos

Yanıtlar:


58

Çok fena değil, ama bazı uyarılar var.

  1. çözümünüz ne kadar taşınabilir olacak? Seçtiğiniz ikili sistem her yerde aynı mı olacak, sonuçları aynı formatta yayınlayacak mı? Ayarlarında farklı çıktılar LANGmı?
  2. Bu işleminize ne kadar ekstra yük ekliyor? İkili dosya oluşturmak çok daha fazla yüke neden olur ve kütüphane çağrılarını yürütmekten (genellikle konuşma) daha fazla kaynak gerektirir. Bu, senaryonuzda kabul edilebilir mi?
  3. Güvenlik sorunları var mı? Birisi seçtiğiniz ikiliyi bir başkasıyla değiştirip daha sonra nefretli işler yapabilir mi? İkili sisteminiz için kullanıcı tarafından sağlanan arkıları kullanıyor musunuz ve sağlayabilirler mi ;rm -rf /(örneğin) (bazı API'lerin argümanları komut satırında belirtmekten daha güvenli bir şekilde belirtmenize izin vereceğini unutmayın)

Genelde, tahmin edebileceğim bilinen bir ortamdayken, ikili çıktının ayrıştırılması kolay olduğunda (gerekirse - yalnızca bir çıkış koduna ihtiyacınız olabilir) ve bunu yapmam gerekmediğinde, ikili dosyaları çalıştırmaktan mutlu olurum sıklıkla.

Belirttiginiz gibi diger mesele, binaryin ne yaptigini çoğaltmak için ne kadar calismasidir? Kaldırabileceğiniz bir kütüphane kullanıyor mu?


13
Forking a binary results in a lot more load and requires more resources than executing library calls (generally speaking). Bu Windows'ta olsaydı, katılıyorum. Bununla birlikte, OP, Unix'in çatallığın yeterince düşük olduğunu, bir kütüphanenin dinamik olarak yüklenmesinin çatallamadan daha kötü olup olmadığını söylemenin zor olduğunu belirtir.
wallyk

1
Normalde bir kütüphanenin bir defa yüklenmesini beklerim , bununla birlikte ikili dosya birden çok kez çalıştırılabilir. Her zaman olduğu gibi, kullanım senaryolarına bağlı, ancak dikkate alınması gerekiyor (daha sonra reddedilirse)
Brian Agnew

3
Hiçbir Windows üzerinde "forking", yani tırnak vardır Unix özgü olması.
Cody Gray

5
NT çekirdeği, Win32 aracılığıyla gösterilmese de çatallaşmayı destekler. Neyse, ben bir dış program çalıştırarak maliyeti daha gelecekti inanmazdı execdan daha fork. Bölümleri yükleme, paylaşılan nesnelere bağlama, her biri için init kodu çalıştırma. Ve sonra tüm verileri hem girdi hem de çıktılar için diğer tarafta ayrıştırılacak metne dönüştürmek zorunda kaldık.
spektrumlar

8
Adil olmak gerekirse, udevadm info -q path -n /dev/ttyUSB2yine de Windows üzerinde çalışmayacak, bu yüzden bütün tartışmalar biraz teorik.
MSalters,

37

Potansiyel bir vektör ortaya koyduktan sonra enjeksiyon açıklarına karşı korunmaya büyük özen gösterir. Şimdi aklınızın ön saflarında yer alıyor, ancak daha sonra seçme yeteneğine ihtiyacınız olabilir ttyUSB0-3, o zaman bu liste diğer yerlerde kullanılacak, böylece tek bir sorumluluk ilkesini takip etmeli ve ardından müşterinin belirleme zorunluluğu olacaktır. Listedeki keyfi bir cihaz ve bu değişikliği yapan geliştirici listenin sonunda güvenli olmayan bir şekilde kullanıldığını bilmiyor.

Başka bir deyişle, bildiğiniz en dikkatsiz geliştirici gibi kod, kodun görünüşte alakasız bir bölümünde güvensiz bir değişiklik yapıyor.

İkincisi, CLI araçlarının çıktısı, dokümanlar özel olarak bunları işaretlemediği sürece genellikle kararlı arayüzler olarak kabul edilmez. Çalıştığınız bir komut dosyası için kendinize sorun giderebileceğiniz, ancak bir müşteriye dağıttığınız bir şey için onlara güveniyor olabilirsiniz.

Üçüncüsü, yazılımınızdan bir değer elde etmenin kolay bir yolunu istiyorsanız, başka birinin de istediği gibi bir olasılık var. İstediğinizi zaten yapan bir kütüphane arayın. libudevsistemime zaten kuruldu:

#include <libudev.h>
#include <sys/stat.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
    struct stat statbuf;

    if (stat("dev/ttyUSB2", &statbuf) < 0)
        return -1;
    struct udev* udev = udev_new();
    struct udev_device *dev = udev_device_new_from_devnum(udev, 'c', statbuf.st_rdev);

    printf("%s\n", udev_device_get_devpath(dev));

    udev_device_unref(dev);
    udev_unref(udev);
    return 0;
}

Bu kütüphanede başka kullanışlı işlevler de var. Tahminime göre eğer buna ihtiyacınız olursa, ilgili işlevlerden bazıları da işe yarayabilir.


2
TEŞEKKÜR EDERİM. Libudev'i aramak için çok zaman harcadım. Sadece bulamadım!
johnny_boy

2
OP’nin programı, başka bir kullanıcının çevresi üzerinde kontrol sahibi olduğu durumlarda çağrılmadığı sürece "enjeksiyon güvenlik açığı" nın nasıl bir sorun olduğunu görmüyorum, bu özellikle de intihar durumunda olduğu gibi (ama daha az oranda cgi ve ssh zorla komutu). Normal programların, çalıştıkları kullanıcıya karşı sertleştirilmesi gerekmiyor.
R. ..

2
@R ..: "Enjeksiyon zayıflığı", genelleştirip ttyUSB2kullandığınız zamandır sprintf(buf, "udevadm info -q path -n /dev/%s", argv[1]). Şimdi bu oldukça güvenli olarak görülmektedir, ama ne olursa argv[1]olduğunu "nul || echo OOPS"?
MSalters,

3
@R ..: sadece daha fazla hayal gücüne ihtiyacınız var. Öncelikle bu sabit bir dizedir, daha sonra kullanıcı tarafından sağlanan bir argümandır, daha sonra kullanıcı tarafından çalıştırılan başka bir program tarafından sağlanan bir argümandır, ancak anlamadıkları, daha sonra da tarayıcılarının bir web sayfasından çıkardığı bir argümandır. İşler "yokuş aşağı" devam ederse, sonuçta birisi girdiyi sterilize etmekle ilgili sorumluluk almak zorundadır ve Karl, gelebileceği her yerde bu noktayı tanımlamanın son derece dikkatli olduğunu söylüyor.
Steve Jessop

6
Önlem prensibi gerçekten "bildiğiniz en dikkatsiz geliştiricinin güvensiz bir değişiklik yaptığını varsayarsak" olsa da, bir istismarla sonuçlanamayacağını garanti etmek için şimdi kod yazmak zorunda kaldım, o zaman şahsen yataktan çıkamadım. Bildiğim en dikkatsiz geliştiriciler o bile değil gibi Makyavel'dir görünmesi çalışıyorum .
Steve Jessop,

16

Özel bir durumda, çağırmak istediğiniz yerde udevadm, udevbir kütüphane olarak kabul edip uygun işlev çağrıları yapabileceğinizden şüpheleniyor muyum ?

Örneğin bir "bilgi" modunda çağırdığınızda udevadm kendisi ne yaptığına bakmak olabilir: https://github.com/gentoo/eudev/blob/master/src/udev/udevadm-info.c ve eşdeğer arama yapmak udevadm'in yaptığı gibi.

Bu, Brian Agnew'in mükemmel cevabında sayısız olumsuzluktan kaçınacaktır - örneğin, belli bir yolda mevcut olan ikiliye güvenmemek, çatallama masraflarını önlemek gibi.


Teşekkürler, tam olarak bu repoyu buldum ve bir süre daha araştırmaya başladım.
johnny_boy

1
… Ve libudev'i kullanmak özellikle zor değil. Hata yönetimi biraz eksiktir, ancak API'leri sorgulamak ve izlemek saymak oldukça kolaydır. Eğer denemeniz durumunda, korkunuzun mücadelesi 2 saatten az olabilir.
spektrumlar

7

Sorunuz orman cevabı gibi görünüyordu ve buradaki cevaplar ağaç cevabı gibi görünüyor, bu yüzden size bir orman cevabı vereceğimi düşündüm.

Bu nadiren C programlarının nasıl yazıldığını gösterir. Her zaman kabuk scriptleri nasıl yazılır ve bazen Python, perl veya Ruby programları nasıl yazılır.

İnsanlar, tipik olarak sistem kütüphanelerinin kolay kullanımı ve hızlı bir şekilde işletim sistemi sistem çağrıları için doğrudan düşük seviyeli erişim için C yazmaktadır. Ve C yazmak zor bir dildir, bu yüzden eğer insanlar bu şeylere ihtiyaç duymazlarsa, o zaman C kullanmazlar. Ayrıca, C programlarının genellikle yalnızca paylaşılan kütüphanelere ve yapılandırma dosyalarına bağımlı olması beklenir.

Bir alt işleme geçmek özellikle hızlı değildir ve düşük seviyeli sistem tesislerine ince taneli ve kontrollü bir erişim gerektirmez ve harici bir çalıştırılabilirliğe muhtemelen şaşırtıcı bir bağımlılık getirir, bu nedenle görülmesi nadirdir. C programlarında.

Bazı ek kaygılar var. Söz konusu güvenlik ve taşınabilirlik, insanların söz konusu olduğu için tamamen geçerlidir. Elbette kabuk betikleri için de aynı derecede geçerlidir, ancak insanlar kabuk betiklerinde bu tür sorunları beklemektedir. Ancak, C programlarının tipik olarak daha tehlikeli hale getiren bu güvenlik sınıfına sahip olmaları beklenmemektedir.

Ancak, benim görüşüme göre, popenprogramın geri kalanı ile etkileşime girecek şekilde en büyük endişeleri yapmak zorunda . popenbir alt süreç oluşturmak, çıktısını okumak ve çıkış durumunu toplamak zorundadır. Bu arada, bu süreç 'stderr, kafa karıştırıcı çıktıya neden olabilecek, programınızla aynı stderr'e bağlanacak ve stdin diğer ilginç sorunlara neden olabilecek programınızla aynı olacaktır. Bunu , kabuk tarafından yorumlandığından beri </dev/null 2>/dev/nullilettiğiniz dizeye dahil ederek çözebilirsiniz popen.

Ve popenbir çocuk süreci yaratır. Sinyal işleme veya çatallama işlemleri ile ilgili herhangi bir şey yaparsanız, garip SIGCHLDsinyaller alabilirsiniz. Çağrılarınız waitgarip bir şekilde etkileşime girebilir popenve muhtemelen garip yarış koşulları oluşturabilir.

Güvenlik ve taşınabilirlik endişeleri elbette var. Kabuk betikleri veya sistemdeki diğer çalıştırılabilir dosyaları başlatan herhangi bir şey için olduğu gibi. Ayrıca, programınızı kullanan kişilerin, geçirdiğiniz dizeye kabuk meta-yakalayıcıları girememelerine dikkat edin, popençünkü bu dize doğrudan shbirlikte verilir sh -c <string from popen as a single argument>.

Ancak, kullanarak bir C programı görmenin neden garip olduğunu sanmıyorum popen. Garip olmasının nedeni, C'nin tipik olarak düşük seviyeli bir dil olması ve popendüşük seviyeli olmamasıdır. Çünkü, programların kullanım popenyerlerinde tasarım kısıtlamaları kullanılması, programınızın standart giriş ve çıkışlarıyla garip bir şekilde etkileşime gireceği ve kendi süreç yönetiminizi veya sinyal işlemenizi yapmak için acı verici olmasını sağlayacak. C programlarının tipik olarak harici çalıştırılabilir dosyalara bağımlı olması beklenmez.


0

Programınız bilgisayar korsanlığına vb. Maruz kalabilir. Kendinizi bu tür etkinliklerden korumanın bir yolu, mevcut makine ortamınızın bir kopyasını oluşturmak ve programınızı chroot adlı bir sistem kullanarak çalıştırmaktır.

Programınızın bakış açısından normal bir ortamda yürütülmesi, güvenlik açısından, birisi programınızı sonlandırırsa, yalnızca kopyayı hazırladığınızda sağladığınız öğelere erişebilir.

Daha fazla ayrıntı için bkz Böyle bir kurulum chroot hapis denir chroot hapis .

Genelde herkesin erişimine açık sunucuları ayarlamak için kullanılır.


3
OP, diğer insanların çalıştırması için bir program yazıyor, güvenilmeyen ikili dosyalar ile oynamıyor veya kendi sisteminde kaynak göstermiyor.
Peter Cordes
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.