Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'landlock-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux

Pull landlock updates from Mickaël Salaün:
"We can now scope a Landlock domain thanks to a new "scoped" field that
can deny interactions with resources outside of this domain.

The LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET flag denies connections to an
abstract UNIX socket created outside of the current scoped domain, and
the LANDLOCK_SCOPE_SIGNAL flag denies sending a signal to processes
outside of the current scoped domain.

These restrictions also apply to nested domains according to their
scope. The related changes will also be useful to support other kind
of IPC isolations"

* tag 'landlock-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux:
landlock: Document LANDLOCK_SCOPE_SIGNAL
samples/landlock: Add support for signal scoping
selftests/landlock: Test signal created by out-of-bound message
selftests/landlock: Test signal scoping for threads
selftests/landlock: Test signal scoping
landlock: Add signal scoping
landlock: Document LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
samples/landlock: Add support for abstract UNIX socket scoping
selftests/landlock: Test inherited restriction of abstract UNIX socket
selftests/landlock: Test connected and unconnected datagram UNIX socket
selftests/landlock: Test UNIX sockets with any address formats
selftests/landlock: Test abstract UNIX socket scoping
selftests/landlock: Test handling of unknown scope
landlock: Add abstract UNIX socket scoping

+2359 -47
+56 -2
Documentation/userspace-api/landlock.rst
··· 8 8 ===================================== 9 9 10 10 :Author: Mickaël Salaün 11 - :Date: July 2024 11 + :Date: September 2024 12 12 13 13 The goal of Landlock is to enable to restrict ambient rights (e.g. global 14 14 filesystem or network access) for a set of processes. Because Landlock ··· 81 81 .handled_access_net = 82 82 LANDLOCK_ACCESS_NET_BIND_TCP | 83 83 LANDLOCK_ACCESS_NET_CONNECT_TCP, 84 + .scoped = 85 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | 86 + LANDLOCK_SCOPE_SIGNAL, 84 87 }; 85 88 86 89 Because we may not know on which kernel version an application will be ··· 122 119 case 4: 123 120 /* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */ 124 121 ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV; 122 + __attribute__((fallthrough)); 123 + case 5: 124 + /* Removes LANDLOCK_SCOPE_* for ABI < 6 */ 125 + ruleset_attr.scoped &= ~(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | 126 + LANDLOCK_SCOPE_SIGNAL); 125 127 } 126 128 127 129 This enables to create an inclusive ruleset that will contain our rules. ··· 314 306 process, a sandboxed process should have a subset of the target process rules, 315 307 which means the tracee must be in a sub-domain of the tracer. 316 308 309 + IPC scoping 310 + ----------- 311 + 312 + Similar to the implicit `Ptrace restrictions`_, we may want to further restrict 313 + interactions between sandboxes. Each Landlock domain can be explicitly scoped 314 + for a set of actions by specifying it on a ruleset. For example, if a 315 + sandboxed process should not be able to :manpage:`connect(2)` to a 316 + non-sandboxed process through abstract :manpage:`unix(7)` sockets, we can 317 + specify such restriction with ``LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET``. 318 + Moreover, if a sandboxed process should not be able to send a signal to a 319 + non-sandboxed process, we can specify this restriction with 320 + ``LANDLOCK_SCOPE_SIGNAL``. 321 + 322 + A sandboxed process can connect to a non-sandboxed process when its domain is 323 + not scoped. If a process's domain is scoped, it can only connect to sockets 324 + created by processes in the same scope. 325 + Moreover, If a process is scoped to send signal to a non-scoped process, it can 326 + only send signals to processes in the same scope. 327 + 328 + A connected datagram socket behaves like a stream socket when its domain is 329 + scoped, meaning if the domain is scoped after the socket is connected , it can 330 + still :manpage:`send(2)` data just like a stream socket. However, in the same 331 + scenario, a non-connected datagram socket cannot send data (with 332 + :manpage:`sendto(2)`) outside its scope. 333 + 334 + A process with a scoped domain can inherit a socket created by a non-scoped 335 + process. The process cannot connect to this socket since it has a scoped 336 + domain. 337 + 338 + IPC scoping does not support exceptions, so if a domain is scoped, no rules can 339 + be added to allow access to resources or processes outside of the scope. 340 + 317 341 Truncating files 318 342 ---------------- 319 343 ··· 444 404 ------------- 445 405 446 406 .. kernel-doc:: include/uapi/linux/landlock.h 447 - :identifiers: fs_access net_access 407 + :identifiers: fs_access net_access scope 448 408 449 409 Creating a new ruleset 450 410 ---------------------- ··· 580 540 581 541 Starting with the Landlock ABI version 5, it is possible to restrict the use of 582 542 :manpage:`ioctl(2)` using the new ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right. 543 + 544 + Abstract UNIX socket scoping (ABI < 6) 545 + -------------------------------------- 546 + 547 + Starting with the Landlock ABI version 6, it is possible to restrict 548 + connections to an abstract :manpage:`unix(7)` socket by setting 549 + ``LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET`` to the ``scoped`` ruleset attribute. 550 + 551 + Signal scoping (ABI < 6) 552 + ------------------------ 553 + 554 + Starting with the Landlock ABI version 6, it is possible to restrict 555 + :manpage:`signal(7)` sending by setting ``LANDLOCK_SCOPE_SIGNAL`` to the 556 + ``scoped`` ruleset attribute. 583 557 584 558 .. _kernel_support: 585 559
+30
include/uapi/linux/landlock.h
··· 44 44 * flags`_). 45 45 */ 46 46 __u64 handled_access_net; 47 + /** 48 + * @scoped: Bitmask of scopes (cf. `Scope flags`_) 49 + * restricting a Landlock domain from accessing outside 50 + * resources (e.g. IPCs). 51 + */ 52 + __u64 scoped; 47 53 }; 48 54 49 55 /* ··· 280 274 #define LANDLOCK_ACCESS_NET_BIND_TCP (1ULL << 0) 281 275 #define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1) 282 276 /* clang-format on */ 277 + 278 + /** 279 + * DOC: scope 280 + * 281 + * Scope flags 282 + * ~~~~~~~~~~~ 283 + * 284 + * These flags enable to isolate a sandboxed process from a set of IPC actions. 285 + * Setting a flag for a ruleset will isolate the Landlock domain to forbid 286 + * connections to resources outside the domain. 287 + * 288 + * Scopes: 289 + * 290 + * - %LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET: Restrict a sandboxed process from 291 + * connecting to an abstract UNIX socket created by a process outside the 292 + * related Landlock domain (e.g. a parent domain or a non-sandboxed process). 293 + * - %LANDLOCK_SCOPE_SIGNAL: Restrict a sandboxed process from sending a signal 294 + * to another process outside the domain. 295 + */ 296 + /* clang-format off */ 297 + #define LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET (1ULL << 0) 298 + #define LANDLOCK_SCOPE_SIGNAL (1ULL << 1) 299 + /* clang-format on*/ 300 + 283 301 #endif /* _UAPI_LINUX_LANDLOCK_H */
+69 -4
samples/landlock/sandboxer.c
··· 14 14 #include <fcntl.h> 15 15 #include <linux/landlock.h> 16 16 #include <linux/prctl.h> 17 + #include <linux/socket.h> 17 18 #include <stddef.h> 18 19 #include <stdio.h> 19 20 #include <stdlib.h> ··· 23 22 #include <sys/stat.h> 24 23 #include <sys/syscall.h> 25 24 #include <unistd.h> 25 + #include <stdbool.h> 26 26 27 27 #ifndef landlock_create_ruleset 28 28 static inline int ··· 57 55 #define ENV_FS_RW_NAME "LL_FS_RW" 58 56 #define ENV_TCP_BIND_NAME "LL_TCP_BIND" 59 57 #define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT" 58 + #define ENV_SCOPED_NAME "LL_SCOPED" 60 59 #define ENV_DELIMITER ":" 61 60 62 61 static int parse_path(char *env_path, const char ***const path_list) ··· 187 184 return ret; 188 185 } 189 186 187 + /* Returns true on error, false otherwise. */ 188 + static bool check_ruleset_scope(const char *const env_var, 189 + struct landlock_ruleset_attr *ruleset_attr) 190 + { 191 + char *env_type_scope, *env_type_scope_next, *ipc_scoping_name; 192 + bool error = false; 193 + bool abstract_scoping = false; 194 + bool signal_scoping = false; 195 + 196 + /* Scoping is not supported by Landlock ABI */ 197 + if (!(ruleset_attr->scoped & 198 + (LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL))) 199 + goto out_unset; 200 + 201 + env_type_scope = getenv(env_var); 202 + /* Scoping is not supported by the user */ 203 + if (!env_type_scope || strcmp("", env_type_scope) == 0) 204 + goto out_unset; 205 + 206 + env_type_scope = strdup(env_type_scope); 207 + env_type_scope_next = env_type_scope; 208 + while ((ipc_scoping_name = 209 + strsep(&env_type_scope_next, ENV_DELIMITER))) { 210 + if (strcmp("a", ipc_scoping_name) == 0 && !abstract_scoping) { 211 + abstract_scoping = true; 212 + } else if (strcmp("s", ipc_scoping_name) == 0 && 213 + !signal_scoping) { 214 + signal_scoping = true; 215 + } else { 216 + fprintf(stderr, "Unknown or duplicate scope \"%s\"\n", 217 + ipc_scoping_name); 218 + error = true; 219 + goto out_free_name; 220 + } 221 + } 222 + 223 + out_free_name: 224 + free(env_type_scope); 225 + 226 + out_unset: 227 + if (!abstract_scoping) 228 + ruleset_attr->scoped &= ~LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET; 229 + if (!signal_scoping) 230 + ruleset_attr->scoped &= ~LANDLOCK_SCOPE_SIGNAL; 231 + 232 + unsetenv(env_var); 233 + return error; 234 + } 235 + 190 236 /* clang-format off */ 191 237 192 238 #define ACCESS_FS_ROUGHLY_READ ( \ ··· 260 208 261 209 /* clang-format on */ 262 210 263 - #define LANDLOCK_ABI_LAST 5 211 + #define LANDLOCK_ABI_LAST 6 264 212 265 213 int main(const int argc, char *const argv[], char *const *const envp) 266 214 { ··· 275 223 .handled_access_fs = access_fs_rw, 276 224 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | 277 225 LANDLOCK_ACCESS_NET_CONNECT_TCP, 226 + .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | 227 + LANDLOCK_SCOPE_SIGNAL, 278 228 }; 279 229 280 230 if (argc < 2) { 281 231 fprintf(stderr, 282 - "usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\"%s " 232 + "usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\" %s " 283 233 "<cmd> [args]...\n\n", 284 234 ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME, 285 - ENV_TCP_CONNECT_NAME, argv[0]); 235 + ENV_TCP_CONNECT_NAME, ENV_SCOPED_NAME, argv[0]); 286 236 fprintf(stderr, 287 237 "Execute a command in a restricted environment.\n\n"); 288 238 fprintf(stderr, ··· 305 251 fprintf(stderr, 306 252 "* %s: list of ports allowed to connect (client).\n", 307 253 ENV_TCP_CONNECT_NAME); 254 + fprintf(stderr, "* %s: list of scoped IPCs.\n", 255 + ENV_SCOPED_NAME); 308 256 fprintf(stderr, 309 257 "\nexample:\n" 310 258 "%s=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" " 311 259 "%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " 312 260 "%s=\"9418\" " 313 261 "%s=\"80:443\" " 262 + "%s=\"a:s\" " 314 263 "%s bash -i\n\n", 315 264 ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME, 316 - ENV_TCP_CONNECT_NAME, argv[0]); 265 + ENV_TCP_CONNECT_NAME, ENV_SCOPED_NAME, argv[0]); 317 266 fprintf(stderr, 318 267 "This sandboxer can use Landlock features " 319 268 "up to ABI version %d.\n", ··· 384 327 /* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */ 385 328 ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV; 386 329 330 + __attribute__((fallthrough)); 331 + case 5: 332 + /* Removes LANDLOCK_SCOPE_* for ABI < 6 */ 333 + ruleset_attr.scoped &= ~(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | 334 + LANDLOCK_SCOPE_SIGNAL); 387 335 fprintf(stderr, 388 336 "Hint: You should update the running kernel " 389 337 "to leverage Landlock features " ··· 419 357 ruleset_attr.handled_access_net &= 420 358 ~LANDLOCK_ACCESS_NET_CONNECT_TCP; 421 359 } 360 + 361 + if (check_ruleset_scope(ENV_SCOPED_NAME, &ruleset_attr)) 362 + return 1; 422 363 423 364 ruleset_fd = 424 365 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+1 -1
security/landlock/cred.h
··· 26 26 return cred->security + landlock_blob_sizes.lbs_cred; 27 27 } 28 28 29 - static inline const struct landlock_ruleset *landlock_get_current_domain(void) 29 + static inline struct landlock_ruleset *landlock_get_current_domain(void) 30 30 { 31 31 return landlock_cred(current_cred())->domain; 32 32 }
+25
security/landlock/fs.c
··· 1639 1639 return -EACCES; 1640 1640 } 1641 1641 1642 + static void hook_file_set_fowner(struct file *file) 1643 + { 1644 + struct landlock_ruleset *new_dom, *prev_dom; 1645 + 1646 + /* 1647 + * Lock already held by __f_setown(), see commit 26f204380a3c ("fs: Fix 1648 + * file_set_fowner LSM hook inconsistencies"). 1649 + */ 1650 + lockdep_assert_held(&file_f_owner(file)->lock); 1651 + new_dom = landlock_get_current_domain(); 1652 + landlock_get_ruleset(new_dom); 1653 + prev_dom = landlock_file(file)->fown_domain; 1654 + landlock_file(file)->fown_domain = new_dom; 1655 + 1656 + /* Called in an RCU read-side critical section. */ 1657 + landlock_put_ruleset_deferred(prev_dom); 1658 + } 1659 + 1660 + static void hook_file_free_security(struct file *file) 1661 + { 1662 + landlock_put_ruleset_deferred(landlock_file(file)->fown_domain); 1663 + } 1664 + 1642 1665 static struct security_hook_list landlock_hooks[] __ro_after_init = { 1643 1666 LSM_HOOK_INIT(inode_free_security_rcu, hook_inode_free_security_rcu), 1644 1667 ··· 1686 1663 LSM_HOOK_INIT(file_truncate, hook_file_truncate), 1687 1664 LSM_HOOK_INIT(file_ioctl, hook_file_ioctl), 1688 1665 LSM_HOOK_INIT(file_ioctl_compat, hook_file_ioctl_compat), 1666 + LSM_HOOK_INIT(file_set_fowner, hook_file_set_fowner), 1667 + LSM_HOOK_INIT(file_free_security, hook_file_free_security), 1689 1668 }; 1690 1669 1691 1670 __init void landlock_add_fs_hooks(void)
+7
security/landlock/fs.h
··· 52 52 * needed to authorize later operations on the open file. 53 53 */ 54 54 access_mask_t allowed_access; 55 + /** 56 + * @fown_domain: Domain of the task that set the PID that may receive a 57 + * signal e.g., SIGURG when writing MSG_OOB to the related socket. 58 + * This pointer is protected by the related file->f_owner->lock, as for 59 + * fown_struct's members: pid, uid, and euid. 60 + */ 61 + struct landlock_ruleset *fown_domain; 55 62 }; 56 63 57 64 /**
+3
security/landlock/limits.h
··· 26 26 #define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1) 27 27 #define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET) 28 28 29 + #define LANDLOCK_LAST_SCOPE LANDLOCK_SCOPE_SIGNAL 30 + #define LANDLOCK_MASK_SCOPE ((LANDLOCK_LAST_SCOPE << 1) - 1) 31 + #define LANDLOCK_NUM_SCOPE __const_hweight64(LANDLOCK_MASK_SCOPE) 29 32 /* clang-format on */ 30 33 31 34 #endif /* _SECURITY_LANDLOCK_LIMITS_H */
+5 -2
security/landlock/ruleset.c
··· 52 52 53 53 struct landlock_ruleset * 54 54 landlock_create_ruleset(const access_mask_t fs_access_mask, 55 - const access_mask_t net_access_mask) 55 + const access_mask_t net_access_mask, 56 + const access_mask_t scope_mask) 56 57 { 57 58 struct landlock_ruleset *new_ruleset; 58 59 59 60 /* Informs about useless ruleset. */ 60 - if (!fs_access_mask && !net_access_mask) 61 + if (!fs_access_mask && !net_access_mask && !scope_mask) 61 62 return ERR_PTR(-ENOMSG); 62 63 new_ruleset = create_ruleset(1); 63 64 if (IS_ERR(new_ruleset)) ··· 67 66 landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0); 68 67 if (net_access_mask) 69 68 landlock_add_net_access_mask(new_ruleset, net_access_mask, 0); 69 + if (scope_mask) 70 + landlock_add_scope_mask(new_ruleset, scope_mask, 0); 70 71 return new_ruleset; 71 72 } 72 73
+23 -1
security/landlock/ruleset.h
··· 35 35 static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS); 36 36 /* Makes sure all network access rights can be stored. */ 37 37 static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_NET); 38 + /* Makes sure all scoped rights can be stored. */ 39 + static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_SCOPE); 38 40 /* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */ 39 41 static_assert(sizeof(unsigned long) >= sizeof(access_mask_t)); 40 42 ··· 44 42 struct access_masks { 45 43 access_mask_t fs : LANDLOCK_NUM_ACCESS_FS; 46 44 access_mask_t net : LANDLOCK_NUM_ACCESS_NET; 45 + access_mask_t scope : LANDLOCK_NUM_SCOPE; 47 46 }; 48 47 49 48 typedef u16 layer_mask_t; ··· 236 233 237 234 struct landlock_ruleset * 238 235 landlock_create_ruleset(const access_mask_t access_mask_fs, 239 - const access_mask_t access_mask_net); 236 + const access_mask_t access_mask_net, 237 + const access_mask_t scope_mask); 240 238 241 239 void landlock_put_ruleset(struct landlock_ruleset *const ruleset); 242 240 void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset); ··· 284 280 ruleset->access_masks[layer_level].net |= net_mask; 285 281 } 286 282 283 + static inline void 284 + landlock_add_scope_mask(struct landlock_ruleset *const ruleset, 285 + const access_mask_t scope_mask, const u16 layer_level) 286 + { 287 + access_mask_t mask = scope_mask & LANDLOCK_MASK_SCOPE; 288 + 289 + /* Should already be checked in sys_landlock_create_ruleset(). */ 290 + WARN_ON_ONCE(scope_mask != mask); 291 + ruleset->access_masks[layer_level].scope |= mask; 292 + } 293 + 287 294 static inline access_mask_t 288 295 landlock_get_raw_fs_access_mask(const struct landlock_ruleset *const ruleset, 289 296 const u16 layer_level) ··· 316 301 const u16 layer_level) 317 302 { 318 303 return ruleset->access_masks[layer_level].net; 304 + } 305 + 306 + static inline access_mask_t 307 + landlock_get_scope_mask(const struct landlock_ruleset *const ruleset, 308 + const u16 layer_level) 309 + { 310 + return ruleset->access_masks[layer_level].scope; 319 311 } 320 312 321 313 bool landlock_unmask_layers(const struct landlock_rule *const rule,
+12 -5
security/landlock/syscalls.c
··· 97 97 */ 98 98 ruleset_size = sizeof(ruleset_attr.handled_access_fs); 99 99 ruleset_size += sizeof(ruleset_attr.handled_access_net); 100 + ruleset_size += sizeof(ruleset_attr.scoped); 100 101 BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size); 101 - BUILD_BUG_ON(sizeof(ruleset_attr) != 16); 102 + BUILD_BUG_ON(sizeof(ruleset_attr) != 24); 102 103 103 104 path_beneath_size = sizeof(path_beneath_attr.allowed_access); 104 105 path_beneath_size += sizeof(path_beneath_attr.parent_fd); ··· 150 149 .write = fop_dummy_write, 151 150 }; 152 151 153 - #define LANDLOCK_ABI_VERSION 5 152 + #define LANDLOCK_ABI_VERSION 6 154 153 155 154 /** 156 155 * sys_landlock_create_ruleset - Create a new ruleset ··· 171 170 * Possible returned errors are: 172 171 * 173 172 * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; 174 - * - %EINVAL: unknown @flags, or unknown access, or too small @size; 175 - * - %E2BIG or %EFAULT: @attr or @size inconsistencies; 173 + * - %EINVAL: unknown @flags, or unknown access, or unknown scope, or too small @size; 174 + * - %E2BIG: @attr or @size inconsistencies; 175 + * - %EFAULT: @attr or @size inconsistencies; 176 176 * - %ENOMSG: empty &landlock_ruleset_attr.handled_access_fs. 177 177 */ 178 178 SYSCALL_DEFINE3(landlock_create_ruleset, ··· 215 213 LANDLOCK_MASK_ACCESS_NET) 216 214 return -EINVAL; 217 215 216 + /* Checks IPC scoping content (and 32-bits cast). */ 217 + if ((ruleset_attr.scoped | LANDLOCK_MASK_SCOPE) != LANDLOCK_MASK_SCOPE) 218 + return -EINVAL; 219 + 218 220 /* Checks arguments and transforms to kernel struct. */ 219 221 ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs, 220 - ruleset_attr.handled_access_net); 222 + ruleset_attr.handled_access_net, 223 + ruleset_attr.scoped); 221 224 if (IS_ERR(ruleset)) 222 225 return PTR_ERR(ruleset); 223 226
+193
security/landlock/task.c
··· 13 13 #include <linux/lsm_hooks.h> 14 14 #include <linux/rcupdate.h> 15 15 #include <linux/sched.h> 16 + #include <net/af_unix.h> 17 + #include <net/sock.h> 16 18 17 19 #include "common.h" 18 20 #include "cred.h" 21 + #include "fs.h" 19 22 #include "ruleset.h" 20 23 #include "setup.h" 21 24 #include "task.h" ··· 111 108 return task_ptrace(parent, current); 112 109 } 113 110 111 + /** 112 + * domain_is_scoped - Checks if the client domain is scoped in the same 113 + * domain as the server. 114 + * 115 + * @client: IPC sender domain. 116 + * @server: IPC receiver domain. 117 + * @scope: The scope restriction criteria. 118 + * 119 + * Returns: True if the @client domain is scoped to access the @server, 120 + * unless the @server is also scoped in the same domain as @client. 121 + */ 122 + static bool domain_is_scoped(const struct landlock_ruleset *const client, 123 + const struct landlock_ruleset *const server, 124 + access_mask_t scope) 125 + { 126 + int client_layer, server_layer; 127 + struct landlock_hierarchy *client_walker, *server_walker; 128 + 129 + /* Quick return if client has no domain */ 130 + if (WARN_ON_ONCE(!client)) 131 + return false; 132 + 133 + client_layer = client->num_layers - 1; 134 + client_walker = client->hierarchy; 135 + /* 136 + * client_layer must be a signed integer with greater capacity 137 + * than client->num_layers to ensure the following loop stops. 138 + */ 139 + BUILD_BUG_ON(sizeof(client_layer) > sizeof(client->num_layers)); 140 + 141 + server_layer = server ? (server->num_layers - 1) : -1; 142 + server_walker = server ? server->hierarchy : NULL; 143 + 144 + /* 145 + * Walks client's parent domains down to the same hierarchy level 146 + * as the server's domain, and checks that none of these client's 147 + * parent domains are scoped. 148 + */ 149 + for (; client_layer > server_layer; client_layer--) { 150 + if (landlock_get_scope_mask(client, client_layer) & scope) 151 + return true; 152 + 153 + client_walker = client_walker->parent; 154 + } 155 + /* 156 + * Walks server's parent domains down to the same hierarchy level as 157 + * the client's domain. 158 + */ 159 + for (; server_layer > client_layer; server_layer--) 160 + server_walker = server_walker->parent; 161 + 162 + for (; client_layer >= 0; client_layer--) { 163 + if (landlock_get_scope_mask(client, client_layer) & scope) { 164 + /* 165 + * Client and server are at the same level in the 166 + * hierarchy. If the client is scoped, the request is 167 + * only allowed if this domain is also a server's 168 + * ancestor. 169 + */ 170 + return server_walker != client_walker; 171 + } 172 + client_walker = client_walker->parent; 173 + server_walker = server_walker->parent; 174 + } 175 + return false; 176 + } 177 + 178 + static bool sock_is_scoped(struct sock *const other, 179 + const struct landlock_ruleset *const domain) 180 + { 181 + const struct landlock_ruleset *dom_other; 182 + 183 + /* The credentials will not change. */ 184 + lockdep_assert_held(&unix_sk(other)->lock); 185 + dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain; 186 + return domain_is_scoped(domain, dom_other, 187 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 188 + } 189 + 190 + static bool is_abstract_socket(struct sock *const sock) 191 + { 192 + struct unix_address *addr = unix_sk(sock)->addr; 193 + 194 + if (!addr) 195 + return false; 196 + 197 + if (addr->len >= offsetof(struct sockaddr_un, sun_path) + 1 && 198 + addr->name->sun_path[0] == '\0') 199 + return true; 200 + 201 + return false; 202 + } 203 + 204 + static int hook_unix_stream_connect(struct sock *const sock, 205 + struct sock *const other, 206 + struct sock *const newsk) 207 + { 208 + const struct landlock_ruleset *const dom = 209 + landlock_get_current_domain(); 210 + 211 + /* Quick return for non-landlocked tasks. */ 212 + if (!dom) 213 + return 0; 214 + 215 + if (is_abstract_socket(other) && sock_is_scoped(other, dom)) 216 + return -EPERM; 217 + 218 + return 0; 219 + } 220 + 221 + static int hook_unix_may_send(struct socket *const sock, 222 + struct socket *const other) 223 + { 224 + const struct landlock_ruleset *const dom = 225 + landlock_get_current_domain(); 226 + 227 + if (!dom) 228 + return 0; 229 + 230 + /* 231 + * Checks if this datagram socket was already allowed to be connected 232 + * to other. 233 + */ 234 + if (unix_peer(sock->sk) == other->sk) 235 + return 0; 236 + 237 + if (is_abstract_socket(other->sk) && sock_is_scoped(other->sk, dom)) 238 + return -EPERM; 239 + 240 + return 0; 241 + } 242 + 243 + static int hook_task_kill(struct task_struct *const p, 244 + struct kernel_siginfo *const info, const int sig, 245 + const struct cred *const cred) 246 + { 247 + bool is_scoped; 248 + const struct landlock_ruleset *dom; 249 + 250 + if (cred) { 251 + /* Dealing with USB IO. */ 252 + dom = landlock_cred(cred)->domain; 253 + } else { 254 + dom = landlock_get_current_domain(); 255 + } 256 + 257 + /* Quick return for non-landlocked tasks. */ 258 + if (!dom) 259 + return 0; 260 + 261 + rcu_read_lock(); 262 + is_scoped = domain_is_scoped(dom, landlock_get_task_domain(p), 263 + LANDLOCK_SCOPE_SIGNAL); 264 + rcu_read_unlock(); 265 + if (is_scoped) 266 + return -EPERM; 267 + 268 + return 0; 269 + } 270 + 271 + static int hook_file_send_sigiotask(struct task_struct *tsk, 272 + struct fown_struct *fown, int signum) 273 + { 274 + const struct landlock_ruleset *dom; 275 + bool is_scoped = false; 276 + 277 + /* Lock already held by send_sigio() and send_sigurg(). */ 278 + lockdep_assert_held(&fown->lock); 279 + dom = landlock_file(fown->file)->fown_domain; 280 + 281 + /* Quick return for unowned socket. */ 282 + if (!dom) 283 + return 0; 284 + 285 + rcu_read_lock(); 286 + is_scoped = domain_is_scoped(dom, landlock_get_task_domain(tsk), 287 + LANDLOCK_SCOPE_SIGNAL); 288 + rcu_read_unlock(); 289 + if (is_scoped) 290 + return -EPERM; 291 + 292 + return 0; 293 + } 294 + 114 295 static struct security_hook_list landlock_hooks[] __ro_after_init = { 115 296 LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check), 116 297 LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme), 298 + 299 + LSM_HOOK_INIT(unix_stream_connect, hook_unix_stream_connect), 300 + LSM_HOOK_INIT(unix_may_send, hook_unix_may_send), 301 + 302 + LSM_HOOK_INIT(task_kill, hook_task_kill), 303 + LSM_HOOK_INIT(file_send_sigiotask, hook_file_send_sigiotask), 117 304 }; 118 305 119 306 __init void landlock_add_task_hooks(void)
+1 -1
tools/testing/selftests/landlock/base_test.c
··· 76 76 const struct landlock_ruleset_attr ruleset_attr = { 77 77 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 78 78 }; 79 - ASSERT_EQ(5, landlock_create_ruleset(NULL, 0, 79 + ASSERT_EQ(6, landlock_create_ruleset(NULL, 0, 80 80 LANDLOCK_CREATE_RULESET_VERSION)); 81 81 82 82 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
+39
tools/testing/selftests/landlock/common.h
··· 7 7 * Copyright © 2021 Microsoft Corporation 8 8 */ 9 9 10 + #include <arpa/inet.h> 10 11 #include <errno.h> 11 12 #include <linux/landlock.h> 12 13 #include <linux/securebits.h> ··· 15 14 #include <sys/socket.h> 16 15 #include <sys/syscall.h> 17 16 #include <sys/types.h> 17 + #include <sys/un.h> 18 18 #include <sys/wait.h> 19 19 #include <unistd.h> 20 20 21 21 #include "../kselftest_harness.h" 22 + 23 + #define TMP_DIR "tmp" 22 24 23 25 #ifndef __maybe_unused 24 26 #define __maybe_unused __attribute__((__unused__)) ··· 229 225 { 230 226 TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 231 227 } 228 + } 229 + 230 + struct protocol_variant { 231 + int domain; 232 + int type; 233 + }; 234 + 235 + struct service_fixture { 236 + struct protocol_variant protocol; 237 + /* port is also stored in ipv4_addr.sin_port or ipv6_addr.sin6_port */ 238 + unsigned short port; 239 + union { 240 + struct sockaddr_in ipv4_addr; 241 + struct sockaddr_in6 ipv6_addr; 242 + struct { 243 + struct sockaddr_un unix_addr; 244 + socklen_t unix_addr_len; 245 + }; 246 + }; 247 + }; 248 + 249 + static pid_t __maybe_unused sys_gettid(void) 250 + { 251 + return syscall(__NR_gettid); 252 + } 253 + 254 + static void __maybe_unused set_unix_address(struct service_fixture *const srv, 255 + const unsigned short index) 256 + { 257 + srv->unix_addr.sun_family = AF_UNIX; 258 + sprintf(srv->unix_addr.sun_path, 259 + "_selftests-landlock-abstract-unix-tid%d-index%d", sys_gettid(), 260 + index); 261 + srv->unix_addr_len = SUN_LEN(&srv->unix_addr); 262 + srv->unix_addr.sun_path[0] = '\0'; 232 263 }
-1
tools/testing/selftests/landlock/fs_test.c
··· 59 59 #define RENAME_EXCHANGE (1 << 1) 60 60 #endif 61 61 62 - #define TMP_DIR "tmp" 63 62 #define BINARY_PATH "./true" 64 63 65 64 /* Paths (sibling number and depth) */
+1 -30
tools/testing/selftests/landlock/net_test.c
··· 36 36 TCP_SANDBOX, 37 37 }; 38 38 39 - struct protocol_variant { 40 - int domain; 41 - int type; 42 - }; 43 - 44 - struct service_fixture { 45 - struct protocol_variant protocol; 46 - /* port is also stored in ipv4_addr.sin_port or ipv6_addr.sin6_port */ 47 - unsigned short port; 48 - union { 49 - struct sockaddr_in ipv4_addr; 50 - struct sockaddr_in6 ipv6_addr; 51 - struct { 52 - struct sockaddr_un unix_addr; 53 - socklen_t unix_addr_len; 54 - }; 55 - }; 56 - }; 57 - 58 - static pid_t sys_gettid(void) 59 - { 60 - return syscall(__NR_gettid); 61 - } 62 - 63 39 static int set_service(struct service_fixture *const srv, 64 40 const struct protocol_variant prot, 65 41 const unsigned short index) ··· 68 92 return 0; 69 93 70 94 case AF_UNIX: 71 - srv->unix_addr.sun_family = prot.domain; 72 - sprintf(srv->unix_addr.sun_path, 73 - "_selftests-landlock-net-tid%d-index%d", sys_gettid(), 74 - index); 75 - srv->unix_addr_len = SUN_LEN(&srv->unix_addr); 76 - srv->unix_addr.sun_path[0] = '\0'; 95 + set_unix_address(srv, index); 77 96 return 0; 78 97 } 79 98 return 1;
+1041
tools/testing/selftests/landlock/scoped_abstract_unix_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Landlock tests - Abstract UNIX socket 4 + * 5 + * Copyright © 2024 Tahera Fahimi <fahimitahera@gmail.com> 6 + */ 7 + 8 + #define _GNU_SOURCE 9 + #include <errno.h> 10 + #include <fcntl.h> 11 + #include <linux/landlock.h> 12 + #include <sched.h> 13 + #include <signal.h> 14 + #include <stddef.h> 15 + #include <sys/prctl.h> 16 + #include <sys/socket.h> 17 + #include <sys/stat.h> 18 + #include <sys/types.h> 19 + #include <sys/un.h> 20 + #include <sys/wait.h> 21 + #include <unistd.h> 22 + 23 + #include "common.h" 24 + #include "scoped_common.h" 25 + 26 + /* Number of pending connections queue to be hold. */ 27 + const short backlog = 10; 28 + 29 + static void create_fs_domain(struct __test_metadata *const _metadata) 30 + { 31 + int ruleset_fd; 32 + struct landlock_ruleset_attr ruleset_attr = { 33 + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR, 34 + }; 35 + 36 + ruleset_fd = 37 + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 38 + EXPECT_LE(0, ruleset_fd) 39 + { 40 + TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 41 + } 42 + EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 43 + EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 44 + EXPECT_EQ(0, close(ruleset_fd)); 45 + } 46 + 47 + FIXTURE(scoped_domains) 48 + { 49 + struct service_fixture stream_address, dgram_address; 50 + }; 51 + 52 + #include "scoped_base_variants.h" 53 + 54 + FIXTURE_SETUP(scoped_domains) 55 + { 56 + drop_caps(_metadata); 57 + 58 + memset(&self->stream_address, 0, sizeof(self->stream_address)); 59 + memset(&self->dgram_address, 0, sizeof(self->dgram_address)); 60 + set_unix_address(&self->stream_address, 0); 61 + set_unix_address(&self->dgram_address, 1); 62 + } 63 + 64 + FIXTURE_TEARDOWN(scoped_domains) 65 + { 66 + } 67 + 68 + /* 69 + * Test unix_stream_connect() and unix_may_send() for a child connecting to its 70 + * parent, when they have scoped domain or no domain. 71 + */ 72 + TEST_F(scoped_domains, connect_to_parent) 73 + { 74 + pid_t child; 75 + bool can_connect_to_parent; 76 + int status; 77 + int pipe_parent[2]; 78 + int stream_server, dgram_server; 79 + 80 + /* 81 + * can_connect_to_parent is true if a child process can connect to its 82 + * parent process. This depends on the child process not being isolated 83 + * from the parent with a dedicated Landlock domain. 84 + */ 85 + can_connect_to_parent = !variant->domain_child; 86 + 87 + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 88 + if (variant->domain_both) { 89 + create_scoped_domain(_metadata, 90 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 91 + if (!__test_passed(_metadata)) 92 + return; 93 + } 94 + 95 + child = fork(); 96 + ASSERT_LE(0, child); 97 + if (child == 0) { 98 + int err; 99 + int stream_client, dgram_client; 100 + char buf_child; 101 + 102 + EXPECT_EQ(0, close(pipe_parent[1])); 103 + if (variant->domain_child) 104 + create_scoped_domain( 105 + _metadata, LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 106 + 107 + stream_client = socket(AF_UNIX, SOCK_STREAM, 0); 108 + ASSERT_LE(0, stream_client); 109 + dgram_client = socket(AF_UNIX, SOCK_DGRAM, 0); 110 + ASSERT_LE(0, dgram_client); 111 + 112 + /* Waits for the server. */ 113 + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 114 + 115 + err = connect(stream_client, &self->stream_address.unix_addr, 116 + self->stream_address.unix_addr_len); 117 + if (can_connect_to_parent) { 118 + EXPECT_EQ(0, err); 119 + } else { 120 + EXPECT_EQ(-1, err); 121 + EXPECT_EQ(EPERM, errno); 122 + } 123 + EXPECT_EQ(0, close(stream_client)); 124 + 125 + err = connect(dgram_client, &self->dgram_address.unix_addr, 126 + self->dgram_address.unix_addr_len); 127 + if (can_connect_to_parent) { 128 + EXPECT_EQ(0, err); 129 + } else { 130 + EXPECT_EQ(-1, err); 131 + EXPECT_EQ(EPERM, errno); 132 + } 133 + EXPECT_EQ(0, close(dgram_client)); 134 + _exit(_metadata->exit_code); 135 + return; 136 + } 137 + EXPECT_EQ(0, close(pipe_parent[0])); 138 + if (variant->domain_parent) 139 + create_scoped_domain(_metadata, 140 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 141 + 142 + stream_server = socket(AF_UNIX, SOCK_STREAM, 0); 143 + ASSERT_LE(0, stream_server); 144 + dgram_server = socket(AF_UNIX, SOCK_DGRAM, 0); 145 + ASSERT_LE(0, dgram_server); 146 + ASSERT_EQ(0, bind(stream_server, &self->stream_address.unix_addr, 147 + self->stream_address.unix_addr_len)); 148 + ASSERT_EQ(0, bind(dgram_server, &self->dgram_address.unix_addr, 149 + self->dgram_address.unix_addr_len)); 150 + ASSERT_EQ(0, listen(stream_server, backlog)); 151 + 152 + /* Signals to child that the parent is listening. */ 153 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 154 + 155 + ASSERT_EQ(child, waitpid(child, &status, 0)); 156 + EXPECT_EQ(0, close(stream_server)); 157 + EXPECT_EQ(0, close(dgram_server)); 158 + 159 + if (WIFSIGNALED(status) || !WIFEXITED(status) || 160 + WEXITSTATUS(status) != EXIT_SUCCESS) 161 + _metadata->exit_code = KSFT_FAIL; 162 + } 163 + 164 + /* 165 + * Test unix_stream_connect() and unix_may_send() for a parent connecting to 166 + * its child, when they have scoped domain or no domain. 167 + */ 168 + TEST_F(scoped_domains, connect_to_child) 169 + { 170 + pid_t child; 171 + bool can_connect_to_child; 172 + int err_stream, err_dgram, errno_stream, errno_dgram, status; 173 + int pipe_child[2], pipe_parent[2]; 174 + char buf; 175 + int stream_client, dgram_client; 176 + 177 + /* 178 + * can_connect_to_child is true if a parent process can connect to its 179 + * child process. The parent process is not isolated from the child 180 + * with a dedicated Landlock domain. 181 + */ 182 + can_connect_to_child = !variant->domain_parent; 183 + 184 + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); 185 + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 186 + if (variant->domain_both) { 187 + create_scoped_domain(_metadata, 188 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 189 + if (!__test_passed(_metadata)) 190 + return; 191 + } 192 + 193 + child = fork(); 194 + ASSERT_LE(0, child); 195 + if (child == 0) { 196 + int stream_server, dgram_server; 197 + 198 + EXPECT_EQ(0, close(pipe_parent[1])); 199 + EXPECT_EQ(0, close(pipe_child[0])); 200 + if (variant->domain_child) 201 + create_scoped_domain( 202 + _metadata, LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 203 + 204 + /* Waits for the parent to be in a domain, if any. */ 205 + ASSERT_EQ(1, read(pipe_parent[0], &buf, 1)); 206 + 207 + stream_server = socket(AF_UNIX, SOCK_STREAM, 0); 208 + ASSERT_LE(0, stream_server); 209 + dgram_server = socket(AF_UNIX, SOCK_DGRAM, 0); 210 + ASSERT_LE(0, dgram_server); 211 + ASSERT_EQ(0, 212 + bind(stream_server, &self->stream_address.unix_addr, 213 + self->stream_address.unix_addr_len)); 214 + ASSERT_EQ(0, bind(dgram_server, &self->dgram_address.unix_addr, 215 + self->dgram_address.unix_addr_len)); 216 + ASSERT_EQ(0, listen(stream_server, backlog)); 217 + 218 + /* Signals to the parent that child is listening. */ 219 + ASSERT_EQ(1, write(pipe_child[1], ".", 1)); 220 + 221 + /* Waits to connect. */ 222 + ASSERT_EQ(1, read(pipe_parent[0], &buf, 1)); 223 + EXPECT_EQ(0, close(stream_server)); 224 + EXPECT_EQ(0, close(dgram_server)); 225 + _exit(_metadata->exit_code); 226 + return; 227 + } 228 + EXPECT_EQ(0, close(pipe_child[1])); 229 + EXPECT_EQ(0, close(pipe_parent[0])); 230 + 231 + if (variant->domain_parent) 232 + create_scoped_domain(_metadata, 233 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 234 + 235 + /* Signals that the parent is in a domain, if any. */ 236 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 237 + 238 + stream_client = socket(AF_UNIX, SOCK_STREAM, 0); 239 + ASSERT_LE(0, stream_client); 240 + dgram_client = socket(AF_UNIX, SOCK_DGRAM, 0); 241 + ASSERT_LE(0, dgram_client); 242 + 243 + /* Waits for the child to listen */ 244 + ASSERT_EQ(1, read(pipe_child[0], &buf, 1)); 245 + err_stream = connect(stream_client, &self->stream_address.unix_addr, 246 + self->stream_address.unix_addr_len); 247 + errno_stream = errno; 248 + err_dgram = connect(dgram_client, &self->dgram_address.unix_addr, 249 + self->dgram_address.unix_addr_len); 250 + errno_dgram = errno; 251 + if (can_connect_to_child) { 252 + EXPECT_EQ(0, err_stream); 253 + EXPECT_EQ(0, err_dgram); 254 + } else { 255 + EXPECT_EQ(-1, err_stream); 256 + EXPECT_EQ(-1, err_dgram); 257 + EXPECT_EQ(EPERM, errno_stream); 258 + EXPECT_EQ(EPERM, errno_dgram); 259 + } 260 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 261 + EXPECT_EQ(0, close(stream_client)); 262 + EXPECT_EQ(0, close(dgram_client)); 263 + 264 + ASSERT_EQ(child, waitpid(child, &status, 0)); 265 + if (WIFSIGNALED(status) || !WIFEXITED(status) || 266 + WEXITSTATUS(status) != EXIT_SUCCESS) 267 + _metadata->exit_code = KSFT_FAIL; 268 + } 269 + 270 + FIXTURE(scoped_vs_unscoped) 271 + { 272 + struct service_fixture parent_stream_address, parent_dgram_address, 273 + child_stream_address, child_dgram_address; 274 + }; 275 + 276 + #include "scoped_multiple_domain_variants.h" 277 + 278 + FIXTURE_SETUP(scoped_vs_unscoped) 279 + { 280 + drop_caps(_metadata); 281 + 282 + memset(&self->parent_stream_address, 0, 283 + sizeof(self->parent_stream_address)); 284 + set_unix_address(&self->parent_stream_address, 0); 285 + memset(&self->parent_dgram_address, 0, 286 + sizeof(self->parent_dgram_address)); 287 + set_unix_address(&self->parent_dgram_address, 1); 288 + memset(&self->child_stream_address, 0, 289 + sizeof(self->child_stream_address)); 290 + set_unix_address(&self->child_stream_address, 2); 291 + memset(&self->child_dgram_address, 0, 292 + sizeof(self->child_dgram_address)); 293 + set_unix_address(&self->child_dgram_address, 3); 294 + } 295 + 296 + FIXTURE_TEARDOWN(scoped_vs_unscoped) 297 + { 298 + } 299 + 300 + /* 301 + * Test unix_stream_connect and unix_may_send for parent, child and 302 + * grand child processes when they can have scoped or non-scoped domains. 303 + */ 304 + TEST_F(scoped_vs_unscoped, unix_scoping) 305 + { 306 + pid_t child; 307 + int status; 308 + bool can_connect_to_parent, can_connect_to_child; 309 + int pipe_parent[2]; 310 + int stream_server_parent, dgram_server_parent; 311 + 312 + can_connect_to_child = (variant->domain_grand_child != SCOPE_SANDBOX); 313 + can_connect_to_parent = (can_connect_to_child && 314 + (variant->domain_children != SCOPE_SANDBOX)); 315 + 316 + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 317 + 318 + if (variant->domain_all == OTHER_SANDBOX) 319 + create_fs_domain(_metadata); 320 + else if (variant->domain_all == SCOPE_SANDBOX) 321 + create_scoped_domain(_metadata, 322 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 323 + 324 + child = fork(); 325 + ASSERT_LE(0, child); 326 + if (child == 0) { 327 + int stream_server_child, dgram_server_child; 328 + int pipe_child[2]; 329 + pid_t grand_child; 330 + 331 + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); 332 + 333 + if (variant->domain_children == OTHER_SANDBOX) 334 + create_fs_domain(_metadata); 335 + else if (variant->domain_children == SCOPE_SANDBOX) 336 + create_scoped_domain( 337 + _metadata, LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 338 + 339 + grand_child = fork(); 340 + ASSERT_LE(0, grand_child); 341 + if (grand_child == 0) { 342 + char buf; 343 + int stream_err, dgram_err, stream_errno, dgram_errno; 344 + int stream_client, dgram_client; 345 + 346 + EXPECT_EQ(0, close(pipe_parent[1])); 347 + EXPECT_EQ(0, close(pipe_child[1])); 348 + 349 + if (variant->domain_grand_child == OTHER_SANDBOX) 350 + create_fs_domain(_metadata); 351 + else if (variant->domain_grand_child == SCOPE_SANDBOX) 352 + create_scoped_domain( 353 + _metadata, 354 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 355 + 356 + stream_client = socket(AF_UNIX, SOCK_STREAM, 0); 357 + ASSERT_LE(0, stream_client); 358 + dgram_client = socket(AF_UNIX, SOCK_DGRAM, 0); 359 + ASSERT_LE(0, dgram_client); 360 + 361 + ASSERT_EQ(1, read(pipe_child[0], &buf, 1)); 362 + stream_err = connect( 363 + stream_client, 364 + &self->child_stream_address.unix_addr, 365 + self->child_stream_address.unix_addr_len); 366 + stream_errno = errno; 367 + dgram_err = connect( 368 + dgram_client, 369 + &self->child_dgram_address.unix_addr, 370 + self->child_dgram_address.unix_addr_len); 371 + dgram_errno = errno; 372 + if (can_connect_to_child) { 373 + EXPECT_EQ(0, stream_err); 374 + EXPECT_EQ(0, dgram_err); 375 + } else { 376 + EXPECT_EQ(-1, stream_err); 377 + EXPECT_EQ(-1, dgram_err); 378 + EXPECT_EQ(EPERM, stream_errno); 379 + EXPECT_EQ(EPERM, dgram_errno); 380 + } 381 + 382 + EXPECT_EQ(0, close(stream_client)); 383 + stream_client = socket(AF_UNIX, SOCK_STREAM, 0); 384 + ASSERT_LE(0, stream_client); 385 + /* Datagram sockets can "reconnect". */ 386 + 387 + ASSERT_EQ(1, read(pipe_parent[0], &buf, 1)); 388 + stream_err = connect( 389 + stream_client, 390 + &self->parent_stream_address.unix_addr, 391 + self->parent_stream_address.unix_addr_len); 392 + stream_errno = errno; 393 + dgram_err = connect( 394 + dgram_client, 395 + &self->parent_dgram_address.unix_addr, 396 + self->parent_dgram_address.unix_addr_len); 397 + dgram_errno = errno; 398 + if (can_connect_to_parent) { 399 + EXPECT_EQ(0, stream_err); 400 + EXPECT_EQ(0, dgram_err); 401 + } else { 402 + EXPECT_EQ(-1, stream_err); 403 + EXPECT_EQ(-1, dgram_err); 404 + EXPECT_EQ(EPERM, stream_errno); 405 + EXPECT_EQ(EPERM, dgram_errno); 406 + } 407 + EXPECT_EQ(0, close(stream_client)); 408 + EXPECT_EQ(0, close(dgram_client)); 409 + 410 + _exit(_metadata->exit_code); 411 + return; 412 + } 413 + EXPECT_EQ(0, close(pipe_child[0])); 414 + if (variant->domain_child == OTHER_SANDBOX) 415 + create_fs_domain(_metadata); 416 + else if (variant->domain_child == SCOPE_SANDBOX) 417 + create_scoped_domain( 418 + _metadata, LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 419 + 420 + stream_server_child = socket(AF_UNIX, SOCK_STREAM, 0); 421 + ASSERT_LE(0, stream_server_child); 422 + dgram_server_child = socket(AF_UNIX, SOCK_DGRAM, 0); 423 + ASSERT_LE(0, dgram_server_child); 424 + 425 + ASSERT_EQ(0, bind(stream_server_child, 426 + &self->child_stream_address.unix_addr, 427 + self->child_stream_address.unix_addr_len)); 428 + ASSERT_EQ(0, bind(dgram_server_child, 429 + &self->child_dgram_address.unix_addr, 430 + self->child_dgram_address.unix_addr_len)); 431 + ASSERT_EQ(0, listen(stream_server_child, backlog)); 432 + 433 + ASSERT_EQ(1, write(pipe_child[1], ".", 1)); 434 + ASSERT_EQ(grand_child, waitpid(grand_child, &status, 0)); 435 + EXPECT_EQ(0, close(stream_server_child)) 436 + EXPECT_EQ(0, close(dgram_server_child)); 437 + return; 438 + } 439 + EXPECT_EQ(0, close(pipe_parent[0])); 440 + 441 + if (variant->domain_parent == OTHER_SANDBOX) 442 + create_fs_domain(_metadata); 443 + else if (variant->domain_parent == SCOPE_SANDBOX) 444 + create_scoped_domain(_metadata, 445 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 446 + 447 + stream_server_parent = socket(AF_UNIX, SOCK_STREAM, 0); 448 + ASSERT_LE(0, stream_server_parent); 449 + dgram_server_parent = socket(AF_UNIX, SOCK_DGRAM, 0); 450 + ASSERT_LE(0, dgram_server_parent); 451 + ASSERT_EQ(0, bind(stream_server_parent, 452 + &self->parent_stream_address.unix_addr, 453 + self->parent_stream_address.unix_addr_len)); 454 + ASSERT_EQ(0, bind(dgram_server_parent, 455 + &self->parent_dgram_address.unix_addr, 456 + self->parent_dgram_address.unix_addr_len)); 457 + 458 + ASSERT_EQ(0, listen(stream_server_parent, backlog)); 459 + 460 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 461 + ASSERT_EQ(child, waitpid(child, &status, 0)); 462 + EXPECT_EQ(0, close(stream_server_parent)); 463 + EXPECT_EQ(0, close(dgram_server_parent)); 464 + 465 + if (WIFSIGNALED(status) || !WIFEXITED(status) || 466 + WEXITSTATUS(status) != EXIT_SUCCESS) 467 + _metadata->exit_code = KSFT_FAIL; 468 + } 469 + 470 + FIXTURE(outside_socket) 471 + { 472 + struct service_fixture address, transit_address; 473 + }; 474 + 475 + FIXTURE_VARIANT(outside_socket) 476 + { 477 + const bool child_socket; 478 + const int type; 479 + }; 480 + 481 + /* clang-format off */ 482 + FIXTURE_VARIANT_ADD(outside_socket, allow_dgram_child) { 483 + /* clang-format on */ 484 + .child_socket = true, 485 + .type = SOCK_DGRAM, 486 + }; 487 + 488 + /* clang-format off */ 489 + FIXTURE_VARIANT_ADD(outside_socket, deny_dgram_server) { 490 + /* clang-format on */ 491 + .child_socket = false, 492 + .type = SOCK_DGRAM, 493 + }; 494 + 495 + /* clang-format off */ 496 + FIXTURE_VARIANT_ADD(outside_socket, allow_stream_child) { 497 + /* clang-format on */ 498 + .child_socket = true, 499 + .type = SOCK_STREAM, 500 + }; 501 + 502 + /* clang-format off */ 503 + FIXTURE_VARIANT_ADD(outside_socket, deny_stream_server) { 504 + /* clang-format on */ 505 + .child_socket = false, 506 + .type = SOCK_STREAM, 507 + }; 508 + 509 + FIXTURE_SETUP(outside_socket) 510 + { 511 + drop_caps(_metadata); 512 + 513 + memset(&self->transit_address, 0, sizeof(self->transit_address)); 514 + set_unix_address(&self->transit_address, 0); 515 + memset(&self->address, 0, sizeof(self->address)); 516 + set_unix_address(&self->address, 1); 517 + } 518 + 519 + FIXTURE_TEARDOWN(outside_socket) 520 + { 521 + } 522 + 523 + /* 524 + * Test unix_stream_connect and unix_may_send for parent and child processes 525 + * when connecting socket has different domain than the process using it. 526 + */ 527 + TEST_F(outside_socket, socket_with_different_domain) 528 + { 529 + pid_t child; 530 + int err, status; 531 + int pipe_child[2], pipe_parent[2]; 532 + char buf_parent; 533 + int server_socket; 534 + 535 + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); 536 + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 537 + 538 + child = fork(); 539 + ASSERT_LE(0, child); 540 + if (child == 0) { 541 + int client_socket; 542 + char buf_child; 543 + 544 + EXPECT_EQ(0, close(pipe_parent[1])); 545 + EXPECT_EQ(0, close(pipe_child[0])); 546 + 547 + /* Client always has a domain. */ 548 + create_scoped_domain(_metadata, 549 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 550 + 551 + if (variant->child_socket) { 552 + int data_socket, passed_socket, stream_server; 553 + 554 + passed_socket = socket(AF_UNIX, variant->type, 0); 555 + ASSERT_LE(0, passed_socket); 556 + stream_server = socket(AF_UNIX, SOCK_STREAM, 0); 557 + ASSERT_LE(0, stream_server); 558 + ASSERT_EQ(0, bind(stream_server, 559 + &self->transit_address.unix_addr, 560 + self->transit_address.unix_addr_len)); 561 + ASSERT_EQ(0, listen(stream_server, backlog)); 562 + ASSERT_EQ(1, write(pipe_child[1], ".", 1)); 563 + data_socket = accept(stream_server, NULL, NULL); 564 + ASSERT_LE(0, data_socket); 565 + ASSERT_EQ(0, send_fd(data_socket, passed_socket)); 566 + EXPECT_EQ(0, close(passed_socket)); 567 + EXPECT_EQ(0, close(stream_server)); 568 + } 569 + 570 + client_socket = socket(AF_UNIX, variant->type, 0); 571 + ASSERT_LE(0, client_socket); 572 + 573 + /* Waits for parent signal for connection. */ 574 + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 575 + err = connect(client_socket, &self->address.unix_addr, 576 + self->address.unix_addr_len); 577 + if (variant->child_socket) { 578 + EXPECT_EQ(0, err); 579 + } else { 580 + EXPECT_EQ(-1, err); 581 + EXPECT_EQ(EPERM, errno); 582 + } 583 + EXPECT_EQ(0, close(client_socket)); 584 + _exit(_metadata->exit_code); 585 + return; 586 + } 587 + EXPECT_EQ(0, close(pipe_child[1])); 588 + EXPECT_EQ(0, close(pipe_parent[0])); 589 + 590 + if (variant->child_socket) { 591 + int client_child = socket(AF_UNIX, SOCK_STREAM, 0); 592 + 593 + ASSERT_LE(0, client_child); 594 + ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 595 + ASSERT_EQ(0, connect(client_child, 596 + &self->transit_address.unix_addr, 597 + self->transit_address.unix_addr_len)); 598 + server_socket = recv_fd(client_child); 599 + EXPECT_EQ(0, close(client_child)); 600 + } else { 601 + server_socket = socket(AF_UNIX, variant->type, 0); 602 + } 603 + ASSERT_LE(0, server_socket); 604 + 605 + /* Server always has a domain. */ 606 + create_scoped_domain(_metadata, LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 607 + 608 + ASSERT_EQ(0, bind(server_socket, &self->address.unix_addr, 609 + self->address.unix_addr_len)); 610 + if (variant->type == SOCK_STREAM) 611 + ASSERT_EQ(0, listen(server_socket, backlog)); 612 + 613 + /* Signals to child that the parent is listening. */ 614 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 615 + 616 + ASSERT_EQ(child, waitpid(child, &status, 0)); 617 + EXPECT_EQ(0, close(server_socket)); 618 + 619 + if (WIFSIGNALED(status) || !WIFEXITED(status) || 620 + WEXITSTATUS(status) != EXIT_SUCCESS) 621 + _metadata->exit_code = KSFT_FAIL; 622 + } 623 + 624 + static const char stream_path[] = TMP_DIR "/stream.sock"; 625 + static const char dgram_path[] = TMP_DIR "/dgram.sock"; 626 + 627 + /* clang-format off */ 628 + FIXTURE(various_address_sockets) {}; 629 + /* clang-format on */ 630 + 631 + FIXTURE_VARIANT(various_address_sockets) 632 + { 633 + const int domain; 634 + }; 635 + 636 + /* clang-format off */ 637 + FIXTURE_VARIANT_ADD(various_address_sockets, pathname_socket_scoped_domain) { 638 + /* clang-format on */ 639 + .domain = SCOPE_SANDBOX, 640 + }; 641 + 642 + /* clang-format off */ 643 + FIXTURE_VARIANT_ADD(various_address_sockets, pathname_socket_other_domain) { 644 + /* clang-format on */ 645 + .domain = OTHER_SANDBOX, 646 + }; 647 + 648 + /* clang-format off */ 649 + FIXTURE_VARIANT_ADD(various_address_sockets, pathname_socket_no_domain) { 650 + /* clang-format on */ 651 + .domain = NO_SANDBOX, 652 + }; 653 + 654 + FIXTURE_SETUP(various_address_sockets) 655 + { 656 + drop_caps(_metadata); 657 + 658 + umask(0077); 659 + ASSERT_EQ(0, mkdir(TMP_DIR, 0700)); 660 + } 661 + 662 + FIXTURE_TEARDOWN(various_address_sockets) 663 + { 664 + EXPECT_EQ(0, unlink(stream_path)); 665 + EXPECT_EQ(0, unlink(dgram_path)); 666 + EXPECT_EQ(0, rmdir(TMP_DIR)); 667 + } 668 + 669 + TEST_F(various_address_sockets, scoped_pathname_sockets) 670 + { 671 + socklen_t size_stream, size_dgram; 672 + pid_t child; 673 + int status; 674 + char buf_child, buf_parent; 675 + int pipe_parent[2]; 676 + int unnamed_sockets[2]; 677 + int stream_pathname_socket, dgram_pathname_socket, 678 + stream_abstract_socket, dgram_abstract_socket, data_socket; 679 + struct service_fixture stream_abstract_addr, dgram_abstract_addr; 680 + struct sockaddr_un stream_pathname_addr = { 681 + .sun_family = AF_UNIX, 682 + }; 683 + struct sockaddr_un dgram_pathname_addr = { 684 + .sun_family = AF_UNIX, 685 + }; 686 + 687 + /* Pathname address. */ 688 + snprintf(stream_pathname_addr.sun_path, 689 + sizeof(stream_pathname_addr.sun_path), "%s", stream_path); 690 + size_stream = offsetof(struct sockaddr_un, sun_path) + 691 + strlen(stream_pathname_addr.sun_path); 692 + snprintf(dgram_pathname_addr.sun_path, 693 + sizeof(dgram_pathname_addr.sun_path), "%s", dgram_path); 694 + size_dgram = offsetof(struct sockaddr_un, sun_path) + 695 + strlen(dgram_pathname_addr.sun_path); 696 + 697 + /* Abstract address. */ 698 + memset(&stream_abstract_addr, 0, sizeof(stream_abstract_addr)); 699 + set_unix_address(&stream_abstract_addr, 0); 700 + memset(&dgram_abstract_addr, 0, sizeof(dgram_abstract_addr)); 701 + set_unix_address(&dgram_abstract_addr, 1); 702 + 703 + /* Unnamed address for datagram socket. */ 704 + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_DGRAM, 0, unnamed_sockets)); 705 + 706 + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 707 + 708 + child = fork(); 709 + ASSERT_LE(0, child); 710 + if (child == 0) { 711 + int err; 712 + 713 + EXPECT_EQ(0, close(pipe_parent[1])); 714 + EXPECT_EQ(0, close(unnamed_sockets[1])); 715 + 716 + if (variant->domain == SCOPE_SANDBOX) 717 + create_scoped_domain( 718 + _metadata, LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 719 + else if (variant->domain == OTHER_SANDBOX) 720 + create_fs_domain(_metadata); 721 + 722 + /* Waits for parent to listen. */ 723 + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 724 + EXPECT_EQ(0, close(pipe_parent[0])); 725 + 726 + /* Checks that we can send data through a datagram socket. */ 727 + ASSERT_EQ(1, write(unnamed_sockets[0], "a", 1)); 728 + EXPECT_EQ(0, close(unnamed_sockets[0])); 729 + 730 + /* Connects with pathname sockets. */ 731 + stream_pathname_socket = socket(AF_UNIX, SOCK_STREAM, 0); 732 + ASSERT_LE(0, stream_pathname_socket); 733 + ASSERT_EQ(0, connect(stream_pathname_socket, 734 + &stream_pathname_addr, size_stream)); 735 + ASSERT_EQ(1, write(stream_pathname_socket, "b", 1)); 736 + EXPECT_EQ(0, close(stream_pathname_socket)); 737 + 738 + /* Sends without connection. */ 739 + dgram_pathname_socket = socket(AF_UNIX, SOCK_DGRAM, 0); 740 + ASSERT_LE(0, dgram_pathname_socket); 741 + err = sendto(dgram_pathname_socket, "c", 1, 0, 742 + &dgram_pathname_addr, size_dgram); 743 + EXPECT_EQ(1, err); 744 + 745 + /* Sends with connection. */ 746 + ASSERT_EQ(0, connect(dgram_pathname_socket, 747 + &dgram_pathname_addr, size_dgram)); 748 + ASSERT_EQ(1, write(dgram_pathname_socket, "d", 1)); 749 + EXPECT_EQ(0, close(dgram_pathname_socket)); 750 + 751 + /* Connects with abstract sockets. */ 752 + stream_abstract_socket = socket(AF_UNIX, SOCK_STREAM, 0); 753 + ASSERT_LE(0, stream_abstract_socket); 754 + err = connect(stream_abstract_socket, 755 + &stream_abstract_addr.unix_addr, 756 + stream_abstract_addr.unix_addr_len); 757 + if (variant->domain == SCOPE_SANDBOX) { 758 + EXPECT_EQ(-1, err); 759 + EXPECT_EQ(EPERM, errno); 760 + } else { 761 + EXPECT_EQ(0, err); 762 + ASSERT_EQ(1, write(stream_abstract_socket, "e", 1)); 763 + } 764 + EXPECT_EQ(0, close(stream_abstract_socket)); 765 + 766 + /* Sends without connection. */ 767 + dgram_abstract_socket = socket(AF_UNIX, SOCK_DGRAM, 0); 768 + ASSERT_LE(0, dgram_abstract_socket); 769 + err = sendto(dgram_abstract_socket, "f", 1, 0, 770 + &dgram_abstract_addr.unix_addr, 771 + dgram_abstract_addr.unix_addr_len); 772 + if (variant->domain == SCOPE_SANDBOX) { 773 + EXPECT_EQ(-1, err); 774 + EXPECT_EQ(EPERM, errno); 775 + } else { 776 + EXPECT_EQ(1, err); 777 + } 778 + 779 + /* Sends with connection. */ 780 + err = connect(dgram_abstract_socket, 781 + &dgram_abstract_addr.unix_addr, 782 + dgram_abstract_addr.unix_addr_len); 783 + if (variant->domain == SCOPE_SANDBOX) { 784 + EXPECT_EQ(-1, err); 785 + EXPECT_EQ(EPERM, errno); 786 + } else { 787 + EXPECT_EQ(0, err); 788 + ASSERT_EQ(1, write(dgram_abstract_socket, "g", 1)); 789 + } 790 + EXPECT_EQ(0, close(dgram_abstract_socket)); 791 + 792 + _exit(_metadata->exit_code); 793 + return; 794 + } 795 + EXPECT_EQ(0, close(pipe_parent[0])); 796 + EXPECT_EQ(0, close(unnamed_sockets[0])); 797 + 798 + /* Sets up pathname servers. */ 799 + stream_pathname_socket = socket(AF_UNIX, SOCK_STREAM, 0); 800 + ASSERT_LE(0, stream_pathname_socket); 801 + ASSERT_EQ(0, bind(stream_pathname_socket, &stream_pathname_addr, 802 + size_stream)); 803 + ASSERT_EQ(0, listen(stream_pathname_socket, backlog)); 804 + 805 + dgram_pathname_socket = socket(AF_UNIX, SOCK_DGRAM, 0); 806 + ASSERT_LE(0, dgram_pathname_socket); 807 + ASSERT_EQ(0, bind(dgram_pathname_socket, &dgram_pathname_addr, 808 + size_dgram)); 809 + 810 + /* Sets up abstract servers. */ 811 + stream_abstract_socket = socket(AF_UNIX, SOCK_STREAM, 0); 812 + ASSERT_LE(0, stream_abstract_socket); 813 + ASSERT_EQ(0, 814 + bind(stream_abstract_socket, &stream_abstract_addr.unix_addr, 815 + stream_abstract_addr.unix_addr_len)); 816 + 817 + dgram_abstract_socket = socket(AF_UNIX, SOCK_DGRAM, 0); 818 + ASSERT_LE(0, dgram_abstract_socket); 819 + ASSERT_EQ(0, bind(dgram_abstract_socket, &dgram_abstract_addr.unix_addr, 820 + dgram_abstract_addr.unix_addr_len)); 821 + ASSERT_EQ(0, listen(stream_abstract_socket, backlog)); 822 + 823 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 824 + EXPECT_EQ(0, close(pipe_parent[1])); 825 + 826 + /* Reads from unnamed socket. */ 827 + ASSERT_EQ(1, read(unnamed_sockets[1], &buf_parent, sizeof(buf_parent))); 828 + ASSERT_EQ('a', buf_parent); 829 + EXPECT_LE(0, close(unnamed_sockets[1])); 830 + 831 + /* Reads from pathname sockets. */ 832 + data_socket = accept(stream_pathname_socket, NULL, NULL); 833 + ASSERT_LE(0, data_socket); 834 + ASSERT_EQ(1, read(data_socket, &buf_parent, sizeof(buf_parent))); 835 + ASSERT_EQ('b', buf_parent); 836 + EXPECT_EQ(0, close(data_socket)); 837 + EXPECT_EQ(0, close(stream_pathname_socket)); 838 + 839 + ASSERT_EQ(1, 840 + read(dgram_pathname_socket, &buf_parent, sizeof(buf_parent))); 841 + ASSERT_EQ('c', buf_parent); 842 + ASSERT_EQ(1, 843 + read(dgram_pathname_socket, &buf_parent, sizeof(buf_parent))); 844 + ASSERT_EQ('d', buf_parent); 845 + EXPECT_EQ(0, close(dgram_pathname_socket)); 846 + 847 + if (variant->domain != SCOPE_SANDBOX) { 848 + /* Reads from abstract sockets if allowed to send. */ 849 + data_socket = accept(stream_abstract_socket, NULL, NULL); 850 + ASSERT_LE(0, data_socket); 851 + ASSERT_EQ(1, 852 + read(data_socket, &buf_parent, sizeof(buf_parent))); 853 + ASSERT_EQ('e', buf_parent); 854 + EXPECT_EQ(0, close(data_socket)); 855 + 856 + ASSERT_EQ(1, read(dgram_abstract_socket, &buf_parent, 857 + sizeof(buf_parent))); 858 + ASSERT_EQ('f', buf_parent); 859 + ASSERT_EQ(1, read(dgram_abstract_socket, &buf_parent, 860 + sizeof(buf_parent))); 861 + ASSERT_EQ('g', buf_parent); 862 + } 863 + 864 + /* Waits for all abstract socket tests. */ 865 + ASSERT_EQ(child, waitpid(child, &status, 0)); 866 + EXPECT_EQ(0, close(stream_abstract_socket)); 867 + EXPECT_EQ(0, close(dgram_abstract_socket)); 868 + 869 + if (WIFSIGNALED(status) || !WIFEXITED(status) || 870 + WEXITSTATUS(status) != EXIT_SUCCESS) 871 + _metadata->exit_code = KSFT_FAIL; 872 + } 873 + 874 + TEST(datagram_sockets) 875 + { 876 + struct service_fixture connected_addr, non_connected_addr; 877 + int server_conn_socket, server_unconn_socket; 878 + int pipe_parent[2], pipe_child[2]; 879 + int status; 880 + char buf; 881 + pid_t child; 882 + 883 + drop_caps(_metadata); 884 + memset(&connected_addr, 0, sizeof(connected_addr)); 885 + set_unix_address(&connected_addr, 0); 886 + memset(&non_connected_addr, 0, sizeof(non_connected_addr)); 887 + set_unix_address(&non_connected_addr, 1); 888 + 889 + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 890 + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); 891 + 892 + child = fork(); 893 + ASSERT_LE(0, child); 894 + if (child == 0) { 895 + int client_conn_socket, client_unconn_socket; 896 + 897 + EXPECT_EQ(0, close(pipe_parent[1])); 898 + EXPECT_EQ(0, close(pipe_child[0])); 899 + 900 + client_conn_socket = socket(AF_UNIX, SOCK_DGRAM, 0); 901 + client_unconn_socket = socket(AF_UNIX, SOCK_DGRAM, 0); 902 + ASSERT_LE(0, client_conn_socket); 903 + ASSERT_LE(0, client_unconn_socket); 904 + 905 + /* Waits for parent to listen. */ 906 + ASSERT_EQ(1, read(pipe_parent[0], &buf, 1)); 907 + ASSERT_EQ(0, 908 + connect(client_conn_socket, &connected_addr.unix_addr, 909 + connected_addr.unix_addr_len)); 910 + 911 + /* 912 + * Both connected and non-connected sockets can send data when 913 + * the domain is not scoped. 914 + */ 915 + ASSERT_EQ(1, send(client_conn_socket, ".", 1, 0)); 916 + ASSERT_EQ(1, sendto(client_unconn_socket, ".", 1, 0, 917 + &non_connected_addr.unix_addr, 918 + non_connected_addr.unix_addr_len)); 919 + ASSERT_EQ(1, write(pipe_child[1], ".", 1)); 920 + 921 + /* Scopes the domain. */ 922 + create_scoped_domain(_metadata, 923 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 924 + 925 + /* 926 + * Connected socket sends data to the receiver, but the 927 + * non-connected socket must fail to send data. 928 + */ 929 + ASSERT_EQ(1, send(client_conn_socket, ".", 1, 0)); 930 + ASSERT_EQ(-1, sendto(client_unconn_socket, ".", 1, 0, 931 + &non_connected_addr.unix_addr, 932 + non_connected_addr.unix_addr_len)); 933 + ASSERT_EQ(EPERM, errno); 934 + ASSERT_EQ(1, write(pipe_child[1], ".", 1)); 935 + 936 + EXPECT_EQ(0, close(client_conn_socket)); 937 + EXPECT_EQ(0, close(client_unconn_socket)); 938 + _exit(_metadata->exit_code); 939 + return; 940 + } 941 + EXPECT_EQ(0, close(pipe_parent[0])); 942 + EXPECT_EQ(0, close(pipe_child[1])); 943 + 944 + server_conn_socket = socket(AF_UNIX, SOCK_DGRAM, 0); 945 + server_unconn_socket = socket(AF_UNIX, SOCK_DGRAM, 0); 946 + ASSERT_LE(0, server_conn_socket); 947 + ASSERT_LE(0, server_unconn_socket); 948 + 949 + ASSERT_EQ(0, bind(server_conn_socket, &connected_addr.unix_addr, 950 + connected_addr.unix_addr_len)); 951 + ASSERT_EQ(0, bind(server_unconn_socket, &non_connected_addr.unix_addr, 952 + non_connected_addr.unix_addr_len)); 953 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 954 + 955 + /* Waits for child to test. */ 956 + ASSERT_EQ(1, read(pipe_child[0], &buf, 1)); 957 + ASSERT_EQ(1, recv(server_conn_socket, &buf, 1, 0)); 958 + ASSERT_EQ(1, recv(server_unconn_socket, &buf, 1, 0)); 959 + 960 + /* 961 + * Connected datagram socket will receive data, but 962 + * non-connected datagram socket does not receive data. 963 + */ 964 + ASSERT_EQ(1, read(pipe_child[0], &buf, 1)); 965 + ASSERT_EQ(1, recv(server_conn_socket, &buf, 1, 0)); 966 + 967 + /* Waits for all tests to finish. */ 968 + ASSERT_EQ(child, waitpid(child, &status, 0)); 969 + EXPECT_EQ(0, close(server_conn_socket)); 970 + EXPECT_EQ(0, close(server_unconn_socket)); 971 + 972 + if (WIFSIGNALED(status) || !WIFEXITED(status) || 973 + WEXITSTATUS(status) != EXIT_SUCCESS) 974 + _metadata->exit_code = KSFT_FAIL; 975 + } 976 + 977 + TEST(self_connect) 978 + { 979 + struct service_fixture connected_addr, non_connected_addr; 980 + int connected_socket, non_connected_socket, status; 981 + pid_t child; 982 + 983 + drop_caps(_metadata); 984 + memset(&connected_addr, 0, sizeof(connected_addr)); 985 + set_unix_address(&connected_addr, 0); 986 + memset(&non_connected_addr, 0, sizeof(non_connected_addr)); 987 + set_unix_address(&non_connected_addr, 1); 988 + 989 + connected_socket = socket(AF_UNIX, SOCK_DGRAM, 0); 990 + non_connected_socket = socket(AF_UNIX, SOCK_DGRAM, 0); 991 + ASSERT_LE(0, connected_socket); 992 + ASSERT_LE(0, non_connected_socket); 993 + 994 + ASSERT_EQ(0, bind(connected_socket, &connected_addr.unix_addr, 995 + connected_addr.unix_addr_len)); 996 + ASSERT_EQ(0, bind(non_connected_socket, &non_connected_addr.unix_addr, 997 + non_connected_addr.unix_addr_len)); 998 + 999 + child = fork(); 1000 + ASSERT_LE(0, child); 1001 + if (child == 0) { 1002 + /* Child's domain is scoped. */ 1003 + create_scoped_domain(_metadata, 1004 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 1005 + 1006 + /* 1007 + * The child inherits the sockets, and cannot connect or 1008 + * send data to them. 1009 + */ 1010 + ASSERT_EQ(-1, 1011 + connect(connected_socket, &connected_addr.unix_addr, 1012 + connected_addr.unix_addr_len)); 1013 + ASSERT_EQ(EPERM, errno); 1014 + 1015 + ASSERT_EQ(-1, sendto(connected_socket, ".", 1, 0, 1016 + &connected_addr.unix_addr, 1017 + connected_addr.unix_addr_len)); 1018 + ASSERT_EQ(EPERM, errno); 1019 + 1020 + ASSERT_EQ(-1, sendto(non_connected_socket, ".", 1, 0, 1021 + &non_connected_addr.unix_addr, 1022 + non_connected_addr.unix_addr_len)); 1023 + ASSERT_EQ(EPERM, errno); 1024 + 1025 + EXPECT_EQ(0, close(connected_socket)); 1026 + EXPECT_EQ(0, close(non_connected_socket)); 1027 + _exit(_metadata->exit_code); 1028 + return; 1029 + } 1030 + 1031 + /* Waits for all tests to finish. */ 1032 + ASSERT_EQ(child, waitpid(child, &status, 0)); 1033 + EXPECT_EQ(0, close(connected_socket)); 1034 + EXPECT_EQ(0, close(non_connected_socket)); 1035 + 1036 + if (WIFSIGNALED(status) || !WIFEXITED(status) || 1037 + WEXITSTATUS(status) != EXIT_SUCCESS) 1038 + _metadata->exit_code = KSFT_FAIL; 1039 + } 1040 + 1041 + TEST_HARNESS_MAIN
+156
tools/testing/selftests/landlock/scoped_base_variants.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Landlock scoped_domains variants 4 + * 5 + * See the hierarchy variants from ptrace_test.c 6 + * 7 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 8 + * Copyright © 2019-2020 ANSSI 9 + * Copyright © 2024 Tahera Fahimi <fahimitahera@gmail.com> 10 + */ 11 + 12 + /* clang-format on */ 13 + FIXTURE_VARIANT(scoped_domains) 14 + { 15 + bool domain_both; 16 + bool domain_parent; 17 + bool domain_child; 18 + }; 19 + 20 + /* 21 + * No domain 22 + * 23 + * P1-. P1 -> P2 : allow 24 + * \ P2 -> P1 : allow 25 + * 'P2 26 + */ 27 + /* clang-format off */ 28 + FIXTURE_VARIANT_ADD(scoped_domains, without_domain) { 29 + /* clang-format on */ 30 + .domain_both = false, 31 + .domain_parent = false, 32 + .domain_child = false, 33 + }; 34 + 35 + /* 36 + * Child domain 37 + * 38 + * P1--. P1 -> P2 : allow 39 + * \ P2 -> P1 : deny 40 + * .'-----. 41 + * | P2 | 42 + * '------' 43 + */ 44 + /* clang-format off */ 45 + FIXTURE_VARIANT_ADD(scoped_domains, child_domain) { 46 + /* clang-format on */ 47 + .domain_both = false, 48 + .domain_parent = false, 49 + .domain_child = true, 50 + }; 51 + 52 + /* 53 + * Parent domain 54 + * .------. 55 + * | P1 --. P1 -> P2 : deny 56 + * '------' \ P2 -> P1 : allow 57 + * ' 58 + * P2 59 + */ 60 + /* clang-format off */ 61 + FIXTURE_VARIANT_ADD(scoped_domains, parent_domain) { 62 + /* clang-format on */ 63 + .domain_both = false, 64 + .domain_parent = true, 65 + .domain_child = false, 66 + }; 67 + 68 + /* 69 + * Parent + child domain (siblings) 70 + * .------. 71 + * | P1 ---. P1 -> P2 : deny 72 + * '------' \ P2 -> P1 : deny 73 + * .---'--. 74 + * | P2 | 75 + * '------' 76 + */ 77 + /* clang-format off */ 78 + FIXTURE_VARIANT_ADD(scoped_domains, sibling_domain) { 79 + /* clang-format on */ 80 + .domain_both = false, 81 + .domain_parent = true, 82 + .domain_child = true, 83 + }; 84 + 85 + /* 86 + * Same domain (inherited) 87 + * .-------------. 88 + * | P1----. | P1 -> P2 : allow 89 + * | \ | P2 -> P1 : allow 90 + * | ' | 91 + * | P2 | 92 + * '-------------' 93 + */ 94 + /* clang-format off */ 95 + FIXTURE_VARIANT_ADD(scoped_domains, inherited_domain) { 96 + /* clang-format on */ 97 + .domain_both = true, 98 + .domain_parent = false, 99 + .domain_child = false, 100 + }; 101 + 102 + /* 103 + * Inherited + child domain 104 + * .-----------------. 105 + * | P1----. | P1 -> P2 : allow 106 + * | \ | P2 -> P1 : deny 107 + * | .-'----. | 108 + * | | P2 | | 109 + * | '------' | 110 + * '-----------------' 111 + */ 112 + /* clang-format off */ 113 + FIXTURE_VARIANT_ADD(scoped_domains, nested_domain) { 114 + /* clang-format on */ 115 + .domain_both = true, 116 + .domain_parent = false, 117 + .domain_child = true, 118 + }; 119 + 120 + /* 121 + * Inherited + parent domain 122 + * .-----------------. 123 + * |.------. | P1 -> P2 : deny 124 + * || P1 ----. | P2 -> P1 : allow 125 + * |'------' \ | 126 + * | ' | 127 + * | P2 | 128 + * '-----------------' 129 + */ 130 + /* clang-format off */ 131 + FIXTURE_VARIANT_ADD(scoped_domains, nested_and_parent_domain) { 132 + /* clang-format on */ 133 + .domain_both = true, 134 + .domain_parent = true, 135 + .domain_child = false, 136 + }; 137 + 138 + /* 139 + * Inherited + parent and child domain (siblings) 140 + * .-----------------. 141 + * | .------. | P1 -> P2 : deny 142 + * | | P1 . | P2 -> P1 : deny 143 + * | '------'\ | 144 + * | \ | 145 + * | .--'---. | 146 + * | | P2 | | 147 + * | '------' | 148 + * '-----------------' 149 + */ 150 + /* clang-format off */ 151 + FIXTURE_VARIANT_ADD(scoped_domains, forked_domains) { 152 + /* clang-format on */ 153 + .domain_both = true, 154 + .domain_parent = true, 155 + .domain_child = true, 156 + };
+28
tools/testing/selftests/landlock/scoped_common.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Landlock scope test helpers 4 + * 5 + * Copyright © 2024 Tahera Fahimi <fahimitahera@gmail.com> 6 + */ 7 + 8 + #define _GNU_SOURCE 9 + 10 + #include <sys/types.h> 11 + 12 + static void create_scoped_domain(struct __test_metadata *const _metadata, 13 + const __u16 scope) 14 + { 15 + int ruleset_fd; 16 + const struct landlock_ruleset_attr ruleset_attr = { 17 + .scoped = scope, 18 + }; 19 + 20 + ruleset_fd = 21 + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 22 + ASSERT_LE(0, ruleset_fd) 23 + { 24 + TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 25 + } 26 + enforce_ruleset(_metadata, ruleset_fd); 27 + EXPECT_EQ(0, close(ruleset_fd)); 28 + }
+152
tools/testing/selftests/landlock/scoped_multiple_domain_variants.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Landlock variants for three processes with various domains. 4 + * 5 + * Copyright © 2024 Tahera Fahimi <fahimitahera@gmail.com> 6 + */ 7 + 8 + enum sandbox_type { 9 + NO_SANDBOX, 10 + SCOPE_SANDBOX, 11 + /* Any other type of sandboxing domain */ 12 + OTHER_SANDBOX, 13 + }; 14 + 15 + /* clang-format on */ 16 + FIXTURE_VARIANT(scoped_vs_unscoped) 17 + { 18 + const int domain_all; 19 + const int domain_parent; 20 + const int domain_children; 21 + const int domain_child; 22 + const int domain_grand_child; 23 + }; 24 + 25 + /* 26 + * .-----------------. 27 + * | ####### | P3 -> P2 : allow 28 + * | P1----# P2 # | P3 -> P1 : deny 29 + * | # | # | 30 + * | # P3 # | 31 + * | ####### | 32 + * '-----------------' 33 + */ 34 + /* clang-format off */ 35 + FIXTURE_VARIANT_ADD(scoped_vs_unscoped, deny_scoped) { 36 + .domain_all = OTHER_SANDBOX, 37 + .domain_parent = NO_SANDBOX, 38 + .domain_children = SCOPE_SANDBOX, 39 + .domain_child = NO_SANDBOX, 40 + .domain_grand_child = NO_SANDBOX, 41 + /* clang-format on */ 42 + }; 43 + 44 + /* 45 + * ################### 46 + * # ####### # P3 -> P2 : allow 47 + * # P1----# P2 # # P3 -> P1 : deny 48 + * # # | # # 49 + * # # P3 # # 50 + * # ####### # 51 + * ################### 52 + */ 53 + /* clang-format off */ 54 + FIXTURE_VARIANT_ADD(scoped_vs_unscoped, all_scoped) { 55 + .domain_all = SCOPE_SANDBOX, 56 + .domain_parent = NO_SANDBOX, 57 + .domain_children = SCOPE_SANDBOX, 58 + .domain_child = NO_SANDBOX, 59 + .domain_grand_child = NO_SANDBOX, 60 + /* clang-format on */ 61 + }; 62 + 63 + /* 64 + * .-----------------. 65 + * | .-----. | P3 -> P2 : allow 66 + * | P1----| P2 | | P3 -> P1 : allow 67 + * | | | | 68 + * | | P3 | | 69 + * | '-----' | 70 + * '-----------------' 71 + */ 72 + /* clang-format off */ 73 + FIXTURE_VARIANT_ADD(scoped_vs_unscoped, allow_with_other_domain) { 74 + .domain_all = OTHER_SANDBOX, 75 + .domain_parent = NO_SANDBOX, 76 + .domain_children = OTHER_SANDBOX, 77 + .domain_child = NO_SANDBOX, 78 + .domain_grand_child = NO_SANDBOX, 79 + /* clang-format on */ 80 + }; 81 + 82 + /* 83 + * .----. ###### P3 -> P2 : allow 84 + * | P1 |----# P2 # P3 -> P1 : allow 85 + * '----' ###### 86 + * | 87 + * P3 88 + */ 89 + /* clang-format off */ 90 + FIXTURE_VARIANT_ADD(scoped_vs_unscoped, allow_with_one_domain) { 91 + .domain_all = NO_SANDBOX, 92 + .domain_parent = OTHER_SANDBOX, 93 + .domain_children = NO_SANDBOX, 94 + .domain_child = SCOPE_SANDBOX, 95 + .domain_grand_child = NO_SANDBOX, 96 + /* clang-format on */ 97 + }; 98 + 99 + /* 100 + * ###### .-----. P3 -> P2 : allow 101 + * # P1 #----| P2 | P3 -> P1 : allow 102 + * ###### '-----' 103 + * | 104 + * P3 105 + */ 106 + /* clang-format off */ 107 + FIXTURE_VARIANT_ADD(scoped_vs_unscoped, allow_with_grand_parent_scoped) { 108 + .domain_all = NO_SANDBOX, 109 + .domain_parent = SCOPE_SANDBOX, 110 + .domain_children = NO_SANDBOX, 111 + .domain_child = OTHER_SANDBOX, 112 + .domain_grand_child = NO_SANDBOX, 113 + /* clang-format on */ 114 + }; 115 + 116 + /* 117 + * ###### ###### P3 -> P2 : allow 118 + * # P1 #----# P2 # P3 -> P1 : allow 119 + * ###### ###### 120 + * | 121 + * .----. 122 + * | P3 | 123 + * '----' 124 + */ 125 + /* clang-format off */ 126 + FIXTURE_VARIANT_ADD(scoped_vs_unscoped, allow_with_parents_domain) { 127 + .domain_all = NO_SANDBOX, 128 + .domain_parent = SCOPE_SANDBOX, 129 + .domain_children = NO_SANDBOX, 130 + .domain_child = SCOPE_SANDBOX, 131 + .domain_grand_child = NO_SANDBOX, 132 + /* clang-format on */ 133 + }; 134 + 135 + /* 136 + * ###### P3 -> P2 : deny 137 + * # P1 #----P2 P3 -> P1 : deny 138 + * ###### | 139 + * | 140 + * ###### 141 + * # P3 # 142 + * ###### 143 + */ 144 + /* clang-format off */ 145 + FIXTURE_VARIANT_ADD(scoped_vs_unscoped, deny_with_self_and_grandparent_domain) { 146 + .domain_all = NO_SANDBOX, 147 + .domain_parent = SCOPE_SANDBOX, 148 + .domain_children = NO_SANDBOX, 149 + .domain_child = NO_SANDBOX, 150 + .domain_grand_child = SCOPE_SANDBOX, 151 + /* clang-format on */ 152 + };
+484
tools/testing/selftests/landlock/scoped_signal_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Landlock tests - Signal Scoping 4 + * 5 + * Copyright © 2024 Tahera Fahimi <fahimitahera@gmail.com> 6 + */ 7 + 8 + #define _GNU_SOURCE 9 + #include <errno.h> 10 + #include <fcntl.h> 11 + #include <linux/landlock.h> 12 + #include <pthread.h> 13 + #include <signal.h> 14 + #include <sys/prctl.h> 15 + #include <sys/types.h> 16 + #include <sys/wait.h> 17 + #include <unistd.h> 18 + 19 + #include "common.h" 20 + #include "scoped_common.h" 21 + 22 + /* This variable is used for handling several signals. */ 23 + static volatile sig_atomic_t is_signaled; 24 + 25 + /* clang-format off */ 26 + FIXTURE(scoping_signals) {}; 27 + /* clang-format on */ 28 + 29 + FIXTURE_VARIANT(scoping_signals) 30 + { 31 + int sig; 32 + }; 33 + 34 + /* clang-format off */ 35 + FIXTURE_VARIANT_ADD(scoping_signals, sigtrap) { 36 + /* clang-format on */ 37 + .sig = SIGTRAP, 38 + }; 39 + 40 + /* clang-format off */ 41 + FIXTURE_VARIANT_ADD(scoping_signals, sigurg) { 42 + /* clang-format on */ 43 + .sig = SIGURG, 44 + }; 45 + 46 + /* clang-format off */ 47 + FIXTURE_VARIANT_ADD(scoping_signals, sighup) { 48 + /* clang-format on */ 49 + .sig = SIGHUP, 50 + }; 51 + 52 + /* clang-format off */ 53 + FIXTURE_VARIANT_ADD(scoping_signals, sigtstp) { 54 + /* clang-format on */ 55 + .sig = SIGTSTP, 56 + }; 57 + 58 + FIXTURE_SETUP(scoping_signals) 59 + { 60 + drop_caps(_metadata); 61 + 62 + is_signaled = 0; 63 + } 64 + 65 + FIXTURE_TEARDOWN(scoping_signals) 66 + { 67 + } 68 + 69 + static void scope_signal_handler(int sig, siginfo_t *info, void *ucontext) 70 + { 71 + if (sig == SIGTRAP || sig == SIGURG || sig == SIGHUP || sig == SIGTSTP) 72 + is_signaled = 1; 73 + } 74 + 75 + /* 76 + * In this test, a child process sends a signal to parent before and 77 + * after getting scoped. 78 + */ 79 + TEST_F(scoping_signals, send_sig_to_parent) 80 + { 81 + int pipe_parent[2]; 82 + int status; 83 + pid_t child; 84 + pid_t parent = getpid(); 85 + struct sigaction action = { 86 + .sa_sigaction = scope_signal_handler, 87 + .sa_flags = SA_SIGINFO, 88 + 89 + }; 90 + 91 + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 92 + ASSERT_LE(0, sigaction(variant->sig, &action, NULL)); 93 + 94 + /* The process should not have already been signaled. */ 95 + EXPECT_EQ(0, is_signaled); 96 + 97 + child = fork(); 98 + ASSERT_LE(0, child); 99 + if (child == 0) { 100 + char buf_child; 101 + int err; 102 + 103 + EXPECT_EQ(0, close(pipe_parent[1])); 104 + 105 + /* 106 + * The child process can send signal to parent when 107 + * domain is not scoped. 108 + */ 109 + err = kill(parent, variant->sig); 110 + ASSERT_EQ(0, err); 111 + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 112 + EXPECT_EQ(0, close(pipe_parent[0])); 113 + 114 + create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL); 115 + 116 + /* 117 + * The child process cannot send signal to the parent 118 + * anymore. 119 + */ 120 + err = kill(parent, variant->sig); 121 + ASSERT_EQ(-1, err); 122 + ASSERT_EQ(EPERM, errno); 123 + 124 + /* 125 + * No matter of the domain, a process should be able to 126 + * send a signal to itself. 127 + */ 128 + ASSERT_EQ(0, is_signaled); 129 + ASSERT_EQ(0, raise(variant->sig)); 130 + ASSERT_EQ(1, is_signaled); 131 + 132 + _exit(_metadata->exit_code); 133 + return; 134 + } 135 + EXPECT_EQ(0, close(pipe_parent[0])); 136 + 137 + /* Waits for a first signal to be received, without race condition. */ 138 + while (!is_signaled && !usleep(1)) 139 + ; 140 + ASSERT_EQ(1, is_signaled); 141 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 142 + EXPECT_EQ(0, close(pipe_parent[1])); 143 + is_signaled = 0; 144 + 145 + ASSERT_EQ(child, waitpid(child, &status, 0)); 146 + if (WIFSIGNALED(status) || !WIFEXITED(status) || 147 + WEXITSTATUS(status) != EXIT_SUCCESS) 148 + _metadata->exit_code = KSFT_FAIL; 149 + 150 + EXPECT_EQ(0, is_signaled); 151 + } 152 + 153 + /* clang-format off */ 154 + FIXTURE(scoped_domains) {}; 155 + /* clang-format on */ 156 + 157 + #include "scoped_base_variants.h" 158 + 159 + FIXTURE_SETUP(scoped_domains) 160 + { 161 + drop_caps(_metadata); 162 + } 163 + 164 + FIXTURE_TEARDOWN(scoped_domains) 165 + { 166 + } 167 + 168 + /* 169 + * This test ensures that a scoped process cannot send signal out of 170 + * scoped domain. 171 + */ 172 + TEST_F(scoped_domains, check_access_signal) 173 + { 174 + pid_t child; 175 + pid_t parent = getpid(); 176 + int status; 177 + bool can_signal_child, can_signal_parent; 178 + int pipe_parent[2], pipe_child[2]; 179 + char buf_parent; 180 + int err; 181 + 182 + can_signal_parent = !variant->domain_child; 183 + can_signal_child = !variant->domain_parent; 184 + 185 + if (variant->domain_both) 186 + create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL); 187 + 188 + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 189 + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); 190 + 191 + child = fork(); 192 + ASSERT_LE(0, child); 193 + if (child == 0) { 194 + char buf_child; 195 + 196 + EXPECT_EQ(0, close(pipe_child[0])); 197 + EXPECT_EQ(0, close(pipe_parent[1])); 198 + 199 + if (variant->domain_child) 200 + create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL); 201 + 202 + ASSERT_EQ(1, write(pipe_child[1], ".", 1)); 203 + EXPECT_EQ(0, close(pipe_child[1])); 204 + 205 + /* Waits for the parent to send signals. */ 206 + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 207 + EXPECT_EQ(0, close(pipe_parent[0])); 208 + 209 + err = kill(parent, 0); 210 + if (can_signal_parent) { 211 + ASSERT_EQ(0, err); 212 + } else { 213 + ASSERT_EQ(-1, err); 214 + ASSERT_EQ(EPERM, errno); 215 + } 216 + /* 217 + * No matter of the domain, a process should be able to 218 + * send a signal to itself. 219 + */ 220 + ASSERT_EQ(0, raise(0)); 221 + 222 + _exit(_metadata->exit_code); 223 + return; 224 + } 225 + EXPECT_EQ(0, close(pipe_parent[0])); 226 + EXPECT_EQ(0, close(pipe_child[1])); 227 + 228 + if (variant->domain_parent) 229 + create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL); 230 + 231 + ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 232 + EXPECT_EQ(0, close(pipe_child[0])); 233 + 234 + err = kill(child, 0); 235 + if (can_signal_child) { 236 + ASSERT_EQ(0, err); 237 + } else { 238 + ASSERT_EQ(-1, err); 239 + ASSERT_EQ(EPERM, errno); 240 + } 241 + ASSERT_EQ(0, raise(0)); 242 + 243 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 244 + EXPECT_EQ(0, close(pipe_parent[1])); 245 + ASSERT_EQ(child, waitpid(child, &status, 0)); 246 + 247 + if (WIFSIGNALED(status) || !WIFEXITED(status) || 248 + WEXITSTATUS(status) != EXIT_SUCCESS) 249 + _metadata->exit_code = KSFT_FAIL; 250 + } 251 + 252 + static int thread_pipe[2]; 253 + 254 + enum thread_return { 255 + THREAD_INVALID = 0, 256 + THREAD_SUCCESS = 1, 257 + THREAD_ERROR = 2, 258 + }; 259 + 260 + void *thread_func(void *arg) 261 + { 262 + char buf; 263 + 264 + if (read(thread_pipe[0], &buf, 1) != 1) 265 + return (void *)THREAD_ERROR; 266 + 267 + return (void *)THREAD_SUCCESS; 268 + } 269 + 270 + TEST(signal_scoping_threads) 271 + { 272 + pthread_t no_sandbox_thread, scoped_thread; 273 + enum thread_return ret = THREAD_INVALID; 274 + 275 + drop_caps(_metadata); 276 + ASSERT_EQ(0, pipe2(thread_pipe, O_CLOEXEC)); 277 + 278 + ASSERT_EQ(0, 279 + pthread_create(&no_sandbox_thread, NULL, thread_func, NULL)); 280 + 281 + /* Restricts the domain after creating the first thread. */ 282 + create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL); 283 + 284 + ASSERT_EQ(EPERM, pthread_kill(no_sandbox_thread, 0)); 285 + ASSERT_EQ(1, write(thread_pipe[1], ".", 1)); 286 + 287 + ASSERT_EQ(0, pthread_create(&scoped_thread, NULL, thread_func, NULL)); 288 + ASSERT_EQ(0, pthread_kill(scoped_thread, 0)); 289 + ASSERT_EQ(1, write(thread_pipe[1], ".", 1)); 290 + 291 + EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret)); 292 + EXPECT_EQ(THREAD_SUCCESS, ret); 293 + EXPECT_EQ(0, pthread_join(scoped_thread, (void **)&ret)); 294 + EXPECT_EQ(THREAD_SUCCESS, ret); 295 + 296 + EXPECT_EQ(0, close(thread_pipe[0])); 297 + EXPECT_EQ(0, close(thread_pipe[1])); 298 + } 299 + 300 + const short backlog = 10; 301 + 302 + static volatile sig_atomic_t signal_received; 303 + 304 + static void handle_sigurg(int sig) 305 + { 306 + if (sig == SIGURG) 307 + signal_received = 1; 308 + else 309 + signal_received = -1; 310 + } 311 + 312 + static int setup_signal_handler(int signal) 313 + { 314 + struct sigaction sa = { 315 + .sa_handler = handle_sigurg, 316 + }; 317 + 318 + if (sigemptyset(&sa.sa_mask)) 319 + return -1; 320 + 321 + sa.sa_flags = SA_SIGINFO | SA_RESTART; 322 + return sigaction(SIGURG, &sa, NULL); 323 + } 324 + 325 + /* clang-format off */ 326 + FIXTURE(fown) {}; 327 + /* clang-format on */ 328 + 329 + enum fown_sandbox { 330 + SANDBOX_NONE, 331 + SANDBOX_BEFORE_FORK, 332 + SANDBOX_BEFORE_SETOWN, 333 + SANDBOX_AFTER_SETOWN, 334 + }; 335 + 336 + FIXTURE_VARIANT(fown) 337 + { 338 + const enum fown_sandbox sandbox_setown; 339 + }; 340 + 341 + /* clang-format off */ 342 + FIXTURE_VARIANT_ADD(fown, no_sandbox) { 343 + /* clang-format on */ 344 + .sandbox_setown = SANDBOX_NONE, 345 + }; 346 + 347 + /* clang-format off */ 348 + FIXTURE_VARIANT_ADD(fown, sandbox_before_fork) { 349 + /* clang-format on */ 350 + .sandbox_setown = SANDBOX_BEFORE_FORK, 351 + }; 352 + 353 + /* clang-format off */ 354 + FIXTURE_VARIANT_ADD(fown, sandbox_before_setown) { 355 + /* clang-format on */ 356 + .sandbox_setown = SANDBOX_BEFORE_SETOWN, 357 + }; 358 + 359 + /* clang-format off */ 360 + FIXTURE_VARIANT_ADD(fown, sandbox_after_setown) { 361 + /* clang-format on */ 362 + .sandbox_setown = SANDBOX_AFTER_SETOWN, 363 + }; 364 + 365 + FIXTURE_SETUP(fown) 366 + { 367 + drop_caps(_metadata); 368 + } 369 + 370 + FIXTURE_TEARDOWN(fown) 371 + { 372 + } 373 + 374 + /* 375 + * Sending an out of bound message will trigger the SIGURG signal 376 + * through file_send_sigiotask. 377 + */ 378 + TEST_F(fown, sigurg_socket) 379 + { 380 + int server_socket, recv_socket; 381 + struct service_fixture server_address; 382 + char buffer_parent; 383 + int status; 384 + int pipe_parent[2], pipe_child[2]; 385 + pid_t child; 386 + 387 + memset(&server_address, 0, sizeof(server_address)); 388 + set_unix_address(&server_address, 0); 389 + 390 + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 391 + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); 392 + 393 + if (variant->sandbox_setown == SANDBOX_BEFORE_FORK) 394 + create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL); 395 + 396 + child = fork(); 397 + ASSERT_LE(0, child); 398 + if (child == 0) { 399 + int client_socket; 400 + char buffer_child; 401 + 402 + EXPECT_EQ(0, close(pipe_parent[1])); 403 + EXPECT_EQ(0, close(pipe_child[0])); 404 + 405 + ASSERT_EQ(0, setup_signal_handler(SIGURG)); 406 + client_socket = socket(AF_UNIX, SOCK_STREAM, 0); 407 + ASSERT_LE(0, client_socket); 408 + 409 + /* Waits for the parent to listen. */ 410 + ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1)); 411 + ASSERT_EQ(0, connect(client_socket, &server_address.unix_addr, 412 + server_address.unix_addr_len)); 413 + 414 + /* 415 + * Waits for the parent to accept the connection, sandbox 416 + * itself, and call fcntl(2). 417 + */ 418 + ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1)); 419 + /* May signal itself. */ 420 + ASSERT_EQ(1, send(client_socket, ".", 1, MSG_OOB)); 421 + EXPECT_EQ(0, close(client_socket)); 422 + ASSERT_EQ(1, write(pipe_child[1], ".", 1)); 423 + EXPECT_EQ(0, close(pipe_child[1])); 424 + 425 + /* Waits for the message to be received. */ 426 + ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1)); 427 + EXPECT_EQ(0, close(pipe_parent[0])); 428 + 429 + if (variant->sandbox_setown == SANDBOX_BEFORE_SETOWN) { 430 + ASSERT_EQ(0, signal_received); 431 + } else { 432 + /* 433 + * A signal is only received if fcntl(F_SETOWN) was 434 + * called before any sandboxing or if the signal 435 + * receiver is in the same domain. 436 + */ 437 + ASSERT_EQ(1, signal_received); 438 + } 439 + _exit(_metadata->exit_code); 440 + return; 441 + } 442 + EXPECT_EQ(0, close(pipe_parent[0])); 443 + EXPECT_EQ(0, close(pipe_child[1])); 444 + 445 + server_socket = socket(AF_UNIX, SOCK_STREAM, 0); 446 + ASSERT_LE(0, server_socket); 447 + ASSERT_EQ(0, bind(server_socket, &server_address.unix_addr, 448 + server_address.unix_addr_len)); 449 + ASSERT_EQ(0, listen(server_socket, backlog)); 450 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 451 + 452 + recv_socket = accept(server_socket, NULL, NULL); 453 + ASSERT_LE(0, recv_socket); 454 + 455 + if (variant->sandbox_setown == SANDBOX_BEFORE_SETOWN) 456 + create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL); 457 + 458 + /* 459 + * Sets the child to receive SIGURG for MSG_OOB. This uncommon use is 460 + * a valid attack scenario which also simplifies this test. 461 + */ 462 + ASSERT_EQ(0, fcntl(recv_socket, F_SETOWN, child)); 463 + 464 + if (variant->sandbox_setown == SANDBOX_AFTER_SETOWN) 465 + create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL); 466 + 467 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 468 + 469 + /* Waits for the child to send MSG_OOB. */ 470 + ASSERT_EQ(1, read(pipe_child[0], &buffer_parent, 1)); 471 + EXPECT_EQ(0, close(pipe_child[0])); 472 + ASSERT_EQ(1, recv(recv_socket, &buffer_parent, 1, MSG_OOB)); 473 + EXPECT_EQ(0, close(recv_socket)); 474 + EXPECT_EQ(0, close(server_socket)); 475 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 476 + EXPECT_EQ(0, close(pipe_parent[1])); 477 + 478 + ASSERT_EQ(child, waitpid(child, &status, 0)); 479 + if (WIFSIGNALED(status) || !WIFEXITED(status) || 480 + WEXITSTATUS(status) != EXIT_SUCCESS) 481 + _metadata->exit_code = KSFT_FAIL; 482 + } 483 + 484 + TEST_HARNESS_MAIN
+33
tools/testing/selftests/landlock/scoped_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Landlock tests - Common scope restriction 4 + * 5 + * Copyright © 2024 Tahera Fahimi <fahimitahera@gmail.com> 6 + */ 7 + 8 + #define _GNU_SOURCE 9 + #include <errno.h> 10 + #include <linux/landlock.h> 11 + #include <sys/prctl.h> 12 + 13 + #include "common.h" 14 + 15 + #define ACCESS_LAST LANDLOCK_SCOPE_SIGNAL 16 + 17 + TEST(ruleset_with_unknown_scope) 18 + { 19 + __u64 scoped_mask; 20 + 21 + for (scoped_mask = 1ULL << 63; scoped_mask != ACCESS_LAST; 22 + scoped_mask >>= 1) { 23 + struct landlock_ruleset_attr ruleset_attr = { 24 + .scoped = scoped_mask, 25 + }; 26 + 27 + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 28 + sizeof(ruleset_attr), 0)); 29 + ASSERT_EQ(EINVAL, errno); 30 + } 31 + } 32 + 33 + TEST_HARNESS_MAIN