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