2. एक सैंडबॉक्स नीति बनाएं

एक एक्सक्यूटर मिलने के बाद, हो सकता है कि आप सैंडबॉक्स के लिए एक सैंडबॉक्स नीति तय करना चाहें. ऐसा न होने पर, सैंडबॉक्स को सिर्फ़ डिफ़ॉल्ट सिस्कॉल नीति से सुरक्षित किया जाता है.

सैंडबॉक्स नीति का मकसद, उन सिस्टम कॉल और तर्कों को सीमित करना है जिन्हें सैंडबॉक्स बना सकता है. साथ ही, उन फ़ाइलों को भी सीमित करना है जिन्हें वह ऐक्सेस कर सकता है. आपको जिस कोड को सैंडबॉक्स करना है उसके लिए ज़रूरी सिस्टम कॉल की पूरी जानकारी होनी चाहिए. syscalls को देखने का एक तरीका यह है कि कोड को Linux के कमांड-लाइन टूल स्ट्रेस की मदद से चलाया जाए.

सिस्टम कॉल की सूची मिलने के बाद, नीति तय करने के लिए PolicyBuilder का इस्तेमाल किया जा सकता है. PolicyBuilder में कई आसान और हेल्पर फ़ंक्शन शामिल होते हैं. इनकी मदद से, कई तरह के काम किए जा सकते हैं. यहां दी गई सूची, उपलब्ध फ़ंक्शन का सिर्फ़ एक छोटा-सा हिस्सा है:

  • प्रोसेस शुरू होने के लिए, किसी भी सिस्टम को अनुमति वाली सूची में जोड़ें:
    • AllowStaticStartup();
    • AllowDynamicStartup();
  • किसी भी ओपन /read/write* syscalls को अनुमति वाली सूची में डालें:
    • AllowOpen();
    • AllowRead();
    • AllowWrite();
  • किसी भी एग्ज़िट/ऐक्सेस/राज्य से जुड़े सिस्टम कॉल को अनुमति वाली सूची में जोड़ें:
    • AllowExit();
    • AllowStat();
    • AllowAccess();
  • नींद या समय से जुड़े सिस्टम के इस्तेमाल को अनुमति दें:
    • AllowTime();
    • AllowSleep();

ये सुविधा फ़ंक्शन, किसी भी काम के सिस्टम को अनुमति वाली सूची में शामिल करते हैं. इसका फ़ायदा यह है कि एक जैसी नीति का इस्तेमाल उन अलग-अलग आर्किटेक्चर पर किया जा सकता है जहां कुछ खास सिस्टम (जैसे, ARM64 में कोई ओपन सिस्ट कॉल नहीं है) उपलब्ध नहीं हैं. हालांकि, इससे सुरक्षा के लिहाज़ से ज़्यादा सुरक्षा जोखिम होने की संभावना कम हो जाती है. उदाहरण के लिए,AllowOpen() की मदद से, Sandboxee को किसी भी ओपन से जुड़े syscall को कॉल करने में मदद मिलती है. अगर आपको सिर्फ़ एक सिस्टम को अनुमति देनी है, तो AllowSyscalls() का इस्तेमाल करें. इसके लिए, एक बार में एक से ज़्यादा सिस्टम कॉल की अनुमति देने के लिए, AllowSyscall(); का इस्तेमाल करें.

अभी तक नीति सिर्फ़ syscall आइडेंटिफ़ायर की जांच करती है. अगर नीति को और मज़बूत बनाना है और ऐसी नीति तय करना है जिसमें सिर्फ़ कुछ खास तर्कों के साथ सिस्कॉल की अनुमति है, तो आपको AddPolicyOnSyscall() या AddPolicyOnSyscalls() का इस्तेमाल करना होगा. ये फ़ंक्शन न सिर्फ़ syscall आईडी को एक तर्क के रूप में लेते हैं, बल्कि Linux कर्नेल से bpf हेल्पर मैक्रो का इस्तेमाल करके एक रॉ secCom-bpf फ़िल्टर भी इस्तेमाल करते हैं. BPF के बारे में ज़्यादा जानकारी के लिए kernel दस्तावेज़ देखें. अगर आपको बार-बार एक ही तरह के बीपीएफ़ कोड लिखने की ज़रूरत है और आपके हिसाब से इसमें उपयोगिता का रैपर होना चाहिए, तो सुविधा के लिए अनुरोध करें.

syscall से जुड़े फ़ंक्शन के अलावा, PolicyBuilder किसी फ़ाइल/डायरेक्ट्री को सैंडबॉक्स में बाइंड करने के लिए, फ़ाइल सिस्टम से जुड़े कई फ़ंक्शन भी उपलब्ध कराता है. जैसे, AddFile() या AddDirectory(). सैंडबॉक्स में फ़ाइल के लिए अस्थायी स्टोरेज जोड़ने के लिए, AddTmpfs() हेल्पर का इस्तेमाल किया जा सकता है.

AddLibrariesForBinary() खास तौर पर काम का फ़ंक्शन है. यह बाइनरी के लिए ज़रूरी लाइब्रेरी और लिंकर जोड़ता है.

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

#include "sandboxed_api/sandbox2/policy.h"
#include "sandboxed_api/sandbox2/policybuilder.h"
#include "sandboxed_api/sandbox2/util/bpf_helper.h"

std::unique_ptr<sandbox2::Policy> CreatePolicy() {
  return sandbox2::PolicyBuilder()
    .AllowSyscall(__NR_read)  // See also AllowRead()
    .AllowTime()              // Allow time, gettimeofday and clock_gettime
    .AddPolicyOnSyscall(__NR_write, {
        ARG(0),        // fd is the first argument of write (argument #0)
        JEQ(1, ALLOW), // allow write only on fd 1
        KILL,          // kill if not fd 1
    })
    .AddPolicyOnSyscall(__NR_mprotect, {
        ARG_32(2), // prot is a 32-bit wide argument, so it's OK to use *_32
                   // macro here
        JNE32(PROT_READ | PROT_WRITE, KILL), // prot must be the RW, otherwise
                                             // kill the process
        ARG(1), // len is a 64-bit argument
        JNE(0x1000, KILL),  // Allow single page syscalls only, otherwise kill
                            // the process
        ALLOW,              // Allow for the syscall to proceed, if prot and
                            // size match
    })
    // Allow the openat() syscall but always return "not found".
    .BlockSyscallWithErrno(__NR_openat, ENOENT)
    .BuildOrDie();
}