2 * SPDX-License-Identifier: GPL-2.0-or-later
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.
9 * Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de>
12 #include <sys/prctl.h>
13 #include <sys/syscall.h>
14 #include <linux/landlock.h>
16 #include "setpriv-landlock.h"
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
)
28 return syscall(__NR_landlock_create_ruleset
, attr
, size
, flags
);
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
)
37 return syscall(__NR_landlock_add_rule
, ruleset_fd
, rule_type
,
42 #ifndef HAVE_LANDLOCK_RESTRICT_SELF
43 static inline int landlock_restrict_self(int ruleset_fd
, uint32_t flags
)
45 return syscall(__NR_landlock_restrict_self
, ruleset_fd
, flags
);
49 #define SETPRIV_EXIT_PRIVERR 127 /* how we exit when we fail to set privs */
51 struct landlock_rule_entry
{
52 struct list_head head
;
53 enum landlock_rule_type rule_type
;
55 struct landlock_path_beneath_attr path_beneath_attr
;
60 unsigned long long value
;
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" },
79 #ifdef LANDLOCK_ACCESS_FS_TRUNCATE
80 { LANDLOCK_ACCESS_FS_TRUNCATE
, "truncate" },
84 static long landlock_access_to_mask(const char *str
, size_t len
)
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
;
94 static uint64_t parse_landlock_fs_access(const char *list
)
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
;
104 if (string_to_bitmask(list
, &r
, landlock_access_to_mask
))
106 _("could not parse landlock fs access: %s"), list
);
112 void parse_landlock_access(struct setpriv_landlock_opts
*opts
, const char *str
)
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
;
123 type
= startswith(str
, "fs:");
125 opts
->access_fs
|= parse_landlock_fs_access(type
);
128 void parse_landlock_rule(struct setpriv_landlock_opts
*opts
, const char *str
)
130 struct landlock_rule_entry
*rule
= xmalloc(sizeof(*rule
));
131 const char *accesses
, *path
;
135 accesses
= startswith(str
, "path-beneath:");
137 errx(EXIT_FAILURE
, _("invalid landlock rule: %s"), str
);
138 path
= strchr(accesses
, ':');
140 errx(EXIT_FAILURE
, _("invalid landlock rule: %s"), str
);
141 rule
->rule_type
= LANDLOCK_RULE_PATH_BENEATH
;
143 accesses_part
= xstrndup(accesses
, path
- accesses
);
144 rule
->path_beneath_attr
.allowed_access
= parse_landlock_fs_access(accesses_part
);
149 parent_fd
= open(path
, O_RDONLY
| O_PATH
| O_CLOEXEC
);
151 err(EXIT_FAILURE
, _("could not open file for landlock: %s"), path
);
153 rule
->path_beneath_attr
.parent_fd
= parent_fd
;
155 list_add(&rule
->head
, &opts
->rules
);
158 void init_landlock_opts(struct setpriv_landlock_opts
*opts
)
160 INIT_LIST_HEAD(&opts
->rules
);
163 void do_landlock(const struct setpriv_landlock_opts
*opts
)
165 struct landlock_rule_entry
*rule
;
166 struct list_head
*entry
;
169 const struct landlock_ruleset_attr ruleset_attr
= {
170 .handled_access_fs
= opts
->access_fs
,
173 fd
= landlock_create_ruleset(&ruleset_attr
, sizeof(ruleset_attr
), 0);
175 err(SETPRIV_EXIT_PRIVERR
, _("landlock_create_ruleset failed"));
177 list_for_each(entry
, &opts
->rules
) {
178 rule
= list_entry(entry
, struct landlock_rule_entry
, head
);
180 assert(rule
->rule_type
== LANDLOCK_RULE_PATH_BENEATH
);
182 ret
= landlock_add_rule(fd
, rule
->rule_type
, &rule
->path_beneath_attr
, 0);
184 err(SETPRIV_EXIT_PRIVERR
, _("adding landlock rule failed"));
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"));
190 if (landlock_restrict_self(fd
, 0) == -1)
191 err(SETPRIV_EXIT_PRIVERR
, _("landlock_restrict_self faild"));
194 void usage_setpriv(FILE *out
)
199 fprintf(out
, _("Landlock accesses:\n"));
200 fprintf(out
, " Access: fs\n");
201 fprintf(out
, " Rule types: path-beneath\n");
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)