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(), "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 
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, "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 		}
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