aboutsummaryrefslogtreecommitdiff
path: root/snag/src/sandboxing.c
blob: 99c0dcc044f5dba860fd4a1a92119b2d5e7bf127 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#define _GNU_SOURCE
#include "landlock_signatures.h"
#include "util.h"
#include "sandboxing.h"
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/prctl.h>
#include <linux/prctl.h>

int create_new_landlock_ruleset(struct landlock_ruleset_attr* ruleset_attr) {

    int abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
    if (abi < 0) {
        /* Degrades gracefully if Landlock is not handled. */
        print("landlock is not enabled, sandboxing will not work\n");
        return -1;
    }
    switch (abi) {
    case 1:
        /* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */
        ruleset_attr->handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
    case 2:
        /* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */
        ruleset_attr->handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
    case 3:
        /* Removes network support for ABI < 4 */
        ruleset_attr->handled_access_net &=
            ~(LANDLOCK_ACCESS_NET_BIND_TCP |
            LANDLOCK_ACCESS_NET_CONNECT_TCP);
    case 4:
        /* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */
        ruleset_attr->handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
    case 5:
        /* Removes LANDLOCK_SCOPE_* for ABI < 6 */
        ruleset_attr->scoped &= ~(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
                                LANDLOCK_SCOPE_SIGNAL);
    case 6 ... 8:
        /* Removes LANDLOCK_ACCESS_FS_RESOLVE_UNIX for ABI < 9 */
        ruleset_attr->handled_access_fs &= ~LANDLOCK_ACCESS_FS_RESOLVE_UNIX;
    }

    int ruleset_fd = landlock_create_ruleset(ruleset_attr, sizeof(*ruleset_attr), 0);
    if (ruleset_fd < 0) {
        print("Failed to create landlock ruleset: %s", strerror(errno));
        return -1;
    }
    return ruleset_fd;
}

bool landlock_add_path_rule(int landlock_fd, const struct landlock_ruleset_attr* ruleset_attr, const char* path, struct landlock_path_beneath_attr* path_beneath) {
    path_beneath->allowed_access &= ruleset_attr->handled_access_fs;
    if (path_beneath->allowed_access) {
        path_beneath->parent_fd = open(path, O_PATH | O_CLOEXEC);
        if (path_beneath->parent_fd < 0) {
            print("Failed to open file %s: %s\n", path, strerror(errno));
            return false;
        }
        int err = landlock_add_rule(landlock_fd, LANDLOCK_RULE_PATH_BENEATH, path_beneath, 0);
        close(path_beneath->parent_fd);
        if (err) {
            print("Failed to update ruleset: %s\n", strerror(errno));
            return false;
        }
    }
    return true;
}

bool landlock_enforce(int landlock_fd) {
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
        print("Failed to restrict privileges: %s\n", strerror(errno));
        return false;
    }
    uint32_t restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON | LANDLOCK_RESTRICT_SELF_TSYNC;
    int abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
    switch (abi) {
    case 1 ... 6:
        /* Removes logging flags for ABI < 7 */
        restrict_flags &= ~(LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF |
                            LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
                            LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF);
    case 7:
        /*
         * Removes multithreaded enforcement flag for ABI < 8
         *
         * WARNING: Without this flag, calling landlock_restrict_self(2) is
         * only equivalent if the calling process is single-threaded. Below
         * ABI v8 (and as of ABI v8, when not using this flag), a Landlock
         * policy would only be enforced for the calling thread and its
         * children (and not for all threads, including parents and siblings).
         */
        restrict_flags &= ~LANDLOCK_RESTRICT_SELF_TSYNC;
    }

    if (landlock_restrict_self(landlock_fd, restrict_flags)) {
        print("Failed to enforce ruleset: %s\n", strerror(errno));
        return false;
    }
    close(landlock_fd);
    return true;
}