16cd6a6acSopenharmony_ci#include <sys/stat.h>
26cd6a6acSopenharmony_ci#include <string.h>
36cd6a6acSopenharmony_ci#include <errno.h>
46cd6a6acSopenharmony_ci#include <stdio.h>
56cd6a6acSopenharmony_ci#include "selinux_internal.h"
66cd6a6acSopenharmony_ci#include "label_internal.h"
76cd6a6acSopenharmony_ci#include "callbacks.h"
86cd6a6acSopenharmony_ci#include <limits.h>
96cd6a6acSopenharmony_ci
106cd6a6acSopenharmony_cistatic int (*myinvalidcon) (const char *p, unsigned l, char *c) = NULL;
116cd6a6acSopenharmony_cistatic int (*mycanoncon) (const char *p, unsigned l, char **c) =  NULL;
126cd6a6acSopenharmony_ci
136cd6a6acSopenharmony_cistatic void
146cd6a6acSopenharmony_ci#ifdef __GNUC__
156cd6a6acSopenharmony_ci    __attribute__ ((format(printf, 1, 2)))
166cd6a6acSopenharmony_ci#endif
176cd6a6acSopenharmony_ci    default_printf(const char *fmt, ...)
186cd6a6acSopenharmony_ci{
196cd6a6acSopenharmony_ci	va_list ap;
206cd6a6acSopenharmony_ci	va_start(ap, fmt);
216cd6a6acSopenharmony_ci	vfprintf(stderr, fmt, ap);
226cd6a6acSopenharmony_ci	va_end(ap);
236cd6a6acSopenharmony_ci}
246cd6a6acSopenharmony_ci
256cd6a6acSopenharmony_civoid
266cd6a6acSopenharmony_ci#ifdef __GNUC__
276cd6a6acSopenharmony_ci    __attribute__ ((format(printf, 1, 2)))
286cd6a6acSopenharmony_ci#endif
296cd6a6acSopenharmony_ci    (*myprintf) (const char *fmt,...) = &default_printf;
306cd6a6acSopenharmony_ciint myprintf_compat = 0;
316cd6a6acSopenharmony_ci
326cd6a6acSopenharmony_civoid set_matchpathcon_printf(void (*f) (const char *fmt, ...))
336cd6a6acSopenharmony_ci{
346cd6a6acSopenharmony_ci	myprintf = f ? f : &default_printf;
356cd6a6acSopenharmony_ci	myprintf_compat = 1;
366cd6a6acSopenharmony_ci}
376cd6a6acSopenharmony_ci
386cd6a6acSopenharmony_ciint compat_validate(struct selabel_handle *rec,
396cd6a6acSopenharmony_ci		    struct selabel_lookup_rec *contexts,
406cd6a6acSopenharmony_ci		    const char *path, unsigned lineno)
416cd6a6acSopenharmony_ci{
426cd6a6acSopenharmony_ci	int rc;
436cd6a6acSopenharmony_ci	char **ctx = &contexts->ctx_raw;
446cd6a6acSopenharmony_ci
456cd6a6acSopenharmony_ci	if (myinvalidcon)
466cd6a6acSopenharmony_ci		rc = myinvalidcon(path, lineno, *ctx);
476cd6a6acSopenharmony_ci	else if (mycanoncon)
486cd6a6acSopenharmony_ci		rc = mycanoncon(path, lineno, ctx);
496cd6a6acSopenharmony_ci	else {
506cd6a6acSopenharmony_ci		rc = selabel_validate(rec, contexts);
516cd6a6acSopenharmony_ci		if (rc < 0) {
526cd6a6acSopenharmony_ci			if (lineno) {
536cd6a6acSopenharmony_ci				COMPAT_LOG(SELINUX_WARNING,
546cd6a6acSopenharmony_ci					    "%s: line %u has invalid context %s\n",
556cd6a6acSopenharmony_ci						path, lineno, *ctx);
566cd6a6acSopenharmony_ci			} else {
576cd6a6acSopenharmony_ci				COMPAT_LOG(SELINUX_WARNING,
586cd6a6acSopenharmony_ci					    "%s: has invalid context %s\n", path, *ctx);
596cd6a6acSopenharmony_ci			}
606cd6a6acSopenharmony_ci		}
616cd6a6acSopenharmony_ci	}
626cd6a6acSopenharmony_ci
636cd6a6acSopenharmony_ci	return rc ? -1 : 0;
646cd6a6acSopenharmony_ci}
656cd6a6acSopenharmony_ci
666cd6a6acSopenharmony_ci#ifndef BUILD_HOST
676cd6a6acSopenharmony_ci
686cd6a6acSopenharmony_cistatic __thread struct selabel_handle *hnd;
696cd6a6acSopenharmony_ci
706cd6a6acSopenharmony_ci/*
716cd6a6acSopenharmony_ci * An array for mapping integers to contexts
726cd6a6acSopenharmony_ci */
736cd6a6acSopenharmony_cistatic __thread char **con_array;
746cd6a6acSopenharmony_cistatic __thread int con_array_size;
756cd6a6acSopenharmony_cistatic __thread int con_array_used;
766cd6a6acSopenharmony_ci
776cd6a6acSopenharmony_cistatic pthread_once_t once = PTHREAD_ONCE_INIT;
786cd6a6acSopenharmony_cistatic pthread_key_t destructor_key;
796cd6a6acSopenharmony_cistatic int destructor_key_initialized = 0;
806cd6a6acSopenharmony_ci
816cd6a6acSopenharmony_cistatic void free_array_elts(void)
826cd6a6acSopenharmony_ci{
836cd6a6acSopenharmony_ci	int i;
846cd6a6acSopenharmony_ci	for (i = 0; i < con_array_used; i++)
856cd6a6acSopenharmony_ci		free(con_array[i]);
866cd6a6acSopenharmony_ci	free(con_array);
876cd6a6acSopenharmony_ci
886cd6a6acSopenharmony_ci	con_array_size = con_array_used = 0;
896cd6a6acSopenharmony_ci	con_array = NULL;
906cd6a6acSopenharmony_ci}
916cd6a6acSopenharmony_ci
926cd6a6acSopenharmony_cistatic int add_array_elt(char *con)
936cd6a6acSopenharmony_ci{
946cd6a6acSopenharmony_ci	char **tmp;
956cd6a6acSopenharmony_ci	if (con_array_size) {
966cd6a6acSopenharmony_ci		while (con_array_used >= con_array_size) {
976cd6a6acSopenharmony_ci			con_array_size *= 2;
986cd6a6acSopenharmony_ci			tmp = (char **)realloc(con_array, sizeof(char*) *
996cd6a6acSopenharmony_ci						     con_array_size);
1006cd6a6acSopenharmony_ci			if (!tmp) {
1016cd6a6acSopenharmony_ci				free_array_elts();
1026cd6a6acSopenharmony_ci				return -1;
1036cd6a6acSopenharmony_ci			}
1046cd6a6acSopenharmony_ci			con_array = tmp;
1056cd6a6acSopenharmony_ci		}
1066cd6a6acSopenharmony_ci	} else {
1076cd6a6acSopenharmony_ci		con_array_size = 1000;
1086cd6a6acSopenharmony_ci		con_array = (char **)malloc(sizeof(char*) * con_array_size);
1096cd6a6acSopenharmony_ci		if (!con_array) {
1106cd6a6acSopenharmony_ci			con_array_size = con_array_used = 0;
1116cd6a6acSopenharmony_ci			return -1;
1126cd6a6acSopenharmony_ci		}
1136cd6a6acSopenharmony_ci	}
1146cd6a6acSopenharmony_ci
1156cd6a6acSopenharmony_ci	con_array[con_array_used] = strdup(con);
1166cd6a6acSopenharmony_ci	if (!con_array[con_array_used])
1176cd6a6acSopenharmony_ci		return -1;
1186cd6a6acSopenharmony_ci	return con_array_used++;
1196cd6a6acSopenharmony_ci}
1206cd6a6acSopenharmony_ci
1216cd6a6acSopenharmony_civoid set_matchpathcon_invalidcon(int (*f) (const char *p, unsigned l, char *c))
1226cd6a6acSopenharmony_ci{
1236cd6a6acSopenharmony_ci	myinvalidcon = f;
1246cd6a6acSopenharmony_ci}
1256cd6a6acSopenharmony_ci
1266cd6a6acSopenharmony_cistatic int default_canoncon(const char *path, unsigned lineno, char **context)
1276cd6a6acSopenharmony_ci{
1286cd6a6acSopenharmony_ci	char *tmpcon;
1296cd6a6acSopenharmony_ci	if (security_canonicalize_context_raw(*context, &tmpcon) < 0) {
1306cd6a6acSopenharmony_ci		if (errno == ENOENT)
1316cd6a6acSopenharmony_ci			return 0;
1326cd6a6acSopenharmony_ci		if (lineno)
1336cd6a6acSopenharmony_ci			myprintf("%s:  line %u has invalid context %s\n", path,
1346cd6a6acSopenharmony_ci				 lineno, *context);
1356cd6a6acSopenharmony_ci		else
1366cd6a6acSopenharmony_ci			myprintf("%s:  invalid context %s\n", path, *context);
1376cd6a6acSopenharmony_ci		return 1;
1386cd6a6acSopenharmony_ci	}
1396cd6a6acSopenharmony_ci	free(*context);
1406cd6a6acSopenharmony_ci	*context = tmpcon;
1416cd6a6acSopenharmony_ci	return 0;
1426cd6a6acSopenharmony_ci}
1436cd6a6acSopenharmony_ci
1446cd6a6acSopenharmony_civoid set_matchpathcon_canoncon(int (*f) (const char *p, unsigned l, char **c))
1456cd6a6acSopenharmony_ci{
1466cd6a6acSopenharmony_ci	if (f)
1476cd6a6acSopenharmony_ci		mycanoncon = f;
1486cd6a6acSopenharmony_ci	else
1496cd6a6acSopenharmony_ci		mycanoncon = &default_canoncon;
1506cd6a6acSopenharmony_ci}
1516cd6a6acSopenharmony_ci
1526cd6a6acSopenharmony_cistatic __thread struct selinux_opt options[SELABEL_NOPT];
1536cd6a6acSopenharmony_cistatic __thread int notrans;
1546cd6a6acSopenharmony_ci
1556cd6a6acSopenharmony_civoid set_matchpathcon_flags(unsigned int flags)
1566cd6a6acSopenharmony_ci{
1576cd6a6acSopenharmony_ci	int i;
1586cd6a6acSopenharmony_ci	memset(options, 0, sizeof(options));
1596cd6a6acSopenharmony_ci	i = SELABEL_OPT_BASEONLY;
1606cd6a6acSopenharmony_ci	options[i].type = i;
1616cd6a6acSopenharmony_ci	options[i].value = (flags & MATCHPATHCON_BASEONLY) ? (char*)1 : NULL;
1626cd6a6acSopenharmony_ci	i = SELABEL_OPT_VALIDATE;
1636cd6a6acSopenharmony_ci	options[i].type = i;
1646cd6a6acSopenharmony_ci	options[i].value = (flags & MATCHPATHCON_VALIDATE) ? (char*)1 : NULL;
1656cd6a6acSopenharmony_ci	notrans = flags & MATCHPATHCON_NOTRANS;
1666cd6a6acSopenharmony_ci}
1676cd6a6acSopenharmony_ci
1686cd6a6acSopenharmony_ci/*
1696cd6a6acSopenharmony_ci * An association between an inode and a
1706cd6a6acSopenharmony_ci * specification.
1716cd6a6acSopenharmony_ci */
1726cd6a6acSopenharmony_citypedef struct file_spec {
1736cd6a6acSopenharmony_ci	ino_t ino;		/* inode number */
1746cd6a6acSopenharmony_ci	int specind;		/* index of specification in spec */
1756cd6a6acSopenharmony_ci	char *file;		/* full pathname for diagnostic messages about conflicts */
1766cd6a6acSopenharmony_ci	struct file_spec *next;	/* next association in hash bucket chain */
1776cd6a6acSopenharmony_ci} file_spec_t;
1786cd6a6acSopenharmony_ci
1796cd6a6acSopenharmony_ci/*
1806cd6a6acSopenharmony_ci * The hash table of associations, hashed by inode number.
1816cd6a6acSopenharmony_ci * Chaining is used for collisions, with elements ordered
1826cd6a6acSopenharmony_ci * by inode number in each bucket.  Each hash bucket has a dummy
1836cd6a6acSopenharmony_ci * header.
1846cd6a6acSopenharmony_ci */
1856cd6a6acSopenharmony_ci#define HASH_BITS 16
1866cd6a6acSopenharmony_ci#define HASH_BUCKETS (1 << HASH_BITS)
1876cd6a6acSopenharmony_ci#define HASH_MASK (HASH_BUCKETS-1)
1886cd6a6acSopenharmony_cistatic file_spec_t *fl_head;
1896cd6a6acSopenharmony_ci
1906cd6a6acSopenharmony_ci/*
1916cd6a6acSopenharmony_ci * Try to add an association between an inode and
1926cd6a6acSopenharmony_ci * a specification.  If there is already an association
1936cd6a6acSopenharmony_ci * for the inode and it conflicts with this specification,
1946cd6a6acSopenharmony_ci * then use the specification that occurs later in the
1956cd6a6acSopenharmony_ci * specification array.
1966cd6a6acSopenharmony_ci */
1976cd6a6acSopenharmony_ciint matchpathcon_filespec_add(ino_t ino, int specind, const char *file)
1986cd6a6acSopenharmony_ci{
1996cd6a6acSopenharmony_ci	file_spec_t *prevfl, *fl;
2006cd6a6acSopenharmony_ci	int h, ret;
2016cd6a6acSopenharmony_ci	struct stat sb;
2026cd6a6acSopenharmony_ci
2036cd6a6acSopenharmony_ci	if (!fl_head) {
2046cd6a6acSopenharmony_ci		fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS);
2056cd6a6acSopenharmony_ci		if (!fl_head)
2066cd6a6acSopenharmony_ci			goto oom;
2076cd6a6acSopenharmony_ci		memset(fl_head, 0, sizeof(file_spec_t) * HASH_BUCKETS);
2086cd6a6acSopenharmony_ci	}
2096cd6a6acSopenharmony_ci
2106cd6a6acSopenharmony_ci	h = (ino + (ino >> HASH_BITS)) & HASH_MASK;
2116cd6a6acSopenharmony_ci	for (prevfl = &fl_head[h], fl = fl_head[h].next; fl;
2126cd6a6acSopenharmony_ci	     prevfl = fl, fl = fl->next) {
2136cd6a6acSopenharmony_ci		if (ino == fl->ino) {
2146cd6a6acSopenharmony_ci			ret = lstat(fl->file, &sb);
2156cd6a6acSopenharmony_ci			if (ret < 0 || sb.st_ino != ino) {
2166cd6a6acSopenharmony_ci				fl->specind = specind;
2176cd6a6acSopenharmony_ci				free(fl->file);
2186cd6a6acSopenharmony_ci				fl->file = strdup(file);
2196cd6a6acSopenharmony_ci				if (!fl->file)
2206cd6a6acSopenharmony_ci					goto oom;
2216cd6a6acSopenharmony_ci				return fl->specind;
2226cd6a6acSopenharmony_ci
2236cd6a6acSopenharmony_ci			}
2246cd6a6acSopenharmony_ci
2256cd6a6acSopenharmony_ci			if (!strcmp(con_array[fl->specind],
2266cd6a6acSopenharmony_ci				    con_array[specind]))
2276cd6a6acSopenharmony_ci				return fl->specind;
2286cd6a6acSopenharmony_ci
2296cd6a6acSopenharmony_ci			myprintf
2306cd6a6acSopenharmony_ci			    ("%s:  conflicting specifications for %s and %s, using %s.\n",
2316cd6a6acSopenharmony_ci			     __FUNCTION__, file, fl->file,
2326cd6a6acSopenharmony_ci			     con_array[fl->specind]);
2336cd6a6acSopenharmony_ci			free(fl->file);
2346cd6a6acSopenharmony_ci			fl->file = strdup(file);
2356cd6a6acSopenharmony_ci			if (!fl->file)
2366cd6a6acSopenharmony_ci				goto oom;
2376cd6a6acSopenharmony_ci			return fl->specind;
2386cd6a6acSopenharmony_ci		}
2396cd6a6acSopenharmony_ci
2406cd6a6acSopenharmony_ci		if (ino > fl->ino)
2416cd6a6acSopenharmony_ci			break;
2426cd6a6acSopenharmony_ci	}
2436cd6a6acSopenharmony_ci
2446cd6a6acSopenharmony_ci	fl = malloc(sizeof(file_spec_t));
2456cd6a6acSopenharmony_ci	if (!fl)
2466cd6a6acSopenharmony_ci		goto oom;
2476cd6a6acSopenharmony_ci	fl->ino = ino;
2486cd6a6acSopenharmony_ci	fl->specind = specind;
2496cd6a6acSopenharmony_ci	fl->file = strdup(file);
2506cd6a6acSopenharmony_ci	if (!fl->file)
2516cd6a6acSopenharmony_ci		goto oom_freefl;
2526cd6a6acSopenharmony_ci	fl->next = prevfl->next;
2536cd6a6acSopenharmony_ci	prevfl->next = fl;
2546cd6a6acSopenharmony_ci	return fl->specind;
2556cd6a6acSopenharmony_ci      oom_freefl:
2566cd6a6acSopenharmony_ci	free(fl);
2576cd6a6acSopenharmony_ci      oom:
2586cd6a6acSopenharmony_ci	myprintf("%s:  insufficient memory for file label entry for %s\n",
2596cd6a6acSopenharmony_ci		 __FUNCTION__, file);
2606cd6a6acSopenharmony_ci	return -1;
2616cd6a6acSopenharmony_ci}
2626cd6a6acSopenharmony_ci
2636cd6a6acSopenharmony_ci/*
2646cd6a6acSopenharmony_ci * Evaluate the association hash table distribution.
2656cd6a6acSopenharmony_ci */
2666cd6a6acSopenharmony_civoid matchpathcon_filespec_eval(void)
2676cd6a6acSopenharmony_ci{
2686cd6a6acSopenharmony_ci	file_spec_t *fl;
2696cd6a6acSopenharmony_ci	int h, used, nel, len, longest;
2706cd6a6acSopenharmony_ci
2716cd6a6acSopenharmony_ci	if (!fl_head)
2726cd6a6acSopenharmony_ci		return;
2736cd6a6acSopenharmony_ci
2746cd6a6acSopenharmony_ci	used = 0;
2756cd6a6acSopenharmony_ci	longest = 0;
2766cd6a6acSopenharmony_ci	nel = 0;
2776cd6a6acSopenharmony_ci	for (h = 0; h < HASH_BUCKETS; h++) {
2786cd6a6acSopenharmony_ci		len = 0;
2796cd6a6acSopenharmony_ci		for (fl = fl_head[h].next; fl; fl = fl->next) {
2806cd6a6acSopenharmony_ci			len++;
2816cd6a6acSopenharmony_ci		}
2826cd6a6acSopenharmony_ci		if (len)
2836cd6a6acSopenharmony_ci			used++;
2846cd6a6acSopenharmony_ci		if (len > longest)
2856cd6a6acSopenharmony_ci			longest = len;
2866cd6a6acSopenharmony_ci		nel += len;
2876cd6a6acSopenharmony_ci	}
2886cd6a6acSopenharmony_ci
2896cd6a6acSopenharmony_ci	myprintf
2906cd6a6acSopenharmony_ci	    ("%s:  hash table stats: %d elements, %d/%d buckets used, longest chain length %d\n",
2916cd6a6acSopenharmony_ci	     __FUNCTION__, nel, used, HASH_BUCKETS, longest);
2926cd6a6acSopenharmony_ci}
2936cd6a6acSopenharmony_ci
2946cd6a6acSopenharmony_ci/*
2956cd6a6acSopenharmony_ci * Destroy the association hash table.
2966cd6a6acSopenharmony_ci */
2976cd6a6acSopenharmony_civoid matchpathcon_filespec_destroy(void)
2986cd6a6acSopenharmony_ci{
2996cd6a6acSopenharmony_ci	file_spec_t *fl, *tmp;
3006cd6a6acSopenharmony_ci	int h;
3016cd6a6acSopenharmony_ci
3026cd6a6acSopenharmony_ci	free_array_elts();
3036cd6a6acSopenharmony_ci
3046cd6a6acSopenharmony_ci	if (!fl_head)
3056cd6a6acSopenharmony_ci		return;
3066cd6a6acSopenharmony_ci
3076cd6a6acSopenharmony_ci	for (h = 0; h < HASH_BUCKETS; h++) {
3086cd6a6acSopenharmony_ci		fl = fl_head[h].next;
3096cd6a6acSopenharmony_ci		while (fl) {
3106cd6a6acSopenharmony_ci			tmp = fl;
3116cd6a6acSopenharmony_ci			fl = fl->next;
3126cd6a6acSopenharmony_ci			free(tmp->file);
3136cd6a6acSopenharmony_ci			free(tmp);
3146cd6a6acSopenharmony_ci		}
3156cd6a6acSopenharmony_ci		fl_head[h].next = NULL;
3166cd6a6acSopenharmony_ci	}
3176cd6a6acSopenharmony_ci	free(fl_head);
3186cd6a6acSopenharmony_ci	fl_head = NULL;
3196cd6a6acSopenharmony_ci}
3206cd6a6acSopenharmony_ci
3216cd6a6acSopenharmony_cistatic void matchpathcon_fini_internal(void)
3226cd6a6acSopenharmony_ci{
3236cd6a6acSopenharmony_ci	free_array_elts();
3246cd6a6acSopenharmony_ci
3256cd6a6acSopenharmony_ci	if (hnd) {
3266cd6a6acSopenharmony_ci		selabel_close(hnd);
3276cd6a6acSopenharmony_ci		hnd = NULL;
3286cd6a6acSopenharmony_ci	}
3296cd6a6acSopenharmony_ci}
3306cd6a6acSopenharmony_ci
3316cd6a6acSopenharmony_cistatic void matchpathcon_thread_destructor(void __attribute__((unused)) *ptr)
3326cd6a6acSopenharmony_ci{
3336cd6a6acSopenharmony_ci	matchpathcon_fini_internal();
3346cd6a6acSopenharmony_ci}
3356cd6a6acSopenharmony_ci
3366cd6a6acSopenharmony_civoid __attribute__((destructor)) matchpathcon_lib_destructor(void);
3376cd6a6acSopenharmony_ci
3386cd6a6acSopenharmony_civoid  __attribute__((destructor)) matchpathcon_lib_destructor(void)
3396cd6a6acSopenharmony_ci{
3406cd6a6acSopenharmony_ci	if (destructor_key_initialized)
3416cd6a6acSopenharmony_ci		__selinux_key_delete(destructor_key);
3426cd6a6acSopenharmony_ci}
3436cd6a6acSopenharmony_ci
3446cd6a6acSopenharmony_cistatic void matchpathcon_init_once(void)
3456cd6a6acSopenharmony_ci{
3466cd6a6acSopenharmony_ci	if (__selinux_key_create(&destructor_key, matchpathcon_thread_destructor) == 0)
3476cd6a6acSopenharmony_ci		destructor_key_initialized = 1;
3486cd6a6acSopenharmony_ci}
3496cd6a6acSopenharmony_ci
3506cd6a6acSopenharmony_ciint matchpathcon_init_prefix(const char *path, const char *subset)
3516cd6a6acSopenharmony_ci{
3526cd6a6acSopenharmony_ci	if (!mycanoncon)
3536cd6a6acSopenharmony_ci		mycanoncon = default_canoncon;
3546cd6a6acSopenharmony_ci
3556cd6a6acSopenharmony_ci	__selinux_once(once, matchpathcon_init_once);
3566cd6a6acSopenharmony_ci	__selinux_setspecific(destructor_key, /* some valid address to please GCC */ &selinux_page_size);
3576cd6a6acSopenharmony_ci
3586cd6a6acSopenharmony_ci	options[SELABEL_OPT_SUBSET].type = SELABEL_OPT_SUBSET;
3596cd6a6acSopenharmony_ci	options[SELABEL_OPT_SUBSET].value = subset;
3606cd6a6acSopenharmony_ci	options[SELABEL_OPT_PATH].type = SELABEL_OPT_PATH;
3616cd6a6acSopenharmony_ci	options[SELABEL_OPT_PATH].value = path;
3626cd6a6acSopenharmony_ci
3636cd6a6acSopenharmony_ci	hnd = selabel_open(SELABEL_CTX_FILE, options, SELABEL_NOPT);
3646cd6a6acSopenharmony_ci	return hnd ? 0 : -1;
3656cd6a6acSopenharmony_ci}
3666cd6a6acSopenharmony_ci
3676cd6a6acSopenharmony_ci
3686cd6a6acSopenharmony_ciint matchpathcon_init(const char *path)
3696cd6a6acSopenharmony_ci{
3706cd6a6acSopenharmony_ci	return matchpathcon_init_prefix(path, NULL);
3716cd6a6acSopenharmony_ci}
3726cd6a6acSopenharmony_ci
3736cd6a6acSopenharmony_civoid matchpathcon_fini(void)
3746cd6a6acSopenharmony_ci{
3756cd6a6acSopenharmony_ci	matchpathcon_fini_internal();
3766cd6a6acSopenharmony_ci}
3776cd6a6acSopenharmony_ci
3786cd6a6acSopenharmony_ci/*
3796cd6a6acSopenharmony_ci * We do not want to resolve a symlink to a real path if it is the final
3806cd6a6acSopenharmony_ci * component of the name.  Thus we split the pathname on the last "/" and
3816cd6a6acSopenharmony_ci * determine a real path component of the first portion.  We then have to
3826cd6a6acSopenharmony_ci * copy the last part back on to get the final real path.  Wheww.
3836cd6a6acSopenharmony_ci */
3846cd6a6acSopenharmony_ciint realpath_not_final(const char *name, char *resolved_path)
3856cd6a6acSopenharmony_ci{
3866cd6a6acSopenharmony_ci	char *last_component;
3876cd6a6acSopenharmony_ci	char *tmp_path, *p;
3886cd6a6acSopenharmony_ci	size_t len = 0;
3896cd6a6acSopenharmony_ci	int rc = 0;
3906cd6a6acSopenharmony_ci
3916cd6a6acSopenharmony_ci	tmp_path = strdup(name);
3926cd6a6acSopenharmony_ci	if (!tmp_path) {
3936cd6a6acSopenharmony_ci		myprintf("symlink_realpath(%s) strdup() failed: %m\n",
3946cd6a6acSopenharmony_ci			name);
3956cd6a6acSopenharmony_ci		rc = -1;
3966cd6a6acSopenharmony_ci		goto out;
3976cd6a6acSopenharmony_ci	}
3986cd6a6acSopenharmony_ci
3996cd6a6acSopenharmony_ci	last_component = strrchr(tmp_path, '/');
4006cd6a6acSopenharmony_ci
4016cd6a6acSopenharmony_ci	if (last_component == tmp_path) {
4026cd6a6acSopenharmony_ci		last_component++;
4036cd6a6acSopenharmony_ci		p = strcpy(resolved_path, "");
4046cd6a6acSopenharmony_ci	} else if (last_component) {
4056cd6a6acSopenharmony_ci		*last_component = '\0';
4066cd6a6acSopenharmony_ci		last_component++;
4076cd6a6acSopenharmony_ci		p = realpath(tmp_path, resolved_path);
4086cd6a6acSopenharmony_ci	} else {
4096cd6a6acSopenharmony_ci		last_component = tmp_path;
4106cd6a6acSopenharmony_ci		p = realpath("./", resolved_path);
4116cd6a6acSopenharmony_ci	}
4126cd6a6acSopenharmony_ci
4136cd6a6acSopenharmony_ci	if (!p) {
4146cd6a6acSopenharmony_ci		myprintf("symlink_realpath(%s) realpath() failed: %m\n",
4156cd6a6acSopenharmony_ci			name);
4166cd6a6acSopenharmony_ci		rc = -1;
4176cd6a6acSopenharmony_ci		goto out;
4186cd6a6acSopenharmony_ci	}
4196cd6a6acSopenharmony_ci
4206cd6a6acSopenharmony_ci	len = strlen(p);
4216cd6a6acSopenharmony_ci	if (len + strlen(last_component) + 2 > PATH_MAX) {
4226cd6a6acSopenharmony_ci		myprintf("symlink_realpath(%s) failed: Filename too long \n",
4236cd6a6acSopenharmony_ci			name);
4246cd6a6acSopenharmony_ci		errno = ENAMETOOLONG;
4256cd6a6acSopenharmony_ci		rc = -1;
4266cd6a6acSopenharmony_ci		goto out;
4276cd6a6acSopenharmony_ci	}
4286cd6a6acSopenharmony_ci
4296cd6a6acSopenharmony_ci	resolved_path += len;
4306cd6a6acSopenharmony_ci	strcpy(resolved_path, "/");
4316cd6a6acSopenharmony_ci	resolved_path += 1;
4326cd6a6acSopenharmony_ci	strcpy(resolved_path, last_component);
4336cd6a6acSopenharmony_ciout:
4346cd6a6acSopenharmony_ci	free(tmp_path);
4356cd6a6acSopenharmony_ci	return rc;
4366cd6a6acSopenharmony_ci}
4376cd6a6acSopenharmony_ci
4386cd6a6acSopenharmony_cistatic int matchpathcon_internal(const char *path, mode_t mode, char ** con)
4396cd6a6acSopenharmony_ci{
4406cd6a6acSopenharmony_ci	char stackpath[PATH_MAX + 1];
4416cd6a6acSopenharmony_ci	char *p = NULL;
4426cd6a6acSopenharmony_ci	if (!hnd && (matchpathcon_init_prefix(NULL, NULL) < 0))
4436cd6a6acSopenharmony_ci			return -1;
4446cd6a6acSopenharmony_ci
4456cd6a6acSopenharmony_ci	if (S_ISLNK(mode)) {
4466cd6a6acSopenharmony_ci		if (!realpath_not_final(path, stackpath))
4476cd6a6acSopenharmony_ci			path = stackpath;
4486cd6a6acSopenharmony_ci	} else {
4496cd6a6acSopenharmony_ci		p = realpath(path, stackpath);
4506cd6a6acSopenharmony_ci		if (p)
4516cd6a6acSopenharmony_ci			path = p;
4526cd6a6acSopenharmony_ci	}
4536cd6a6acSopenharmony_ci
4546cd6a6acSopenharmony_ci	return notrans ?
4556cd6a6acSopenharmony_ci		selabel_lookup_raw(hnd, con, path, mode) :
4566cd6a6acSopenharmony_ci		selabel_lookup(hnd, con, path, mode);
4576cd6a6acSopenharmony_ci}
4586cd6a6acSopenharmony_ci
4596cd6a6acSopenharmony_ciint matchpathcon(const char *path, mode_t mode, char ** con) {
4606cd6a6acSopenharmony_ci	return matchpathcon_internal(path, mode, con);
4616cd6a6acSopenharmony_ci}
4626cd6a6acSopenharmony_ci
4636cd6a6acSopenharmony_ciint matchpathcon_index(const char *name, mode_t mode, char ** con)
4646cd6a6acSopenharmony_ci{
4656cd6a6acSopenharmony_ci	int i = matchpathcon_internal(name, mode, con);
4666cd6a6acSopenharmony_ci
4676cd6a6acSopenharmony_ci	if (i < 0)
4686cd6a6acSopenharmony_ci		return -1;
4696cd6a6acSopenharmony_ci
4706cd6a6acSopenharmony_ci	return add_array_elt(*con);
4716cd6a6acSopenharmony_ci}
4726cd6a6acSopenharmony_ci
4736cd6a6acSopenharmony_civoid matchpathcon_checkmatches(char *str __attribute__((unused)))
4746cd6a6acSopenharmony_ci{
4756cd6a6acSopenharmony_ci	selabel_stats(hnd);
4766cd6a6acSopenharmony_ci}
4776cd6a6acSopenharmony_ci
4786cd6a6acSopenharmony_ci/* Compare two contexts to see if their differences are "significant",
4796cd6a6acSopenharmony_ci * or whether the only difference is in the user. */
4806cd6a6acSopenharmony_ciint selinux_file_context_cmp(const char * a,
4816cd6a6acSopenharmony_ci			     const char * b)
4826cd6a6acSopenharmony_ci{
4836cd6a6acSopenharmony_ci	const char *rest_a, *rest_b;	/* Rest of the context after the user */
4846cd6a6acSopenharmony_ci	if (!a && !b)
4856cd6a6acSopenharmony_ci		return 0;
4866cd6a6acSopenharmony_ci	if (!a)
4876cd6a6acSopenharmony_ci		return -1;
4886cd6a6acSopenharmony_ci	if (!b)
4896cd6a6acSopenharmony_ci		return 1;
4906cd6a6acSopenharmony_ci	rest_a = strchr(a, ':');
4916cd6a6acSopenharmony_ci	rest_b = strchr(b, ':');
4926cd6a6acSopenharmony_ci	if (!rest_a && !rest_b)
4936cd6a6acSopenharmony_ci		return 0;
4946cd6a6acSopenharmony_ci	if (!rest_a)
4956cd6a6acSopenharmony_ci		return -1;
4966cd6a6acSopenharmony_ci	if (!rest_b)
4976cd6a6acSopenharmony_ci		return 1;
4986cd6a6acSopenharmony_ci	return strcmp(rest_a, rest_b);
4996cd6a6acSopenharmony_ci}
5006cd6a6acSopenharmony_ci
5016cd6a6acSopenharmony_ciint selinux_file_context_verify(const char *path, mode_t mode)
5026cd6a6acSopenharmony_ci{
5036cd6a6acSopenharmony_ci	char * con = NULL;
5046cd6a6acSopenharmony_ci	char * fcontext = NULL;
5056cd6a6acSopenharmony_ci	int rc = 0;
5066cd6a6acSopenharmony_ci	char stackpath[PATH_MAX + 1];
5076cd6a6acSopenharmony_ci	char *p = NULL;
5086cd6a6acSopenharmony_ci
5096cd6a6acSopenharmony_ci	if (S_ISLNK(mode)) {
5106cd6a6acSopenharmony_ci		if (!realpath_not_final(path, stackpath))
5116cd6a6acSopenharmony_ci			path = stackpath;
5126cd6a6acSopenharmony_ci	} else {
5136cd6a6acSopenharmony_ci		p = realpath(path, stackpath);
5146cd6a6acSopenharmony_ci		if (p)
5156cd6a6acSopenharmony_ci			path = p;
5166cd6a6acSopenharmony_ci	}
5176cd6a6acSopenharmony_ci
5186cd6a6acSopenharmony_ci	rc = lgetfilecon_raw(path, &con);
5196cd6a6acSopenharmony_ci	if (rc == -1) {
5206cd6a6acSopenharmony_ci		if (errno != ENOTSUP)
5216cd6a6acSopenharmony_ci			return -1;
5226cd6a6acSopenharmony_ci		else
5236cd6a6acSopenharmony_ci			return 0;
5246cd6a6acSopenharmony_ci	}
5256cd6a6acSopenharmony_ci
5266cd6a6acSopenharmony_ci	if (!hnd && (matchpathcon_init_prefix(NULL, NULL) < 0))
5276cd6a6acSopenharmony_ci			return -1;
5286cd6a6acSopenharmony_ci
5296cd6a6acSopenharmony_ci	if (selabel_lookup_raw(hnd, &fcontext, path, mode) != 0) {
5306cd6a6acSopenharmony_ci		if (errno != ENOENT)
5316cd6a6acSopenharmony_ci			rc = -1;
5326cd6a6acSopenharmony_ci		else
5336cd6a6acSopenharmony_ci			rc = 0;
5346cd6a6acSopenharmony_ci	} else {
5356cd6a6acSopenharmony_ci		/*
5366cd6a6acSopenharmony_ci		 * Need to set errno to 0 as it can be set to ENOENT if the
5376cd6a6acSopenharmony_ci		 * file_contexts.subs file does not exist (see selabel_open in
5386cd6a6acSopenharmony_ci		 * label.c), thus causing confusion if errno is checked on return.
5396cd6a6acSopenharmony_ci		 */
5406cd6a6acSopenharmony_ci		errno = 0;
5416cd6a6acSopenharmony_ci		rc = (selinux_file_context_cmp(fcontext, con) == 0);
5426cd6a6acSopenharmony_ci	}
5436cd6a6acSopenharmony_ci
5446cd6a6acSopenharmony_ci	freecon(con);
5456cd6a6acSopenharmony_ci	freecon(fcontext);
5466cd6a6acSopenharmony_ci	return rc;
5476cd6a6acSopenharmony_ci}
5486cd6a6acSopenharmony_ci
5496cd6a6acSopenharmony_ciint selinux_lsetfilecon_default(const char *path)
5506cd6a6acSopenharmony_ci{
5516cd6a6acSopenharmony_ci	struct stat st;
5526cd6a6acSopenharmony_ci	int rc = -1;
5536cd6a6acSopenharmony_ci	char * scontext = NULL;
5546cd6a6acSopenharmony_ci	if (lstat(path, &st) != 0)
5556cd6a6acSopenharmony_ci		return rc;
5566cd6a6acSopenharmony_ci
5576cd6a6acSopenharmony_ci	if (!hnd && (matchpathcon_init_prefix(NULL, NULL) < 0))
5586cd6a6acSopenharmony_ci			return -1;
5596cd6a6acSopenharmony_ci
5606cd6a6acSopenharmony_ci	/* If there's an error determining the context, or it has none,
5616cd6a6acSopenharmony_ci	   return to allow default context */
5626cd6a6acSopenharmony_ci	if (selabel_lookup_raw(hnd, &scontext, path, st.st_mode)) {
5636cd6a6acSopenharmony_ci		if (errno == ENOENT)
5646cd6a6acSopenharmony_ci			rc = 0;
5656cd6a6acSopenharmony_ci	} else {
5666cd6a6acSopenharmony_ci		rc = lsetfilecon_raw(path, scontext);
5676cd6a6acSopenharmony_ci		freecon(scontext);
5686cd6a6acSopenharmony_ci	}
5696cd6a6acSopenharmony_ci	return rc;
5706cd6a6acSopenharmony_ci}
5716cd6a6acSopenharmony_ci
5726cd6a6acSopenharmony_ci#endif
573