1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <getopt.h>
5#include <errno.h>
6#include <selinux/selinux.h>
7#include <selinux/label.h>
8
9static size_t digest_len;
10
11static void usage(const char *progname)
12{
13	fprintf(stderr,
14		"usage: %s -b backend [-d] [-v] [-B] [-i] [-f file]\n\n"
15		"Where:\n\t"
16		"-b  The backend - \"file\", \"media\", \"x\", \"db\" or "
17			"\"prop\"\n\t"
18		"-v  Run \"cat <specfile_list> | openssl dgst -sha1 -hex\"\n\t"
19		"    on the list of specfiles to compare the SHA1 digests.\n\t"
20		"-B  Use base specfiles only (valid for \"-b file\" only).\n\t"
21		"-i  Do not request a digest.\n\t"
22		"-f  Optional file containing the specs (defaults to\n\t"
23		"    those used by loaded policy).\n\n",
24		progname);
25	exit(1);
26}
27
28static int run_check_digest(char *cmd, char *selabel_digest)
29{
30	FILE *fp;
31	char files_digest[128];
32	char *files_ptr;
33	int rc = 0;
34
35	fp = popen(cmd, "r");
36	if (!fp) {
37		printf("Failed to run command line\n");
38		return -1;
39	}
40
41	/* Only expect one line "(stdin)= x.." so read and find first space */
42	while (fgets(files_digest, sizeof(files_digest) - 1, fp) != NULL)
43		;
44
45	files_ptr = strstr(files_digest, " ");
46
47	rc = strncmp(selabel_digest, files_ptr + 1, digest_len * 2);
48	if (rc) {
49		printf("Failed validation:\n\tselabel_digest: %s\n\t"
50				    "files_digest:   %s\n",
51				    selabel_digest, files_ptr + 1);
52	} else {
53		printf("Passed validation - digest: %s\n", selabel_digest);
54	}
55
56	pclose(fp);
57	return rc;
58}
59
60int main(int argc, char **argv)
61{
62	int backend = 0, rc, opt, i, validate = 0;
63	char *baseonly = NULL, *file = NULL, *digest = (char *)1;
64	char **specfiles = NULL;
65	unsigned char *sha1_digest = NULL;
66	size_t num_specfiles;
67
68	char cmd_buf[4096];
69	char *cmd_ptr;
70	char *sha1_buf;
71
72	struct selabel_handle *hnd;
73	struct selinux_opt selabel_option[] = {
74		{ SELABEL_OPT_PATH, file },
75		{ SELABEL_OPT_BASEONLY, baseonly },
76		{ SELABEL_OPT_DIGEST, digest }
77	};
78
79	if (argc < 3)
80		usage(argv[0]);
81
82	while ((opt = getopt(argc, argv, "ib:Bvf:")) > 0) {
83		switch (opt) {
84		case 'b':
85			if (!strcasecmp(optarg, "file")) {
86				backend = SELABEL_CTX_FILE;
87			} else if (!strcmp(optarg, "media")) {
88				backend = SELABEL_CTX_MEDIA;
89			} else if (!strcmp(optarg, "x")) {
90				backend = SELABEL_CTX_X;
91			} else if (!strcmp(optarg, "db")) {
92				backend = SELABEL_CTX_DB;
93			} else if (!strcmp(optarg, "prop")) {
94				backend = SELABEL_CTX_ANDROID_PROP;
95			} else {
96				fprintf(stderr, "Unknown backend: %s\n",
97								    optarg);
98				usage(argv[0]);
99			}
100			break;
101		case 'B':
102			baseonly = (char *)1;
103			break;
104		case 'v':
105			validate = 1;
106			break;
107		case 'i':
108			digest = NULL;
109			break;
110		case 'f':
111			file = optarg;
112			break;
113		default:
114			usage(argv[0]);
115		}
116	}
117
118	memset(cmd_buf, 0, sizeof(cmd_buf));
119
120	selabel_option[0].value = file;
121	selabel_option[1].value = baseonly;
122	selabel_option[2].value = digest;
123
124	hnd = selabel_open(backend, selabel_option, 3);
125	if (!hnd) {
126		switch (errno) {
127		case EOVERFLOW:
128			fprintf(stderr, "ERROR Number of specfiles or specfile"
129					" buffer caused an overflow.\n");
130			break;
131		default:
132			fprintf(stderr, "ERROR: selabel_open: %s\n",
133						    strerror(errno));
134		}
135		return -1;
136	}
137
138	rc = selabel_digest(hnd, &sha1_digest, &digest_len, &specfiles,
139							    &num_specfiles);
140
141	if (rc) {
142		switch (errno) {
143		case EINVAL:
144			fprintf(stderr, "No digest available.\n");
145			break;
146		default:
147			fprintf(stderr, "selabel_digest ERROR: %s\n",
148						    strerror(errno));
149		}
150		goto err;
151	}
152
153	sha1_buf = malloc(digest_len * 2 + 1);
154	if (!sha1_buf) {
155		fprintf(stderr, "Could not malloc buffer ERROR: %s\n",
156						    strerror(errno));
157		rc = -1;
158		goto err;
159	}
160
161	printf("SHA1 digest: ");
162	for (i = 0; i < digest_len; i++)
163		sprintf(&(sha1_buf[i * 2]), "%02x", sha1_digest[i]);
164
165	printf("%s\n", sha1_buf);
166	printf("calculated using the following specfile(s):\n");
167
168	if (specfiles) {
169		cmd_ptr = &cmd_buf[0];
170		sprintf(cmd_ptr, "/usr/bin/cat ");
171		cmd_ptr = &cmd_buf[0] + strlen(cmd_buf);
172
173		for (i = 0; i < num_specfiles; i++) {
174			sprintf(cmd_ptr, "%s ", specfiles[i]);
175			cmd_ptr += strlen(specfiles[i]) + 1;
176			printf("%s\n", specfiles[i]);
177		}
178		sprintf(cmd_ptr, "| /usr/bin/openssl dgst -sha1 -hex");
179
180		if (validate)
181			rc = run_check_digest(cmd_buf, sha1_buf);
182	}
183
184	free(sha1_buf);
185err:
186	selabel_close(hnd);
187	return rc;
188}
189