มันเป็นความคิดที่ดีที่จะเรียกคำสั่งเชลล์จากภายใน C หรือไม่?


50

มีคำสั่ง unix shell ( udevadm info -q path -n /dev/ttyUSB2) ที่ฉันต้องการโทรจากโปรแกรม C อาจใช้เวลาประมาณหนึ่งสัปดาห์ในการต่อสู้ฉันสามารถนำไปใช้ใหม่ได้ด้วยตัวเอง แต่ฉันไม่ต้องการทำเช่นนั้น

เป็นที่ยอมรับอย่างกว้างขวางในทางปฏิบัติที่ดีสำหรับฉันเพียงแค่โทรpopen("my_command", "r");หรือว่าจะนำเสนอปัญหาความปลอดภัยที่ยอมรับไม่ได้และส่งต่อปัญหาความเข้ากันได้? ฉันรู้สึกผิดที่จะทำสิ่งนี้ แต่ฉันไม่สามารถบอกได้ว่าทำไมมันถึงไม่ดี


8
สิ่งหนึ่งที่ควรพิจารณาคือการโจมตีด้วยการฉีด
MetaFight

8
ฉันส่วนใหญ่คิดกรณีที่คุณให้ผู้ใช้ใส่เป็นอาร์กิวเมนต์คำสั่ง shell
MetaFight

8
มีใครบางคนที่ทำให้โปรแกรมที่เป็นอันตรายudevadmอยู่ในไดเรกทอรีเดียวกับโปรแกรมของคุณและใช้งานเพื่อเพิ่มสิทธิ์ ฉันไม่ค่อยรู้เรื่องความปลอดภัยของยูนิกซ์ แต่โปรแกรมของคุณจะทำงานด้วยsetuid rootหรือไม่?
Andrew Piliser

7
@AndrewPiliser ถ้าไดเรกทอรีปัจจุบันไม่ได้อยู่บนPATHแล้วไม่มี - ./udevadmมันจะต้องมีการทำงานกับ IIRC Windows จะเรียกใช้ executables ไดเรกทอรีเดียวกันได้
Izkata

4
เมื่อใดก็ตามที่เป็นไปได้ให้เรียกใช้โปรแกรมที่ต้องการโดยตรงโดยไม่ต้องผ่านเชลล์
CodesInChaos

คำตอบ:


58

มันไม่ได้เลวร้ายนักโดยเฉพาะ แต่มีข้อแม้อยู่บ้าง

  1. โซลูชันของคุณจะพกพาได้อย่างไร ไบนารีที่คุณเลือกจะทำงานเหมือนกันทุกที่หรือไม่ส่งออกผลลัพธ์ในรูปแบบอื่น ๆ หรือไม่ มันจะเอาท์พุทที่แตกต่างกันในการตั้งค่าLANGอื่น ๆ ?
  2. ภาระนี้เพิ่มในกระบวนการของคุณมากเพียงใด การแยกไบนารีจะส่งผลให้โหลดมากขึ้นและต้องการทรัพยากรมากกว่าเรียกใช้การเรียกใช้ไลบรารี (โดยทั่วไปจะพูด) สิ่งนี้ยอมรับได้ในสถานการณ์ของคุณหรือไม่?
  3. มีปัญหาด้านความปลอดภัยหรือไม่ ใครบางคนสามารถแทนที่ไบนารีที่คุณเลือกด้วยอันอื่นและทำสิ่งชั่วร้ายได้หลังจากนั้น? คุณใช้ args ที่ผู้ใช้ระบุสำหรับไบนารีของคุณหรือไม่และพวกเขาสามารถให้;rm -rf /(ตัวอย่าง) (โปรดทราบว่า API บางตัวจะช่วยให้คุณระบุ args ได้อย่างปลอดภัยมากกว่าเพียงแค่ระบุไว้ในบรรทัดคำสั่ง)

ฉันมักจะมีความสุขในการเรียกใช้งานไบนารีเมื่อฉันอยู่ในสภาพแวดล้อมที่เป็นที่รู้จักซึ่งฉันสามารถคาดการณ์ได้เมื่อเอาต์พุตไบนารีง่ายต่อการแยกวิเคราะห์ (ถ้าจำเป็น - คุณอาจต้องใช้รหัสออก) และฉันก็ไม่จำเป็นต้องทำเช่นกัน บ่อยครั้ง.

ดังที่คุณได้บันทึกไว้ปัญหาอื่น ๆ คือการทำงานซ้ำซ้อนกับไบนารี มันใช้ห้องสมุดที่คุณสามารถใช้ประโยชน์ได้หรือไม่?


13
Forking a binary results in a lot more load and requires more resources than executing library calls (generally speaking). หากสิ่งนี้อยู่บน Windows ฉันจะเห็นด้วย อย่างไรก็ตาม OP ระบุ Unix ที่การฟอร์กมีค่าใช้จ่ายต่ำพอที่จะบอกได้ยากว่าการโหลดไลบรารี่แบบไดนามิกนั้นแย่กว่าการฟอร์กหรือไม่
wallyk

1
ปกติฉันคาดหวังว่าห้องสมุดจะโหลดหนึ่งครั้งในขณะที่ไบนารีอาจถูกดำเนินการหลายครั้ง เช่นเคยมันขึ้นอยู่กับสถานการณ์การใช้งาน แต่จำเป็นต้องพิจารณา (หากถูกไล่ออกในภายหลัง)
Brian Agnew

3
ไม่มี "ฟอร์ก" บน Windows ดังนั้นคำพูดที่มีจะเป็นระบบปฏิบัติการยูนิกซ์เฉพาะ
Cody Grey

5
เคอร์เนล NT รองรับการฟอร์กกิ้งถึงแม้ว่ามันจะไม่ถูกเปิดเผยผ่านทาง Win32 อย่างไรก็ตามผมจะเชื่อว่าค่าใช้จ่ายของการใช้โปรแกรมภายนอกจะมาเพิ่มเติมจากกว่าจากexec forkกำลังโหลดส่วนเชื่อมโยงในวัตถุที่ใช้ร่วมกันเรียกใช้รหัสเริ่มต้นสำหรับแต่ละ แล้วต้องแปลงข้อมูลทั้งหมดเป็นข้อความเพื่อแยกวิเคราะห์ในด้านอื่น ๆ ทั้งสำหรับอินพุตและเอาต์พุต
สเปกตรัม

8
เพื่อความเป็นธรรมudevadm info -q path -n /dev/ttyUSB2จะไม่ทำงานบน Windows ต่อไปดังนั้นการอภิปรายเรื่องการฟอร์กกิ้งทั้งหมดจึงเป็นเรื่องทางทฤษฎีเล็กน้อย
MSalters

37

ใช้ความระมัดระวังอย่างมากเพื่อป้องกันช่องโหว่การฉีดเมื่อคุณแนะนำเวกเตอร์ที่มีศักยภาพ มันอยู่ในระดับแนวหน้าของจิตใจของคุณตอนนี้ แต่ต่อมาคุณอาจต้องการความสามารถในการเลือกttyUSB0-3จากนั้นรายการนั้นจะถูกนำไปใช้ในที่อื่น ๆ เพื่อที่จะได้รับการแยกตัวออกไปตามหลักการความรับผิดชอบเดี่ยวจากนั้นลูกค้าจะมีความต้องการ อุปกรณ์พลการในรายการและนักพัฒนาที่ทำว่าการเปลี่ยนแปลงจะมีความคิดว่ารายการในที่สุดได้รับใช้ในทางที่ไม่ปลอดภัย

กล่าวอีกนัยหนึ่งคือรหัสราวกับว่านักพัฒนาที่ประมาทที่สุดที่คุณรู้จักกำลังทำการเปลี่ยนแปลงที่ไม่ปลอดภัยในส่วนที่ไม่เกี่ยวข้องกับรหัส

ประการที่สองผลลัพธ์ของเครื่องมือ CLI โดยทั่วไปแล้วจะไม่ถือว่าเป็นอินเทอร์เฟซที่มีเสถียรภาพเว้นแต่เอกสารจะทำเครื่องหมายเป็นพิเศษเช่นนั้น คุณอาจนับว่าโอเคสำหรับสคริปต์ที่คุณเรียกใช้ซึ่งคุณสามารถแก้ไขปัญหาได้ด้วยตัวเอง แต่ไม่ใช่สำหรับสิ่งที่คุณปรับใช้กับลูกค้า

ประการที่สามถ้าคุณต้องการวิธีที่ง่ายในการดึงค่าจากซอฟต์แวร์ของคุณโอกาสที่คนอื่นก็ต้องการเช่นกัน ค้นหาไลบรารีที่ทำสิ่งที่คุณต้องการอยู่แล้ว libudevติดตั้งแล้วในระบบของฉัน:

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

มีฟังก์ชั่นที่มีประโยชน์อื่น ๆ ในห้องสมุดนั้น ฉันเดาว่าถ้าคุณต้องการสิ่งนี้ฟังก์ชั่นที่เกี่ยวข้องบางอย่างอาจมีประโยชน์เช่นกัน


2
ขอขอบคุณ. ฉันใช้เวลานานมากในการมองหา libudev ฉันหามันไม่เจอ!
johnny_boy

2
ฉันไม่เห็นว่า "ช่องโหว่การฉีด" เป็นปัญหาที่นี่ได้อย่างไรเว้นแต่โปรแกรมของ OP ถูกเรียกใช้เมื่อผู้ใช้รายอื่นมีการควบคุมสภาพแวดล้อมของตัวเองซึ่งเป็นกรณีหลักในกรณีของ suid (อาจเป็นไปได้ แต่ในระดับที่น้อยกว่า cgi และ ssh บังคับคำสั่ง) ไม่มีเหตุผลที่โปรแกรมปกติจะต้องแข็งตัวกับผู้ใช้ที่พวกเขากำลังทำงานอยู่
. ..

2
@R .. : "การฉีดช่องโหว่" คือเมื่อคุณกล่าวสรุปและการใช้งานttyUSB2 sprintf(buf, "udevadm info -q path -n /dev/%s", argv[1])ตอนนี้ที่ปรากฏค่อนข้างปลอดภัย แต่ถ้าargv[1]เป็น"nul || echo OOPS"?
MSalters

3
@R .. : คุณแค่ต้องการจินตนาการมากขึ้น อันดับแรกมันเป็นสตริงที่คงที่จากนั้นเป็นอาร์กิวเมนต์ที่ผู้ใช้กำหนดจากนั้นเป็นอาร์กิวเมนต์ที่โปรแกรมอื่นดำเนินการโดยผู้ใช้ แต่พวกเขาไม่เข้าใจแล้วมันเป็นอาร์กิวเมนต์ที่เบราว์เซอร์ของพวกเขาแตกออกจากหน้าเว็บ หากสิ่งต่าง ๆ ยังคง“ ตกต่ำ” ในที่สุดก็มีคนต้องรับผิดชอบในการฆ่าเชื้อข้อมูลและคาร์ลบอกว่ามันใช้ความระมัดระวังอย่างมากในการระบุจุดนั้นไม่ว่ามันจะมาที่ใด
Steve Jessop

6
แม้ว่าถ้าหลักการป้องกันไว้ก่อนจริงๆคือ "สมมติว่านักพัฒนาที่ประมาทที่สุดที่คุณรู้จักกำลังทำการเปลี่ยนแปลงที่ไม่ปลอดภัย" และฉันต้องเขียนโค้ดเพื่อรับประกันว่าไม่สามารถส่งผลให้เกิดการเอารัดเอาเปรียบได้โดยส่วนตัวแล้วฉันก็ไม่สามารถลุกจากเตียงได้ นักพัฒนาประมาทมากที่สุดที่ฉันรู้ให้ Machievelli มีลักษณะเหมือนเขาจะไม่ได้พยายาม
Steve Jessop

16

ในกรณีเฉพาะของคุณที่คุณต้องการเรียกใช้udevadmฉันสงสัยว่าคุณสามารถดึงเข้ามาในudevฐานะห้องสมุดและทำการเรียกฟังก์ชันที่เหมาะสมเป็นทางเลือกได้หรือไม่?

เช่นคุณสามารถดูสิ่งที่ udevadm ทำเองเมื่อเรียกใช้ในโหมด "info": https://github.com/gentoo/eudev/blob/master/src/udev/udevadm-info.cและทำการโทร ตามที่ udevadm กำลังทำอยู่

สิ่งนี้จะหลีกเลี่ยงข้อเสีย - ข้อ จำกัด ทางการค้าที่แจกแจงไว้ในคำตอบที่ยอดเยี่ยมของ Brian Agnew - เช่นไม่พึ่งพาไบนารีที่มีอยู่ในบางเส้นทางหลีกเลี่ยงค่าใช้จ่ายในการตีเป็นต้น


ขอบคุณฉันพบ repo ที่แน่นอนและฉันสิ้นสุดมองผ่านสำหรับบิต
johnny_boy

1
…และ libudev นั้นใช้งานได้ยากโดยเฉพาะ การจัดการข้อผิดพลาดของมันค่อนข้างขาดหายไป แต่การแจกแจงการสืบค้นและการตรวจสอบ API นั้นค่อนข้างตรงไปตรงมา การต่อสู้กับความกลัวของคุณอาจกลายเป็นน้อยกว่า 2 ชั่วโมงถ้าคุณลองดู
สเปกตรัม

7

คำถามของคุณดูเหมือนจะเรียกคำตอบของฟอเรสต์และคำตอบที่นี่ดูเหมือนกับคำตอบของต้นไม้ดังนั้นฉันคิดว่าฉันจะให้คำตอบกับฟอเรสต์ให้คุณ

นี่เป็นสิ่งที่ไม่ค่อยเขียนโปรแกรม C เป็นวิธีที่เชลล์สคริปต์จะเขียนเสมอและบางครั้งวิธีเขียนโปรแกรม Python, perl หรือ Ruby

โดยทั่วไปผู้คนเขียนด้วยภาษา C เพื่อให้ง่ายต่อการใช้งานไลบรารี่ของระบบและการเข้าถึงการเรียกใช้ระบบปฏิบัติการระดับต่ำและความเร็ว และ C เป็นภาษาที่ยากต่อการเขียนดังนั้นหากผู้คนไม่ต้องการสิ่งเหล่านั้นพวกเขาก็ไม่ได้ใช้ C. โดยทั่วไปแล้วโปรแกรม C นั้นคาดว่าจะมีการพึ่งพาไลบรารีที่ใช้ร่วมกันและไฟล์กำหนดค่าเท่านั้น

การปอกเปลือกออกไปยังกระบวนการย่อยนั้นไม่รวดเร็วเป็นพิเศษและไม่จำเป็นต้องมีการเข้าถึงอย่างละเอียดและควบคุมสิ่งอำนวยความสะดวกของระบบระดับต่ำ ในโปรแกรม C

มีข้อกังวลเพิ่มเติมอยู่บ้าง ความปลอดภัยและการพกพาที่ผู้คนพูดถึงนั้นมีความสมบูรณ์ แน่นอนว่ามันใช้ได้กับเชลล์สคริปต์แน่นอน แต่ผู้คนคาดหวังว่าปัญหาเหล่านั้นในเชลล์สคริปต์ แต่โดยทั่วไปโปรแกรม C มักไม่ได้รับการคาดหวังว่าจะมีความกังวลด้านความปลอดภัยระดับนี้ซึ่งทำให้เป็นอันตรายมากขึ้น

แต่ในความคิดของฉันความกังวลที่ใหญ่ที่สุดเกี่ยวกับวิธีที่popenจะโต้ตอบกับส่วนที่เหลือของโปรแกรมของคุณ popenต้องสร้างกระบวนการลูกอ่านเอาต์พุตและรวบรวมสถานะทางออก ในระหว่างนี้ stderr ของกระบวนการนั้นจะเชื่อมต่อกับ stderr เดียวกันกับโปรแกรมของคุณซึ่งอาจทำให้เกิดผลลัพธ์ที่สับสนและ stdin นั้นจะเหมือนกับโปรแกรมของคุณซึ่งอาจทำให้เกิดปัญหาที่น่าสนใจอื่น ๆ คุณสามารถแก้ไขได้โดยรวม</dev/null 2>/dev/nullไว้ในสตริงที่คุณส่งผ่านpopenเนื่องจากเชลล์ตีความโดย

และpopenสร้างกระบวนการลูก หากคุณทำอะไรกับการจัดการสัญญาณหรือฟอร์กกระบวนการด้วยตัวคุณเองคุณอาจได้รับSIGCHLDสัญญาณแปลก ๆ การโทรติดต่อของคุณwaitอาจมีปฏิกิริยาแปลก ๆpopenและอาจทำให้เกิดสภาพการแข่งขันที่แปลกประหลาด

แน่นอนเรื่องความปลอดภัยและการพกพานั้นแน่นอน เนื่องจากมันใช้สำหรับเชลล์สคริปต์หรือสิ่งใดก็ตามที่เริ่มต้นการทำงานอื่น ๆ บนระบบ และคุณจะต้องระมัดระวังว่าคนที่ใช้โปรแกรมของคุณจะไม่สามารถที่จะได้รับเปลือก meta-charcaters เป็นสตริงที่คุณผ่านเข้าไปpopenเพราะสตริงที่ให้ไปด้วยshsh -c <string from popen as a single argument>

แต่ผมไม่คิดว่าพวกเขาว่าทำไมมันดูแปลก ๆ โปรแกรม C popenใช้ เหตุผลที่แปลกคือเพราะ C เป็นภาษาระดับต่ำและpopenไม่ใช่ระดับต่ำ และเนื่องจากการใช้popenข้อ จำกัด การออกแบบสถานที่ในโปรแกรมของคุณเพราะมันจะมีปฏิสัมพันธ์กับอินพุตและเอาต์พุตมาตรฐานของโปรแกรมและทำให้การจัดการกระบวนการหรือการจัดการสัญญาณของคุณเป็นเรื่องลำบาก และเนื่องจากโดยทั่วไปแล้วโปรแกรม C มักไม่ได้คาดหวังว่าจะต้องพึ่งพาโปรแกรมภายนอก


0

โปรแกรมของคุณอาจถูกแฮ็คเป็นต้นวิธีหนึ่งในการป้องกันตัวคุณเองจากกิจกรรมประเภทนี้คือการสร้างสำเนาของสภาพแวดล้อมเครื่องปัจจุบันของคุณและเรียกใช้โปรแกรมของคุณโดยใช้ระบบที่เรียกว่า chroot

จากมุมมองของโปรแกรมของคุณมันทำงานในสภาพแวดล้อมปกติจากด้านความปลอดภัยหากใครบางคนทำลายโปรแกรมของคุณมันจะสามารถเข้าถึงองค์ประกอบที่คุณให้ไว้เมื่อคุณทำสำเนาเท่านั้น

เช่นการตั้งค่าที่เรียกว่าคุก chroot สำหรับรายละเอียดเพิ่มเติมดู คุก chroot

โดยปกติจะใช้สำหรับการตั้งค่าเซิร์ฟเวอร์ที่สาธารณชนเข้าถึงเป็นต้น


3
OP กำลังเขียนโปรแกรมเพื่อให้ผู้อื่นทำงานไม่ใช่เล่นกับไบนารีหรือแหล่งข้อมูลที่ไม่น่าเชื่อถือบนระบบของเขา
Peter Cordes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.