2. Korumalı alan politikası oluşturma
Bir yürütücünüz olduğunda, Sandboxee için bir Sandbox Politikası tanımlamak isteyebilirsiniz. Aksi takdirde Sandboxee yalnızca Varsayılan Syscall Politikası tarafından korunur.
Korumalı alan politikasının amacı, korumalı alanın yapabileceği sistem çağrılarını ve bağımsız değişkenleri, ayrıca erişebileceği dosyaları kısıtlamaktır. Korumalı alana almak istediğiniz kodun gerektirdiği sistem çağrılarını ayrıntılı bir şekilde anlamanız gerekir. Sistem çağrılarını gözlemlemenin bir yolu, kodu Linux'un komut satırı aracı strace ile çalıştırmaktır.
Sistem çağrılarının listesini aldıktan sonra politikayı tanımlamak için PolicyBuilder'ı kullanabilirsiniz.
PolicyBuilder, birçok yaygın işlemi (çoğu DefaultPolicyBuilder() içinde yer alır) yapmanıza olanak tanıyan birçok kolaylık ve yardımcı işlevle birlikte gelir. Aşağıdaki liste, kullanılabilir işlevlerin yalnızca küçük bir kısmını içerir:
- İşlem başlatma için tüm sistem çağrılarını izin verilenler listesine ekleyin:
AllowStaticStartup();AllowDynamicStartup();
- Tüm open/read/write* sistem çağrılarını izin verilenler listesine ekleyin:
AllowOpen();AllowRead();AllowWrite();
- Çıkış/erişim/durumla ilgili tüm sistem çağrılarını izin verilenler listesine ekleyin:
AllowExit();AllowStat();AllowAccess();
- Uyku/zamanla ilgili tüm sistem çağrılarını izin verilenler listesine ekleyin:
AllowTime();AllowSleep();
Bu kolaylık işlevleri, ilgili tüm sistem çağrılarını izin verilenler listesine ekler. Bu yöntemin avantajı, belirli sistem çağrılarının kullanılamadığı farklı mimarilerde aynı politikanın kullanılabilmesidir (ör.ARM64'te OPEN sistem çağrısı yoktur). Ancak bu yöntemde, gerekenden daha fazla sistem çağrısının etkinleştirilmesi gibi küçük bir güvenlik riski vardır. Örneğin, AllowOpen() işlevi, Sandboxee'nin açık olan tüm sistem çağrılarını çağırmasına olanak tanır. Yalnızca belirli bir syscall'ı izin verilenler listesine eklemek istiyorsanız AllowSyscall(); kullanabilirsiniz. Birden fazla syscall'ı aynı anda izin verilenler listesine eklemek için AllowSyscalls() kullanabilirsiniz.
Şu ana kadar politika yalnızca syscall tanımlayıcısını kontrol etmektedir. Politikayı daha da güçlendirmek istiyorsanız ve yalnızca belirli bağımsız değişkenlere sahip bir sistem çağrısına izin veren bir politika tanımlamak istiyorsanız AddPolicyOnSyscall() veya AddPolicyOnSyscalls() kullanmanız gerekir. Bu işlevler, bağımsız değişken olarak yalnızca sistem çağrısı kimliğini değil, aynı zamanda Linux çekirdeğindeki bpf yardımcı makrolarını kullanan ham bir seccomp-bpf filtresini de alır. BPF hakkında daha fazla bilgi için çekirdek belgelerine bakın. Kullanılabilirlik sarmalayıcısı olması gerektiğini düşündüğünüz tekrarlayan BPF kodları yazıyorsanız özellik isteğinde bulunabilirsiniz.
PolicyBuilder, syscall ile ilgili işlevlerin yanı sıra bir dosyayı/dizini korumalı alana bağlamak için AddFile() veya AddDirectory() gibi dosya sistemiyle ilgili bir dizi işlev de sağlar. Korumalı alana geçici bir dosya depolama alanı eklemek için AddTmpfs() yardımcısı kullanılabilir.
Özellikle faydalı bir işlev olan AddLibrariesForBinary(), ikili dosya için gereken kitaplıkları ve bağlayıcıyı ekler.
İzin verilenler listesine eklenecek sistem çağrılarını belirlemek maalesef hâlâ biraz manuel çalışma gerektiriyor. İkili programınızın ihtiyaç duyduğunu bildiğiniz sistem çağrılarını içeren bir politika oluşturun ve bu politikayı yaygın bir iş yüküyle çalıştırın. Bir ihlal tetiklenirse sistem çağrısını izin verilenler listesine ekleyin ve işlemi tekrarlayın. İzin verilenler listesine eklemenin riskli olabileceğini düşündüğünüz ve programın hataları düzgün şekilde işlediği bir ihlalle karşılaşırsanız BlockSyscallWithErrno() ile bunun yerine hata döndürmesini sağlayabilirsiniz.
Aşağıdaki örnek, sandbox2::PolicyBuilder doğrudan oluşturarak write ve mprotect bağımsız değişkenlerinin kısıtlanmasını ve openat'ün engellenmesini (aksi takdirde DefaultPolicyBuilder()'in izin vereceği) gösterir:
#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();
}