]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/setpriv-landlock.c
Merge branch 'PR/libsmartcols-cell-data' of github.com:karelzak/util-linux-work
[thirdparty/util-linux.git] / sys-utils / setpriv-landlock.c
1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de>
10 */
11
12 #include <sys/prctl.h>
13 #include <sys/syscall.h>
14 #include <linux/landlock.h>
15
16 #include "setpriv-landlock.h"
17
18 #include "strutils.h"
19 #include "xalloc.h"
20 #include "nls.h"
21 #include "c.h"
22
23 #ifndef HAVE_LANDLOCK_CREATE_RULESET
24 static inline int landlock_create_ruleset(
25 const struct landlock_ruleset_attr *attr,
26 size_t size, uint32_t flags)
27 {
28 return syscall(__NR_landlock_create_ruleset, attr, size, flags);
29 }
30 #endif
31
32 #ifndef HAVE_LANDLOCK_ADD_RULE
33 static inline int landlock_add_rule(
34 int ruleset_fd, enum landlock_rule_type rule_type,
35 const void *rule_attr, uint32_t flags)
36 {
37 return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type,
38 rule_attr, flags);
39 }
40 #endif
41
42 #ifndef HAVE_LANDLOCK_RESTRICT_SELF
43 static inline int landlock_restrict_self(int ruleset_fd, uint32_t flags)
44 {
45 return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
46 }
47 #endif
48
49 #define SETPRIV_EXIT_PRIVERR 127 /* how we exit when we fail to set privs */
50
51 struct landlock_rule_entry {
52 struct list_head head;
53 enum landlock_rule_type rule_type;
54 union {
55 struct landlock_path_beneath_attr path_beneath_attr;
56 };
57 };
58
59 static const struct {
60 unsigned long long value;
61 const char *type;
62 } landlock_access_fs[] = {
63 { LANDLOCK_ACCESS_FS_EXECUTE, "execute" },
64 { LANDLOCK_ACCESS_FS_WRITE_FILE, "write-file" },
65 { LANDLOCK_ACCESS_FS_READ_FILE, "read-file" },
66 { LANDLOCK_ACCESS_FS_READ_DIR, "read-dir" },
67 { LANDLOCK_ACCESS_FS_REMOVE_DIR, "remove-dir" },
68 { LANDLOCK_ACCESS_FS_REMOVE_FILE, "remove-file" },
69 { LANDLOCK_ACCESS_FS_MAKE_CHAR, "make-char" },
70 { LANDLOCK_ACCESS_FS_MAKE_DIR, "make-dir" },
71 { LANDLOCK_ACCESS_FS_MAKE_REG, "make-reg" },
72 { LANDLOCK_ACCESS_FS_MAKE_SOCK, "make-sock" },
73 { LANDLOCK_ACCESS_FS_MAKE_FIFO, "make-fifo" },
74 { LANDLOCK_ACCESS_FS_MAKE_BLOCK, "make-block" },
75 { LANDLOCK_ACCESS_FS_MAKE_SYM, "make-sym" },
76 #ifdef LANDLOCK_ACCESS_FS_REFER
77 { LANDLOCK_ACCESS_FS_REFER, "refer" },
78 #endif
79 #ifdef LANDLOCK_ACCESS_FS_TRUNCATE
80 { LANDLOCK_ACCESS_FS_TRUNCATE, "truncate" },
81 #endif
82 };
83
84 static long landlock_access_to_mask(const char *str, size_t len)
85 {
86 size_t i;
87
88 for (i = 0; i < ARRAY_SIZE(landlock_access_fs); i++)
89 if (strncmp(landlock_access_fs[i].type, str, len) == 0)
90 return landlock_access_fs[i].value;
91 return -1;
92 }
93
94 static uint64_t parse_landlock_fs_access(const char *list)
95 {
96 unsigned long r = 0;
97 size_t i;
98
99 /* without argument, match all */
100 if (list[0] == '\0') {
101 for (i = 0; i < ARRAY_SIZE(landlock_access_fs); i++)
102 r |= landlock_access_fs[i].value;
103 } else {
104 if (string_to_bitmask(list, &r, landlock_access_to_mask))
105 errx(EXIT_FAILURE,
106 _("could not parse landlock fs access: %s"), list);
107 }
108
109 return r;
110 }
111
112 void parse_landlock_access(struct setpriv_landlock_opts *opts, const char *str)
113 {
114 const char *type;
115 size_t i;
116
117 if (strcmp(str, "fs") == 0) {
118 for (i = 0; i < ARRAY_SIZE(landlock_access_fs); i++)
119 opts->access_fs |= landlock_access_fs[i].value;
120 return;
121 }
122
123 type = startswith(str, "fs:");
124 if (type)
125 opts->access_fs |= parse_landlock_fs_access(type);
126 }
127
128 void parse_landlock_rule(struct setpriv_landlock_opts *opts, const char *str)
129 {
130 struct landlock_rule_entry *rule = xmalloc(sizeof(*rule));
131 const char *accesses, *path;
132 char *accesses_part;
133 int parent_fd;
134
135 accesses = startswith(str, "path-beneath:");
136 if (!accesses)
137 errx(EXIT_FAILURE, _("invalid landlock rule: %s"), str);
138 path = strchr(accesses, ':');
139 if (!path)
140 errx(EXIT_FAILURE, _("invalid landlock rule: %s"), str);
141 rule->rule_type = LANDLOCK_RULE_PATH_BENEATH;
142
143 accesses_part = xstrndup(accesses, path - accesses);
144 rule->path_beneath_attr.allowed_access = parse_landlock_fs_access(accesses_part);
145 free(accesses_part);
146
147 path++;
148
149 parent_fd = open(path, O_RDONLY | O_PATH | O_CLOEXEC);
150 if (parent_fd == -1)
151 err(EXIT_FAILURE, _("could not open file for landlock: %s"), path);
152
153 rule->path_beneath_attr.parent_fd = parent_fd;
154
155 list_add(&rule->head, &opts->rules);
156 }
157
158 void init_landlock_opts(struct setpriv_landlock_opts *opts)
159 {
160 INIT_LIST_HEAD(&opts->rules);
161 }
162
163 void do_landlock(const struct setpriv_landlock_opts *opts)
164 {
165 struct landlock_rule_entry *rule;
166 struct list_head *entry;
167 int fd, ret;
168
169 const struct landlock_ruleset_attr ruleset_attr = {
170 .handled_access_fs = opts->access_fs,
171 };
172
173 fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
174 if (fd == -1)
175 err(SETPRIV_EXIT_PRIVERR, _("landlock_create_ruleset failed"));
176
177 list_for_each(entry, &opts->rules) {
178 rule = list_entry(entry, struct landlock_rule_entry, head);
179
180 assert(rule->rule_type == LANDLOCK_RULE_PATH_BENEATH);
181
182 ret = landlock_add_rule(fd, rule->rule_type, &rule->path_beneath_attr, 0);
183 if (ret == -1)
184 err(SETPRIV_EXIT_PRIVERR, _("adding landlock rule failed"));
185 }
186
187 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
188 err(SETPRIV_EXIT_PRIVERR, _("disallow granting new privileges for landlock failed"));
189
190 if (landlock_restrict_self(fd, 0) == -1)
191 err(SETPRIV_EXIT_PRIVERR, _("landlock_restrict_self faild"));
192 }
193
194 void usage_setpriv(FILE *out)
195 {
196 size_t i;
197
198 fprintf(out, "\n");
199 fprintf(out, _("Landlock accesses:\n"));
200 fprintf(out, " Access: fs\n");
201 fprintf(out, " Rule types: path-beneath\n");
202
203 fprintf(out, " Rules: ");
204 for (i = 0; i < ARRAY_SIZE(landlock_access_fs); i++) {
205 fprintf(out, "%s", landlock_access_fs[i].type);
206 if (i == ARRAY_SIZE(landlock_access_fs) - 1)
207 fprintf(out, "\n");
208 else
209 fprintf(out, ",");
210 }
211 }