• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/procd/jail/landlock.c

  1 /*
  2  * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
  3  *
  4  * This program is free software; you can redistribute it and/or modify
  5  * it under the terms of the GNU Lesser General Public License version 2.1
  6  * as published by the Free Software Foundation
  7  *
  8  * This program is distributed in the hope that it will be useful,
  9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 11  * GNU General Public License for more details.
 12  */
 13 
 14 #define _GNU_SOURCE
 15 
 16 #include <errno.h>
 17 #include <fcntl.h>
 18 #include <stdlib.h>
 19 #include <string.h>
 20 #include <unistd.h>
 21 #include <sys/syscall.h>
 22 
 23 #include <linux/landlock.h>
 24 
 25 #include "log.h"
 26 #include "landlock.h"
 27 
 28 static int landlock_abi_cached = -2;
 29 
 30 static int sys_landlock_create_ruleset(const struct landlock_ruleset_attr *attr,
 31                                        size_t size, uint32_t flags)
 32 {
 33         return syscall(SYS_landlock_create_ruleset, attr, size, flags);
 34 }
 35 
 36 static int sys_landlock_add_rule(int ruleset_fd, enum landlock_rule_type type,
 37                                  const void *rule_attr, uint32_t flags)
 38 {
 39         return syscall(SYS_landlock_add_rule, ruleset_fd, type, rule_attr, flags);
 40 }
 41 
 42 static int sys_landlock_restrict_self(int ruleset_fd, uint32_t flags)
 43 {
 44         return syscall(SYS_landlock_restrict_self, ruleset_fd, flags);
 45 }
 46 
 47 static int landlock_abi(void)
 48 {
 49         if (landlock_abi_cached == -2)
 50                 landlock_abi_cached = sys_landlock_create_ruleset(NULL, 0,
 51                                                            LANDLOCK_CREATE_RULESET_VERSION);
 52         return landlock_abi_cached;
 53 }
 54 
 55 bool landlock_available(void)
 56 {
 57         return landlock_abi() >= 1;
 58 }
 59 
 60 int landlock_config_add(struct landlock_config *cfg, const char *path, uint64_t access)
 61 {
 62         struct landlock_rule *r;
 63 
 64         r = realloc(cfg->rules, sizeof(*r) * (cfg->n + 1));
 65         if (!r)
 66                 return -ENOMEM;
 67         cfg->rules = r;
 68         r[cfg->n].path = strdup(path);
 69         if (!r[cfg->n].path)
 70                 return -ENOMEM;
 71         r[cfg->n].access = access;
 72         cfg->n++;
 73         return 0;
 74 }
 75 
 76 int landlock_config_add_paths(struct landlock_config *cfg, const char *paths,
 77                               uint64_t access)
 78 {
 79         char *dup, *tok, *save;
 80         int rc = 0;
 81 
 82         if (!paths || !*paths)
 83                 return 0;
 84 
 85         dup = strdup(paths);
 86         if (!dup)
 87                 return -ENOMEM;
 88 
 89         for (tok = strtok_r(dup, ":", &save); tok; tok = strtok_r(NULL, ":", &save)) {
 90                 rc = landlock_config_add(cfg, tok, access);
 91                 if (rc)
 92                         break;
 93         }
 94         free(dup);
 95         return rc;
 96 }
 97 
 98 int landlock_apply(const struct landlock_config *cfg)
 99 {
100         struct landlock_ruleset_attr ra = { 0 };
101         int ruleset_fd, rc = 0;
102         size_t i;
103 
104         if (cfg->n == 0)
105                 return 0;
106         if (!landlock_available())
107                 return -ENOSYS;
108 
109         for (i = 0; i < cfg->n; i++)
110                 ra.handled_access_fs |= cfg->rules[i].access;
111 
112         ruleset_fd = sys_landlock_create_ruleset(&ra, sizeof(ra), 0);
113         if (ruleset_fd < 0) {
114                 int saved_errno = errno;
115                 ERROR("landlock_create_ruleset: %s\n", strerror(saved_errno));
116                 return -saved_errno;
117         }
118 
119         for (i = 0; i < cfg->n; i++) {
120                 struct landlock_path_beneath_attr pa = {
121                         .allowed_access = cfg->rules[i].access,
122                 };
123                 int saved_errno;
124 
125                 pa.parent_fd = open(cfg->rules[i].path, O_PATH | O_CLOEXEC);
126                 if (pa.parent_fd < 0) {
127                         saved_errno = errno;
128                         ERROR("landlock: open(%s): %s\n", cfg->rules[i].path,
129                               strerror(saved_errno));
130                         rc = -saved_errno;
131                         goto out;
132                 }
133                 if (sys_landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
134                                           &pa, 0)) {
135                         saved_errno = errno;
136                         ERROR("landlock_add_rule(%s): %s\n", cfg->rules[i].path,
137                               strerror(saved_errno));
138                         rc = -saved_errno;
139                         close(pa.parent_fd);
140                         goto out;
141                 }
142                 close(pa.parent_fd);
143         }
144 
145         if (sys_landlock_restrict_self(ruleset_fd, 0)) {
146                 int saved_errno = errno;
147                 ERROR("landlock_restrict_self: %s\n", strerror(saved_errno));
148                 rc = -saved_errno;
149         }
150 
151 out:
152         close(ruleset_fd);
153         return rc;
154 }
155 
156 void landlock_config_free(struct landlock_config *cfg)
157 {
158         size_t i;
159 
160         for (i = 0; i < cfg->n; i++)
161                 free(cfg->rules[i].path);
162         free(cfg->rules);
163         cfg->rules = NULL;
164         cfg->n = 0;
165 }
166 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt