blob: f3fa4a9bb73fbac57e1b6c53b9fe1f2d04fd8f07 [file] [log] [blame]
Joshua Brindle13cd4c82008-08-19 15:30:36 -04001#include <unistd.h>
2#include <errno.h>
3#include <stdio.h>
4#include <stdio_ext.h>
5#include <stdlib.h>
6#include <string.h>
7#include <ctype.h>
8#include <pwd.h>
9#include "selinux_internal.h"
10#include "context_internal.h"
11#include "get_context_list_internal.h"
12
13int get_default_context_with_role(const char *user,
14 const char *role,
Stephen Smalley9eb9c932014-02-19 09:16:17 -050015 char * fromcon,
16 char ** newcon)
Joshua Brindle13cd4c82008-08-19 15:30:36 -040017{
Stephen Smalley9eb9c932014-02-19 09:16:17 -050018 char **conary;
Joshua Brindle13cd4c82008-08-19 15:30:36 -040019 char **ptr;
20 context_t con;
21 const char *role2;
22 int rc;
23
24 rc = get_ordered_context_list(user, fromcon, &conary);
25 if (rc <= 0)
26 return -1;
27
28 for (ptr = conary; *ptr; ptr++) {
29 con = context_new(*ptr);
30 if (!con)
31 continue;
32 role2 = context_role_get(con);
33 if (role2 && !strcmp(role, role2)) {
34 context_free(con);
35 break;
36 }
37 context_free(con);
38 }
39
40 rc = -1;
Richard Hainesaed37212011-11-27 16:08:10 +000041 if (!(*ptr)) {
42 errno = EINVAL;
Joshua Brindle13cd4c82008-08-19 15:30:36 -040043 goto out;
Richard Hainesaed37212011-11-27 16:08:10 +000044 }
Joshua Brindle13cd4c82008-08-19 15:30:36 -040045 *newcon = strdup(*ptr);
46 if (!(*newcon))
47 goto out;
48 rc = 0;
49 out:
50 freeconary(conary);
51 return rc;
52}
53
54hidden_def(get_default_context_with_role)
55
56int get_default_context_with_rolelevel(const char *user,
57 const char *role,
58 const char *level,
Stephen Smalley9eb9c932014-02-19 09:16:17 -050059 char * fromcon,
60 char ** newcon)
Joshua Brindle13cd4c82008-08-19 15:30:36 -040061{
62
63 int rc = 0;
64 int freefrom = 0;
65 context_t con;
66 char *newfromcon;
67 if (!level)
68 return get_default_context_with_role(user, role, fromcon,
69 newcon);
70
71 if (!fromcon) {
72 rc = getcon(&fromcon);
73 if (rc < 0)
74 return rc;
75 freefrom = 1;
76 }
77
78 rc = -1;
79 con = context_new(fromcon);
80 if (!con)
81 goto out;
82
83 if (context_range_set(con, level))
84 goto out;
85
86 newfromcon = context_str(con);
87 if (!newfromcon)
88 goto out;
89
90 rc = get_default_context_with_role(user, role, newfromcon, newcon);
91
92 out:
93 context_free(con);
94 if (freefrom)
95 freecon(fromcon);
96 return rc;
97
98}
99
100int get_default_context(const char *user,
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500101 char * fromcon, char ** newcon)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400102{
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500103 char **conary;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400104 int rc;
105
106 rc = get_ordered_context_list(user, fromcon, &conary);
107 if (rc <= 0)
108 return -1;
109
110 *newcon = strdup(conary[0]);
111 freeconary(conary);
112 if (!(*newcon))
113 return -1;
114 return 0;
115}
116
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500117static int find_partialcon(char ** list,
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400118 unsigned int nreach, char *part)
119{
120 const char *conrole, *contype;
121 char *partrole, *parttype, *ptr;
122 context_t con;
123 unsigned int i;
124
125 partrole = part;
126 ptr = part;
127 while (*ptr && !isspace(*ptr) && *ptr != ':')
128 ptr++;
129 if (*ptr != ':')
130 return -1;
131 *ptr++ = 0;
132 parttype = ptr;
133 while (*ptr && !isspace(*ptr) && *ptr != ':')
134 ptr++;
135 *ptr = 0;
136
137 for (i = 0; i < nreach; i++) {
138 con = context_new(list[i]);
139 if (!con)
140 return -1;
141 conrole = context_role_get(con);
142 contype = context_type_get(con);
143 if (!conrole || !contype) {
144 context_free(con);
145 return -1;
146 }
147 if (!strcmp(conrole, partrole) && !strcmp(contype, parttype)) {
148 context_free(con);
149 return i;
150 }
151 context_free(con);
152 }
153
154 return -1;
155}
156
157static int get_context_order(FILE * fp,
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500158 char * fromcon,
159 char ** reachable,
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400160 unsigned int nreach,
161 unsigned int *ordering, unsigned int *nordered)
162{
163 char *start, *end = NULL;
164 char *line = NULL;
165 size_t line_len = 0;
166 ssize_t len;
167 int found = 0;
168 const char *fromrole, *fromtype;
169 char *linerole, *linetype;
170 unsigned int i;
171 context_t con;
172 int rc;
173
174 errno = -EINVAL;
175
176 /* Extract the role and type of the fromcon for matching.
177 User identity and MLS range can be variable. */
178 con = context_new(fromcon);
179 if (!con)
180 return -1;
181 fromrole = context_role_get(con);
182 fromtype = context_type_get(con);
183 if (!fromrole || !fromtype) {
184 context_free(con);
185 return -1;
186 }
187
188 while ((len = getline(&line, &line_len, fp)) > 0) {
189 if (line[len - 1] == '\n')
190 line[len - 1] = 0;
191
192 /* Skip leading whitespace. */
193 start = line;
194 while (*start && isspace(*start))
195 start++;
196 if (!(*start))
197 continue;
198
199 /* Find the end of the (partial) fromcon in the line. */
200 end = start;
201 while (*end && !isspace(*end))
202 end++;
203 if (!(*end))
204 continue;
205
206 /* Check for a match. */
207 linerole = start;
208 while (*start && !isspace(*start) && *start != ':')
209 start++;
210 if (*start != ':')
211 continue;
212 *start = 0;
213 linetype = ++start;
214 while (*start && !isspace(*start) && *start != ':')
215 start++;
216 if (!(*start))
217 continue;
218 *start = 0;
219 if (!strcmp(fromrole, linerole) && !strcmp(fromtype, linetype)) {
220 found = 1;
221 break;
222 }
223 }
224
225 if (!found) {
226 errno = ENOENT;
227 rc = -1;
228 goto out;
229 }
230
231 start = ++end;
232 while (*start) {
233 /* Skip leading whitespace */
234 while (*start && isspace(*start))
235 start++;
236 if (!(*start))
237 break;
238
239 /* Find the end of this partial context. */
240 end = start;
241 while (*end && !isspace(*end))
242 end++;
243 if (*end)
244 *end++ = 0;
245
246 /* Check for a match in the reachable list. */
247 rc = find_partialcon(reachable, nreach, start);
248 if (rc < 0) {
249 /* No match, skip it. */
250 start = end;
251 continue;
252 }
253
254 /* If a match is found and the entry is not already ordered
255 (e.g. due to prior match in prior config file), then set
256 the ordering for it. */
257 i = rc;
258 if (ordering[i] == nreach)
259 ordering[i] = (*nordered)++;
260 start = end;
261 }
262
263 rc = 0;
264
265 out:
266 context_free(con);
267 free(line);
268 return rc;
269}
270
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500271static int get_failsafe_context(const char *user, char ** newcon)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400272{
273 FILE *fp;
274 char buf[255], *ptr;
275 size_t plen, nlen;
276 int rc;
277
278 fp = fopen(selinux_failsafe_context_path(), "r");
279 if (!fp)
280 return -1;
281
282 ptr = fgets_unlocked(buf, sizeof buf, fp);
283 fclose(fp);
284
285 if (!ptr)
286 return -1;
287 plen = strlen(ptr);
288 if (buf[plen - 1] == '\n')
289 buf[plen - 1] = 0;
290
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400291 nlen = strlen(user) + 1 + plen + 1;
292 *newcon = malloc(nlen);
293 if (!(*newcon))
294 return -1;
295 rc = snprintf(*newcon, nlen, "%s:%s", user, ptr);
296 if (rc < 0 || (size_t) rc >= nlen) {
297 free(*newcon);
298 *newcon = 0;
299 return -1;
300 }
301
302 /* If possible, check the context to catch
303 errors early rather than waiting until the
304 caller tries to use setexeccon on the context.
305 But this may not always be possible, e.g. if
306 selinuxfs isn't mounted. */
307 if (security_check_context(*newcon) && errno != ENOENT) {
308 free(*newcon);
309 *newcon = 0;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400310 return -1;
311 }
312
313 return 0;
314}
315
316struct context_order {
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500317 char * con;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400318 unsigned int order;
319};
320
321static int order_compare(const void *A, const void *B)
322{
323 const struct context_order *c1 = A, *c2 = B;
324 if (c1->order < c2->order)
325 return -1;
326 else if (c1->order > c2->order)
327 return 1;
328 return strcmp(c1->con, c2->con);
329}
330
331int get_ordered_context_list_with_level(const char *user,
332 const char *level,
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500333 char * fromcon,
334 char *** list)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400335{
336 int rc;
337 int freefrom = 0;
338 context_t con;
339 char *newfromcon;
340
341 if (!level)
342 return get_ordered_context_list(user, fromcon, list);
343
344 if (!fromcon) {
345 rc = getcon(&fromcon);
346 if (rc < 0)
347 return rc;
348 freefrom = 1;
349 }
350
351 rc = -1;
352 con = context_new(fromcon);
353 if (!con)
354 goto out;
355
356 if (context_range_set(con, level))
357 goto out;
358
359 newfromcon = context_str(con);
360 if (!newfromcon)
361 goto out;
362
363 rc = get_ordered_context_list(user, newfromcon, list);
364
365 out:
366 context_free(con);
367 if (freefrom)
368 freecon(fromcon);
369 return rc;
370}
371
372hidden_def(get_ordered_context_list_with_level)
373
374int get_default_context_with_level(const char *user,
375 const char *level,
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500376 char * fromcon,
377 char ** newcon)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400378{
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500379 char **conary;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400380 int rc;
381
382 rc = get_ordered_context_list_with_level(user, level, fromcon, &conary);
383 if (rc <= 0)
384 return -1;
385
386 *newcon = strdup(conary[0]);
387 freeconary(conary);
388 if (!(*newcon))
389 return -1;
390 return 0;
391}
392
393int get_ordered_context_list(const char *user,
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500394 char * fromcon,
395 char *** list)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400396{
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500397 char **reachable = NULL;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400398 unsigned int *ordering = NULL;
399 struct context_order *co = NULL;
400 char **ptr;
401 int rc = 0;
402 unsigned int nreach = 0, nordered = 0, freefrom = 0, i;
403 FILE *fp;
404 char *fname = NULL;
405 size_t fname_len;
406 const char *user_contexts_path = selinux_user_contexts_path();
407
408 if (!fromcon) {
409 /* Get the current context and use it for the starting context */
410 rc = getcon(&fromcon);
411 if (rc < 0)
412 return rc;
413 freefrom = 1;
414 }
415
416 /* Determine the set of reachable contexts for the user. */
417 rc = security_compute_user(fromcon, user, &reachable);
Daniel J Walsh7bc4ffb2010-12-14 15:45:10 -0500418 if (rc < 0)
419 goto failsafe;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400420 nreach = 0;
421 for (ptr = reachable; *ptr; ptr++)
422 nreach++;
423 if (!nreach)
424 goto failsafe;
425
426 /* Initialize ordering array. */
427 ordering = malloc(nreach * sizeof(unsigned int));
428 if (!ordering)
Dan Walsh403f2cf2013-10-09 15:29:50 -0400429 goto failsafe;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400430 for (i = 0; i < nreach; i++)
431 ordering[i] = nreach;
432
433 /* Determine the ordering to apply from the optional per-user config
434 and from the global config. */
435 fname_len = strlen(user_contexts_path) + strlen(user) + 2;
436 fname = malloc(fname_len);
437 if (!fname)
Dan Walsh403f2cf2013-10-09 15:29:50 -0400438 goto failsafe;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400439 snprintf(fname, fname_len, "%s%s", user_contexts_path, user);
440 fp = fopen(fname, "r");
441 if (fp) {
442 __fsetlocking(fp, FSETLOCKING_BYCALLER);
443 rc = get_context_order(fp, fromcon, reachable, nreach, ordering,
444 &nordered);
445 fclose(fp);
446 if (rc < 0 && errno != ENOENT) {
447 fprintf(stderr,
448 "%s: error in processing configuration file %s\n",
449 __FUNCTION__, fname);
450 /* Fall through, try global config */
451 }
452 }
453 free(fname);
454 fp = fopen(selinux_default_context_path(), "r");
455 if (fp) {
456 __fsetlocking(fp, FSETLOCKING_BYCALLER);
457 rc = get_context_order(fp, fromcon, reachable, nreach, ordering,
458 &nordered);
459 fclose(fp);
460 if (rc < 0 && errno != ENOENT) {
461 fprintf(stderr,
462 "%s: error in processing configuration file %s\n",
463 __FUNCTION__, selinux_default_context_path());
464 /* Fall through */
465 }
Dan Walsh403f2cf2013-10-09 15:29:50 -0400466 rc = 0;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400467 }
468
Dan Walsh403f2cf2013-10-09 15:29:50 -0400469 if (!nordered)
470 goto failsafe;
471
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400472 /* Apply the ordering. */
Dan Walsh403f2cf2013-10-09 15:29:50 -0400473 co = malloc(nreach * sizeof(struct context_order));
474 if (!co)
475 goto failsafe;
476 for (i = 0; i < nreach; i++) {
477 co[i].con = reachable[i];
478 co[i].order = ordering[i];
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400479 }
Dan Walsh403f2cf2013-10-09 15:29:50 -0400480 qsort(co, nreach, sizeof(struct context_order), order_compare);
481 for (i = 0; i < nreach; i++)
482 reachable[i] = co[i].con;
483 free(co);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400484
Dan Walsh403f2cf2013-10-09 15:29:50 -0400485 /* Only report the ordered entries to the caller. */
486 if (nordered <= nreach) {
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400487 for (i = nordered; i < nreach; i++)
488 free(reachable[i]);
489 reachable[nordered] = NULL;
490 rc = nordered;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400491 }
492
493 out:
Eric Parisaa62cd62012-11-29 09:41:38 -0500494 if (rc > 0)
495 *list = reachable;
496 else
497 freeconary(reachable);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400498
499 free(ordering);
500 if (freefrom)
501 freecon(fromcon);
502
503 return rc;
504
505 failsafe:
506 /* Unable to determine a reachable context list, try to fall back to
507 the "failsafe" context to at least permit root login
508 for emergency recovery if possible. */
509 freeconary(reachable);
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500510 reachable = malloc(2 * sizeof(char *));
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400511 if (!reachable) {
512 rc = -1;
513 goto out;
514 }
515 reachable[0] = reachable[1] = 0;
516 rc = get_failsafe_context(user, &reachable[0]);
517 if (rc < 0) {
518 freeconary(reachable);
519 reachable = NULL;
520 goto out;
521 }
522 rc = 1; /* one context in the list */
523 goto out;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400524}
525
526hidden_def(get_ordered_context_list)