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
13int 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
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,
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
100int 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
117static 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
157static 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
271static 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
316struct context_order {
317	char * con;
318	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,
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
372hidden_def(get_ordered_context_list_with_level)
373
374int 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
393int 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
526hidden_def(get_ordered_context_list)
527