xref: /linux/tools/testing/selftests/namespaces/listns_permissions_test.c (revision 1f8ee4a1f94afcf7680a9537a397c27a4919ccec)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <sched.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <linux/nsfs.h>
11 #include <sys/capability.h>
12 #include <sys/ioctl.h>
13 #include <sys/prctl.h>
14 #include <sys/stat.h>
15 #include <sys/syscall.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <unistd.h>
19 #include "../kselftest_harness.h"
20 #include "../filesystems/utils.h"
21 #include "wrappers.h"
22 
23 /*
24  * Test that unprivileged users can only see namespaces they're currently in.
25  * Create a namespace, drop privileges, verify we can only see our own namespaces.
26  */
27 TEST(listns_unprivileged_current_only)
28 {
29 	struct ns_id_req req = {
30 		.size = sizeof(req),
31 		.spare = 0,
32 		.ns_id = 0,
33 		.ns_type = CLONE_NEWNET,
34 		.spare2 = 0,
35 		.user_ns_id = 0,
36 	};
37 	__u64 ns_ids[100];
38 	ssize_t ret;
39 	int pipefd[2];
40 	pid_t pid;
41 	int status;
42 	bool found_ours;
43 	int unexpected_count;
44 
45 	ASSERT_EQ(pipe(pipefd), 0);
46 
47 	pid = fork();
48 	ASSERT_GE(pid, 0);
49 
50 	if (pid == 0) {
51 		int fd;
52 		__u64 our_netns_id;
53 		bool found_ours;
54 		int unexpected_count;
55 
56 		close(pipefd[0]);
57 
58 		/* Create user namespace to be unprivileged */
59 		if (setup_userns() < 0) {
60 			close(pipefd[1]);
61 			exit(1);
62 		}
63 
64 		/* Create a network namespace */
65 		if (unshare(CLONE_NEWNET) < 0) {
66 			close(pipefd[1]);
67 			exit(1);
68 		}
69 
70 		/* Get our network namespace ID */
71 		fd = open("/proc/self/ns/net", O_RDONLY);
72 		if (fd < 0) {
73 			close(pipefd[1]);
74 			exit(1);
75 		}
76 
77 		if (ioctl(fd, NS_GET_ID, &our_netns_id) < 0) {
78 			close(fd);
79 			close(pipefd[1]);
80 			exit(1);
81 		}
82 		close(fd);
83 
84 		/* Now we're unprivileged - list all network namespaces */
85 		ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0);
86 		if (ret < 0) {
87 			close(pipefd[1]);
88 			exit(1);
89 		}
90 
91 		/* We should only see our own network namespace */
92 		found_ours = false;
93 		unexpected_count = 0;
94 
95 		for (ssize_t i = 0; i < ret; i++) {
96 			if (ns_ids[i] == our_netns_id) {
97 				found_ours = true;
98 			} else {
99 				/* This is either init_net (which we can see) or unexpected */
100 				unexpected_count++;
101 			}
102 		}
103 
104 		/* Send results to parent */
105 		write(pipefd[1], &found_ours, sizeof(found_ours));
106 		write(pipefd[1], &unexpected_count, sizeof(unexpected_count));
107 		close(pipefd[1]);
108 		exit(0);
109 	}
110 
111 	/* Parent */
112 	close(pipefd[1]);
113 
114 	found_ours = false;
115 	unexpected_count = 0;
116 	read(pipefd[0], &found_ours, sizeof(found_ours));
117 	read(pipefd[0], &unexpected_count, sizeof(unexpected_count));
118 	close(pipefd[0]);
119 
120 	waitpid(pid, &status, 0);
121 	ASSERT_TRUE(WIFEXITED(status));
122 	ASSERT_EQ(WEXITSTATUS(status), 0);
123 
124 	/* Child should have seen its own namespace */
125 	ASSERT_TRUE(found_ours);
126 
127 	TH_LOG("Unprivileged child saw its own namespace, plus %d others (likely init_net)",
128 			unexpected_count);
129 }
130 
131 TEST_HARNESS_MAIN
132