Ngăn quá trình mở bộ mô tả tệp mới trên Linux nhưng cho phép nhận bộ mô tả tệp qua ổ cắm


9

Tôi hiện đang làm việc trong một dự án nơi tôi có một quy trình cha mẹ thiết lập một ổ cắm, dĩa và sau đó sử dụng ổ cắm này để liên lạc. Đứa trẻ, nếu nó muốn mở một tệp (hoặc bất kỳ tài nguyên dựa trên mô tả tệp nào khác) phải luôn luôn đi đến cha mẹ, yêu cầu tài nguyên và nhận được fdgửi qua ổ cắm. Hơn nữa, tôi muốn ngăn trẻ mở bất kỳ mô tả tập tin nào.

Tôi đã vấp ngã trong setrlimitđó ngăn chặn thành công đứa trẻ mở mô tả tập tin mới, nhưng dường như nó cũng làm mất hiệu lực bất kỳ mô tả tập tin nào được gửi qua kết nối ổ cắm ban đầu. Có phương pháp nào trên Linux cho phép một quy trình duy nhất mở bất kỳ tệp nào, gửi mô tả tệp của nó tới các quy trình khác và cho phép chúng sử dụng chúng mà không cho phép các quy trình khác này tự mở bất kỳ mô tả tệp nào không?

Đối với trường hợp sử dụng của tôi có thể là bất kỳ cấu hình kernel, cuộc gọi hệ thống, v.v ... miễn là nó có thể được áp dụng sau ngã ba và miễn là nó áp dụng cho tất cả các mô tả tệp (không chỉ các tệp mà cả ổ cắm, ổ cắm, v.v.).


1
Bạn có thể quan tâm đến seccomp.
dùng253751

Câu trả lời:


6

Những gì bạn có ở đây chính xác là trường hợp sử dụng của seccomp .

Sử dụng seccomp, bạn có thể lọc các tòa nhà cao tầng theo nhiều cách khác nhau. Những gì bạn muốn làm trong tình huống này là, ngay sau khi fork(), để cài đặt một seccompbộ lọc không cho phép việc sử dụng open(2), openat(2), socket(2)(và nhiều hơn nữa). Để thực hiện điều này, bạn có thể làm như sau:

  1. Đầu tiên, tạo một bối cảnh seccomp sử dụng seccomp_init(3)với hành vi mặc định của SCMP_ACT_ALLOW.
  2. Sau đó, thêm một quy tắc vào bối cảnh sử dụng seccomp_rule_add(3)cho mỗi tòa nhà mà bạn muốn từ chối. Bạn có thể sử dụng SCMP_ACT_KILLđể tiêu diệt quá trình nếu cố gắng tòa nhà, SCMP_ACT_ERRNO(val)để làm cho tòa nhà không trả lại errnogiá trị được chỉ định hoặc bất kỳ actiongiá trị nào khác được xác định trong trang thủ công.
  3. Tải bối cảnh sử dụng seccomp_load(3)để làm cho nó hiệu quả.

Trước khi tiếp tục, LƯU Ý rằng cách tiếp cận danh sách đen như thế này nói chung yếu hơn so với cách tiếp cận danh sách trắng. Nó cho phép bất kỳ tòa nhà chọc trời nào không được phép rõ ràng và có thể dẫn đến bỏ qua bộ lọc . Nếu bạn tin rằng quy trình con bạn muốn thực thi có thể đang cố gắng tránh bộ lọc hoặc nếu bạn đã biết những tòa nhà nào sẽ cần cho trẻ em, thì cách tiếp cận danh sách trắng sẽ tốt hơn và bạn nên làm ngược lại với điều trên: tạo bộ lọc với hành động mặc định của SCMP_ACT_KILLvà cho phép các tòa nhà chọc trời cần thiết với SCMP_ACT_ALLOW. Về mặt mã, sự khác biệt là tối thiểu (danh sách trắng có thể dài hơn, nhưng các bước là như nhau).

Đây là một ví dụ về những điều trên (Tôi đang làm exit(-1)trong trường hợp có lỗi chỉ vì đơn giản):

#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);
}

Bây giờ, trong chương trình của bạn, bạn có thể gọi hàm trên để áp dụng bộ lọc seccomp ngay sau fork(), như thế này:

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

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

    // Child code here...

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

Một vài lưu ý quan trọng trên seccomp:

  • Một bộ lọc seccomp, một khi được áp dụng, không thể được loại bỏ hoặc thay đổi bởi quy trình.
  • Nếu fork(2)hoặc clone(2)được bộ lọc cho phép, mọi tiến trình con sẽ bị ràng buộc bởi cùng một bộ lọc.
  • Nếu execve(2)được cho phép, bộ lọc hiện tại sẽ được duy trì trong suốt cuộc gọi đến execve(2).
  • Nếu tòa nhà prctl(2)được cho phép, quy trình có thể áp dụng các bộ lọc tiếp theo.

2
Danh sách đen cho một hộp cát? Nói chung là một ý tưởng tồi, bạn muốn danh sách trắng thay thế.
Ded repeatator

@Ded repeatator Tôi biết, nhưng cách tiếp cận danh sách trắng không áp dụng tốt cho tình huống của OP vì họ chỉ muốn không cho phép mở mô tả tệp mới. Tôi sẽ thêm một ghi chú ở cuối.
Marco Bonelli

Cảm ơn câu trả lời, đó là những gì tôi cần. Một danh sách trắng thực sự tốt hơn cho ứng dụng mà tôi dự định ban đầu. Tôi chỉ không nghĩ rằng có nhiều thứ người ta nên hạn chế hơn là chỉ mở các mô tả tập tin.
jklmnn

@jklmnn vâng, chính xác. Tôi thực sự chỉ quên rằng socket(2)cũng có thể tạo ra một fd, vì vậy cũng nên bị chặn. Nếu bạn biết đứa trẻ xử lý một cách tiếp cận danh sách trắng là tốt hơn.
Marco Bonelli

@MarcoBonelli Một danh sách trắng chắc chắn là tốt hơn. Ăn nói lấc cấc, creat(), dup(), và dup2()là tất cả các cuộc gọi hệ thống Linux mà tập tin trở lại mô tả. Có rất nhiều cách xung quanh một danh sách đen ...
Andrew Henle
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.