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