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