ป้องกันกระบวนการจากการเปิดไฟล์ descriptor ใหม่บน Linux แต่อนุญาตให้รับไฟล์ descriptor ผ่านซ็อกเก็ต


9

ขณะนี้ฉันกำลังทำงานในโครงการที่ฉันมีกระบวนการผู้ปกครองที่ตั้งค่า socketpair ส้อมและจากนั้นใช้ socketpair นี้เพื่อสื่อสาร เด็กถ้ามันต้องการที่จะเปิดไฟล์ (หรือทรัพยากรอื่น ๆ ที่ใช้อธิบายไฟล์อื่น ๆ ) ควรจะไปที่ผู้ปกครองขอทรัพยากรและได้รับการfdส่งผ่าน socketpair นอกจากนี้ฉันต้องการป้องกันเด็กจากการเปิดไฟล์ descriptor ใด ๆ ด้วยตัวเอง

ฉันสะดุดsetrlimitซึ่งประสบความสำเร็จในการป้องกันเด็กจากการเปิดไฟล์ descriptors ใหม่ แต่ก็ดูเหมือนว่าจะทำให้โมเดอเรเตอร์ไฟล์ใด ๆ ที่ส่งผ่านการเชื่อมต่อซ็อกเก็ตเริ่มต้นใช้งานไม่ได้ มีวิธีการใด ๆ บน Linux ที่อนุญาตให้กระบวนการเดี่ยวเปิดไฟล์ใด ๆ ส่งไฟล์ descriptor ไปยังกระบวนการอื่นและอนุญาตให้ใช้โดยไม่อนุญาตให้กระบวนการอื่นเปิดไฟล์ descriptor ด้วยตัวเอง?

สำหรับกรณีการใช้งานของฉันที่สามารถกำหนดค่าเคอร์เนลใด ๆ การเรียกของระบบและอื่น ๆ ตราบใดที่มันสามารถใช้หลังจาก fork และตราบใดที่มันใช้กับตัวอธิบายไฟล์ทั้งหมด (ไม่ใช่แค่ไฟล์ แต่ยังซ็อกเก็ตซ็อกเก็ตคู่ ฯลฯ )


1
คุณอาจสนใจใน seccomp
253751

คำตอบ:


6

สิ่งที่คุณมีตรงนี้คือกรณีการใช้งานseccompอย่างแน่นอน

ด้วย seccomp คุณสามารถกรอง syscalls ได้หลายวิธี สิ่งที่คุณต้องการจะทำอย่างไรในสถานการณ์เช่นนี้เป็นขวาหลังจากที่fork(), การติดตั้งseccompตัวกรองที่ไม่อนุญาตให้ใช้open(2), openat(2), socket(2)(และอื่น ๆ ) ในการทำสิ่งนี้ให้สำเร็จคุณสามารถทำสิ่งต่อไปนี้:

  1. ขั้นแรกให้สร้างบริบท seccomp ใช้กับการทำงานเริ่มต้นของseccomp_init(3)SCMP_ACT_ALLOW
  2. จากนั้นเพิ่มกฎในบริบทที่ใช้seccomp_rule_add(3)สำหรับ syscall แต่ละรายการที่คุณต้องการปฏิเสธ คุณสามารถใช้SCMP_ACT_KILLเพื่อฆ่ากระบวนการหากพยายาม syscall SCMP_ACT_ERRNO(val)เพื่อทำให้ syscall ล้มเหลวในการส่งคืนerrnoค่าที่ระบุหรือactionค่าอื่น ๆ ที่กำหนดไว้ในหน้าคู่มือ
  3. โหลดบริบทที่ใช้seccomp_load(3)เพื่อให้มีประสิทธิภาพ

ก่อนดำเนินการต่อโปรดทราบว่าวิธีการที่บัญชีดำแบบนี้โดยทั่วไปแล้วจะอ่อนแอกว่าวิธีที่อยู่ในบัญชีปลอดภัย จะอนุญาตให้ syscall ใด ๆ ที่ไม่ได้รับอนุญาตอย่างชัดเจนและอาจส่งผลให้ตัวกรองผ่านได้ หากคุณเชื่อว่ากระบวนการลูกที่คุณต้องการเรียกใช้อาจพยายามหลีกเลี่ยงตัวกรองโดยมีเจตนาร้ายหรือถ้าคุณรู้อยู่แล้วว่าเด็ก ๆ ต้องการตึกระฟ้าแบบใดวิธีการที่ปลอดภัยจะดีกว่าและคุณควรทำสิ่งตรงกันข้ามกับด้านบน: สร้างตัวกรองที่มีการดำเนินการเริ่มต้นของSCMP_ACT_KILLและช่วยให้ syscalls SCMP_ACT_ALLOWจำเป็นด้วย ในแง่ของรหัสความแตกต่างนั้นน้อยที่สุด (รายการที่อนุญาตอาจจะยาวกว่า แต่ขั้นตอนเหมือนกัน)

นี่คือตัวอย่างของข้างต้น (ฉันกำลังทำexit(-1)ในกรณีของข้อผิดพลาดเพียงเพื่อความเรียบง่าย):

#include <stdlib.h>
#include <seccomp.h>

static void secure(void) {
    int err;
    scmp_filter_ctx ctx;

    int blacklist[] = {
        SCMP_SYS(open),
        SCMP_SYS(openat),
        SCMP_SYS(creat),
        SCMP_SYS(socket),
        SCMP_SYS(open_by_handle_at),
        // ... possibly more ...
    };

    // Create a new seccomp context, allowing every syscall by default.
    ctx = seccomp_init(SCMP_ACT_ALLOW);
    if (ctx == NULL)
        exit(-1);

    /* Now add a filter for each syscall that you want to disallow.
       In this case, we'll use SCMP_ACT_KILL to kill the process if it
       attempts to execute the specified syscall. */

    for (unsigned i = 0; i < sizeof(blacklist) / sizeof(blacklist[0]); i++) {
        err = seccomp_rule_add(ctx, SCMP_ACT_KILL, blacklist[i], 0);
        if (err)
            exit(-1);
    }

    // Load the context making it effective.
    err = seccomp_load(ctx);
    if (err)
        exit(-1);
}

ตอนนี้ในโปรแกรมของคุณคุณสามารถเรียกใช้ฟังก์ชันข้างต้นเพื่อใช้ตัวกรอง seccomp หลังจากfork()นั้นเช่นนี้

child_pid = fork();
if (child_pid == -1)
    exit(-1);

if (child_pid == 0) {
    secure();

    // Child code here...

    exit(0);
} else {
    // Parent code here...
}

หมายเหตุสำคัญบางประการเกี่ยวกับ seccomp:

  • ตัวกรอง seccomp ที่ใช้ครั้งเดียวจะไม่สามารถลบหรือแก้ไขได้โดยกระบวนการ
  • หากfork(2)หรือclone(2)ได้รับอนุญาตจากตัวกรองกระบวนการลูกใด ๆ จะถูก จำกัด โดยตัวกรองเดียวกัน
  • หากได้รับอนุญาตการกรองที่มีอยู่จะถูกเก็บไว้ข้ามเรียกร้องให้execve(2)execve(2)
  • หากprctl(2)อนุญาตให้ syscall กระบวนการสามารถใช้ตัวกรองเพิ่มเติมได้

2
ขึ้นบัญชีดำสำหรับกล่องทราย? โดยทั่วไปแล้วเป็นความคิดที่ไม่ดีคุณต้องการบัญชีขาวแทน
Deduplicator

@Dupuplicator ฉันรู้ แต่วิธีการที่อนุญาตใช้ไม่ได้กับสถานการณ์ของ OP ได้เป็นอย่างดีเนื่องจากพวกเขาต้องการที่จะไม่อนุญาตให้เปิดตัวอธิบายไฟล์ใหม่ ฉันจะเพิ่มบันทึกย่อในตอนท้าย
Marco Bonelli

ขอบคุณสำหรับคำตอบนั่นคือสิ่งที่ฉันต้องการ บัญชีขาวนั้นดีกว่าสำหรับแอปพลิเคชันที่ฉันตั้งใจไว้ ฉันไม่ทราบว่ามีสิ่งที่ควร จำกัด มากกว่าการเปิดไฟล์อธิบาย
jklmnn

@ jklmnn ใช่แน่นอน ฉันแค่ลืมไปว่าsocket(2)สร้างได้ด้วยfdดังนั้นควรบล็อกด้วย หากคุณรู้ว่าเด็กประมวลผลรายการที่อนุญาตจะดีกว่า
Marco Bonelli

@MarcoBonelli บัญชีขาวดีกว่าแน่นอน เฉพาะหน้า, creat(), dup()และdup2()มีทุกสายระบบ Linux ที่อธิบายไฟล์ผลตอบแทน มีหลายวิธีในการขึ้นบัญชีดำ ...
Andrew Henle
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.