1 #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
get_default_context_with_role(const char * user,const char * role,char * fromcon,char ** newcon)13 int get_default_context_with_role(const char *user,
14 const char *role,
15 char * fromcon,
16 char ** newcon)
17 {
18 char **conary;
19 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;
41 if (!(*ptr)) {
42 errno = EINVAL;
43 goto out;
44 }
45 *newcon = strdup(*ptr);
46 if (!(*newcon))
47 goto out;
48 rc = 0;
49 out:
50 freeconary(conary);
51 return rc;
52 }
53
hidden_def(get_default_context_with_role)54 hidden_def(get_default_context_with_role)
55
56 int get_default_context_with_rolelevel(const char *user,
57 const char *role,
58 const char *level,
59 char * fromcon,
60 char ** newcon)
61 {
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
get_default_context(const char * user,char * fromcon,char ** newcon)100 int get_default_context(const char *user,
101 char * fromcon, char ** newcon)
102 {
103 char **conary;
104 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
find_partialcon(char ** list,unsigned int nreach,char * part)117 static int find_partialcon(char ** list,
118 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
get_context_order(FILE * fp,char * fromcon,char ** reachable,unsigned int nreach,unsigned int * ordering,unsigned int * nordered)157 static int get_context_order(FILE * fp,
158 char * fromcon,
159 char ** reachable,
160 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
get_failsafe_context(const char * user,char ** newcon)271 static int get_failsafe_context(const char *user, char ** newcon)
272 {
273 FILE *fp;
274 char buf[255], *ptr;
275 size_t plen, nlen;
276 int rc;
277
278 fp = fopen(selinux_failsafe_context_path(), "re");
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
291 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;
310 return -1;
311 }
312
313 return 0;
314 }
315
316 struct context_order {
317 char * con;
318 unsigned int order;
319 };
320
order_compare(const void * A,const void * B)321 static 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
get_ordered_context_list_with_level(const char * user,const char * level,char * fromcon,char *** list)331 int get_ordered_context_list_with_level(const char *user,
332 const char *level,
333 char * fromcon,
334 char *** list)
335 {
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
hidden_def(get_ordered_context_list_with_level)372 hidden_def(get_ordered_context_list_with_level)
373
374 int get_default_context_with_level(const char *user,
375 const char *level,
376 char * fromcon,
377 char ** newcon)
378 {
379 char **conary;
380 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
get_ordered_context_list(const char * user,char * fromcon,char *** list)393 int get_ordered_context_list(const char *user,
394 char * fromcon,
395 char *** list)
396 {
397 char **reachable = NULL;
398 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);
418 if (rc < 0)
419 goto failsafe;
420 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)
429 goto failsafe;
430 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)
438 goto failsafe;
439 snprintf(fname, fname_len, "%s%s", user_contexts_path, user);
440 fp = fopen(fname, "re");
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(), "re");
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 }
466 rc = 0;
467 }
468
469 if (!nordered)
470 goto failsafe;
471
472 /* Apply the ordering. */
473 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];
479 }
480 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);
484
485 /* Only report the ordered entries to the caller. */
486 if (nordered <= nreach) {
487 for (i = nordered; i < nreach; i++)
488 free(reachable[i]);
489 reachable[nordered] = NULL;
490 rc = nordered;
491 }
492
493 out:
494 if (rc > 0)
495 *list = reachable;
496 else
497 freeconary(reachable);
498
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);
510 reachable = malloc(2 * sizeof(char *));
511 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;
524 }
525
526 hidden_def(get_ordered_context_list)
527