16cd6a6acSopenharmony_ci#include <stdio.h>
26cd6a6acSopenharmony_ci#include <stdlib.h>
36cd6a6acSopenharmony_ci#include <string.h>
46cd6a6acSopenharmony_ci#include <getopt.h>
56cd6a6acSopenharmony_ci#include <errno.h>
66cd6a6acSopenharmony_ci#include <selinux/selinux.h>
76cd6a6acSopenharmony_ci#include <selinux/label.h>
86cd6a6acSopenharmony_ci
96cd6a6acSopenharmony_cistatic size_t digest_len;
106cd6a6acSopenharmony_ci
116cd6a6acSopenharmony_cistatic __attribute__ ((__noreturn__)) void usage(const char *progname)
126cd6a6acSopenharmony_ci{
136cd6a6acSopenharmony_ci	fprintf(stderr,
146cd6a6acSopenharmony_ci		"usage: %s -b backend [-d] [-v] [-B] [-i] [-f file]\n\n"
156cd6a6acSopenharmony_ci		"Where:\n\t"
166cd6a6acSopenharmony_ci		"-b  The backend - \"file\", \"media\", \"x\", \"db\" or "
176cd6a6acSopenharmony_ci			"\"prop\"\n\t"
186cd6a6acSopenharmony_ci		"-v  Run \"cat <specfile_list> | openssl dgst -sha1 -hex\"\n\t"
196cd6a6acSopenharmony_ci		"    on the list of specfiles to compare the SHA1 digests.\n\t"
206cd6a6acSopenharmony_ci		"-B  Use base specfiles only (valid for \"-b file\" only).\n\t"
216cd6a6acSopenharmony_ci		"-i  Do not request a digest.\n\t"
226cd6a6acSopenharmony_ci		"-f  Optional file containing the specs (defaults to\n\t"
236cd6a6acSopenharmony_ci		"    those used by loaded policy).\n\n",
246cd6a6acSopenharmony_ci		progname);
256cd6a6acSopenharmony_ci	exit(1);
266cd6a6acSopenharmony_ci}
276cd6a6acSopenharmony_ci
286cd6a6acSopenharmony_cistatic int run_check_digest(char *cmd, char *selabel_digest)
296cd6a6acSopenharmony_ci{
306cd6a6acSopenharmony_ci	FILE *fp;
316cd6a6acSopenharmony_ci	char files_digest[128];
326cd6a6acSopenharmony_ci	char *files_ptr;
336cd6a6acSopenharmony_ci	int rc = 0;
346cd6a6acSopenharmony_ci
356cd6a6acSopenharmony_ci	fp = popen(cmd, "r");
366cd6a6acSopenharmony_ci	if (!fp) {
376cd6a6acSopenharmony_ci		fprintf(stderr, "Failed to run command '%s':  %s\n", cmd, strerror(errno));
386cd6a6acSopenharmony_ci		return -1;
396cd6a6acSopenharmony_ci	}
406cd6a6acSopenharmony_ci
416cd6a6acSopenharmony_ci	/* Only expect one line "(stdin)= x.." so read and find first space */
426cd6a6acSopenharmony_ci	while (fgets(files_digest, sizeof(files_digest) - 1, fp) != NULL)
436cd6a6acSopenharmony_ci		;
446cd6a6acSopenharmony_ci
456cd6a6acSopenharmony_ci	files_ptr = strstr(files_digest, " ");
466cd6a6acSopenharmony_ci
476cd6a6acSopenharmony_ci	rc = strncmp(selabel_digest, files_ptr + 1, digest_len * 2);
486cd6a6acSopenharmony_ci	if (rc) {
496cd6a6acSopenharmony_ci		printf("Failed validation:\n\tselabel_digest: %s\n\t"
506cd6a6acSopenharmony_ci				    "files_digest:   %s\n",
516cd6a6acSopenharmony_ci				    selabel_digest, files_ptr + 1);
526cd6a6acSopenharmony_ci	} else {
536cd6a6acSopenharmony_ci		printf("Passed validation - digest: %s\n", selabel_digest);
546cd6a6acSopenharmony_ci	}
556cd6a6acSopenharmony_ci
566cd6a6acSopenharmony_ci	pclose(fp);
576cd6a6acSopenharmony_ci	return rc;
586cd6a6acSopenharmony_ci}
596cd6a6acSopenharmony_ci
606cd6a6acSopenharmony_ciint main(int argc, char **argv)
616cd6a6acSopenharmony_ci{
626cd6a6acSopenharmony_ci	int backend = 0, rc, opt, validate = 0;
636cd6a6acSopenharmony_ci	char *baseonly = NULL, *file = NULL, *digest = (char *)1;
646cd6a6acSopenharmony_ci	char **specfiles = NULL;
656cd6a6acSopenharmony_ci	unsigned char *sha1_digest = NULL;
666cd6a6acSopenharmony_ci	size_t i, num_specfiles;
676cd6a6acSopenharmony_ci
686cd6a6acSopenharmony_ci	char cmd_buf[4096];
696cd6a6acSopenharmony_ci	char *cmd_ptr;
706cd6a6acSopenharmony_ci	char *sha1_buf;
716cd6a6acSopenharmony_ci
726cd6a6acSopenharmony_ci	struct selabel_handle *hnd;
736cd6a6acSopenharmony_ci	struct selinux_opt selabel_option[] = {
746cd6a6acSopenharmony_ci		{ SELABEL_OPT_PATH, file },
756cd6a6acSopenharmony_ci		{ SELABEL_OPT_BASEONLY, baseonly },
766cd6a6acSopenharmony_ci		{ SELABEL_OPT_DIGEST, digest }
776cd6a6acSopenharmony_ci	};
786cd6a6acSopenharmony_ci
796cd6a6acSopenharmony_ci	if (argc < 3)
806cd6a6acSopenharmony_ci		usage(argv[0]);
816cd6a6acSopenharmony_ci
826cd6a6acSopenharmony_ci	while ((opt = getopt(argc, argv, "ib:Bvf:")) > 0) {
836cd6a6acSopenharmony_ci		switch (opt) {
846cd6a6acSopenharmony_ci		case 'b':
856cd6a6acSopenharmony_ci			if (!strcasecmp(optarg, "file")) {
866cd6a6acSopenharmony_ci				backend = SELABEL_CTX_FILE;
876cd6a6acSopenharmony_ci			} else if (!strcmp(optarg, "media")) {
886cd6a6acSopenharmony_ci				backend = SELABEL_CTX_MEDIA;
896cd6a6acSopenharmony_ci			} else if (!strcmp(optarg, "x")) {
906cd6a6acSopenharmony_ci				backend = SELABEL_CTX_X;
916cd6a6acSopenharmony_ci			} else if (!strcmp(optarg, "db")) {
926cd6a6acSopenharmony_ci				backend = SELABEL_CTX_DB;
936cd6a6acSopenharmony_ci			} else if (!strcmp(optarg, "prop")) {
946cd6a6acSopenharmony_ci				backend = SELABEL_CTX_ANDROID_PROP;
956cd6a6acSopenharmony_ci			} else if (!strcmp(optarg, "service")) {
966cd6a6acSopenharmony_ci				backend = SELABEL_CTX_ANDROID_SERVICE;
976cd6a6acSopenharmony_ci			} else {
986cd6a6acSopenharmony_ci				fprintf(stderr, "Unknown backend: %s\n",
996cd6a6acSopenharmony_ci								    optarg);
1006cd6a6acSopenharmony_ci				usage(argv[0]);
1016cd6a6acSopenharmony_ci			}
1026cd6a6acSopenharmony_ci			break;
1036cd6a6acSopenharmony_ci		case 'B':
1046cd6a6acSopenharmony_ci			baseonly = (char *)1;
1056cd6a6acSopenharmony_ci			break;
1066cd6a6acSopenharmony_ci		case 'v':
1076cd6a6acSopenharmony_ci			validate = 1;
1086cd6a6acSopenharmony_ci			break;
1096cd6a6acSopenharmony_ci		case 'i':
1106cd6a6acSopenharmony_ci			digest = NULL;
1116cd6a6acSopenharmony_ci			break;
1126cd6a6acSopenharmony_ci		case 'f':
1136cd6a6acSopenharmony_ci			file = optarg;
1146cd6a6acSopenharmony_ci			break;
1156cd6a6acSopenharmony_ci		default:
1166cd6a6acSopenharmony_ci			usage(argv[0]);
1176cd6a6acSopenharmony_ci		}
1186cd6a6acSopenharmony_ci	}
1196cd6a6acSopenharmony_ci
1206cd6a6acSopenharmony_ci	memset(cmd_buf, 0, sizeof(cmd_buf));
1216cd6a6acSopenharmony_ci
1226cd6a6acSopenharmony_ci	selabel_option[0].value = file;
1236cd6a6acSopenharmony_ci	selabel_option[1].value = baseonly;
1246cd6a6acSopenharmony_ci	selabel_option[2].value = digest;
1256cd6a6acSopenharmony_ci
1266cd6a6acSopenharmony_ci	hnd = selabel_open(backend, selabel_option, 3);
1276cd6a6acSopenharmony_ci	if (!hnd) {
1286cd6a6acSopenharmony_ci		switch (errno) {
1296cd6a6acSopenharmony_ci		case EOVERFLOW:
1306cd6a6acSopenharmony_ci			fprintf(stderr, "ERROR Number of specfiles or specfile"
1316cd6a6acSopenharmony_ci					" buffer caused an overflow.\n");
1326cd6a6acSopenharmony_ci			break;
1336cd6a6acSopenharmony_ci		default:
1346cd6a6acSopenharmony_ci			fprintf(stderr, "ERROR: selabel_open: %s\n",
1356cd6a6acSopenharmony_ci						    strerror(errno));
1366cd6a6acSopenharmony_ci		}
1376cd6a6acSopenharmony_ci		return -1;
1386cd6a6acSopenharmony_ci	}
1396cd6a6acSopenharmony_ci
1406cd6a6acSopenharmony_ci	rc = selabel_digest(hnd, &sha1_digest, &digest_len, &specfiles,
1416cd6a6acSopenharmony_ci							    &num_specfiles);
1426cd6a6acSopenharmony_ci
1436cd6a6acSopenharmony_ci	if (rc) {
1446cd6a6acSopenharmony_ci		switch (errno) {
1456cd6a6acSopenharmony_ci		case EINVAL:
1466cd6a6acSopenharmony_ci			fprintf(stderr, "No digest available.\n");
1476cd6a6acSopenharmony_ci			break;
1486cd6a6acSopenharmony_ci		default:
1496cd6a6acSopenharmony_ci			fprintf(stderr, "selabel_digest ERROR: %s\n",
1506cd6a6acSopenharmony_ci						    strerror(errno));
1516cd6a6acSopenharmony_ci		}
1526cd6a6acSopenharmony_ci		goto err;
1536cd6a6acSopenharmony_ci	}
1546cd6a6acSopenharmony_ci
1556cd6a6acSopenharmony_ci	sha1_buf = malloc(digest_len * 2 + 1);
1566cd6a6acSopenharmony_ci	if (!sha1_buf) {
1576cd6a6acSopenharmony_ci		fprintf(stderr, "Could not malloc buffer ERROR: %s\n",
1586cd6a6acSopenharmony_ci						    strerror(errno));
1596cd6a6acSopenharmony_ci		rc = -1;
1606cd6a6acSopenharmony_ci		goto err;
1616cd6a6acSopenharmony_ci	}
1626cd6a6acSopenharmony_ci
1636cd6a6acSopenharmony_ci	printf("SHA1 digest: ");
1646cd6a6acSopenharmony_ci	for (i = 0; i < digest_len; i++)
1656cd6a6acSopenharmony_ci		sprintf(&(sha1_buf[i * 2]), "%02x", sha1_digest[i]);
1666cd6a6acSopenharmony_ci
1676cd6a6acSopenharmony_ci	printf("%s\n", sha1_buf);
1686cd6a6acSopenharmony_ci	printf("calculated using the following specfile(s):\n");
1696cd6a6acSopenharmony_ci
1706cd6a6acSopenharmony_ci	if (specfiles) {
1716cd6a6acSopenharmony_ci		cmd_ptr = &cmd_buf[0];
1726cd6a6acSopenharmony_ci		sprintf(cmd_ptr, "/usr/bin/cat ");
1736cd6a6acSopenharmony_ci		cmd_ptr = &cmd_buf[0] + strlen(cmd_buf);
1746cd6a6acSopenharmony_ci
1756cd6a6acSopenharmony_ci		for (i = 0; i < num_specfiles; i++) {
1766cd6a6acSopenharmony_ci			sprintf(cmd_ptr, "%s ", specfiles[i]);
1776cd6a6acSopenharmony_ci			cmd_ptr += strlen(specfiles[i]) + 1;
1786cd6a6acSopenharmony_ci			printf("%s\n", specfiles[i]);
1796cd6a6acSopenharmony_ci		}
1806cd6a6acSopenharmony_ci		sprintf(cmd_ptr, "| /usr/bin/openssl dgst -sha1 -hex");
1816cd6a6acSopenharmony_ci
1826cd6a6acSopenharmony_ci		if (validate)
1836cd6a6acSopenharmony_ci			rc = run_check_digest(cmd_buf, sha1_buf);
1846cd6a6acSopenharmony_ci	}
1856cd6a6acSopenharmony_ci
1866cd6a6acSopenharmony_ci	free(sha1_buf);
1876cd6a6acSopenharmony_cierr:
1886cd6a6acSopenharmony_ci	selabel_close(hnd);
1896cd6a6acSopenharmony_ci	return rc;
1906cd6a6acSopenharmony_ci}
191