लिनक्स पर नई फ़ाइल डिस्क्रिप्टर खोलने से प्रक्रिया को रोकें लेकिन सॉकेट्स के माध्यम से फ़ाइल डिस्क्रिप्टर प्राप्त करने की अनुमति दें


9

मैं वर्तमान में एक प्रोजेक्ट पर काम कर रहा हूं, जहां मेरे पास एक पेरेंट प्रोसेस है जो सॉकेटपेयर, फोर्क्स सेट करता है और फिर कम्युनिकेशन के लिए इस सॉकेटपेयर का उपयोग करता है। बच्चा, यदि वह एक फ़ाइल (या किसी अन्य फ़ाइल डिस्क्रिप्टर आधारित संसाधन) को खोलना चाहता है, तो हमेशा माता-पिता के पास जाना चाहिए, संसाधन का अनुरोध करें और fdसॉकेटपेयर के माध्यम से भेजा जाए। इसके अलावा मैं खुद से किसी भी फाइल डिस्क्रिप्टर को खोलने से बच्चे को रोकना चाहता हूं।

मैं लड़खड़ा गया, setrlimitजो सफलतापूर्वक बच्चे को नई फ़ाइल डिस्क्रिप्टर खोलने से रोकता है, लेकिन यह प्रारंभिक सॉकेट कनेक्शन पर भेजे गए किसी भी फ़ाइल डिस्क्रिप्टर को अमान्य करने के लिए भी लगता है। क्या लिनक्स पर कोई विधि है जो किसी भी प्रक्रिया को किसी भी फ़ाइल को खोलने की अनुमति देती है, अपनी फ़ाइल डिस्क्रिप्टर को अन्य प्रक्रियाओं में भेजती है और उन्हें इन अन्य प्रक्रियाओं को बिना किसी फ़ाइल डिस्क्रिप्टर को स्वयं को खोलने की अनुमति दिए बिना उनका उपयोग करने देती है?

मेरे उपयोग के मामले के लिए जो किसी भी कर्नेल कॉन्फ़िगरेशन, सिस्टम कॉल, आदि हो सकता है जब तक कि यह कांटा के बाद लगाया जा सकता है और जब तक यह सभी फ़ाइल डिस्क्रिप्टर पर लागू होता है (न केवल फाइलें बल्कि सॉकेट, सॉकेटकेस, आदि)।


1
आपकी दिलचस्पी seccomp में हो सकती है।
user253751

जवाबों:


6

आपके पास यहां जो भी है वह वास्तव में seccomp का उपयोग मामला है ।

Seccomp का उपयोग करके, आप विभिन्न तरीकों से syscalls को फ़िल्टर कर सकते हैं। इस स्थिति में आप क्या करना चाहते हैं, ठीक है, fork()एक seccompफिल्टर स्थापित करने के लिए open(2), जो openat(2), socket(2)(और अधिक) के उपयोग को रोक देता है । इसे पूरा करने के लिए, आप निम्नलिखित कार्य कर सकते हैं:

  1. पहले, seccomp_init(3)डिफ़ॉल्ट व्यवहार के साथ प्रयोग करके एक seccomp संदर्भ बनाएं 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);
}

अब, अपने कार्यक्रम में, आप fork()इस तरह से seccomp फ़िल्टर लागू करने के लिए उपरोक्त फ़ंक्शन को कॉल कर सकते हैं :

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

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

    // Child code here...

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

Seccomp पर कुछ महत्वपूर्ण नोट्स:

  • एक सेकंड फ़िल्टर, एक बार लागू होने पर, प्रक्रिया द्वारा हटाया या परिवर्तित नहीं किया जा सकता है।
  • तो fork(2)या clone(2)फिल्टर द्वारा अनुमति दी जाती है, किसी भी बच्चे की प्रक्रिया एक ही फिल्टर से विवश कर दिया जाएगा।
  • यदि execve(2)अनुमति दी जाती है, तो मौजूदा फ़िल्टर को कॉल करने के लिए संरक्षित किया जाएगा execve(2)
  • यदि prctl(2)syscall की अनुमति है, तो प्रक्रिया आगे फ़िल्टर लागू करने में सक्षम है।

2
सैंडबॉक्स के लिए ब्लैकलिस्ट करना? आम तौर पर एक बुरा विचार, आप इसके बजाय सफ़ेद करना चाहते हैं।
डेडुप्लिकेटर

@Deduplicator मुझे पता है, लेकिन एक श्वेतसूची दृष्टिकोण ओपी की स्थिति पर बहुत अच्छी तरह से लागू नहीं होता है क्योंकि वे केवल नई फ़ाइल विवरणकों को खोलना चाहते हैं। मैं अंत में एक नोट जोड़ूंगा।
मार्को बोनेली

जवाब के लिए धन्यवाद, यही मुझे चाहिए। एक श्वेतसूची वास्तव में उस एप्लिकेशन के लिए बेहतर है जिसे मैंने मूल रूप से इरादा किया था। मेरे मन में यह बात नहीं थी कि सिर्फ फाइल डिस्क्रिप्टर खोलने की तुलना में अधिक चीजें प्रतिबंधित होनी चाहिए।
jklmnn

@ जकलमन्न हां, बिल्कुल। मैं वास्तव में सिर्फ यह भूल गया कि socket(2)एक भी बना सकता है fd, इसलिए इसे भी अवरुद्ध किया जाना चाहिए। यदि आप जानते हैं कि बच्चे की प्रक्रिया एक श्वेतसूची दृष्टिकोण बेहतर है।
मार्को बोनेली

@MarcoBonelli एक श्वेतसूची निश्चित रूप से बेहतर है। बेतकल्लुफ़, creat(), dup(), और dup2()सभी लिनक्स सिस्टम कॉल कि वापसी फ़ाइल वर्णनकर्ता हैं। एक ब्लैकलिस्ट के आसपास बहुत सारे तरीके हैं ...
एंड्रयू हेनले
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.