summaryrefslogtreecommitdiff
path: root/lockbox_common.h
blob: 2b78495ceb4428fec3995243b6a415c876ce0f81 (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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
 * lockbox_common.h - shared Landlock LSM infrastructure
 * https://rawnix.org
 *
 * Copyright (c) 2025 zorz@gmx.com
 * SPDX-License-Identifier: BSD-2-Clause
 */

#ifndef LOCKBOX_COMMON_H
#define LOCKBOX_COMMON_H

#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/landlock.h>

#include "util.h"       /* strarray_t */

#ifndef O_PATH
#define O_PATH 010000000
#endif

/* syscall() is declared in <unistd.h> under _GNU_SOURCE;
 * provide a forward declaration for strict C99 + _POSIX_C_SOURCE builds */
extern long syscall(long number, ...);

/* ── Syscall numbers (Linux 5.13+) ──────────────────────────────────── */

#ifndef SYS_landlock_create_ruleset
#define SYS_landlock_create_ruleset 444
#endif
#ifndef SYS_landlock_add_rule
#define SYS_landlock_add_rule       445
#endif
#ifndef SYS_landlock_restrict_self
#define SYS_landlock_restrict_self  446
#endif

/* ── Access right bundles ────────────────────────────────────────────── */

/*
 * ACCESS_RO: read + execute (used for installed package paths)
 * ACCESS_RW: full access (used for build directories)
 *
 * LANDLOCK_ACCESS_FS_TRUNCATE was added in ABI v3 (kernel 6.2).
 * It is included here and masked out at runtime if abi < 3.
 */
#define ACCESS_RO ( \
    LANDLOCK_ACCESS_FS_READ_FILE  | \
    LANDLOCK_ACCESS_FS_READ_DIR   | \
    LANDLOCK_ACCESS_FS_EXECUTE)

#define ACCESS_RW (ACCESS_RO                        | \
    LANDLOCK_ACCESS_FS_WRITE_FILE                   | \
    LANDLOCK_ACCESS_FS_REMOVE_FILE                  | \
    LANDLOCK_ACCESS_FS_REMOVE_DIR                   | \
    LANDLOCK_ACCESS_FS_MAKE_CHAR                    | \
    LANDLOCK_ACCESS_FS_MAKE_DIR                     | \
    LANDLOCK_ACCESS_FS_MAKE_REG                     | \
    LANDLOCK_ACCESS_FS_MAKE_SOCK                    | \
    LANDLOCK_ACCESS_FS_MAKE_FIFO                    | \
    LANDLOCK_ACCESS_FS_MAKE_BLOCK                   | \
    LANDLOCK_ACCESS_FS_MAKE_SYM                     | \
    LANDLOCK_ACCESS_FS_TRUNCATE)

/* ── Syscall wrappers ────────────────────────────────────────────────── */

static inline int ll_create_ruleset(
    const struct landlock_ruleset_attr *attr, size_t size, uint32_t flags)
{
    return (int)syscall(SYS_landlock_create_ruleset, attr, size, flags);
}

static inline int ll_add_rule(int ruleset_fd,
    enum landlock_rule_type type, const void *attr, uint32_t flags)
{
    return (int)syscall(SYS_landlock_add_rule, ruleset_fd, type, attr, flags);
}

static inline int ll_restrict_self(int ruleset_fd)
{
    return (int)syscall(SYS_landlock_restrict_self, ruleset_fd, 0);
}

/*
 * Add a Landlock read rule for path with access rights.
 * Returns 0 on success, -1 on error.
 * If path does not exist, silently succeeds (package may be partially
 * installed or the entry is a directory that was removed).
 */
static inline int ll_add_path(int ruleset_fd, const char *path, uint64_t access)
{
    struct landlock_path_beneath_attr attr;
    int fd, ret;

    fd = open(path, O_PATH | O_CLOEXEC);
    if (fd < 0)
        return 0;   /* missing path — skip silently */

    attr.allowed_access = access;
    attr.parent_fd      = fd;

    ret = ll_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &attr, 0);
    close(fd);
    return ret;
}

/* ── Lockbox profiles ────────────────────────────────────────────────── */

typedef enum {
    LOCKBOX_PROFILE_STANDARD = 0,   /* full base toolchain + declared deps   */
    LOCKBOX_PROFILE_CARGO    = 1,   /* rust + musl + vendor + CLONE_NEWNET   */
} lockbox_profile_t;

/* ── Implicit base toolchain ─────────────────────────────────────────── */

/*
 * Packages always allowed, regardless of MAKEPKG depends/makedepends.
 * These are the core build tools present in every rawnix.org build environment.
 */
extern const char *lockbox_base[];

/*
 * Cargo profile base — reuses lockbox_base at runtime.
 * llvm-toolchain (linked by cargo) transitively requires the full standard
 * base. rust itself comes from the port's depends=(rust).
 */

/* ── mkpkg db walker ──────────────────────────────────────────────── */

/*
 * Walk /var/lib/pkg/db and add Landlock read rules for every file owned
 * by packages in names[].
 *
 * names[0..base_count-1]     lockbox_base — missing packages silently skipped
 * names[base_count..total-1] declared deps — missing packages are fatal
 *
 * Returns 0 on success, -1 if a declared dep is not installed.
 */
int lb_add_rules_for_packages(int ruleset_fd,
                               const char **names, int name_count,
                               int base_count);

/*
 * As above, with a third severity region for closure-derived names:
 *
 *   names[0 .. base_count)           base      — missing warns
 *   names[base_count .. implied)     declared  — missing is fatal
 *   names[implied .. name_count)     implied   — missing warns
 *
 * lb_add_rules_for_packages() is this with implied == name_count.
 */
int lb_add_rules_ex(int ruleset_fd,
                     const char **names, int name_count,
                     int base_count, int implied_start);

/*
 * Transitive closure of declared[] over the "depends =" edges in
 * PKG_DBDIR/deps.db, excluding the declared names themselves and
 * anything in the implied-deny list.
 *
 * Returns a strarray the caller frees with strarray_free(), or NULL when
 * deps.db is absent or nothing was implied — in which case the sandbox is
 * exactly what it was before closure expansion existed.
 */
strarray_t *lb_expand_deps(const char **declared, int declared_count);

/*
 * Set up Landlock ruleset for cargo profile:
 *   - rust + musl from pkgdb (base), declared deps (fatal if missing)
 *   - $SRC/vendor/ and $SRC/.cargo/ read-only
 *   - $CARGO_HOME/ selective read (registry/ and git/ excluded)
 *   - $SRC/, $PKG/, /tmp read-write
 *   - /proc, /dev, /etc/ssl read-only
 *
 * Returns 0 on success, -1 on error.
 */
int lb_setup_cargo_ruleset(int ruleset_fd, uint64_t all_rw,
                            const char **dep_names, int dep_count);

/* convenience: max path length for lockbox internals */
#define LB_PATH_MAX 4096

#endif /* LOCKBOX_COMMON_H */