2. إنشاء سياسة وضع الحماية

بعد أن يكون لديك جهة تنفيذ، ستحتاج على الأرجح إلى تحديد سياسة وضع الحماية لتطبيق Sandboxee. وبخلاف ذلك، لا تتم حماية تطبيق Sandboxee إلا من خلال سياسة Syscall التلقائية.

تهدف سياسة وضع الحماية إلى تقييد مكالمات النظام والوسيطات التي يمكن أن ينشئها مستخدم الحماية، بالإضافة إلى الملفات التي يمكنه الوصول إليها. ستحتاج إلى فهم مفصل لاستدعاءات النظام المطلوبة من خلال الكود الذي تخطط لوضعه في وضع الحماية. تتمثل إحدى طرق مراقبة استدعاءات النظام في تشغيل التعليمات البرمجية باستخدام مسار أداة سطر الأوامر في Linux.

بمجرد حصولك على قائمة باستدعاءات النظام، يمكنك استخدام PolicyBuilder لتحديد السياسة. تتوفّر في "أداة إنشاء السياسات" العديد من الوظائف السهلة والمساعِدة التي تتيح إجراء العديد من العمليات الشائعة. القائمة التالية هي مجرد مقتطف صغير من الدوال المتاحة:

  • يمكنك إضافة أي طلب نظام إلى القائمة المسموح بها لبدء تشغيل العملية:
    • AllowStaticStartup();
    • AllowDynamicStartup();
  • يمكنك إضافة أي طلبات أنظمة /read/write* مفتوحة إلى القائمة المسموح بها:
    • AllowOpen();
    • AllowRead();
    • AllowWrite();
  • يمكنك إضافة أي مكالمات نظام ذات صلة بالخروج/الوصول/الحالة إلى القائمة المسموح بها:
    • AllowExit();
    • AllowStat();
    • AllowAccess();
  • يمكنك إضافة أي طلبات أنظمة متعلّقة بالنوم أو الوقت إلى القائمة المسموح بها:
    • AllowTime();
    • AllowSleep();

تضيف وظائف الراحة هذه أي نظام ذات صلة إلى القائمة المسموح بها. ويتميز ذلك بأنه يمكن استخدام نفس السياسة على بُنى مختلفة حيث لا تكون بعض استدعاءات النظام غير متاحة (على سبيل المثال، لا يحتوي نظام ARM64 على استدعاء نظام مفتوح)، ولكن مع المخاطر الأمنية البسيطة التي تتمثل في تفعيل المزيد من Sycsall مما قد يكون ضروريًا. على سبيل المثال، يُمكِّن LetOpen() تطبيق Sandboxee من استدعاء أي نظام syscall مفتوح. إذا كنت تريد إضافة طلب نظام واحد فقط إلى القائمة المسموح بها، يمكنك استخدام AllowSyscall(); للسماح باستدعاءات أنظمة متعددة في الوقت نفسه، ويمكنك استخدام AllowSyscalls().

حتى الآن، تتحقق السياسة من مُعرّف syscall فقط. إذا كنت بحاجة إلى مزيد من تقوية السياسة وأردت تحديد سياسة تسمح فيها باستدعاء نظام فقط باستخدام وسيطات معيّنة، عليك استخدام AddPolicyOnSyscall() أو AddPolicyOnSyscalls(). لا تأخذ هذه الدوال معرّف طلب النظام كوسيطة فحسب، بل تستخدم أيضًا عامل تصفية seccomp-bpf أولي باستخدام وحدات ماكرو مساعد bpf من نواة Linux. راجِع مستندات النواة (kernel) للحصول على مزيد من المعلومات حول BPF. إذا وجدت نفسك تكتب كود BPF متكررًا تعتقد أنه يجب أن يكون به برنامج تضمين قابلية الاستخدام، فلا تتردد في تقديم طلب ميزة.

بالإضافة إلى الدوال المرتبطة باستدعاء النظام، يوفّر PolicyBuilder أيضًا عددًا من الدوال ذات الصلة بنظام الملفات، مثل AddFile() أو AddDirectory() لربط ملف/دليل في وضع الحماية. يمكن استخدام مساعد AddTmpfs() لإضافة مساحة تخزين مؤقتة للملفات في وضع الحماية.

وهي دالة مفيدة بشكل خاص وهي AddLibrariesForBinary() التي تضيف المكتبات وأداة الربط المطلوبة من خلال البرنامج الثنائي.

لا يزال إيجاد طلبات النظم إلى القائمة المسموح بها يتطلب بعض العمل اليدوي للأسف. أنشئ سياسة باستدعاءات النظام التي تعرفها باحتياجات برنامجك الثنائي وشغلها بأعباء عمل شائعة. وفي حال حدوث انتهاك، يُرجى إضافة طلب النظام إلى القائمة المسموح بها وتكرار العملية. إذا واجهت مخالفة تعتقد أنّها قد تؤدي إلى إضافة موقعك الإلكتروني إلى القائمة المسموح بها وعالج البرنامج الأخطاء بشكل ملائم، يمكنك محاولة عرض رسالة خطأ بدلاً من ذلك باستخدام 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();
}