• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <unistd.h>
2 #include <errno.h>
3 #include <stdio.h>
4 #include <stdio_ext.h>
5 #include <stdint.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <ctype.h>
9 #include <pwd.h>
10 #include "selinux_internal.h"
11 #include "context_internal.h"
12 #include "get_context_list_internal.h"
13 
get_default_context_with_role(const char * user,const char * role,const char * fromcon,char ** newcon)14 int get_default_context_with_role(const char *user,
15 				  const char *role,
16 				  const char *fromcon,
17 				  char ** newcon)
18 {
19 	char **conary;
20 	char **ptr;
21 	context_t con;
22 	const char *role2;
23 	int rc;
24 
25 	rc = get_ordered_context_list(user, fromcon, &conary);
26 	if (rc <= 0)
27 		return -1;
28 
29 	for (ptr = conary; *ptr; ptr++) {
30 		con = context_new(*ptr);
31 		if (!con)
32 			continue;
33 		role2 = context_role_get(con);
34 		if (role2 && !strcmp(role, role2)) {
35 			context_free(con);
36 			break;
37 		}
38 		context_free(con);
39 	}
40 
41 	rc = -1;
42 	if (!(*ptr)) {
43 		errno = EINVAL;
44 		goto out;
45 	}
46 	*newcon = strdup(*ptr);
47 	if (!(*newcon))
48 		goto out;
49 	rc = 0;
50       out:
51 	freeconary(conary);
52 	return rc;
53 }
54 
55 
get_default_context_with_rolelevel(const char * user,const char * role,const char * level,const char * fromcon,char ** newcon)56 int get_default_context_with_rolelevel(const char *user,
57 				       const char *role,
58 				       const char *level,
59 				       const char *fromcon,
60 				       char ** newcon)
61 {
62 
63 	int rc;
64 	char *backup_fromcon = NULL;
65 	context_t con;
66 	const char *newfromcon;
67 
68 	if (!level)
69 		return get_default_context_with_role(user, role, fromcon,
70 						     newcon);
71 
72 	if (!fromcon) {
73 		rc = getcon(&backup_fromcon);
74 		if (rc < 0)
75 			return rc;
76 		fromcon = backup_fromcon;
77 	}
78 
79 	rc = -1;
80 	con = context_new(fromcon);
81 	if (!con)
82 		goto out;
83 
84 	if (context_range_set(con, level))
85 		goto out;
86 
87 	newfromcon = context_str(con);
88 	if (!newfromcon)
89 		goto out;
90 
91 	rc = get_default_context_with_role(user, role, newfromcon, newcon);
92 
93       out:
94 	context_free(con);
95 	freecon(backup_fromcon);
96 	return rc;
97 
98 }
99 
get_default_context(const char * user,const char * fromcon,char ** newcon)100 int get_default_context(const char *user,
101 			const 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 
is_in_reachable(char ** reachable,const char * usercon_str)117 static int is_in_reachable(char **reachable, const char *usercon_str)
118 {
119 	if (!reachable)
120 		return 0;
121 
122 	for (; *reachable != NULL; reachable++) {
123 		if (strcmp(*reachable, usercon_str) == 0) {
124 			return 1;
125 		}
126 	}
127 	return 0;
128 }
129 
get_context_user(FILE * fp,const char * fromcon,const char * user,char *** reachable,unsigned int * nreachable)130 static int get_context_user(FILE * fp,
131 			     const char * fromcon,
132 			     const char * user,
133 			     char ***reachable,
134 			     unsigned int *nreachable)
135 {
136 	char *start, *end = NULL;
137 	char *line = NULL;
138 	size_t line_len = 0, usercon_len;
139 	size_t user_len = strlen(user);
140 	ssize_t len;
141 	int found = 0;
142 	const char *fromrole, *fromtype, *fromlevel;
143 	char *linerole, *linetype;
144 	char **new_reachable = NULL;
145 	char *usercon_str;
146 	context_t con;
147 	context_t usercon;
148 
149 	int rc;
150 
151 	errno = EINVAL;
152 
153 	/* Extract the role and type of the fromcon for matching.
154 	   User identity and MLS range can be variable. */
155 	con = context_new(fromcon);
156 	if (!con)
157 		return -1;
158 	fromrole = context_role_get(con);
159 	fromtype = context_type_get(con);
160 	fromlevel = context_range_get(con);
161 	if (!fromrole || !fromtype) {
162 		context_free(con);
163 		return -1;
164 	}
165 
166 	while ((len = getline(&line, &line_len, fp)) > 0) {
167 		if (line[len - 1] == '\n')
168 			line[len - 1] = 0;
169 
170 		/* Skip leading whitespace. */
171 		start = line;
172 		while (*start && isspace(*start))
173 			start++;
174 		if (!(*start))
175 			continue;
176 
177 		/* Find the end of the (partial) fromcon in the line. */
178 		end = start;
179 		while (*end && !isspace(*end))
180 			end++;
181 		if (!(*end))
182 			continue;
183 
184 		/* Check for a match. */
185 		linerole = start;
186 		while (*start && !isspace(*start) && *start != ':')
187 			start++;
188 		if (*start != ':')
189 			continue;
190 		*start = 0;
191 		linetype = ++start;
192 		while (*start && !isspace(*start) && *start != ':')
193 			start++;
194 		if (!(*start))
195 			continue;
196 		*start = 0;
197 		if (!strcmp(fromrole, linerole) && !strcmp(fromtype, linetype)) {
198 			found = 1;
199 			break;
200 		}
201 	}
202 
203 	if (!found) {
204 		errno = ENOENT;
205 		rc = -1;
206 		goto out;
207 	}
208 
209 	start = ++end;
210 	while (*start) {
211 		/* Skip leading whitespace */
212 		while (*start && isspace(*start))
213 			start++;
214 		if (!(*start))
215 			break;
216 
217 		/* Find the end of this partial context. */
218 		end = start;
219 		while (*end && !isspace(*end))
220 			end++;
221 		if (*end)
222 			*end++ = 0;
223 
224 		/* Check whether a new context is valid */
225 		if (SIZE_MAX - user_len < strlen(start) + 2) {
226 			fprintf(stderr, "%s: one of partial contexts is too big\n", __FUNCTION__);
227 			errno = EINVAL;
228 			rc = -1;
229 			goto out;
230 		}
231 		usercon_len = user_len + strlen(start) + 2;
232 		usercon_str = malloc(usercon_len);
233 		if (!usercon_str) {
234 			rc = -1;
235 			goto out;
236 		}
237 
238 		/* set range from fromcon in the new usercon */
239 		snprintf(usercon_str, usercon_len, "%s:%s", user, start);
240 		usercon = context_new(usercon_str);
241 		if (!usercon) {
242 			if (errno != EINVAL) {
243 				free(usercon_str);
244 				rc = -1;
245 				goto out;
246 			}
247 			fprintf(stderr,
248 				"%s: can't create a context from %s, skipping\n",
249 				__FUNCTION__, usercon_str);
250 			free(usercon_str);
251 			start = end;
252 			continue;
253 		}
254 		free(usercon_str);
255 		if (context_range_set(usercon, fromlevel) != 0) {
256 			context_free(usercon);
257 			rc = -1;
258 			goto out;
259 		}
260 		usercon_str = context_str(usercon);
261 		if (!usercon_str) {
262 			context_free(usercon);
263 			rc = -1;
264 			goto out;
265 		}
266 
267 		/* check whether usercon is already in reachable */
268 		if (is_in_reachable(*reachable, usercon_str)) {
269 			context_free(usercon);
270 			start = end;
271 			continue;
272 		}
273 		if (security_check_context(usercon_str) == 0) {
274 			new_reachable = realloc(*reachable, (*nreachable + 2) * sizeof(char *));
275 			if (!new_reachable) {
276 				context_free(usercon);
277 				rc = -1;
278 				goto out;
279 			}
280 			*reachable = new_reachable;
281 			new_reachable[*nreachable] = strdup(usercon_str);
282 			if (new_reachable[*nreachable] == NULL) {
283 				context_free(usercon);
284 				rc = -1;
285 				goto out;
286 			}
287 			new_reachable[*nreachable + 1] = 0;
288 			*nreachable += 1;
289 		}
290 		context_free(usercon);
291 		start = end;
292 	}
293 	rc = 0;
294 
295       out:
296 	context_free(con);
297 	free(line);
298 	return rc;
299 }
300 
get_failsafe_context(const char * user,char ** newcon)301 static int get_failsafe_context(const char *user, char ** newcon)
302 {
303 	FILE *fp;
304 	char buf[255], *ptr;
305 	size_t plen, nlen;
306 	int rc;
307 
308 	fp = fopen(selinux_failsafe_context_path(), "re");
309 	if (!fp)
310 		return -1;
311 
312 	ptr = fgets_unlocked(buf, sizeof buf, fp);
313 	fclose(fp);
314 
315 	if (!ptr)
316 		return -1;
317 	plen = strlen(ptr);
318 	if (buf[plen - 1] == '\n')
319 		buf[plen - 1] = 0;
320 
321 	nlen = strlen(user) + 1 + plen + 1;
322 	*newcon = malloc(nlen);
323 	if (!(*newcon))
324 		return -1;
325 	rc = snprintf(*newcon, nlen, "%s:%s", user, ptr);
326 	if (rc < 0 || (size_t) rc >= nlen) {
327 		free(*newcon);
328 		*newcon = 0;
329 		return -1;
330 	}
331 
332 	/* If possible, check the context to catch
333 	   errors early rather than waiting until the
334 	   caller tries to use setexeccon on the context.
335 	   But this may not always be possible, e.g. if
336 	   selinuxfs isn't mounted. */
337 	if (security_check_context(*newcon) && errno != ENOENT) {
338 		free(*newcon);
339 		*newcon = 0;
340 		return -1;
341 	}
342 
343 	return 0;
344 }
345 
get_ordered_context_list_with_level(const char * user,const char * level,const char * fromcon,char *** list)346 int get_ordered_context_list_with_level(const char *user,
347 					const char *level,
348 					const char *fromcon,
349 					char *** list)
350 {
351 	int rc;
352 	char *backup_fromcon = NULL;
353 	context_t con;
354 	const char *newfromcon;
355 
356 	if (!level)
357 		return get_ordered_context_list(user, fromcon, list);
358 
359 	if (!fromcon) {
360 		rc = getcon(&backup_fromcon);
361 		if (rc < 0)
362 			return rc;
363 		fromcon = backup_fromcon;
364 	}
365 
366 	rc = -1;
367 	con = context_new(fromcon);
368 	if (!con)
369 		goto out;
370 
371 	if (context_range_set(con, level))
372 		goto out;
373 
374 	newfromcon = context_str(con);
375 	if (!newfromcon)
376 		goto out;
377 
378 	rc = get_ordered_context_list(user, newfromcon, list);
379 
380       out:
381 	context_free(con);
382 	freecon(backup_fromcon);
383 	return rc;
384 }
385 
386 
get_default_context_with_level(const char * user,const char * level,const char * fromcon,char ** newcon)387 int get_default_context_with_level(const char *user,
388 				   const char *level,
389 				   const char *fromcon,
390 				   char ** newcon)
391 {
392 	char **conary;
393 	int rc;
394 
395 	rc = get_ordered_context_list_with_level(user, level, fromcon, &conary);
396 	if (rc <= 0)
397 		return -1;
398 
399 	*newcon = strdup(conary[0]);
400 	freeconary(conary);
401 	if (!(*newcon))
402 		return -1;
403 	return 0;
404 }
405 
get_ordered_context_list(const char * user,const char * fromcon,char *** list)406 int get_ordered_context_list(const char *user,
407 			     const char *fromcon,
408 			     char *** list)
409 {
410 	char **reachable = NULL;
411 	int rc = 0;
412 	unsigned nreachable = 0;
413 	char *backup_fromcon = NULL;
414 	FILE *fp;
415 	char *fname = NULL;
416 	size_t fname_len;
417 	const char *user_contexts_path = selinux_user_contexts_path();
418 
419 	if (!fromcon) {
420 		/* Get the current context and use it for the starting context */
421 		rc = getcon(&backup_fromcon);
422 		if (rc < 0)
423 			return rc;
424 		fromcon = backup_fromcon;
425 	}
426 
427 	/* Determine the ordering to apply from the optional per-user config
428 	   and from the global config. */
429 	fname_len = strlen(user_contexts_path) + strlen(user) + 2;
430 	fname = malloc(fname_len);
431 	if (!fname)
432 		goto failsafe;
433 	snprintf(fname, fname_len, "%s%s", user_contexts_path, user);
434 	fp = fopen(fname, "re");
435 	if (fp) {
436 		__fsetlocking(fp, FSETLOCKING_BYCALLER);
437 		rc = get_context_user(fp, fromcon, user, &reachable, &nreachable);
438 
439 		fclose(fp);
440 		if (rc < 0 && errno != ENOENT) {
441 			fprintf(stderr,
442 				"%s:  error in processing configuration file %s\n",
443 				__FUNCTION__, fname);
444 			/* Fall through, try global config */
445 		}
446 	}
447 	free(fname);
448 	fp = fopen(selinux_default_context_path(), "re");
449 	if (fp) {
450 		__fsetlocking(fp, FSETLOCKING_BYCALLER);
451 		rc = get_context_user(fp, fromcon, user, &reachable, &nreachable);
452 		fclose(fp);
453 		if (rc < 0 && errno != ENOENT) {
454 			fprintf(stderr,
455 				"%s:  error in processing configuration file %s\n",
456 				__FUNCTION__, selinux_default_context_path());
457 			/* Fall through */
458 		}
459 	}
460 
461 	if (!nreachable)
462 		goto failsafe;
463 
464       out:
465 	if (nreachable > 0) {
466 		*list = reachable;
467 		rc = nreachable;
468 	}
469 	else
470 		freeconary(reachable);
471 
472 	freecon(backup_fromcon);
473 
474 	return rc;
475 
476       failsafe:
477 	/* Unable to determine a reachable context list, try to fall back to
478 	   the "failsafe" context to at least permit root login
479 	   for emergency recovery if possible. */
480 	freeconary(reachable);
481 	reachable = malloc(2 * sizeof(char *));
482 	if (!reachable) {
483 		rc = -1;
484 		goto out;
485 	}
486 	reachable[0] = reachable[1] = 0;
487 	rc = get_failsafe_context(user, &reachable[0]);
488 	if (rc < 0) {
489 		freeconary(reachable);
490 		reachable = NULL;
491 		goto out;
492 	}
493 	nreachable = 1;			/* one context in the list */
494 	goto out;
495 }
496 
497