1*7af41682SXin LI /*
2*7af41682SXin LI * Redistribution and use in source and binary forms, with or without
3*7af41682SXin LI * modification, are permitted provided that the following conditions
4*7af41682SXin LI * are met:
5*7af41682SXin LI * 1. Redistributions of source code must retain the above copyright
6*7af41682SXin LI * notice immediately at the beginning of the file, without modification,
7*7af41682SXin LI * this list of conditions, and the following disclaimer.
8*7af41682SXin LI * 2. Redistributions in binary form must reproduce the above copyright
9*7af41682SXin LI * notice, this list of conditions and the following disclaimer in the
10*7af41682SXin LI * documentation and/or other materials provided with the distribution.
11*7af41682SXin LI *
12*7af41682SXin LI * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13*7af41682SXin LI * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14*7af41682SXin LI * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15*7af41682SXin LI * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
16*7af41682SXin LI * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17*7af41682SXin LI * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18*7af41682SXin LI * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19*7af41682SXin LI * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20*7af41682SXin LI * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21*7af41682SXin LI * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22*7af41682SXin LI * SUCH DAMAGE.
23*7af41682SXin LI */
24*7af41682SXin LI /* Landlock sandbox: read anywhere, write only in $TMPDIR. */
25*7af41682SXin LI #include "file.h"
26*7af41682SXin LI
27*7af41682SXin LI #if HAVE_LINUX_LANDLOCK_H
28*7af41682SXin LI #include "magic.h"
29*7af41682SXin LI #include <linux/landlock.h>
30*7af41682SXin LI #include <sys/prctl.h>
31*7af41682SXin LI #include <sys/syscall.h>
32*7af41682SXin LI #include <sys/stat.h>
33*7af41682SXin LI #include <fcntl.h>
34*7af41682SXin LI #include <stdlib.h>
35*7af41682SXin LI #include <string.h>
36*7af41682SXin LI #include <errno.h>
37*7af41682SXin LI #include <unistd.h>
38*7af41682SXin LI
39*7af41682SXin LI /* glibc only got Landlock wrappers in 2.40, so call via syscall(2). */
40*7af41682SXin LI #ifndef landlock_create_ruleset
41*7af41682SXin LI static inline int
landlock_create_ruleset(const struct landlock_ruleset_attr * attr,size_t size,uint32_t flags)42*7af41682SXin LI landlock_create_ruleset(const struct landlock_ruleset_attr *attr,
43*7af41682SXin LI size_t size, uint32_t flags)
44*7af41682SXin LI {
45*7af41682SXin LI return CAST(int, syscall(__NR_landlock_create_ruleset, attr, size,
46*7af41682SXin LI flags));
47*7af41682SXin LI }
48*7af41682SXin LI #endif
49*7af41682SXin LI
50*7af41682SXin LI #ifndef landlock_add_rule
51*7af41682SXin LI static inline int
landlock_add_rule(int ruleset_fd,enum landlock_rule_type rule_type,const void * rule_attr,uint32_t flags)52*7af41682SXin LI landlock_add_rule(int ruleset_fd, enum landlock_rule_type rule_type,
53*7af41682SXin LI const void *rule_attr, uint32_t flags)
54*7af41682SXin LI {
55*7af41682SXin LI return CAST(int, syscall(__NR_landlock_add_rule, ruleset_fd,
56*7af41682SXin LI rule_type, rule_attr, flags));
57*7af41682SXin LI }
58*7af41682SXin LI #endif
59*7af41682SXin LI
60*7af41682SXin LI #ifndef landlock_restrict_self
61*7af41682SXin LI static inline int
landlock_restrict_self(int ruleset_fd,uint32_t flags)62*7af41682SXin LI landlock_restrict_self(int ruleset_fd, uint32_t flags)
63*7af41682SXin LI {
64*7af41682SXin LI return CAST(int, syscall(__NR_landlock_restrict_self, ruleset_fd,
65*7af41682SXin LI flags));
66*7af41682SXin LI }
67*7af41682SXin LI #endif
68*7af41682SXin LI
69*7af41682SXin LI /* A missing path (e.g. unset $TMPDIR) is not fatal, just skipped. */
70*7af41682SXin LI static int
landlock_allow_path(int ruleset_fd,const char * path,uint64_t allowed)71*7af41682SXin LI landlock_allow_path(int ruleset_fd, const char *path, uint64_t allowed)
72*7af41682SXin LI {
73*7af41682SXin LI struct landlock_path_beneath_attr pb;
74*7af41682SXin LI int rv;
75*7af41682SXin LI
76*7af41682SXin LI pb.allowed_access = allowed;
77*7af41682SXin LI pb.parent_fd = open(path, O_PATH | O_CLOEXEC);
78*7af41682SXin LI if (pb.parent_fd == -1)
79*7af41682SXin LI return errno == ENOENT ? 0 : -1;
80*7af41682SXin LI rv = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &pb, 0);
81*7af41682SXin LI (void)close(pb.parent_fd);
82*7af41682SXin LI return rv;
83*7af41682SXin LI }
84*7af41682SXin LI
85*7af41682SXin LI int
enable_landlock(int flags,int action)86*7af41682SXin LI enable_landlock(int flags, int action)
87*7af41682SXin LI {
88*7af41682SXin LI struct landlock_ruleset_attr attr;
89*7af41682SXin LI struct stat sb;
90*7af41682SXin LI int ruleset_fd, abi, needs_write;
91*7af41682SXin LI const char *tmpdir;
92*7af41682SXin LI
93*7af41682SXin LI (void)flags;
94*7af41682SXin LI
95*7af41682SXin LI /* Magic build modes write outside /tmp; just skip Landlock
96*7af41682SXin LI for those. seccomp still applies. */
97*7af41682SXin LI if (action == FILE_COMPILE || action == FILE_CHECK ||
98*7af41682SXin LI action == FILE_LIST)
99*7af41682SXin LI return 0;
100*7af41682SXin LI
101*7af41682SXin LI /* Pipe input gets copied to a tempfile in /tmp. */
102*7af41682SXin LI needs_write = fstat(STDIN_FILENO, &sb) == 0 && S_ISFIFO(sb.st_mode);
103*7af41682SXin LI
104*7af41682SXin LI abi = CAST(int, syscall(__NR_landlock_create_ruleset, NULL, 0,
105*7af41682SXin LI LANDLOCK_CREATE_RULESET_VERSION));
106*7af41682SXin LI if (abi < 1)
107*7af41682SXin LI return 0;
108*7af41682SXin LI
109*7af41682SXin LI (void)memset(&attr, 0, sizeof(attr));
110*7af41682SXin LI attr.handled_access_fs =
111*7af41682SXin LI LANDLOCK_ACCESS_FS_EXECUTE |
112*7af41682SXin LI LANDLOCK_ACCESS_FS_WRITE_FILE |
113*7af41682SXin LI LANDLOCK_ACCESS_FS_READ_FILE |
114*7af41682SXin LI LANDLOCK_ACCESS_FS_READ_DIR |
115*7af41682SXin LI LANDLOCK_ACCESS_FS_REMOVE_DIR |
116*7af41682SXin LI LANDLOCK_ACCESS_FS_REMOVE_FILE |
117*7af41682SXin LI LANDLOCK_ACCESS_FS_MAKE_CHAR |
118*7af41682SXin LI LANDLOCK_ACCESS_FS_MAKE_DIR |
119*7af41682SXin LI LANDLOCK_ACCESS_FS_MAKE_REG |
120*7af41682SXin LI LANDLOCK_ACCESS_FS_MAKE_SOCK |
121*7af41682SXin LI LANDLOCK_ACCESS_FS_MAKE_FIFO |
122*7af41682SXin LI LANDLOCK_ACCESS_FS_MAKE_BLOCK |
123*7af41682SXin LI LANDLOCK_ACCESS_FS_MAKE_SYM;
124*7af41682SXin LI #ifdef LANDLOCK_ACCESS_FS_REFER
125*7af41682SXin LI if (abi >= 2)
126*7af41682SXin LI attr.handled_access_fs |= LANDLOCK_ACCESS_FS_REFER;
127*7af41682SXin LI #endif
128*7af41682SXin LI #ifdef LANDLOCK_ACCESS_FS_TRUNCATE
129*7af41682SXin LI if (abi >= 3)
130*7af41682SXin LI attr.handled_access_fs |= LANDLOCK_ACCESS_FS_TRUNCATE;
131*7af41682SXin LI #endif
132*7af41682SXin LI #ifdef LANDLOCK_ACCESS_NET_BIND_TCP
133*7af41682SXin LI if (abi >= 4) {
134*7af41682SXin LI attr.handled_access_net =
135*7af41682SXin LI LANDLOCK_ACCESS_NET_BIND_TCP |
136*7af41682SXin LI LANDLOCK_ACCESS_NET_CONNECT_TCP;
137*7af41682SXin LI }
138*7af41682SXin LI #endif
139*7af41682SXin LI
140*7af41682SXin LI ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
141*7af41682SXin LI if (ruleset_fd == -1)
142*7af41682SXin LI return -1;
143*7af41682SXin LI
144*7af41682SXin LI if (landlock_allow_path(ruleset_fd, "/",
145*7af41682SXin LI LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR) == -1)
146*7af41682SXin LI goto fail;
147*7af41682SXin LI
148*7af41682SXin LI if (needs_write) {
149*7af41682SXin LI uint64_t tmp_access =
150*7af41682SXin LI LANDLOCK_ACCESS_FS_READ_FILE |
151*7af41682SXin LI LANDLOCK_ACCESS_FS_READ_DIR |
152*7af41682SXin LI LANDLOCK_ACCESS_FS_WRITE_FILE |
153*7af41682SXin LI LANDLOCK_ACCESS_FS_MAKE_REG |
154*7af41682SXin LI LANDLOCK_ACCESS_FS_REMOVE_FILE;
155*7af41682SXin LI #ifdef LANDLOCK_ACCESS_FS_TRUNCATE
156*7af41682SXin LI if (abi >= 3)
157*7af41682SXin LI tmp_access |= LANDLOCK_ACCESS_FS_TRUNCATE;
158*7af41682SXin LI #endif
159*7af41682SXin LI tmpdir = getenv("TMPDIR");
160*7af41682SXin LI if (tmpdir == NULL || *tmpdir == '\0')
161*7af41682SXin LI tmpdir = "/tmp";
162*7af41682SXin LI if (landlock_allow_path(ruleset_fd, tmpdir, tmp_access) == -1)
163*7af41682SXin LI goto fail;
164*7af41682SXin LI }
165*7af41682SXin LI
166*7af41682SXin LI if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
167*7af41682SXin LI goto fail;
168*7af41682SXin LI if (landlock_restrict_self(ruleset_fd, 0) == -1)
169*7af41682SXin LI goto fail;
170*7af41682SXin LI
171*7af41682SXin LI (void)close(ruleset_fd);
172*7af41682SXin LI return 0;
173*7af41682SXin LI fail:
174*7af41682SXin LI (void)close(ruleset_fd);
175*7af41682SXin LI return -1;
176*7af41682SXin LI }
177*7af41682SXin LI
178*7af41682SXin LI #endif /* HAVE_LINUX_LANDLOCK_H */
179