1 /*
2 * Copyright (c) 2020-2022 The strace developers.
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "tests.h"
9
10 #ifdef HAVE_SELINUX_RUNTIME
11
12 # include <assert.h>
13 # include <errno.h>
14 # include <stdlib.h>
15 # include <string.h>
16 # include <sys/stat.h>
17 # include <unistd.h>
18 # include <selinux/selinux.h>
19 # include <selinux/label.h>
20
21 # include "xmalloc.h"
22
23 # define TEST_SECONTEXT
24 # include "secontext.h"
25
26 ATTRIBUTE_FORMAT((printf, 2, 0)) ATTRIBUTE_MALLOC
27 static char *
28 secontext_format(char *context, const char *fmt)
29 {
30 int saved_errno = errno;
31 char *res = context ? xasprintf(fmt, context) : xstrdup("");
32 free(context);
33 errno = saved_errno;
34 return res;
35 }
36
37 # define FORMAT_SPACE_BEFORE(string) secontext_format(string, " [%s]")
38 # define FORMAT_SPACE_AFTER(string) secontext_format(string, "[%s] ")
39
40 static char *
41 strip_trailing_newlines(char *context)
42 {
43 /*
44 * On the CI at least, the context may have a trailing \n,
45 * let's remove it just in case.
46 */
47 size_t len = strlen(context);
48 for (; len > 0; --len) {
49 if (context[len - 1] != '\n')
50 break;
51 }
52 context[len] = '\0';
53 return context;
54 }
55
56 char *
57 get_secontext_field(const char *full_context, enum secontext_field field)
58 {
59 int saved_errno = errno;
60
61 if (!full_context)
62 return NULL;
63
64 char *saveptr = NULL;
65 const char *token;
66 unsigned int i;
67
68 char *ctx_copy = xstrdup(full_context);
69 char *context = NULL;
70 for (token = strtok_r(ctx_copy, ":", &saveptr), i = 0;
71 token; token = strtok_r(NULL, ":", &saveptr), i++) {
72 if (i == field) {
73 context = xstrdup(token);
74 break;
75 }
76 }
77 if (!context)
78 context = xstrdup(full_context);
79 free(ctx_copy);
80
81 errno = saved_errno;
82 return context;
83 }
84
85 static char *
86 raw_expected_secontext_full_file(const char *filename)
87 {
88 int saved_errno = errno;
89 char *secontext;
90
91 static struct selabel_handle *hdl;
92 if (!hdl) {
93 hdl = selabel_open(SELABEL_CTX_FILE, NULL, 0);
94 if (!hdl)
95 perror_msg_and_skip("selabel_open");
96 }
97
98 char *resolved = realpath(filename, NULL);
99 if (!resolved)
100 perror_msg_and_fail("realpath: %s", filename);
101
102 struct stat statbuf;
103 if (stat(resolved, &statbuf) < 0)
104 perror_msg_and_fail("stat: %s", resolved);
105
106 if (selabel_lookup(hdl, &secontext, resolved, statbuf.st_mode) < 0)
107 perror_msg_and_skip("selabel_lookup: %s", resolved);
108 free(resolved);
109
110 char *full_secontext = xstrdup(secontext);
111 freecon(secontext);
112 errno = saved_errno;
113 return full_secontext;
114 }
115
116 static char *
117 raw_expected_secontext_short_file(const char *filename)
118 {
119 int saved_errno = errno;
120
121 char *ctx = raw_expected_secontext_full_file(filename);
122 char *type = get_secontext_field(ctx, SECONTEXT_TYPE);
123 free(ctx);
124
125 errno = saved_errno;
126 return type;
127 }
128
129 static char *
130 raw_secontext_full_file(const char *filename)
131 {
132 int saved_errno = errno;
133 char *full_secontext = NULL;
134 char *secontext;
135
136 if (getfilecon(filename, &secontext) >= 0) {
137 full_secontext = strip_trailing_newlines(xstrdup(secontext));
138 freecon(secontext);
139 }
140 errno = saved_errno;
141 return full_secontext;
142 }
143
144 static char *
145 raw_secontext_full_fd(int fd)
146 {
147 int saved_errno = errno;
148 char *full_secontext = NULL;
149 char *secontext;
150
151 if (fgetfilecon(fd, &secontext) >= 0) {
152 full_secontext = strip_trailing_newlines(xstrdup(secontext));
153 freecon(secontext);
154 }
155 errno = saved_errno;
156 return full_secontext;
157 }
158
159 char *
160 get_secontext_field_file(const char *file, enum secontext_field field)
161 {
162 char *ctx = raw_secontext_full_file(file);
163 char *type = get_secontext_field(ctx, field);
164 free(ctx);
165
166 return type;
167 }
168
169 char *
170 get_secontext_field_fd(int fd, enum secontext_field field)
171 {
172 char *ctx = raw_secontext_full_fd(fd);
173 char *type = get_secontext_field(ctx, field);
174 free(ctx);
175
176 return type;
177 }
178
179 static char *
180 raw_secontext_short_file(const char *filename)
181 {
182 return get_secontext_field_file(filename, SECONTEXT_TYPE);
183 }
184
185 static char *
186 raw_secontext_short_fd(int fd)
187 {
188 return get_secontext_field_fd(fd, SECONTEXT_TYPE);
189 }
190
191 static char *
192 raw_secontext_full_pid(pid_t pid)
193 {
194 int saved_errno = errno;
195 char *full_secontext = NULL;
196 char *secontext;
197
198 if (getpidcon(pid, &secontext) == 0) {
199 full_secontext = strip_trailing_newlines(xstrdup(secontext));
200 freecon(secontext);
201 }
202 errno = saved_errno;
203 return full_secontext;
204 }
205
206 static char *
207 raw_secontext_short_pid(pid_t pid)
208 {
209 int saved_errno = errno;
210
211 char *ctx = raw_secontext_full_pid(pid);
212 char *type = get_secontext_field(ctx, SECONTEXT_TYPE);
213 free(ctx);
214
215 errno = saved_errno;
216 return type;
217 }
218
219 char *
220 secontext_full_file(const char *filename, bool mismatch)
221 {
222 int saved_errno = errno;
223 char *context = raw_secontext_full_file(filename);
224 if (context && mismatch) {
225 char *expected = raw_expected_secontext_full_file(filename);
226 if (expected && strcmp(context, expected)) {
227 char *context_mismatch =
228 xasprintf("%s!!%s", context, expected);
229 free(context);
230 context = context_mismatch;
231 }
232 free(expected);
233 }
234 errno = saved_errno;
235 return FORMAT_SPACE_BEFORE(context);
236 }
237
238 char *
239 secontext_full_fd(int fd)
240 {
241 int saved_errno = errno;
242 char *context = raw_secontext_full_fd(fd);
243 errno = saved_errno;
244 return FORMAT_SPACE_BEFORE(context);
245 }
246
247 char *
248 secontext_full_pid(pid_t pid)
249 {
250 return FORMAT_SPACE_AFTER(raw_secontext_full_pid(pid));
251 }
252
253 char *
254 secontext_short_file(const char *filename, bool mismatch)
255 {
256 int saved_errno = errno;
257 char *context = raw_secontext_short_file(filename);
258 if (context && mismatch) {
259 char *expected = raw_expected_secontext_short_file(filename);
260 if (expected && strcmp(context, expected)) {
261 char *context_mismatch =
262 xasprintf("%s!!%s", context, expected);
263 free(context);
264 context = context_mismatch;
265 }
266 free(expected);
267 }
268 errno = saved_errno;
269 return FORMAT_SPACE_BEFORE(context);
270 }
271
272 char *
273 secontext_short_fd(int fd)
274 {
275 int saved_errno = errno;
276 char *context = raw_secontext_short_fd(fd);
277 errno = saved_errno;
278 return FORMAT_SPACE_BEFORE(context);
279 }
280
281 char *
282 secontext_short_pid(pid_t pid)
283 {
284 return FORMAT_SPACE_AFTER(raw_secontext_short_pid(pid));
285 }
286
287 void reset_secontext_file(const char *file)
288 {
289 char *proper_ctx = raw_expected_secontext_full_file(file);
290 (void) setfilecon(file, proper_ctx);
291 free(proper_ctx);
292 }
293
294 void
295 update_secontext_field(const char *file, enum secontext_field field,
296 const char *newvalue)
297 {
298 int saved_errno = errno;
299 assert(field >= SECONTEXT_USER && field <= SECONTEXT_TYPE);
300
301 char *ctx = raw_secontext_full_file(file);
302 if (ctx == NULL)
303 return;
304
305 char *saveptr = NULL;
306 char *token;
307 int nfields;
308 char *split[4];
309
310 for (token = strtok_r(ctx, ":", &saveptr), nfields = 0;
311 token; token = strtok_r(NULL, ":", &saveptr), nfields++) {
312 assert(nfields < 4);
313 split[nfields] = token;
314 }
315 assert(nfields == 4);
316
317 split[field] = (char *)newvalue;
318
319 char *newcontext = xasprintf("%s:%s:%s:%s", split[0], split[1],
320 split[2], split[3]);
321
322 (void) setfilecon(file, newcontext);
323
324 free(newcontext);
325 free(ctx);
326 errno = saved_errno;
327 }
328
329 #endif /* HAVE_SELINUX_RUNTIME */