1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2018 FUJITSU LIMITED. All rights reserved.
4 * Author: Xiao Yang <[email protected]>
5 */
6
7 /*\
8 * [Description]
9 *
10 * This test verifies that:
11 *
12 * - Without a user namespace, getxattr(2) should get same data when
13 * acquiring the value of system.posix_acl_access twice.
14 * - With/Without mapped root UID in a user namespaces, getxattr(2) should
15 * get same data when acquiring the value of system.posix_acl_access twice.
16 *
17 * This issue included by getxattr05 has been fixed in kernel:
18 * 82c9a927bc5d ("getxattr: use correct xattr length")
19 */
20
21 #define _GNU_SOURCE
22 #include "config.h"
23 #include <errno.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <stdlib.h>
27
28 #ifdef HAVE_SYS_XATTR_H
29 # include <sys/xattr.h>
30 #endif
31
32 #ifdef HAVE_LIBACL
33 # include <sys/acl.h>
34 #endif
35
36 #include "tst_test.h"
37 #include "lapi/sched.h"
38
39 #if defined(HAVE_SYS_XATTR_H) && defined(HAVE_LIBACL)
40
41 #define TEST_FILE "testfile"
42 #define SELF_USERNS "/proc/self/ns/user"
43 #define MAX_USERNS "/proc/sys/user/max_user_namespaces"
44 #define UID_MAP "/proc/self/uid_map"
45
46 static acl_t acl;
47 static int orig_max_userns = -1;
48 static int user_ns_supported = 1;
49
50 static struct tcase {
51 /* 0: without userns, 1: with userns */
52 int set_userns;
53 /* 0: don't map root UID in userns, 1: map root UID in userns */
54 int map_root;
55 } tcases[] = {
56 {0, 0},
57 {1, 0},
58 {1, 1},
59 };
60
verify_getxattr(void)61 static void verify_getxattr(void)
62 {
63 ssize_t i, res1, res2;
64 char buf1[128], buf2[132];
65
66 res1 = SAFE_GETXATTR(TEST_FILE, "system.posix_acl_access",
67 buf1, sizeof(buf1));
68 res2 = SAFE_GETXATTR(TEST_FILE, "system.posix_acl_access",
69 buf2, sizeof(buf2));
70
71 if (res1 != res2) {
72 tst_res(TFAIL, "Return different sizes when acquiring "
73 "the value of system.posix_acl_access twice");
74 return;
75 }
76
77 for (i = 0; i < res1; i++) {
78 if (buf1[i] != buf2[i])
79 break;
80 }
81
82 if (i < res1) {
83 tst_res(TFAIL, "Got different data(%02x != %02x) at %ld",
84 buf1[i], buf2[i], i);
85 return;
86 }
87
88 tst_res(TPASS, "Got same data when acquiring the value of "
89 "system.posix_acl_access twice");
90 }
91
do_unshare(int map_root)92 static void do_unshare(int map_root)
93 {
94 int res;
95
96 /* unshare() should support CLONE_NEWUSER flag since Linux 3.8 */
97 res = unshare(CLONE_NEWUSER);
98 if (res == -1)
99 tst_brk(TFAIL | TERRNO, "unshare(CLONE_NEWUSER) failed");
100
101 if (map_root) {
102 /* uid_map file should exist since Linux 3.8 because
103 * it is available on Linux 3.5
104 */
105 SAFE_ACCESS(UID_MAP, F_OK);
106
107 SAFE_FILE_PRINTF(UID_MAP, "%d %d %d", 0, 0, 1);
108 }
109 }
110
do_getxattr(unsigned int n)111 static void do_getxattr(unsigned int n)
112 {
113 struct tcase *tc = &tcases[n];
114 pid_t pid;
115
116 if (tc->set_userns && !user_ns_supported) {
117 tst_res(TCONF, "user namespace not available");
118 return;
119 }
120
121 pid = SAFE_FORK();
122 if (!pid) {
123 if (tc->set_userns)
124 do_unshare(tc->map_root);
125
126 verify_getxattr();
127 exit(0);
128 }
129
130 tst_reap_children();
131 }
132
setup(void)133 static void setup(void)
134 {
135 const char *acl_text = "u::rw-,u:root:rwx,g::r--,o::r--,m::rwx";
136 int res;
137
138 SAFE_TOUCH(TEST_FILE, 0644, NULL);
139
140 acl = acl_from_text(acl_text);
141 if (!acl)
142 tst_brk(TBROK | TERRNO, "acl_from_text() failed");
143
144 res = acl_set_file(TEST_FILE, ACL_TYPE_ACCESS, acl);
145 if (res == -1) {
146 if (errno == EOPNOTSUPP)
147 tst_brk(TCONF | TERRNO, "acl_set_file()");
148
149 tst_brk(TBROK | TERRNO, "acl_set_file(%s) failed", TEST_FILE);
150 }
151
152 /* The default value of max_user_namespaces is set to 0 on some distros,
153 * We need to change the default value to call unshare().
154 */
155 if (access(SELF_USERNS, F_OK) != 0) {
156 user_ns_supported = 0;
157 } else if (!access(MAX_USERNS, F_OK)) {
158 SAFE_FILE_SCANF(MAX_USERNS, "%d", &orig_max_userns);
159 SAFE_FILE_PRINTF(MAX_USERNS, "%d", 10);
160 }
161
162 }
163
cleanup(void)164 static void cleanup(void)
165 {
166 if (orig_max_userns != -1)
167 SAFE_FILE_PRINTF(MAX_USERNS, "%d", orig_max_userns);
168
169 if (acl)
170 acl_free(acl);
171 }
172
173 static struct tst_test test = {
174 .needs_tmpdir = 1,
175 .needs_root = 1,
176 .forks_child = 1,
177 .setup = setup,
178 .cleanup = cleanup,
179 .tcnt = ARRAY_SIZE(tcases),
180 .test = do_getxattr,
181 .tags = (const struct tst_tag[]) {
182 {"linux-git", "82c9a927bc5d"},
183 {}
184 },
185 };
186
187 #else /* HAVE_SYS_XATTR_H && HAVE_LIBACL*/
188 TST_TEST_TCONF("<sys/xattr.h> or <sys/acl.h> does not exist.");
189 #endif
190