blob: 05a5cfa4cfd5015a6252e8bb574e564e243cff29 [file] [log] [blame]
Joshua Brindle13cd4c82008-08-19 15:30:36 -04001#include <sys/stat.h>
2#include <string.h>
3#include <errno.h>
4#include <stdio.h>
5#include "selinux_internal.h"
6#include "label_internal.h"
7#include "callbacks.h"
Eric Paris7df397d2011-08-17 11:24:25 -04008#include <limits.h>
Joshua Brindle13cd4c82008-08-19 15:30:36 -04009
William Roberts0fdfdcc2016-09-26 10:33:39 -070010static int (*myinvalidcon) (const char *p, unsigned l, char *c) = NULL;
11static int (*mycanoncon) (const char *p, unsigned l, char **c) = NULL;
12
13static void
14#ifdef __GNUC__
15 __attribute__ ((format(printf, 1, 2)))
16#endif
17 default_printf(const char *fmt, ...)
18{
19 va_list ap;
20 va_start(ap, fmt);
21 vfprintf(stderr, fmt, ap);
22 va_end(ap);
23}
24
25void
26#ifdef __GNUC__
27 __attribute__ ((format(printf, 1, 2)))
28#endif
29 (*myprintf) (const char *fmt,...) = &default_printf;
30int myprintf_compat = 0;
31
32void set_matchpathcon_printf(void (*f) (const char *fmt, ...))
33{
34 myprintf = f ? f : &default_printf;
35 myprintf_compat = 1;
36}
37
Stephen Smalley87a58b62018-04-20 10:12:57 -040038int compat_validate(struct selabel_handle *rec,
39 struct selabel_lookup_rec *contexts,
William Roberts0fdfdcc2016-09-26 10:33:39 -070040 const char *path, unsigned lineno)
41{
42 int rc;
43 char **ctx = &contexts->ctx_raw;
44
45 if (myinvalidcon)
46 rc = myinvalidcon(path, lineno, *ctx);
47 else if (mycanoncon)
48 rc = mycanoncon(path, lineno, ctx);
49 else {
Stephen Smalley87a58b62018-04-20 10:12:57 -040050 rc = selabel_validate(rec, contexts);
William Roberts0fdfdcc2016-09-26 10:33:39 -070051 if (rc < 0) {
52 if (lineno) {
53 COMPAT_LOG(SELINUX_WARNING,
54 "%s: line %u has invalid context %s\n",
55 path, lineno, *ctx);
56 } else {
57 COMPAT_LOG(SELINUX_WARNING,
58 "%s: has invalid context %s\n", path, *ctx);
59 }
60 }
61 }
62
63 return rc ? -1 : 0;
64}
65
66#ifndef BUILD_HOST
67
Joshua Brindle13cd4c82008-08-19 15:30:36 -040068static __thread struct selabel_handle *hnd;
69
70/*
71 * An array for mapping integers to contexts
72 */
73static __thread char **con_array;
74static __thread int con_array_size;
75static __thread int con_array_used;
76
Eamon Walsha29ff332010-12-02 14:08:59 -050077static pthread_once_t once = PTHREAD_ONCE_INIT;
78static pthread_key_t destructor_key;
Daniel J Walsh1629d2f2011-04-06 16:58:29 -040079static int destructor_key_initialized = 0;
Eamon Walsha29ff332010-12-02 14:08:59 -050080
Joshua Brindle13cd4c82008-08-19 15:30:36 -040081static int add_array_elt(char *con)
82{
83 if (con_array_size) {
84 while (con_array_used >= con_array_size) {
85 con_array_size *= 2;
86 con_array = (char **)realloc(con_array, sizeof(char*) *
87 con_array_size);
88 if (!con_array) {
89 con_array_size = con_array_used = 0;
90 return -1;
91 }
92 }
93 } else {
94 con_array_size = 1000;
95 con_array = (char **)malloc(sizeof(char*) * con_array_size);
96 if (!con_array) {
97 con_array_size = con_array_used = 0;
98 return -1;
99 }
100 }
101
102 con_array[con_array_used] = strdup(con);
103 if (!con_array[con_array_used])
104 return -1;
105 return con_array_used++;
106}
107
Eamon Walsh569ce542010-12-02 19:30:06 -0500108static void free_array_elts(void)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400109{
110 con_array_size = con_array_used = 0;
111 free(con_array);
112 con_array = NULL;
113}
114
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400115void set_matchpathcon_invalidcon(int (*f) (const char *p, unsigned l, char *c))
116{
117 myinvalidcon = f;
118}
119
120static int default_canoncon(const char *path, unsigned lineno, char **context)
121{
122 char *tmpcon;
123 if (security_canonicalize_context_raw(*context, &tmpcon) < 0) {
124 if (errno == ENOENT)
125 return 0;
126 if (lineno)
127 myprintf("%s: line %u has invalid context %s\n", path,
128 lineno, *context);
129 else
130 myprintf("%s: invalid context %s\n", path, *context);
131 return 1;
132 }
133 free(*context);
134 *context = tmpcon;
135 return 0;
136}
137
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400138void set_matchpathcon_canoncon(int (*f) (const char *p, unsigned l, char **c))
139{
140 if (f)
141 mycanoncon = f;
142 else
143 mycanoncon = &default_canoncon;
144}
145
146static __thread struct selinux_opt options[SELABEL_NOPT];
147static __thread int notrans;
148
149void set_matchpathcon_flags(unsigned int flags)
150{
151 int i;
152 memset(options, 0, sizeof(options));
153 i = SELABEL_OPT_BASEONLY;
154 options[i].type = i;
155 options[i].value = (flags & MATCHPATHCON_BASEONLY) ? (char*)1 : NULL;
156 i = SELABEL_OPT_VALIDATE;
157 options[i].type = i;
158 options[i].value = (flags & MATCHPATHCON_VALIDATE) ? (char*)1 : NULL;
159 notrans = flags & MATCHPATHCON_NOTRANS;
160}
161
162/*
163 * An association between an inode and a
164 * specification.
165 */
166typedef struct file_spec {
167 ino_t ino; /* inode number */
168 int specind; /* index of specification in spec */
169 char *file; /* full pathname for diagnostic messages about conflicts */
170 struct file_spec *next; /* next association in hash bucket chain */
171} file_spec_t;
172
173/*
174 * The hash table of associations, hashed by inode number.
175 * Chaining is used for collisions, with elements ordered
176 * by inode number in each bucket. Each hash bucket has a dummy
177 * header.
178 */
179#define HASH_BITS 16
180#define HASH_BUCKETS (1 << HASH_BITS)
181#define HASH_MASK (HASH_BUCKETS-1)
182static file_spec_t *fl_head;
183
184/*
185 * Try to add an association between an inode and
186 * a specification. If there is already an association
187 * for the inode and it conflicts with this specification,
188 * then use the specification that occurs later in the
189 * specification array.
190 */
191int matchpathcon_filespec_add(ino_t ino, int specind, const char *file)
192{
193 file_spec_t *prevfl, *fl;
194 int h, ret;
195 struct stat sb;
196
197 if (!fl_head) {
198 fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS);
199 if (!fl_head)
200 goto oom;
201 memset(fl_head, 0, sizeof(file_spec_t) * HASH_BUCKETS);
202 }
203
204 h = (ino + (ino >> HASH_BITS)) & HASH_MASK;
205 for (prevfl = &fl_head[h], fl = fl_head[h].next; fl;
206 prevfl = fl, fl = fl->next) {
207 if (ino == fl->ino) {
208 ret = lstat(fl->file, &sb);
209 if (ret < 0 || sb.st_ino != ino) {
210 fl->specind = specind;
211 free(fl->file);
212 fl->file = malloc(strlen(file) + 1);
213 if (!fl->file)
214 goto oom;
215 strcpy(fl->file, file);
216 return fl->specind;
217
218 }
219
220 if (!strcmp(con_array[fl->specind],
221 con_array[specind]))
222 return fl->specind;
223
224 myprintf
225 ("%s: conflicting specifications for %s and %s, using %s.\n",
226 __FUNCTION__, file, fl->file,
227 con_array[fl->specind]);
228 free(fl->file);
229 fl->file = malloc(strlen(file) + 1);
230 if (!fl->file)
231 goto oom;
232 strcpy(fl->file, file);
233 return fl->specind;
234 }
235
236 if (ino > fl->ino)
237 break;
238 }
239
240 fl = malloc(sizeof(file_spec_t));
241 if (!fl)
242 goto oom;
243 fl->ino = ino;
244 fl->specind = specind;
245 fl->file = malloc(strlen(file) + 1);
246 if (!fl->file)
247 goto oom_freefl;
248 strcpy(fl->file, file);
249 fl->next = prevfl->next;
250 prevfl->next = fl;
251 return fl->specind;
252 oom_freefl:
253 free(fl);
254 oom:
255 myprintf("%s: insufficient memory for file label entry for %s\n",
256 __FUNCTION__, file);
257 return -1;
258}
259
260/*
261 * Evaluate the association hash table distribution.
262 */
263void matchpathcon_filespec_eval(void)
264{
265 file_spec_t *fl;
266 int h, used, nel, len, longest;
267
268 if (!fl_head)
269 return;
270
271 used = 0;
272 longest = 0;
273 nel = 0;
274 for (h = 0; h < HASH_BUCKETS; h++) {
275 len = 0;
276 for (fl = fl_head[h].next; fl; fl = fl->next) {
277 len++;
278 }
279 if (len)
280 used++;
281 if (len > longest)
282 longest = len;
283 nel += len;
284 }
285
286 myprintf
287 ("%s: hash table stats: %d elements, %d/%d buckets used, longest chain length %d\n",
288 __FUNCTION__, nel, used, HASH_BUCKETS, longest);
289}
290
291/*
292 * Destroy the association hash table.
293 */
294void matchpathcon_filespec_destroy(void)
295{
296 file_spec_t *fl, *tmp;
297 int h;
298
Eamon Walsh569ce542010-12-02 19:30:06 -0500299 free_array_elts();
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400300
301 if (!fl_head)
302 return;
303
304 for (h = 0; h < HASH_BUCKETS; h++) {
305 fl = fl_head[h].next;
306 while (fl) {
307 tmp = fl;
308 fl = fl->next;
309 free(tmp->file);
310 free(tmp);
311 }
312 fl_head[h].next = NULL;
313 }
314 free(fl_head);
315 fl_head = NULL;
316}
317
Eamon Walsh569ce542010-12-02 19:30:06 -0500318static void matchpathcon_thread_destructor(void __attribute__((unused)) *ptr)
319{
320 matchpathcon_fini();
321}
322
Daniel P. Berrangec9a8ff92012-01-23 15:41:12 +0000323void __attribute__((destructor)) matchpathcon_lib_destructor(void);
324
Laurent Bigonvillea4f84102012-03-26 20:45:49 +0200325void hidden __attribute__((destructor)) matchpathcon_lib_destructor(void)
Eamon Walshf0b31272011-03-09 11:43:33 -0500326{
Daniel J Walsh1629d2f2011-04-06 16:58:29 -0400327 if (destructor_key_initialized)
328 __selinux_key_delete(destructor_key);
Eamon Walshf0b31272011-03-09 11:43:33 -0500329}
330
Eamon Walsha29ff332010-12-02 14:08:59 -0500331static void matchpathcon_init_once(void)
332{
Daniel J Walsh1629d2f2011-04-06 16:58:29 -0400333 if (__selinux_key_create(&destructor_key, matchpathcon_thread_destructor) == 0)
334 destructor_key_initialized = 1;
Eamon Walsha29ff332010-12-02 14:08:59 -0500335}
336
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400337int matchpathcon_init_prefix(const char *path, const char *subset)
338{
339 if (!mycanoncon)
340 mycanoncon = default_canoncon;
341
Eamon Walsha29ff332010-12-02 14:08:59 -0500342 __selinux_once(once, matchpathcon_init_once);
343 __selinux_setspecific(destructor_key, (void *)1);
344
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400345 options[SELABEL_OPT_SUBSET].type = SELABEL_OPT_SUBSET;
346 options[SELABEL_OPT_SUBSET].value = subset;
347 options[SELABEL_OPT_PATH].type = SELABEL_OPT_PATH;
348 options[SELABEL_OPT_PATH].value = path;
349
350 hnd = selabel_open(SELABEL_CTX_FILE, options, SELABEL_NOPT);
351 return hnd ? 0 : -1;
352}
353
354hidden_def(matchpathcon_init_prefix)
355
356int matchpathcon_init(const char *path)
357{
358 return matchpathcon_init_prefix(path, NULL);
359}
360
361void matchpathcon_fini(void)
362{
Eamon Walsh569ce542010-12-02 19:30:06 -0500363 free_array_elts();
364
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400365 if (hnd) {
366 selabel_close(hnd);
367 hnd = NULL;
368 }
369}
370
Eric Paris7df397d2011-08-17 11:24:25 -0400371/*
372 * We do not want to resolve a symlink to a real path if it is the final
373 * component of the name. Thus we split the pathname on the last "/" and
374 * determine a real path component of the first portion. We then have to
375 * copy the last part back on to get the final real path. Wheww.
376 */
Eric Paris2b06f472011-09-23 17:38:09 -0400377int realpath_not_final(const char *name, char *resolved_path)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400378{
Eric Paris7df397d2011-08-17 11:24:25 -0400379 char *last_component;
380 char *tmp_path, *p;
381 size_t len = 0;
382 int rc = 0;
383
384 tmp_path = strdup(name);
385 if (!tmp_path) {
Eric Paris09b635f2011-09-15 17:58:05 -0400386 myprintf("symlink_realpath(%s) strdup() failed: %s\n",
Eric Paris7df397d2011-08-17 11:24:25 -0400387 name, strerror(errno));
388 rc = -1;
389 goto out;
390 }
391
392 last_component = strrchr(tmp_path, '/');
393
394 if (last_component == tmp_path) {
395 last_component++;
Eric Paris12e2a0f2012-06-20 17:44:17 -0400396 p = strcpy(resolved_path, "");
Eric Paris7df397d2011-08-17 11:24:25 -0400397 } else if (last_component) {
398 *last_component = '\0';
399 last_component++;
400 p = realpath(tmp_path, resolved_path);
401 } else {
402 last_component = tmp_path;
403 p = realpath("./", resolved_path);
404 }
405
406 if (!p) {
Eric Paris09b635f2011-09-15 17:58:05 -0400407 myprintf("symlink_realpath(%s) realpath() failed: %s\n",
Eric Paris7df397d2011-08-17 11:24:25 -0400408 name, strerror(errno));
409 rc = -1;
410 goto out;
411 }
412
413 len = strlen(p);
Eric Paris7bfaa632011-09-15 17:58:52 -0400414 if (len + strlen(last_component) + 2 > PATH_MAX) {
Eric Paris09b635f2011-09-15 17:58:05 -0400415 myprintf("symlink_realpath(%s) failed: Filename too long \n",
Eric Paris7df397d2011-08-17 11:24:25 -0400416 name);
Unto Stene1a74392019-05-10 16:52:08 +0300417 errno = ENAMETOOLONG;
Eric Paris7df397d2011-08-17 11:24:25 -0400418 rc = -1;
419 goto out;
420 }
421
422 resolved_path += len;
Eric Paris7bfaa632011-09-15 17:58:52 -0400423 strcpy(resolved_path, "/");
424 resolved_path += 1;
Eric Paris7df397d2011-08-17 11:24:25 -0400425 strcpy(resolved_path, last_component);
426out:
427 free(tmp_path);
428 return rc;
429}
430
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500431int matchpathcon(const char *path, mode_t mode, char ** con)
Eric Paris7df397d2011-08-17 11:24:25 -0400432{
433 char stackpath[PATH_MAX + 1];
434 char *p = NULL;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400435 if (!hnd && (matchpathcon_init_prefix(NULL, NULL) < 0))
436 return -1;
437
Eric Paris7df397d2011-08-17 11:24:25 -0400438 if (S_ISLNK(mode)) {
Eric Paris2b06f472011-09-23 17:38:09 -0400439 if (!realpath_not_final(path, stackpath))
Eric Paris7df397d2011-08-17 11:24:25 -0400440 path = stackpath;
441 } else {
442 p = realpath(path, stackpath);
443 if (p)
444 path = p;
445 }
446
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400447 return notrans ?
Eric Paris7df397d2011-08-17 11:24:25 -0400448 selabel_lookup_raw(hnd, con, path, mode) :
449 selabel_lookup(hnd, con, path, mode);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400450}
451
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500452int matchpathcon_index(const char *name, mode_t mode, char ** con)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400453{
454 int i = matchpathcon(name, mode, con);
455
456 if (i < 0)
457 return -1;
458
459 return add_array_elt(*con);
460}
461
462void matchpathcon_checkmatches(char *str __attribute__((unused)))
463{
464 selabel_stats(hnd);
465}
466
467/* Compare two contexts to see if their differences are "significant",
468 * or whether the only difference is in the user. */
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500469int selinux_file_context_cmp(const char * a,
470 const char * b)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400471{
472 char *rest_a, *rest_b; /* Rest of the context after the user */
473 if (!a && !b)
474 return 0;
475 if (!a)
476 return -1;
477 if (!b)
478 return 1;
479 rest_a = strchr((char *)a, ':');
480 rest_b = strchr((char *)b, ':');
481 if (!rest_a && !rest_b)
482 return 0;
483 if (!rest_a)
484 return -1;
485 if (!rest_b)
486 return 1;
487 return strcmp(rest_a, rest_b);
488}
489
490int selinux_file_context_verify(const char *path, mode_t mode)
491{
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500492 char * con = NULL;
493 char * fcontext = NULL;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400494 int rc = 0;
Petr Lautrbach7c1a5e32016-06-20 16:10:18 +0200495 char stackpath[PATH_MAX + 1];
496 char *p = NULL;
497
498 if (S_ISLNK(mode)) {
499 if (!realpath_not_final(path, stackpath))
500 path = stackpath;
501 } else {
502 p = realpath(path, stackpath);
503 if (p)
504 path = p;
505 }
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400506
507 rc = lgetfilecon_raw(path, &con);
508 if (rc == -1) {
509 if (errno != ENOTSUP)
Richard Hainesbc1a8e22011-03-09 16:34:08 +0000510 return -1;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400511 else
512 return 0;
513 }
514
515 if (!hnd && (matchpathcon_init_prefix(NULL, NULL) < 0))
516 return -1;
517
518 if (selabel_lookup_raw(hnd, &fcontext, path, mode) != 0) {
519 if (errno != ENOENT)
Richard Hainesbc1a8e22011-03-09 16:34:08 +0000520 rc = -1;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400521 else
522 rc = 0;
Richard Hainesbc1a8e22011-03-09 16:34:08 +0000523 } else {
524 /*
525 * Need to set errno to 0 as it can be set to ENOENT if the
526 * file_contexts.subs file does not exist (see selabel_open in
527 * label.c), thus causing confusion if errno is checked on return.
528 */
529 errno = 0;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400530 rc = (selinux_file_context_cmp(fcontext, con) == 0);
Richard Hainesbc1a8e22011-03-09 16:34:08 +0000531 }
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400532
533 freecon(con);
534 freecon(fcontext);
535 return rc;
536}
537
538int selinux_lsetfilecon_default(const char *path)
539{
540 struct stat st;
541 int rc = -1;
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500542 char * scontext = NULL;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400543 if (lstat(path, &st) != 0)
544 return rc;
545
546 if (!hnd && (matchpathcon_init_prefix(NULL, NULL) < 0))
547 return -1;
548
549 /* If there's an error determining the context, or it has none,
550 return to allow default context */
551 if (selabel_lookup_raw(hnd, &scontext, path, st.st_mode)) {
552 if (errno == ENOENT)
553 rc = 0;
554 } else {
555 rc = lsetfilecon_raw(path, scontext);
556 freecon(scontext);
557 }
558 return rc;
559}
560
William Roberts0fdfdcc2016-09-26 10:33:39 -0700561#endif