• 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 	const char *usercon_str2;
147 	context_t con;
148 	context_t usercon;
149 
150 	int rc;
151 
152 	errno = EINVAL;
153 
154 	/* Extract the role and type of the fromcon for matching.
155 	   User identity and MLS range can be variable. */
156 	con = context_new(fromcon);
157 	if (!con)
158 		return -1;
159 	fromrole = context_role_get(con);
160 	fromtype = context_type_get(con);
161 	fromlevel = context_range_get(con);
162 	if (!fromrole || !fromtype) {
163 		context_free(con);
164 		return -1;
165 	}
166 
167 	while ((len = getline(&line, &line_len, fp)) > 0) {
168 		if (line[len - 1] == '\n')
169 			line[len - 1] = 0;
170 
171 		/* Skip leading whitespace. */
172 		start = line;
173 		while (*start && isspace(*start))
174 			start++;
175 		if (!(*start))
176 			continue;
177 
178 		/* Find the end of the (partial) fromcon in the line. */
179 		end = start;
180 		while (*end && !isspace(*end))
181 			end++;
182 		if (!(*end))
183 			continue;
184 
185 		/* Check for a match. */
186 		linerole = start;
187 		while (*start && !isspace(*start) && *start != ':')
188 			start++;
189 		if (*start != ':')
190 			continue;
191 		*start = 0;
192 		linetype = ++start;
193 		while (*start && !isspace(*start) && *start != ':')
194 			start++;
195 		if (!(*start))
196 			continue;
197 		*start = 0;
198 		if (!strcmp(fromrole, linerole) && !strcmp(fromtype, linetype)) {
199 			found = 1;
200 			break;
201 		}
202 	}
203 
204 	if (!found) {
205 		errno = ENOENT;
206 		rc = -1;
207 		goto out;
208 	}
209 
210 	start = ++end;
211 	while (*start) {
212 		/* Skip leading whitespace */
213 		while (*start && isspace(*start))
214 			start++;
215 		if (!(*start))
216 			break;
217 
218 		/* Find the end of this partial context. */
219 		end = start;
220 		while (*end && !isspace(*end))
221 			end++;
222 		if (*end)
223 			*end++ = 0;
224 
225 		/* Check whether a new context is valid */
226 		if (SIZE_MAX - user_len < strlen(start) + 2) {
227 			fprintf(stderr, "%s: one of partial contexts is too big\n", __FUNCTION__);
228 			errno = EINVAL;
229 			rc = -1;
230 			goto out;
231 		}
232 		usercon_len = user_len + strlen(start) + 2;
233 		usercon_str = malloc(usercon_len);
234 		if (!usercon_str) {
235 			rc = -1;
236 			goto out;
237 		}
238 
239 		/* set range from fromcon in the new usercon */
240 		snprintf(usercon_str, usercon_len, "%s:%s", user, start);
241 		usercon = context_new(usercon_str);
242 		if (!usercon) {
243 			if (errno != EINVAL) {
244 				free(usercon_str);
245 				rc = -1;
246 				goto out;
247 			}
248 			fprintf(stderr,
249 				"%s: can't create a context from %s, skipping\n",
250 				__FUNCTION__, usercon_str);
251 			free(usercon_str);
252 			start = end;
253 			continue;
254 		}
255 		free(usercon_str);
256 		if (context_range_set(usercon, fromlevel) != 0) {
257 			context_free(usercon);
258 			rc = -1;
259 			goto out;
260 		}
261 		usercon_str2 = context_str(usercon);
262 		if (!usercon_str2) {
263 			context_free(usercon);
264 			rc = -1;
265 			goto out;
266 		}
267 
268 		/* check whether usercon is already in reachable */
269 		if (is_in_reachable(*reachable, usercon_str2)) {
270 			context_free(usercon);
271 			start = end;
272 			continue;
273 		}
274 		if (security_check_context(usercon_str2) == 0) {
275 			new_reachable = realloc(*reachable, (*nreachable + 2) * sizeof(char *));
276 			if (!new_reachable) {
277 				context_free(usercon);
278 				rc = -1;
279 				goto out;
280 			}
281 			*reachable = new_reachable;
282 			new_reachable[*nreachable] = strdup(usercon_str2);
283 			if (new_reachable[*nreachable] == NULL) {
284 				context_free(usercon);
285 				rc = -1;
286 				goto out;
287 			}
288 			new_reachable[*nreachable + 1] = 0;
289 			*nreachable += 1;
290 		}
291 		context_free(usercon);
292 		start = end;
293 	}
294 	rc = 0;
295 
296       out:
297 	context_free(con);
298 	free(line);
299 	return rc;
300 }
301 
get_failsafe_context(const char * user,char ** newcon)302 static int get_failsafe_context(const char *user, char ** newcon)
303 {
304 	FILE *fp;
305 	char buf[255], *ptr;
306 	size_t plen, nlen;
307 	int rc;
308 
309 	fp = fopen(selinux_failsafe_context_path(), "re");
310 	if (!fp)
311 		return -1;
312 
313 	ptr = fgets_unlocked(buf, sizeof buf, fp);
314 	fclose(fp);
315 
316 	if (!ptr)
317 		return -1;
318 	plen = strlen(ptr);
319 	if (buf[plen - 1] == '\n')
320 		buf[plen - 1] = 0;
321 
322 	nlen = strlen(user) + 1 + plen + 1;
323 	*newcon = malloc(nlen);
324 	if (!(*newcon))
325 		return -1;
326 	rc = snprintf(*newcon, nlen, "%s:%s", user, ptr);
327 	if (rc < 0 || (size_t) rc >= nlen) {
328 		free(*newcon);
329 		*newcon = 0;
330 		return -1;
331 	}
332 
333 	/* If possible, check the context to catch
334 	   errors early rather than waiting until the
335 	   caller tries to use setexeccon on the context.
336 	   But this may not always be possible, e.g. if
337 	   selinuxfs isn't mounted. */
338 	if (security_check_context(*newcon) && errno != ENOENT) {
339 		free(*newcon);
340 		*newcon = 0;
341 		return -1;
342 	}
343 
344 	return 0;
345 }
346 
get_ordered_context_list_with_level(const char * user,const char * level,const char * fromcon,char *** list)347 int get_ordered_context_list_with_level(const char *user,
348 					const char *level,
349 					const char *fromcon,
350 					char *** list)
351 {
352 	int rc;
353 	char *backup_fromcon = NULL;
354 	context_t con;
355 	const char *newfromcon;
356 
357 	if (!level)
358 		return get_ordered_context_list(user, fromcon, list);
359 
360 	if (!fromcon) {
361 		rc = getcon(&backup_fromcon);
362 		if (rc < 0)
363 			return rc;
364 		fromcon = backup_fromcon;
365 	}
366 
367 	rc = -1;
368 	con = context_new(fromcon);
369 	if (!con)
370 		goto out;
371 
372 	if (context_range_set(con, level))
373 		goto out;
374 
375 	newfromcon = context_str(con);
376 	if (!newfromcon)
377 		goto out;
378 
379 	rc = get_ordered_context_list(user, newfromcon, list);
380 
381       out:
382 	context_free(con);
383 	freecon(backup_fromcon);
384 	return rc;
385 }
386 
387 
get_default_context_with_level(const char * user,const char * level,const char * fromcon,char ** newcon)388 int get_default_context_with_level(const char *user,
389 				   const char *level,
390 				   const char *fromcon,
391 				   char ** newcon)
392 {
393 	char **conary;
394 	int rc;
395 
396 	rc = get_ordered_context_list_with_level(user, level, fromcon, &conary);
397 	if (rc <= 0)
398 		return -1;
399 
400 	*newcon = strdup(conary[0]);
401 	freeconary(conary);
402 	if (!(*newcon))
403 		return -1;
404 	return 0;
405 }
406 
get_ordered_context_list(const char * user,const char * fromcon,char *** list)407 int get_ordered_context_list(const char *user,
408 			     const char *fromcon,
409 			     char *** list)
410 {
411 	char **reachable = NULL;
412 	int rc = 0;
413 	unsigned nreachable = 0;
414 	char *backup_fromcon = NULL;
415 	FILE *fp;
416 	char *fname = NULL;
417 	size_t fname_len;
418 	const char *user_contexts_path = selinux_user_contexts_path();
419 
420 	if (!fromcon) {
421 		/* Get the current context and use it for the starting context */
422 		rc = getcon(&backup_fromcon);
423 		if (rc < 0)
424 			return rc;
425 		fromcon = backup_fromcon;
426 	}
427 
428 	/* Determine the ordering to apply from the optional per-user config
429 	   and from the global config. */
430 	fname_len = strlen(user_contexts_path) + strlen(user) + 2;
431 	fname = malloc(fname_len);
432 	if (!fname)
433 		goto failsafe;
434 	snprintf(fname, fname_len, "%s%s", user_contexts_path, user);
435 	fp = fopen(fname, "re");
436 	if (fp) {
437 		__fsetlocking(fp, FSETLOCKING_BYCALLER);
438 		rc = get_context_user(fp, fromcon, user, &reachable, &nreachable);
439 
440 		fclose(fp);
441 		if (rc < 0 && errno != ENOENT) {
442 			fprintf(stderr,
443 				"%s:  error in processing configuration file %s\n",
444 				__FUNCTION__, fname);
445 			/* Fall through, try global config */
446 		}
447 	}
448 	free(fname);
449 	fp = fopen(selinux_default_context_path(), "re");
450 	if (fp) {
451 		__fsetlocking(fp, FSETLOCKING_BYCALLER);
452 		rc = get_context_user(fp, fromcon, user, &reachable, &nreachable);
453 		fclose(fp);
454 		if (rc < 0 && errno != ENOENT) {
455 			fprintf(stderr,
456 				"%s:  error in processing configuration file %s\n",
457 				__FUNCTION__, selinux_default_context_path());
458 			/* Fall through */
459 		}
460 	}
461 
462 	if (!nreachable)
463 		goto failsafe;
464 
465       out:
466 	if (nreachable > 0) {
467 		*list = reachable;
468 		rc = nreachable;
469 	}
470 	else
471 		freeconary(reachable);
472 
473 	freecon(backup_fromcon);
474 
475 	return rc;
476 
477       failsafe:
478 	/* Unable to determine a reachable context list, try to fall back to
479 	   the "failsafe" context to at least permit root login
480 	   for emergency recovery if possible. */
481 	freeconary(reachable);
482 	reachable = malloc(2 * sizeof(char *));
483 	if (!reachable) {
484 		rc = -1;
485 		goto out;
486 	}
487 	reachable[0] = reachable[1] = 0;
488 	rc = get_failsafe_context(user, &reachable[0]);
489 	if (rc < 0) {
490 		freeconary(reachable);
491 		reachable = NULL;
492 		goto out;
493 	}
494 	nreachable = 1;			/* one context in the list */
495 	goto out;
496 }
497 
498