xref: /freebsd/contrib/file/src/landlock.c (revision 7af41682a96bf7058b82665c33bb9b1bfa079c17)
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