Apa yang Anda miliki di sini adalah kasus penggunaan seccomp .
Dengan menggunakan seccomp, Anda dapat memfilter panggilan sys dengan berbagai cara. Apa yang ingin Anda lakukan dalam situasi ini, tepat setelah fork()
, untuk menginstal seccomp
filter yang melarang penggunaan open(2)
, openat(2)
, socket(2)
(dan lebih). Untuk mencapai ini, Anda dapat melakukan hal berikut:
- Pertama, buat konteks seccomp menggunakan
seccomp_init(3)
dengan perilaku default SCMP_ACT_ALLOW
.
- Kemudian tambahkan aturan ke konteks menggunakan
seccomp_rule_add(3)
untuk setiap syscall yang ingin Anda tolak. Anda dapat menggunakan SCMP_ACT_KILL
untuk membunuh proses jika syscall dicoba, SCMP_ACT_ERRNO(val)
untuk membuat syscall gagal mengembalikan nilai yang ditentukan errno
, atau action
nilai lain yang ditentukan dalam halaman manual.
- Memuat konteks menggunakan
seccomp_load(3)
untuk membuatnya efektif.
Sebelum melanjutkan, CATATAN bahwa pendekatan daftar hitam seperti ini secara umum lebih lemah daripada pendekatan daftar putih. Ini memungkinkan syscall yang tidak dianulir secara eksplisit, dan dapat mengakibatkan bypass filter . Jika Anda yakin bahwa proses anak yang ingin Anda lakukan bisa berbahaya mencoba menghindari filter, atau jika Anda sudah tahu syscalls mana yang dibutuhkan oleh anak-anak, pendekatan daftar putih lebih baik, dan Anda harus melakukan kebalikan dari yang di atas: buat filter dengan aksi default SCMP_ACT_KILL
dan izinkan syscall yang diperlukan SCMP_ACT_ALLOW
. Dalam hal kode perbedaannya minimal (daftar putih mungkin lebih lama, tetapi langkah-langkahnya sama).
Berikut adalah contoh di atas (Saya melakukan exit(-1)
jika ada kesalahan hanya karena kesederhanaan):
#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);
}
Sekarang, di program Anda, Anda dapat memanggil fungsi di atas untuk menerapkan filter seccomp tepat setelah fork()
, seperti ini:
child_pid = fork();
if (child_pid == -1)
exit(-1);
if (child_pid == 0) {
secure();
// Child code here...
exit(0);
} else {
// Parent code here...
}
Beberapa catatan penting pada seccomp:
- Filter seccomp, sekali diterapkan, tidak dapat dihapus atau diubah oleh proses.
- Jika
fork(2)
atau clone(2)
diizinkan oleh filter, setiap proses anak akan dibatasi oleh filter yang sama.
- Jika
execve(2)
diizinkan, filter yang ada akan dipertahankan pada panggilan ke execve(2)
.
- Jika
prctl(2)
syscall diizinkan, proses dapat menerapkan filter lebih lanjut.