1 /*
2 * Check decoding of linkat syscall.
3 *
4 * Copyright (c) 2016-2022 The strace developers.
5 * All rights reserved.
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "tests.h"
11 #include "scno.h"
12
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <sys/stat.h>
19 #include <string.h>
20
21 #include "secontext.h"
22 #include "xmalloc.h"
23
24 static void
25 mangle_secontext_field(const char *path, enum secontext_field field,
26 const char *new_val, const char *fallback_val)
27 {
28 char *orig = get_secontext_field_file(path, field);
29 if (!orig)
30 return;
31
32 update_secontext_field(path, field,
33 strcmp(new_val, orig) ? new_val : fallback_val);
34
35 free(orig);
36 }
37
38 int
39 main(void)
40 {
41 /*
42 * Make sure the current workdir of the tracee
43 * is different from the current workdir of the tracer.
44 */
45 create_and_enter_subdir("linkat_subdir");
46
47 static const char sample_1[] = "linkat_sample_old";
48 static const char sample_2[] = "linkat_sample_new";
49 const long fd_old = (long) 0xdeadbeefffffffffULL;
50 const long fd_new = (long) 0xdeadbeeffffffffeULL;
51
52 char *my_secontext = SECONTEXT_PID_MY();
53
54 (void) unlink(sample_1);
55 (void) unlink(sample_2);
56
57 long rc = syscall(__NR_linkat, fd_old, sample_1, fd_new, sample_2, 0);
58 printf("%s%s(%d, \"%s\", %d, \"%s\", 0) = %ld %s (%m)\n",
59 my_secontext, "linkat",
60 (int) fd_old, sample_1, (int) fd_new, sample_2,
61 rc, errno2name());
62
63 rc = syscall(__NR_linkat, -100, sample_1, -100, sample_2, -1L);
64 printf("%s%s(%s, \"%s\", %s, \"%s\", %s) = %ld %s (%m)\n",
65 my_secontext, "linkat",
66 "AT_FDCWD", sample_1, "AT_FDCWD", sample_2,
67 "AT_SYMLINK_NOFOLLOW|AT_REMOVEDIR|AT_SYMLINK_FOLLOW"
68 "|AT_NO_AUTOMOUNT|AT_EMPTY_PATH|AT_RECURSIVE|0xffff60ff",
69 rc, errno2name());
70
71 /*
72 * Tests with AT_FDCWD.
73 */
74
75 int fd_sample_1 = open(sample_1, O_RDONLY | O_CREAT, 0400);
76 if (fd_sample_1 < 0)
77 perror_msg_and_fail("open");
78 if (close(fd_sample_1))
79 perror_msg_and_fail("close");
80
81 char *sample_1_secontext = SECONTEXT_FILE(sample_1);
82
83 rc = syscall(__NR_linkat, -100, sample_1, -100, sample_2, 0);
84 /* no context printed for sample_2 since file doesn't exist yet */
85 printf("%s%s(AT_FDCWD, \"%s\"%s, AT_FDCWD, \"%s\", 0) = %s\n",
86 my_secontext, "linkat",
87 sample_1, sample_1_secontext,
88 sample_2,
89 sprintrc(rc));
90
91 const char *sample_2_secontext = sample_1_secontext;
92
93 rc = syscall(__NR_linkat, -100, sample_1, -100, sample_2, 0);
94 printf("%s%s(AT_FDCWD, \"%s\"%s, AT_FDCWD, \"%s\"%s, 0) = %s\n",
95 my_secontext, "linkat",
96 sample_1, sample_1_secontext,
97 sample_2, sample_2_secontext,
98 sprintrc(rc));
99
100 int fd_sample_2 = open(sample_2, O_RDONLY | O_CREAT, 0400);
101 if (fd_sample_2 < 0)
102 perror_msg_and_fail("open");
103 if (close(fd_sample_2))
104 perror_msg_and_fail("close");
105
106 if (*sample_1_secontext && strstr(sample_1_secontext, "!!"))
107 reset_secontext_file(sample_1);
108
109 free(sample_1_secontext);
110
111 #ifdef PRINT_SECONTEXT_MISMATCH
112 errno = 0;
113 mangle_secontext_field(sample_1, SECONTEXT_USER, "system_u",
114 "unconfined_u");
115 sample_1_secontext = SECONTEXT_FILE(sample_1);
116
117 # ifdef PRINT_SECONTEXT_FULL
118 /* The mismatch should be detected */
119 if (*sample_1_secontext && strstr(sample_1_secontext, "!!") == NULL)
120 perror_msg_and_fail("Context mismatch not detected: %s",
121 sample_1_secontext);
122 if (*sample_1_secontext && strstr(sample_1_secontext, "system_u") == NULL)
123 perror_msg_and_fail("Context mismatch not detected: %s",
124 sample_1_secontext);
125 # else
126 /* The mismatch cannot be detected since it's on user part */
127 if (*sample_1_secontext && strstr(sample_1_secontext, "!!") != NULL)
128 perror_msg_and_fail("Context mismatch detected: %s",
129 sample_1_secontext);
130 # endif
131
132 free(sample_1_secontext);
133 #endif
134
135 errno = 0;
136 mangle_secontext_field(sample_1, SECONTEXT_TYPE, "default_t",
137 "unconfined_t");
138 sample_1_secontext = SECONTEXT_FILE(sample_1);
139 sample_2_secontext = sample_1_secontext;
140
141 #ifdef PRINT_SECONTEXT_MISMATCH
142 if (*sample_1_secontext && strstr(sample_1_secontext, "!!") == NULL)
143 perror_msg_and_fail("Context mismatch not detected: %s",
144 sample_1_secontext);
145 if (*sample_1_secontext && strstr(sample_1_secontext, "default_t") == NULL)
146 perror_msg_and_fail("Context mismatch not detected: %s",
147 sample_1_secontext);
148 #endif
149
150 rc = syscall(__NR_linkat, -100, sample_1, -100, sample_2, 0);
151 printf("%s%s(AT_FDCWD, \"%s\"%s, AT_FDCWD, \"%s\"%s, 0) = %s\n",
152 my_secontext, "linkat",
153 sample_1, sample_1_secontext,
154 sample_2, sample_2_secontext,
155 sprintrc(rc));
156
157 if (unlink(sample_2))
158 perror_msg_and_fail("unlink: %s", sample_2);
159
160 /*
161 * Tests with dirfd.
162 */
163
164 int dfd_old = get_dir_fd(".");
165 char *cwd = get_fd_path(dfd_old);
166
167 errno = 0;
168 mangle_secontext_field(".", SECONTEXT_TYPE, "default_t",
169 "unconfined_t");
170 char *dfd_old_secontext = SECONTEXT_FILE(".");
171
172 #ifdef PRINT_SECONTEXT_MISMATCH
173 if (*dfd_old_secontext && strstr(dfd_old_secontext, "!!") == NULL)
174 perror_msg_and_fail("Context mismatch not detected: %s",
175 dfd_old_secontext);
176 if (*dfd_old_secontext && strstr(dfd_old_secontext, "default_t") == NULL)
177 perror_msg_and_fail("Context mismatch not detected: %s",
178 dfd_old_secontext);
179 #endif
180
181 rc = syscall(__NR_linkat, dfd_old, sample_1, -100, sample_2, 0);
182 /* no context printed for sample_2 since file doesn't exist yet */
183 printf("%s%s(%d%s, \"%s\"%s, AT_FDCWD, \"%s\", 0) = %s\n",
184 my_secontext, "linkat",
185 dfd_old, dfd_old_secontext,
186 sample_1, sample_1_secontext,
187 sample_2,
188 sprintrc(rc));
189
190 rc = syscall(__NR_linkat, dfd_old, sample_1, -100, sample_2, 0);
191 printf("%s%s(%d%s, \"%s\"%s, AT_FDCWD, \"%s\"%s, 0) = %s\n",
192 my_secontext, "linkat",
193 dfd_old, dfd_old_secontext,
194 sample_1, sample_1_secontext,
195 sample_2, sample_2_secontext,
196 sprintrc(rc));
197
198 if (unlink(sample_2))
199 perror_msg_and_fail("unlink: %s", sample_2);
200
201 static const char new_dir[] = "new";
202 char *new_sample_2 = xasprintf("%s/%s", new_dir, sample_2);
203
204 (void) unlink(new_sample_2);
205 (void) rmdir(new_dir);
206
207 if (mkdir(new_dir, 0700))
208 perror_msg_and_fail("mkdir");
209 char *new_dir_realpath = xasprintf("%s/%s", cwd, new_dir);
210 char *new_dir_secontext = SECONTEXT_FILE(new_dir);
211 int dfd_new = get_dir_fd(new_dir);
212
213 rc = syscall(__NR_linkat, dfd_old, sample_1, dfd_new, sample_2, 0);
214 /* no context printed for sample_2 since file doesn't exist yet */
215 printf("%s%s(%d%s, \"%s\"%s, %d%s, \"%s\", 0) = %s\n",
216 my_secontext, "linkat",
217 dfd_old, dfd_old_secontext,
218 sample_1, sample_1_secontext,
219 dfd_new, new_dir_secontext,
220 sample_2,
221 sprintrc(rc));
222
223 rc = syscall(__NR_linkat, dfd_old, sample_1, dfd_new, sample_2, 0);
224 printf("%s%s(%d%s, \"%s\"%s, %d%s, \"%s\"%s, 0) = %s\n",
225 my_secontext, "linkat",
226 dfd_old, dfd_old_secontext,
227 sample_1, sample_1_secontext,
228 dfd_new, new_dir_secontext,
229 sample_2, SECONTEXT_FILE(new_sample_2),
230 sprintrc(rc));
231
232 char *new_sample_2_realpath = xasprintf("%s/%s", new_dir_realpath, sample_2);
233
234 /* dfd ignored when path is absolute */
235 if (chdir("../.."))
236 perror_msg_and_fail("chdir");
237
238 rc = syscall(__NR_linkat, dfd_old, sample_1, -100, new_sample_2_realpath, 0);
239 printf("%s%s(%d%s, \"%s\"%s, AT_FDCWD, \"%s\"%s, 0) = %s\n",
240 my_secontext, "linkat",
241 dfd_old, dfd_old_secontext,
242 sample_1, sample_1_secontext,
243 new_sample_2_realpath, SECONTEXT_FILE(new_sample_2_realpath),
244 sprintrc(rc));
245
246 if (fchdir(dfd_old))
247 perror_msg_and_fail("fchdir");
248
249 if (unlink(sample_1))
250 perror_msg_and_fail("unlink: %s", sample_1);
251 if (unlink(new_sample_2))
252 perror_msg_and_fail("unlink: %s", new_sample_2);
253 if (rmdir(new_dir))
254 perror_msg_and_fail("rmdir: %s", new_dir);
255
256 leave_and_remove_subdir();
257
258 puts("+++ exited with 0 +++");
259 return 0;
260 }