16cd6a6acSopenharmony_ci#include <unistd.h>
26cd6a6acSopenharmony_ci#include <errno.h>
36cd6a6acSopenharmony_ci#include <stdio.h>
46cd6a6acSopenharmony_ci#include <stdio_ext.h>
56cd6a6acSopenharmony_ci#include <stdint.h>
66cd6a6acSopenharmony_ci#include <stdlib.h>
76cd6a6acSopenharmony_ci#include <string.h>
86cd6a6acSopenharmony_ci#include <ctype.h>
96cd6a6acSopenharmony_ci#include <pwd.h>
106cd6a6acSopenharmony_ci#include "selinux_internal.h"
116cd6a6acSopenharmony_ci#include "context_internal.h"
126cd6a6acSopenharmony_ci#include "get_context_list_internal.h"
136cd6a6acSopenharmony_ci
146cd6a6acSopenharmony_ciint get_default_context_with_role(const char *user,
156cd6a6acSopenharmony_ci				  const char *role,
166cd6a6acSopenharmony_ci				  const char *fromcon,
176cd6a6acSopenharmony_ci				  char ** newcon)
186cd6a6acSopenharmony_ci{
196cd6a6acSopenharmony_ci	char **conary;
206cd6a6acSopenharmony_ci	char **ptr;
216cd6a6acSopenharmony_ci	context_t con;
226cd6a6acSopenharmony_ci	const char *role2;
236cd6a6acSopenharmony_ci	int rc;
246cd6a6acSopenharmony_ci
256cd6a6acSopenharmony_ci	rc = get_ordered_context_list(user, fromcon, &conary);
266cd6a6acSopenharmony_ci	if (rc <= 0)
276cd6a6acSopenharmony_ci		return -1;
286cd6a6acSopenharmony_ci
296cd6a6acSopenharmony_ci	for (ptr = conary; *ptr; ptr++) {
306cd6a6acSopenharmony_ci		con = context_new(*ptr);
316cd6a6acSopenharmony_ci		if (!con)
326cd6a6acSopenharmony_ci			continue;
336cd6a6acSopenharmony_ci		role2 = context_role_get(con);
346cd6a6acSopenharmony_ci		if (role2 && !strcmp(role, role2)) {
356cd6a6acSopenharmony_ci			context_free(con);
366cd6a6acSopenharmony_ci			break;
376cd6a6acSopenharmony_ci		}
386cd6a6acSopenharmony_ci		context_free(con);
396cd6a6acSopenharmony_ci	}
406cd6a6acSopenharmony_ci
416cd6a6acSopenharmony_ci	rc = -1;
426cd6a6acSopenharmony_ci	if (!(*ptr)) {
436cd6a6acSopenharmony_ci		errno = EINVAL;
446cd6a6acSopenharmony_ci		goto out;
456cd6a6acSopenharmony_ci	}
466cd6a6acSopenharmony_ci	*newcon = strdup(*ptr);
476cd6a6acSopenharmony_ci	if (!(*newcon))
486cd6a6acSopenharmony_ci		goto out;
496cd6a6acSopenharmony_ci	rc = 0;
506cd6a6acSopenharmony_ci      out:
516cd6a6acSopenharmony_ci	freeconary(conary);
526cd6a6acSopenharmony_ci	return rc;
536cd6a6acSopenharmony_ci}
546cd6a6acSopenharmony_ci
556cd6a6acSopenharmony_ci
566cd6a6acSopenharmony_ciint get_default_context_with_rolelevel(const char *user,
576cd6a6acSopenharmony_ci				       const char *role,
586cd6a6acSopenharmony_ci				       const char *level,
596cd6a6acSopenharmony_ci				       const char *fromcon,
606cd6a6acSopenharmony_ci				       char ** newcon)
616cd6a6acSopenharmony_ci{
626cd6a6acSopenharmony_ci
636cd6a6acSopenharmony_ci	int rc;
646cd6a6acSopenharmony_ci	char *backup_fromcon = NULL;
656cd6a6acSopenharmony_ci	context_t con;
666cd6a6acSopenharmony_ci	const char *newfromcon;
676cd6a6acSopenharmony_ci
686cd6a6acSopenharmony_ci	if (!level)
696cd6a6acSopenharmony_ci		return get_default_context_with_role(user, role, fromcon,
706cd6a6acSopenharmony_ci						     newcon);
716cd6a6acSopenharmony_ci
726cd6a6acSopenharmony_ci	if (!fromcon) {
736cd6a6acSopenharmony_ci		rc = getcon(&backup_fromcon);
746cd6a6acSopenharmony_ci		if (rc < 0)
756cd6a6acSopenharmony_ci			return rc;
766cd6a6acSopenharmony_ci		fromcon = backup_fromcon;
776cd6a6acSopenharmony_ci	}
786cd6a6acSopenharmony_ci
796cd6a6acSopenharmony_ci	rc = -1;
806cd6a6acSopenharmony_ci	con = context_new(fromcon);
816cd6a6acSopenharmony_ci	if (!con)
826cd6a6acSopenharmony_ci		goto out;
836cd6a6acSopenharmony_ci
846cd6a6acSopenharmony_ci	if (context_range_set(con, level))
856cd6a6acSopenharmony_ci		goto out;
866cd6a6acSopenharmony_ci
876cd6a6acSopenharmony_ci	newfromcon = context_str(con);
886cd6a6acSopenharmony_ci	if (!newfromcon)
896cd6a6acSopenharmony_ci		goto out;
906cd6a6acSopenharmony_ci
916cd6a6acSopenharmony_ci	rc = get_default_context_with_role(user, role, newfromcon, newcon);
926cd6a6acSopenharmony_ci
936cd6a6acSopenharmony_ci      out:
946cd6a6acSopenharmony_ci	context_free(con);
956cd6a6acSopenharmony_ci	freecon(backup_fromcon);
966cd6a6acSopenharmony_ci	return rc;
976cd6a6acSopenharmony_ci
986cd6a6acSopenharmony_ci}
996cd6a6acSopenharmony_ci
1006cd6a6acSopenharmony_ciint get_default_context(const char *user,
1016cd6a6acSopenharmony_ci			const char *fromcon, char ** newcon)
1026cd6a6acSopenharmony_ci{
1036cd6a6acSopenharmony_ci	char **conary;
1046cd6a6acSopenharmony_ci	int rc;
1056cd6a6acSopenharmony_ci
1066cd6a6acSopenharmony_ci	rc = get_ordered_context_list(user, fromcon, &conary);
1076cd6a6acSopenharmony_ci	if (rc <= 0)
1086cd6a6acSopenharmony_ci		return -1;
1096cd6a6acSopenharmony_ci
1106cd6a6acSopenharmony_ci	*newcon = strdup(conary[0]);
1116cd6a6acSopenharmony_ci	freeconary(conary);
1126cd6a6acSopenharmony_ci	if (!(*newcon))
1136cd6a6acSopenharmony_ci		return -1;
1146cd6a6acSopenharmony_ci	return 0;
1156cd6a6acSopenharmony_ci}
1166cd6a6acSopenharmony_ci
1176cd6a6acSopenharmony_cistatic int is_in_reachable(char **reachable, const char *usercon_str)
1186cd6a6acSopenharmony_ci{
1196cd6a6acSopenharmony_ci	if (!reachable)
1206cd6a6acSopenharmony_ci		return 0;
1216cd6a6acSopenharmony_ci
1226cd6a6acSopenharmony_ci	for (; *reachable != NULL; reachable++) {
1236cd6a6acSopenharmony_ci		if (strcmp(*reachable, usercon_str) == 0) {
1246cd6a6acSopenharmony_ci			return 1;
1256cd6a6acSopenharmony_ci		}
1266cd6a6acSopenharmony_ci	}
1276cd6a6acSopenharmony_ci	return 0;
1286cd6a6acSopenharmony_ci}
1296cd6a6acSopenharmony_ci
1306cd6a6acSopenharmony_cistatic int get_context_user(FILE * fp,
1316cd6a6acSopenharmony_ci			     const char * fromcon,
1326cd6a6acSopenharmony_ci			     const char * user,
1336cd6a6acSopenharmony_ci			     char ***reachable,
1346cd6a6acSopenharmony_ci			     unsigned int *nreachable)
1356cd6a6acSopenharmony_ci{
1366cd6a6acSopenharmony_ci	char *start, *end = NULL;
1376cd6a6acSopenharmony_ci	char *line = NULL;
1386cd6a6acSopenharmony_ci	size_t line_len = 0, usercon_len;
1396cd6a6acSopenharmony_ci	size_t user_len = strlen(user);
1406cd6a6acSopenharmony_ci	ssize_t len;
1416cd6a6acSopenharmony_ci	int found = 0;
1426cd6a6acSopenharmony_ci	const char *fromrole, *fromtype, *fromlevel;
1436cd6a6acSopenharmony_ci	char *linerole, *linetype;
1446cd6a6acSopenharmony_ci	char **new_reachable = NULL;
1456cd6a6acSopenharmony_ci	char *usercon_str;
1466cd6a6acSopenharmony_ci	const char *usercon_str2;
1476cd6a6acSopenharmony_ci	context_t con;
1486cd6a6acSopenharmony_ci	context_t usercon;
1496cd6a6acSopenharmony_ci
1506cd6a6acSopenharmony_ci	int rc;
1516cd6a6acSopenharmony_ci
1526cd6a6acSopenharmony_ci	errno = EINVAL;
1536cd6a6acSopenharmony_ci
1546cd6a6acSopenharmony_ci	/* Extract the role and type of the fromcon for matching.
1556cd6a6acSopenharmony_ci	   User identity and MLS range can be variable. */
1566cd6a6acSopenharmony_ci	con = context_new(fromcon);
1576cd6a6acSopenharmony_ci	if (!con)
1586cd6a6acSopenharmony_ci		return -1;
1596cd6a6acSopenharmony_ci	fromrole = context_role_get(con);
1606cd6a6acSopenharmony_ci	fromtype = context_type_get(con);
1616cd6a6acSopenharmony_ci	fromlevel = context_range_get(con);
1626cd6a6acSopenharmony_ci	if (!fromrole || !fromtype) {
1636cd6a6acSopenharmony_ci		context_free(con);
1646cd6a6acSopenharmony_ci		return -1;
1656cd6a6acSopenharmony_ci	}
1666cd6a6acSopenharmony_ci
1676cd6a6acSopenharmony_ci	while ((len = getline(&line, &line_len, fp)) > 0) {
1686cd6a6acSopenharmony_ci		if (line[len - 1] == '\n')
1696cd6a6acSopenharmony_ci			line[len - 1] = 0;
1706cd6a6acSopenharmony_ci
1716cd6a6acSopenharmony_ci		/* Skip leading whitespace. */
1726cd6a6acSopenharmony_ci		start = line;
1736cd6a6acSopenharmony_ci		while (*start && isspace(*start))
1746cd6a6acSopenharmony_ci			start++;
1756cd6a6acSopenharmony_ci		if (!(*start))
1766cd6a6acSopenharmony_ci			continue;
1776cd6a6acSopenharmony_ci
1786cd6a6acSopenharmony_ci		/* Find the end of the (partial) fromcon in the line. */
1796cd6a6acSopenharmony_ci		end = start;
1806cd6a6acSopenharmony_ci		while (*end && !isspace(*end))
1816cd6a6acSopenharmony_ci			end++;
1826cd6a6acSopenharmony_ci		if (!(*end))
1836cd6a6acSopenharmony_ci			continue;
1846cd6a6acSopenharmony_ci
1856cd6a6acSopenharmony_ci		/* Check for a match. */
1866cd6a6acSopenharmony_ci		linerole = start;
1876cd6a6acSopenharmony_ci		while (*start && !isspace(*start) && *start != ':')
1886cd6a6acSopenharmony_ci			start++;
1896cd6a6acSopenharmony_ci		if (*start != ':')
1906cd6a6acSopenharmony_ci			continue;
1916cd6a6acSopenharmony_ci		*start = 0;
1926cd6a6acSopenharmony_ci		linetype = ++start;
1936cd6a6acSopenharmony_ci		while (*start && !isspace(*start) && *start != ':')
1946cd6a6acSopenharmony_ci			start++;
1956cd6a6acSopenharmony_ci		if (!(*start))
1966cd6a6acSopenharmony_ci			continue;
1976cd6a6acSopenharmony_ci		*start = 0;
1986cd6a6acSopenharmony_ci		if (!strcmp(fromrole, linerole) && !strcmp(fromtype, linetype)) {
1996cd6a6acSopenharmony_ci			found = 1;
2006cd6a6acSopenharmony_ci			break;
2016cd6a6acSopenharmony_ci		}
2026cd6a6acSopenharmony_ci	}
2036cd6a6acSopenharmony_ci
2046cd6a6acSopenharmony_ci	if (!found) {
2056cd6a6acSopenharmony_ci		errno = ENOENT;
2066cd6a6acSopenharmony_ci		rc = -1;
2076cd6a6acSopenharmony_ci		goto out;
2086cd6a6acSopenharmony_ci	}
2096cd6a6acSopenharmony_ci
2106cd6a6acSopenharmony_ci	start = ++end;
2116cd6a6acSopenharmony_ci	while (*start) {
2126cd6a6acSopenharmony_ci		/* Skip leading whitespace */
2136cd6a6acSopenharmony_ci		while (*start && isspace(*start))
2146cd6a6acSopenharmony_ci			start++;
2156cd6a6acSopenharmony_ci		if (!(*start))
2166cd6a6acSopenharmony_ci			break;
2176cd6a6acSopenharmony_ci
2186cd6a6acSopenharmony_ci		/* Find the end of this partial context. */
2196cd6a6acSopenharmony_ci		end = start;
2206cd6a6acSopenharmony_ci		while (*end && !isspace(*end))
2216cd6a6acSopenharmony_ci			end++;
2226cd6a6acSopenharmony_ci		if (*end)
2236cd6a6acSopenharmony_ci			*end++ = 0;
2246cd6a6acSopenharmony_ci
2256cd6a6acSopenharmony_ci		/* Check whether a new context is valid */
2266cd6a6acSopenharmony_ci		if (SIZE_MAX - user_len < strlen(start) + 2) {
2276cd6a6acSopenharmony_ci			fprintf(stderr, "%s: one of partial contexts is too big\n", __FUNCTION__);
2286cd6a6acSopenharmony_ci			errno = EINVAL;
2296cd6a6acSopenharmony_ci			rc = -1;
2306cd6a6acSopenharmony_ci			goto out;
2316cd6a6acSopenharmony_ci		}
2326cd6a6acSopenharmony_ci		usercon_len = user_len + strlen(start) + 2;
2336cd6a6acSopenharmony_ci		usercon_str = malloc(usercon_len);
2346cd6a6acSopenharmony_ci		if (!usercon_str) {
2356cd6a6acSopenharmony_ci			rc = -1;
2366cd6a6acSopenharmony_ci			goto out;
2376cd6a6acSopenharmony_ci		}
2386cd6a6acSopenharmony_ci
2396cd6a6acSopenharmony_ci		/* set range from fromcon in the new usercon */
2406cd6a6acSopenharmony_ci		snprintf(usercon_str, usercon_len, "%s:%s", user, start);
2416cd6a6acSopenharmony_ci		usercon = context_new(usercon_str);
2426cd6a6acSopenharmony_ci		if (!usercon) {
2436cd6a6acSopenharmony_ci			if (errno != EINVAL) {
2446cd6a6acSopenharmony_ci				free(usercon_str);
2456cd6a6acSopenharmony_ci				rc = -1;
2466cd6a6acSopenharmony_ci				goto out;
2476cd6a6acSopenharmony_ci			}
2486cd6a6acSopenharmony_ci			fprintf(stderr,
2496cd6a6acSopenharmony_ci				"%s: can't create a context from %s, skipping\n",
2506cd6a6acSopenharmony_ci				__FUNCTION__, usercon_str);
2516cd6a6acSopenharmony_ci			free(usercon_str);
2526cd6a6acSopenharmony_ci			start = end;
2536cd6a6acSopenharmony_ci			continue;
2546cd6a6acSopenharmony_ci		}
2556cd6a6acSopenharmony_ci		free(usercon_str);
2566cd6a6acSopenharmony_ci		if (context_range_set(usercon, fromlevel) != 0) {
2576cd6a6acSopenharmony_ci			context_free(usercon);
2586cd6a6acSopenharmony_ci			rc = -1;
2596cd6a6acSopenharmony_ci			goto out;
2606cd6a6acSopenharmony_ci		}
2616cd6a6acSopenharmony_ci		usercon_str2 = context_str(usercon);
2626cd6a6acSopenharmony_ci		if (!usercon_str2) {
2636cd6a6acSopenharmony_ci			context_free(usercon);
2646cd6a6acSopenharmony_ci			rc = -1;
2656cd6a6acSopenharmony_ci			goto out;
2666cd6a6acSopenharmony_ci		}
2676cd6a6acSopenharmony_ci
2686cd6a6acSopenharmony_ci		/* check whether usercon is already in reachable */
2696cd6a6acSopenharmony_ci		if (is_in_reachable(*reachable, usercon_str2)) {
2706cd6a6acSopenharmony_ci			context_free(usercon);
2716cd6a6acSopenharmony_ci			start = end;
2726cd6a6acSopenharmony_ci			continue;
2736cd6a6acSopenharmony_ci		}
2746cd6a6acSopenharmony_ci		if (security_check_context(usercon_str2) == 0) {
2756cd6a6acSopenharmony_ci			new_reachable = realloc(*reachable, (*nreachable + 2) * sizeof(char *));
2766cd6a6acSopenharmony_ci			if (!new_reachable) {
2776cd6a6acSopenharmony_ci				context_free(usercon);
2786cd6a6acSopenharmony_ci				rc = -1;
2796cd6a6acSopenharmony_ci				goto out;
2806cd6a6acSopenharmony_ci			}
2816cd6a6acSopenharmony_ci			*reachable = new_reachable;
2826cd6a6acSopenharmony_ci			new_reachable[*nreachable] = strdup(usercon_str2);
2836cd6a6acSopenharmony_ci			if (new_reachable[*nreachable] == NULL) {
2846cd6a6acSopenharmony_ci				context_free(usercon);
2856cd6a6acSopenharmony_ci				rc = -1;
2866cd6a6acSopenharmony_ci				goto out;
2876cd6a6acSopenharmony_ci			}
2886cd6a6acSopenharmony_ci			new_reachable[*nreachable + 1] = 0;
2896cd6a6acSopenharmony_ci			*nreachable += 1;
2906cd6a6acSopenharmony_ci		}
2916cd6a6acSopenharmony_ci		context_free(usercon);
2926cd6a6acSopenharmony_ci		start = end;
2936cd6a6acSopenharmony_ci	}
2946cd6a6acSopenharmony_ci	rc = 0;
2956cd6a6acSopenharmony_ci
2966cd6a6acSopenharmony_ci      out:
2976cd6a6acSopenharmony_ci	context_free(con);
2986cd6a6acSopenharmony_ci	free(line);
2996cd6a6acSopenharmony_ci	return rc;
3006cd6a6acSopenharmony_ci}
3016cd6a6acSopenharmony_ci
3026cd6a6acSopenharmony_cistatic int get_failsafe_context(const char *user, char ** newcon)
3036cd6a6acSopenharmony_ci{
3046cd6a6acSopenharmony_ci	FILE *fp;
3056cd6a6acSopenharmony_ci	char buf[255], *ptr;
3066cd6a6acSopenharmony_ci	size_t plen, nlen;
3076cd6a6acSopenharmony_ci	int rc;
3086cd6a6acSopenharmony_ci
3096cd6a6acSopenharmony_ci	fp = fopen(selinux_failsafe_context_path(), "re");
3106cd6a6acSopenharmony_ci	if (!fp)
3116cd6a6acSopenharmony_ci		return -1;
3126cd6a6acSopenharmony_ci
3136cd6a6acSopenharmony_ci	ptr = fgets_unlocked(buf, sizeof buf, fp);
3146cd6a6acSopenharmony_ci	fclose(fp);
3156cd6a6acSopenharmony_ci
3166cd6a6acSopenharmony_ci	if (!ptr)
3176cd6a6acSopenharmony_ci		return -1;
3186cd6a6acSopenharmony_ci	plen = strlen(ptr);
3196cd6a6acSopenharmony_ci	if (buf[plen - 1] == '\n')
3206cd6a6acSopenharmony_ci		buf[plen - 1] = 0;
3216cd6a6acSopenharmony_ci
3226cd6a6acSopenharmony_ci	nlen = strlen(user) + 1 + plen + 1;
3236cd6a6acSopenharmony_ci	*newcon = malloc(nlen);
3246cd6a6acSopenharmony_ci	if (!(*newcon))
3256cd6a6acSopenharmony_ci		return -1;
3266cd6a6acSopenharmony_ci	rc = snprintf(*newcon, nlen, "%s:%s", user, ptr);
3276cd6a6acSopenharmony_ci	if (rc < 0 || (size_t) rc >= nlen) {
3286cd6a6acSopenharmony_ci		free(*newcon);
3296cd6a6acSopenharmony_ci		*newcon = 0;
3306cd6a6acSopenharmony_ci		return -1;
3316cd6a6acSopenharmony_ci	}
3326cd6a6acSopenharmony_ci
3336cd6a6acSopenharmony_ci	/* If possible, check the context to catch
3346cd6a6acSopenharmony_ci	   errors early rather than waiting until the
3356cd6a6acSopenharmony_ci	   caller tries to use setexeccon on the context.
3366cd6a6acSopenharmony_ci	   But this may not always be possible, e.g. if
3376cd6a6acSopenharmony_ci	   selinuxfs isn't mounted. */
3386cd6a6acSopenharmony_ci	if (security_check_context(*newcon) && errno != ENOENT) {
3396cd6a6acSopenharmony_ci		free(*newcon);
3406cd6a6acSopenharmony_ci		*newcon = 0;
3416cd6a6acSopenharmony_ci		return -1;
3426cd6a6acSopenharmony_ci	}
3436cd6a6acSopenharmony_ci
3446cd6a6acSopenharmony_ci	return 0;
3456cd6a6acSopenharmony_ci}
3466cd6a6acSopenharmony_ci
3476cd6a6acSopenharmony_ciint get_ordered_context_list_with_level(const char *user,
3486cd6a6acSopenharmony_ci					const char *level,
3496cd6a6acSopenharmony_ci					const char *fromcon,
3506cd6a6acSopenharmony_ci					char *** list)
3516cd6a6acSopenharmony_ci{
3526cd6a6acSopenharmony_ci	int rc;
3536cd6a6acSopenharmony_ci	char *backup_fromcon = NULL;
3546cd6a6acSopenharmony_ci	context_t con;
3556cd6a6acSopenharmony_ci	const char *newfromcon;
3566cd6a6acSopenharmony_ci
3576cd6a6acSopenharmony_ci	if (!level)
3586cd6a6acSopenharmony_ci		return get_ordered_context_list(user, fromcon, list);
3596cd6a6acSopenharmony_ci
3606cd6a6acSopenharmony_ci	if (!fromcon) {
3616cd6a6acSopenharmony_ci		rc = getcon(&backup_fromcon);
3626cd6a6acSopenharmony_ci		if (rc < 0)
3636cd6a6acSopenharmony_ci			return rc;
3646cd6a6acSopenharmony_ci		fromcon = backup_fromcon;
3656cd6a6acSopenharmony_ci	}
3666cd6a6acSopenharmony_ci
3676cd6a6acSopenharmony_ci	rc = -1;
3686cd6a6acSopenharmony_ci	con = context_new(fromcon);
3696cd6a6acSopenharmony_ci	if (!con)
3706cd6a6acSopenharmony_ci		goto out;
3716cd6a6acSopenharmony_ci
3726cd6a6acSopenharmony_ci	if (context_range_set(con, level))
3736cd6a6acSopenharmony_ci		goto out;
3746cd6a6acSopenharmony_ci
3756cd6a6acSopenharmony_ci	newfromcon = context_str(con);
3766cd6a6acSopenharmony_ci	if (!newfromcon)
3776cd6a6acSopenharmony_ci		goto out;
3786cd6a6acSopenharmony_ci
3796cd6a6acSopenharmony_ci	rc = get_ordered_context_list(user, newfromcon, list);
3806cd6a6acSopenharmony_ci
3816cd6a6acSopenharmony_ci      out:
3826cd6a6acSopenharmony_ci	context_free(con);
3836cd6a6acSopenharmony_ci	freecon(backup_fromcon);
3846cd6a6acSopenharmony_ci	return rc;
3856cd6a6acSopenharmony_ci}
3866cd6a6acSopenharmony_ci
3876cd6a6acSopenharmony_ci
3886cd6a6acSopenharmony_ciint get_default_context_with_level(const char *user,
3896cd6a6acSopenharmony_ci				   const char *level,
3906cd6a6acSopenharmony_ci				   const char *fromcon,
3916cd6a6acSopenharmony_ci				   char ** newcon)
3926cd6a6acSopenharmony_ci{
3936cd6a6acSopenharmony_ci	char **conary;
3946cd6a6acSopenharmony_ci	int rc;
3956cd6a6acSopenharmony_ci
3966cd6a6acSopenharmony_ci	rc = get_ordered_context_list_with_level(user, level, fromcon, &conary);
3976cd6a6acSopenharmony_ci	if (rc <= 0)
3986cd6a6acSopenharmony_ci		return -1;
3996cd6a6acSopenharmony_ci
4006cd6a6acSopenharmony_ci	*newcon = strdup(conary[0]);
4016cd6a6acSopenharmony_ci	freeconary(conary);
4026cd6a6acSopenharmony_ci	if (!(*newcon))
4036cd6a6acSopenharmony_ci		return -1;
4046cd6a6acSopenharmony_ci	return 0;
4056cd6a6acSopenharmony_ci}
4066cd6a6acSopenharmony_ci
4076cd6a6acSopenharmony_ciint get_ordered_context_list(const char *user,
4086cd6a6acSopenharmony_ci			     const char *fromcon,
4096cd6a6acSopenharmony_ci			     char *** list)
4106cd6a6acSopenharmony_ci{
4116cd6a6acSopenharmony_ci	char **reachable = NULL;
4126cd6a6acSopenharmony_ci	int rc = 0;
4136cd6a6acSopenharmony_ci	unsigned nreachable = 0;
4146cd6a6acSopenharmony_ci	char *backup_fromcon = NULL;
4156cd6a6acSopenharmony_ci	FILE *fp;
4166cd6a6acSopenharmony_ci	char *fname = NULL;
4176cd6a6acSopenharmony_ci	size_t fname_len;
4186cd6a6acSopenharmony_ci	const char *user_contexts_path = selinux_user_contexts_path();
4196cd6a6acSopenharmony_ci
4206cd6a6acSopenharmony_ci	if (!fromcon) {
4216cd6a6acSopenharmony_ci		/* Get the current context and use it for the starting context */
4226cd6a6acSopenharmony_ci		rc = getcon(&backup_fromcon);
4236cd6a6acSopenharmony_ci		if (rc < 0)
4246cd6a6acSopenharmony_ci			return rc;
4256cd6a6acSopenharmony_ci		fromcon = backup_fromcon;
4266cd6a6acSopenharmony_ci	}
4276cd6a6acSopenharmony_ci
4286cd6a6acSopenharmony_ci	/* Determine the ordering to apply from the optional per-user config
4296cd6a6acSopenharmony_ci	   and from the global config. */
4306cd6a6acSopenharmony_ci	fname_len = strlen(user_contexts_path) + strlen(user) + 2;
4316cd6a6acSopenharmony_ci	fname = malloc(fname_len);
4326cd6a6acSopenharmony_ci	if (!fname)
4336cd6a6acSopenharmony_ci		goto failsafe;
4346cd6a6acSopenharmony_ci	snprintf(fname, fname_len, "%s%s", user_contexts_path, user);
4356cd6a6acSopenharmony_ci	fp = fopen(fname, "re");
4366cd6a6acSopenharmony_ci	if (fp) {
4376cd6a6acSopenharmony_ci		__fsetlocking(fp, FSETLOCKING_BYCALLER);
4386cd6a6acSopenharmony_ci		rc = get_context_user(fp, fromcon, user, &reachable, &nreachable);
4396cd6a6acSopenharmony_ci
4406cd6a6acSopenharmony_ci		fclose(fp);
4416cd6a6acSopenharmony_ci		if (rc < 0 && errno != ENOENT) {
4426cd6a6acSopenharmony_ci			fprintf(stderr,
4436cd6a6acSopenharmony_ci				"%s:  error in processing configuration file %s\n",
4446cd6a6acSopenharmony_ci				__FUNCTION__, fname);
4456cd6a6acSopenharmony_ci			/* Fall through, try global config */
4466cd6a6acSopenharmony_ci		}
4476cd6a6acSopenharmony_ci	}
4486cd6a6acSopenharmony_ci	free(fname);
4496cd6a6acSopenharmony_ci	fp = fopen(selinux_default_context_path(), "re");
4506cd6a6acSopenharmony_ci	if (fp) {
4516cd6a6acSopenharmony_ci		__fsetlocking(fp, FSETLOCKING_BYCALLER);
4526cd6a6acSopenharmony_ci		rc = get_context_user(fp, fromcon, user, &reachable, &nreachable);
4536cd6a6acSopenharmony_ci		fclose(fp);
4546cd6a6acSopenharmony_ci		if (rc < 0 && errno != ENOENT) {
4556cd6a6acSopenharmony_ci			fprintf(stderr,
4566cd6a6acSopenharmony_ci				"%s:  error in processing configuration file %s\n",
4576cd6a6acSopenharmony_ci				__FUNCTION__, selinux_default_context_path());
4586cd6a6acSopenharmony_ci			/* Fall through */
4596cd6a6acSopenharmony_ci		}
4606cd6a6acSopenharmony_ci	}
4616cd6a6acSopenharmony_ci
4626cd6a6acSopenharmony_ci	if (!nreachable)
4636cd6a6acSopenharmony_ci		goto failsafe;
4646cd6a6acSopenharmony_ci
4656cd6a6acSopenharmony_ci      out:
4666cd6a6acSopenharmony_ci	if (nreachable > 0) {
4676cd6a6acSopenharmony_ci		*list = reachable;
4686cd6a6acSopenharmony_ci		rc = nreachable;
4696cd6a6acSopenharmony_ci	}
4706cd6a6acSopenharmony_ci	else
4716cd6a6acSopenharmony_ci		freeconary(reachable);
4726cd6a6acSopenharmony_ci
4736cd6a6acSopenharmony_ci	freecon(backup_fromcon);
4746cd6a6acSopenharmony_ci
4756cd6a6acSopenharmony_ci	return rc;
4766cd6a6acSopenharmony_ci
4776cd6a6acSopenharmony_ci      failsafe:
4786cd6a6acSopenharmony_ci	/* Unable to determine a reachable context list, try to fall back to
4796cd6a6acSopenharmony_ci	   the "failsafe" context to at least permit root login
4806cd6a6acSopenharmony_ci	   for emergency recovery if possible. */
4816cd6a6acSopenharmony_ci	freeconary(reachable);
4826cd6a6acSopenharmony_ci	reachable = malloc(2 * sizeof(char *));
4836cd6a6acSopenharmony_ci	if (!reachable) {
4846cd6a6acSopenharmony_ci		rc = -1;
4856cd6a6acSopenharmony_ci		goto out;
4866cd6a6acSopenharmony_ci	}
4876cd6a6acSopenharmony_ci	reachable[0] = reachable[1] = 0;
4886cd6a6acSopenharmony_ci	rc = get_failsafe_context(user, &reachable[0]);
4896cd6a6acSopenharmony_ci	if (rc < 0) {
4906cd6a6acSopenharmony_ci		freeconary(reachable);
4916cd6a6acSopenharmony_ci		reachable = NULL;
4926cd6a6acSopenharmony_ci		goto out;
4936cd6a6acSopenharmony_ci	}
4946cd6a6acSopenharmony_ci	nreachable = 1;			/* one context in the list */
4956cd6a6acSopenharmony_ci	goto out;
4966cd6a6acSopenharmony_ci}
4976cd6a6acSopenharmony_ci
498