suricata
util-landlock.c
Go to the documentation of this file.
1/* Copyright (C) 2022 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \file
20 *
21 * \author Eric Leblond <el@stamus-networks.com>
22 */
23
24#include "suricata.h"
25#include "detect-engine.h"
26#include "feature.h"
27#include "util-conf.h"
28#include "util-file.h"
29#include "util-landlock.h"
30#include "util-mem.h"
31#include "util-path.h"
32#include "util-validate.h"
33
34#ifndef HAVE_LINUX_LANDLOCK_H
35
37{
38}
39
40#else /* HAVE_LINUX_LANDLOCK_H */
41
42#include <linux/landlock.h>
43
44#ifndef landlock_create_ruleset
45static inline int landlock_create_ruleset(
46 const struct landlock_ruleset_attr *const attr, const size_t size, const __u32 flags)
47{
48 long r = syscall(__NR_landlock_create_ruleset, attr, size, flags);
49 DEBUG_VALIDATE_BUG_ON(r > INT_MAX);
50 return (int)r;
51}
52#endif
53
54#ifndef landlock_add_rule
55static inline int landlock_add_rule(const int ruleset_fd, const enum landlock_rule_type rule_type,
56 const void *const rule_attr, const __u32 flags)
57{
58 long r = syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, flags);
59 DEBUG_VALIDATE_BUG_ON(r > INT_MAX);
60 return (int)r;
61}
62#endif
63
64#ifndef landlock_restrict_self
65static inline int landlock_restrict_self(const int ruleset_fd, const __u32 flags)
66{
67 long r = syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
68 DEBUG_VALIDATE_BUG_ON(r > INT_MAX);
69 return (int)r;
70}
71#endif
72
73#ifndef LANDLOCK_ACCESS_FS_REFER
74#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
75#endif
76
77#define _LANDLOCK_ACCESS_FS_WRITE \
78 (LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | \
79 LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_MAKE_CHAR | \
80 LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG | \
81 LANDLOCK_ACCESS_FS_MAKE_SOCK | LANDLOCK_ACCESS_FS_MAKE_FIFO | \
82 LANDLOCK_ACCESS_FS_MAKE_BLOCK | LANDLOCK_ACCESS_FS_MAKE_SYM | \
83 LANDLOCK_ACCESS_FS_REFER)
84
85#define _LANDLOCK_ACCESS_FS_READ (LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR)
86
87#define _LANDLOCK_SURI_ACCESS_FS_WRITE \
88 (LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG | \
89 LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_MAKE_SOCK)
90
91struct landlock_ruleset {
92 int fd;
93 struct landlock_ruleset_attr attr;
94};
95
96static inline struct landlock_ruleset *LandlockCreateRuleset(void)
97{
98 struct landlock_ruleset *ruleset = SCCalloc(1, sizeof(struct landlock_ruleset));
99 if (ruleset == NULL) {
100 SCLogError("Can't alloc landlock ruleset");
101 return NULL;
102 }
103
104 ruleset->attr.handled_access_fs =
105 _LANDLOCK_ACCESS_FS_READ | _LANDLOCK_ACCESS_FS_WRITE | LANDLOCK_ACCESS_FS_EXECUTE;
106
107 int abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
108 if (abi < 0) {
109 SCFree(ruleset);
110 return NULL;
111 }
112 if (abi < 2) {
114 SCLogError("Landlock disabled: need Linux 5.19+ for file store support");
115 SCFree(ruleset);
116 return NULL;
117 } else {
118 ruleset->attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
119 }
120 }
121
122 ruleset->fd = landlock_create_ruleset(&ruleset->attr, sizeof(ruleset->attr), 0);
123 if (ruleset->fd < 0) {
124 SCFree(ruleset);
125 SCLogError("Can't create landlock ruleset");
126 return NULL;
127 }
128 return ruleset;
129}
130
131static inline void LandlockEnforceRuleset(struct landlock_ruleset *ruleset)
132{
133 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
134 SCLogError("Can't self restrict (prctl phase): %s", strerror(errno));
135 return;
136 }
137 if (landlock_restrict_self(ruleset->fd, 0)) {
138 SCLogError("Can't self restrict (landlock phase): %s", strerror(errno));
139 }
140}
141
142static int LandlockSandboxingAddRule(
143 struct landlock_ruleset *ruleset, const char *directory, uint64_t permission)
144{
145 struct landlock_path_beneath_attr path_beneath = {
146 .allowed_access = permission & ruleset->attr.handled_access_fs,
147 };
148
149 int dir_fd = open(directory, O_PATH | O_CLOEXEC | O_DIRECTORY);
150 if (dir_fd == -1) {
151 SCLogError("Can't open %s", directory);
152 return -1;
153 }
154 path_beneath.parent_fd = dir_fd;
155
156 if (landlock_add_rule(ruleset->fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0)) {
157 SCLogError("Can't add write rule: %s", strerror(errno));
158 close(dir_fd);
159 return -1;
160 }
161
162 close(dir_fd);
163 return 0;
164}
165
166static inline void LandlockSandboxingWritePath(
167 struct landlock_ruleset *ruleset, const char *directory)
168{
169 if (LandlockSandboxingAddRule(ruleset, directory, _LANDLOCK_SURI_ACCESS_FS_WRITE) == 0) {
170 SCLogConfig("Added write permission to '%s'", directory);
171 }
172}
173
174static inline void LandlockSandboxingReadPath(
175 struct landlock_ruleset *ruleset, const char *directory)
176{
177 if (LandlockSandboxingAddRule(ruleset, directory, _LANDLOCK_ACCESS_FS_READ) == 0) {
178 SCLogConfig("Added read permission to '%s'", directory);
179 }
180}
181
183{
184 /* Read configuration variable and exit if no enforcement */
185 int conf_status;
186 if (SCConfGetBool("security.landlock.enabled", &conf_status) == 0) {
187 conf_status = 0;
188 }
189 if (!conf_status) {
190 SCLogConfig("Landlock is not enabled in configuration");
191 return;
192 }
193 struct landlock_ruleset *ruleset = LandlockCreateRuleset();
194 if (ruleset == NULL) {
195 SCLogError("Kernel does not support Landlock");
196 return;
197 }
198
199 LandlockSandboxingWritePath(ruleset, SCConfigGetLogDirectory());
200 struct stat sb;
201 if (stat(ConfigGetDataDirectory(), &sb) == 0) {
202 LandlockSandboxingAddRule(ruleset, ConfigGetDataDirectory(),
203 _LANDLOCK_SURI_ACCESS_FS_WRITE | _LANDLOCK_ACCESS_FS_READ);
204 }
206 LandlockSandboxingAddRule(ruleset, DetectEngineMpmCachingGetPath(),
207 _LANDLOCK_SURI_ACCESS_FS_WRITE | _LANDLOCK_ACCESS_FS_READ);
208 }
209 if (suri->run_mode == RUNMODE_PCAP_FILE) {
210 const char *pcap_file;
211 if (SCConfGet("pcap-file.file", &pcap_file) == 1) {
212 char *file_name = SCStrdup(pcap_file);
213 if (file_name != NULL) {
214 struct stat statbuf;
215 if (stat(file_name, &statbuf) != -1) {
216 if (S_ISDIR(statbuf.st_mode)) {
217 LandlockSandboxingReadPath(ruleset, file_name);
218 } else {
219 LandlockSandboxingReadPath(ruleset, dirname(file_name));
220 }
221 } else {
222 SCLogError("Can't open pcap file");
223 }
224 SCFree(file_name);
225 }
226 }
227 }
228 if (suri->sig_file) {
229 char *file_name = SCStrdup(suri->sig_file);
230 if (file_name != NULL) {
231 LandlockSandboxingReadPath(ruleset, dirname(file_name));
232 SCFree(file_name);
233 }
234 }
235 if (suri->pid_filename) {
236 char *file_name = SCStrdup(suri->pid_filename);
237 if (file_name != NULL) {
238 LandlockSandboxingWritePath(ruleset, dirname(file_name));
239 SCFree(file_name);
240 }
241 }
243 const char *socketname;
244 if (SCConfGet("unix-command.filename", &socketname) == 1) {
245 if (PathIsAbsolute(socketname)) {
246 char *file_name = SCStrdup(socketname);
247 if (file_name != NULL) {
248 LandlockSandboxingWritePath(ruleset, dirname(file_name));
249 SCFree(file_name);
250 }
251 } else {
252 LandlockSandboxingWritePath(ruleset, LOCAL_STATE_DIR "/run/suricata/");
253 }
254 } else {
255 LandlockSandboxingWritePath(ruleset, LOCAL_STATE_DIR "/run/suricata/");
256 }
257 }
258 if (!suri->sig_file_exclusive) {
259 const char *rule_path;
260 if (SCConfGet("default-rule-path", &rule_path) == 1 && rule_path) {
261 LandlockSandboxingReadPath(ruleset, rule_path);
262 }
263 }
264
265 SCConfNode *read_dirs = SCConfGetNode("security.landlock.directories.read");
266 if (read_dirs) {
267 if (!SCConfNodeIsSequence(read_dirs)) {
268 SCLogWarning("Invalid security.landlock.directories.read configuration section: "
269 "expected a list of directory names.");
270 } else {
271 SCConfNode *directory;
272 TAILQ_FOREACH (directory, &read_dirs->head, next) {
273 LandlockSandboxingReadPath(ruleset, directory->val);
274 }
275 }
276 }
277 SCConfNode *write_dirs = SCConfGetNode("security.landlock.directories.write");
278 if (write_dirs) {
279 if (!SCConfNodeIsSequence(write_dirs)) {
280 SCLogWarning("Invalid security.landlock.directories.write configuration section: "
281 "expected a list of directory names.");
282 } else {
283 SCConfNode *directory;
284 TAILQ_FOREACH (directory, &write_dirs->head, next) {
285 LandlockSandboxingWritePath(ruleset, directory->val);
286 }
287 }
288 }
289 LandlockEnforceRuleset(ruleset);
290 SCFree(ruleset);
291}
292
293#endif /* HAVE_LINUX_LANDLOCK_H */
struct HtpBodyChunk_ * next
SCConfNode * SCConfGetNode(const char *name)
Get a SCConfNode by name.
Definition conf.c:181
int SCConfNodeIsSequence(const SCConfNode *node)
Check if a node is a sequence or node.
Definition conf.c:925
int SCConfGetBool(const char *name, int *val)
Retrieve a configuration value as a boolean.
Definition conf.c:497
int SCConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition conf.c:350
uint8_t flags
Definition decode-gre.h:0
bool DetectEngineMpmCachingEnabled(void)
const char * DetectEngineMpmCachingGetPath(void)
bool RequiresFeature(const char *feature_name)
Definition feature.c:126
#define FEATURE_OUTPUT_FILESTORE
Definition feature.h:28
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:252
@ RUNMODE_PCAP_FILE
Definition runmodes.h:30
char * val
Definition conf.h:39
char * sig_file
Definition suricata.h:138
bool sig_file_exclusive
Definition suricata.h:139
enum SCRunModes run_mode
Definition suricata.h:134
char * pid_filename
Definition suricata.h:140
const char * SCConfigGetLogDirectory(void)
Definition util-conf.c:38
const char * ConfigGetDataDirectory(void)
Definition util-conf.c:80
int ConfUnixSocketIsEnable(void)
Definition util-conf.c:136
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCLogConfig(...)
Definition util-debug.h:229
void LandlockSandboxing(SCInstance *suri)
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define SCStrdup(s)
Definition util-mem.h:56
int PathIsAbsolute(const char *path)
Check if a path is absolute.
Definition util-path.c:44
#define DEBUG_VALIDATE_BUG_ON(exp)