16cd6a6acSopenharmony_ci#include <errno.h>
26cd6a6acSopenharmony_ci#include <getopt.h>
36cd6a6acSopenharmony_ci#include <limits.h>
46cd6a6acSopenharmony_ci#include <selinux/label.h>
56cd6a6acSopenharmony_ci#include <selinux/selinux.h>
66cd6a6acSopenharmony_ci#include <stdio.h>
76cd6a6acSopenharmony_ci#include <stdlib.h>
86cd6a6acSopenharmony_ci#include <string.h>
96cd6a6acSopenharmony_ci#include <sys/stat.h>
106cd6a6acSopenharmony_ci#include <sys/types.h>
116cd6a6acSopenharmony_ci#include <unistd.h>
126cd6a6acSopenharmony_ci
136cd6a6acSopenharmony_cistatic __attribute__ ((__noreturn__)) void usage(const char *progname)
146cd6a6acSopenharmony_ci{
156cd6a6acSopenharmony_ci	fprintf(stderr,
166cd6a6acSopenharmony_ci		"usage:  %s [-V] [-N] [-n] [-m type] [-f file_contexts_file] [-p prefix] [-P policy_root_path] filepath...\n",
176cd6a6acSopenharmony_ci		progname);
186cd6a6acSopenharmony_ci	exit(1);
196cd6a6acSopenharmony_ci}
206cd6a6acSopenharmony_ci
216cd6a6acSopenharmony_cistatic int printmatchpathcon(struct selabel_handle *hnd, const char *path, int header, int mode, int notrans)
226cd6a6acSopenharmony_ci{
236cd6a6acSopenharmony_ci	char *buf = NULL;
246cd6a6acSopenharmony_ci	int rc;
256cd6a6acSopenharmony_ci
266cd6a6acSopenharmony_ci	if (notrans) {
276cd6a6acSopenharmony_ci		rc = selabel_lookup_raw(hnd, &buf, path, mode);
286cd6a6acSopenharmony_ci	} else {
296cd6a6acSopenharmony_ci		rc = selabel_lookup(hnd, &buf, path, mode);
306cd6a6acSopenharmony_ci	}
316cd6a6acSopenharmony_ci	if (rc < 0) {
326cd6a6acSopenharmony_ci		if (errno == ENOENT) {
336cd6a6acSopenharmony_ci			buf = strdup("<<none>>");
346cd6a6acSopenharmony_ci		} else {
356cd6a6acSopenharmony_ci			fprintf(stderr, "selabel_lookup(%s) failed: %s\n", path,
366cd6a6acSopenharmony_ci				strerror(errno));
376cd6a6acSopenharmony_ci			return 1;
386cd6a6acSopenharmony_ci		}
396cd6a6acSopenharmony_ci	}
406cd6a6acSopenharmony_ci	if (header)
416cd6a6acSopenharmony_ci		printf("%s\t%s\n", path, buf);
426cd6a6acSopenharmony_ci	else
436cd6a6acSopenharmony_ci		printf("%s\n", buf);
446cd6a6acSopenharmony_ci
456cd6a6acSopenharmony_ci	freecon(buf);
466cd6a6acSopenharmony_ci	return 0;
476cd6a6acSopenharmony_ci}
486cd6a6acSopenharmony_ci
496cd6a6acSopenharmony_cistatic mode_t string_to_mode(char *s)
506cd6a6acSopenharmony_ci{
516cd6a6acSopenharmony_ci	switch (s[0]) {
526cd6a6acSopenharmony_ci	case 'b':
536cd6a6acSopenharmony_ci		return S_IFBLK;
546cd6a6acSopenharmony_ci	case 'c':
556cd6a6acSopenharmony_ci		return S_IFCHR;
566cd6a6acSopenharmony_ci	case 'd':
576cd6a6acSopenharmony_ci		return S_IFDIR;
586cd6a6acSopenharmony_ci	case 'p':
596cd6a6acSopenharmony_ci		return S_IFIFO;
606cd6a6acSopenharmony_ci	case 'l':
616cd6a6acSopenharmony_ci		return S_IFLNK;
626cd6a6acSopenharmony_ci	case 's':
636cd6a6acSopenharmony_ci		return S_IFSOCK;
646cd6a6acSopenharmony_ci	case 'f':
656cd6a6acSopenharmony_ci		return S_IFREG;
666cd6a6acSopenharmony_ci	default:
676cd6a6acSopenharmony_ci		return -1;
686cd6a6acSopenharmony_ci	}
696cd6a6acSopenharmony_ci	return -1;
706cd6a6acSopenharmony_ci}
716cd6a6acSopenharmony_ci
726cd6a6acSopenharmony_ciint main(int argc, char **argv)
736cd6a6acSopenharmony_ci{
746cd6a6acSopenharmony_ci	int i, force_mode = 0;
756cd6a6acSopenharmony_ci	int header = 1, opt;
766cd6a6acSopenharmony_ci	int verify = 0;
776cd6a6acSopenharmony_ci	int notrans = 0;
786cd6a6acSopenharmony_ci	int error = 0;
796cd6a6acSopenharmony_ci	int quiet = 0;
806cd6a6acSopenharmony_ci	struct selabel_handle *hnd;
816cd6a6acSopenharmony_ci	struct selinux_opt options[SELABEL_NOPT] = {};
826cd6a6acSopenharmony_ci
836cd6a6acSopenharmony_ci	if (argc < 2)
846cd6a6acSopenharmony_ci		usage(argv[0]);
856cd6a6acSopenharmony_ci
866cd6a6acSopenharmony_ci	while ((opt = getopt(argc, argv, "m:Nnf:P:p:Vq")) > 0) {
876cd6a6acSopenharmony_ci		switch (opt) {
886cd6a6acSopenharmony_ci		case 'n':
896cd6a6acSopenharmony_ci			header = 0;
906cd6a6acSopenharmony_ci			break;
916cd6a6acSopenharmony_ci		case 'm':
926cd6a6acSopenharmony_ci			force_mode = string_to_mode(optarg);
936cd6a6acSopenharmony_ci			if (force_mode < 0) {
946cd6a6acSopenharmony_ci				fprintf(stderr, "%s: mode %s is invalid\n", argv[0], optarg);
956cd6a6acSopenharmony_ci				exit(1);
966cd6a6acSopenharmony_ci			}
976cd6a6acSopenharmony_ci			break;
986cd6a6acSopenharmony_ci		case 'V':
996cd6a6acSopenharmony_ci			verify = 1;
1006cd6a6acSopenharmony_ci			break;
1016cd6a6acSopenharmony_ci		case 'N':
1026cd6a6acSopenharmony_ci			notrans = 1;
1036cd6a6acSopenharmony_ci			break;
1046cd6a6acSopenharmony_ci		case 'f':
1056cd6a6acSopenharmony_ci			options[SELABEL_OPT_PATH].type = SELABEL_OPT_PATH;
1066cd6a6acSopenharmony_ci			options[SELABEL_OPT_PATH].value = optarg;
1076cd6a6acSopenharmony_ci			break;
1086cd6a6acSopenharmony_ci		case 'P':
1096cd6a6acSopenharmony_ci			if (selinux_set_policy_root(optarg) < 0 ) {
1106cd6a6acSopenharmony_ci				fprintf(stderr,
1116cd6a6acSopenharmony_ci					"Error setting policy root  %s:  %s\n",
1126cd6a6acSopenharmony_ci					optarg,
1136cd6a6acSopenharmony_ci					errno ? strerror(errno) : "invalid");
1146cd6a6acSopenharmony_ci				exit(1);
1156cd6a6acSopenharmony_ci			}
1166cd6a6acSopenharmony_ci			break;
1176cd6a6acSopenharmony_ci		case 'p':
1186cd6a6acSopenharmony_ci			// This option has been deprecated since libselinux 2.5 (2016):
1196cd6a6acSopenharmony_ci			// https://github.com/SELinuxProject/selinux/commit/26e05da0fc2d0a4bd274320968a88f8acbb3b6a6
1206cd6a6acSopenharmony_ci			fprintf(stderr, "Warning: using %s -p is deprecated\n", argv[0]);
1216cd6a6acSopenharmony_ci			options[SELABEL_OPT_SUBSET].type = SELABEL_OPT_SUBSET;
1226cd6a6acSopenharmony_ci			options[SELABEL_OPT_SUBSET].value = optarg;
1236cd6a6acSopenharmony_ci			break;
1246cd6a6acSopenharmony_ci		case 'q':
1256cd6a6acSopenharmony_ci			quiet = 1;
1266cd6a6acSopenharmony_ci			break;
1276cd6a6acSopenharmony_ci		default:
1286cd6a6acSopenharmony_ci			usage(argv[0]);
1296cd6a6acSopenharmony_ci		}
1306cd6a6acSopenharmony_ci	}
1316cd6a6acSopenharmony_ci	hnd = selabel_open(SELABEL_CTX_FILE, options, SELABEL_NOPT);
1326cd6a6acSopenharmony_ci	if (!hnd) {
1336cd6a6acSopenharmony_ci		fprintf(stderr,
1346cd6a6acSopenharmony_ci			"Error while opening file contexts database: %s\n",
1356cd6a6acSopenharmony_ci			strerror(errno));
1366cd6a6acSopenharmony_ci		return -1;
1376cd6a6acSopenharmony_ci	}
1386cd6a6acSopenharmony_ci	for (i = optind; i < argc; i++) {
1396cd6a6acSopenharmony_ci		int rc, mode = 0;
1406cd6a6acSopenharmony_ci		struct stat buf;
1416cd6a6acSopenharmony_ci		char *path = argv[i];
1426cd6a6acSopenharmony_ci		int len = strlen(path);
1436cd6a6acSopenharmony_ci		if (len > 1  && path[len - 1 ] == '/')
1446cd6a6acSopenharmony_ci			path[len - 1 ] = '\0';
1456cd6a6acSopenharmony_ci
1466cd6a6acSopenharmony_ci		if (lstat(path, &buf) == 0)
1476cd6a6acSopenharmony_ci			mode = buf.st_mode;
1486cd6a6acSopenharmony_ci		if (force_mode)
1496cd6a6acSopenharmony_ci			mode = force_mode;
1506cd6a6acSopenharmony_ci
1516cd6a6acSopenharmony_ci		if (verify) {
1526cd6a6acSopenharmony_ci			rc = selinux_file_context_verify(path, mode);
1536cd6a6acSopenharmony_ci
1546cd6a6acSopenharmony_ci			if (quiet) {
1556cd6a6acSopenharmony_ci				if (rc == 1)
1566cd6a6acSopenharmony_ci					continue;
1576cd6a6acSopenharmony_ci				else
1586cd6a6acSopenharmony_ci					exit(1);
1596cd6a6acSopenharmony_ci			}
1606cd6a6acSopenharmony_ci
1616cd6a6acSopenharmony_ci			if (rc == -1) {
1626cd6a6acSopenharmony_ci				printf("%s error: %s\n", path, strerror(errno));
1636cd6a6acSopenharmony_ci				exit(1);
1646cd6a6acSopenharmony_ci			} else if (rc == 1) {
1656cd6a6acSopenharmony_ci				printf("%s verified.\n", path);
1666cd6a6acSopenharmony_ci			} else {
1676cd6a6acSopenharmony_ci				char * con;
1686cd6a6acSopenharmony_ci				error = 1;
1696cd6a6acSopenharmony_ci				if (notrans)
1706cd6a6acSopenharmony_ci					rc = lgetfilecon_raw(path, &con);
1716cd6a6acSopenharmony_ci				else
1726cd6a6acSopenharmony_ci					rc = lgetfilecon(path, &con);
1736cd6a6acSopenharmony_ci
1746cd6a6acSopenharmony_ci				if (rc >= 0) {
1756cd6a6acSopenharmony_ci					printf("%s has context %s, should be ",
1766cd6a6acSopenharmony_ci					       path, con);
1776cd6a6acSopenharmony_ci					printmatchpathcon(hnd, path, 0, mode, notrans);
1786cd6a6acSopenharmony_ci					freecon(con);
1796cd6a6acSopenharmony_ci				} else {
1806cd6a6acSopenharmony_ci					printf
1816cd6a6acSopenharmony_ci					    ("actual context unknown: %s, should be ",
1826cd6a6acSopenharmony_ci					     strerror(errno));
1836cd6a6acSopenharmony_ci					printmatchpathcon(hnd, path, 0, mode, notrans);
1846cd6a6acSopenharmony_ci				}
1856cd6a6acSopenharmony_ci			}
1866cd6a6acSopenharmony_ci		} else {
1876cd6a6acSopenharmony_ci			error |= printmatchpathcon(hnd, path, header, mode, notrans);
1886cd6a6acSopenharmony_ci		}
1896cd6a6acSopenharmony_ci	}
1906cd6a6acSopenharmony_ci	selabel_close(hnd);
1916cd6a6acSopenharmony_ci	return error;
1926cd6a6acSopenharmony_ci}
193