1 /* Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
2  *
3  * Copyright (C) 2006 Tresys Technology, LLC
4  * Copyright (C) 2006-2007 Red Hat, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, version 2.
9  *
10  */
11 
12 /* Because we _must_ muck around in the internal representation of
13  * the policydb (and include the internal header below) this program
14  * must be statically linked to libsepol like checkpolicy. It is
15  * not clear if it is worthwhile to fix this, as exposing the details
16  * of avrule_blocks - even in an ABI safe way - seems undesirable.
17  */
18 #include <sepol/module.h>
19 #include <sepol/errcodes.h>
20 #include <sepol/policydb/policydb.h>
21 
22 #include <getopt.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <errno.h>
26 #include <sys/mman.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <assert.h>
33 
34 /* for getopt */
35 extern char *optarg;
36 extern int optind;
37 
38 /* This is really a horrible hack, but the base module
39  * is referred to with the following name. The same
40  * thing is done in the linker for displaying error
41  * messages.
42  */
43 #define BASE_NAME "BASE"
44 
usage(char * program_name)45 static void usage(char *program_name)
46 {
47 	printf("usage: %s [-v -g -b] basemodpkg modpkg1 [modpkg2 ... ]\n",
48 	       program_name);
49 	exit(1);
50 }
51 
52 /* Basic string hash and compare for the hashtables used in
53  * generate_requires. Copied from symtab.c.
54  */
reqsymhash(hashtab_t h,hashtab_key_t key)55 static unsigned int reqsymhash(hashtab_t h, hashtab_key_t key)
56 {
57 	char *p, *keyp;
58 	size_t size;
59 	unsigned int val;
60 
61 	val = 0;
62 	keyp = (char *)key;
63 	size = strlen(keyp);
64 	for (p = keyp; ((size_t) (p - keyp)) < size; p++)
65 		val =
66 		    (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p);
67 	return val & (h->size - 1);
68 }
69 
reqsymcmp(hashtab_t h,hashtab_key_t key1,hashtab_key_t key2)70 static int reqsymcmp(hashtab_t h
71 		     __attribute__ ((unused)), hashtab_key_t key1,
72 		     hashtab_key_t key2)
73 {
74 	char *keyp1, *keyp2;
75 
76 	keyp1 = (char *)key1;
77 	keyp2 = (char *)key2;
78 	return strcmp(keyp1, keyp2);
79 }
80 
81 /* Load a policy package from the given filename. Progname is used for
82  * error reporting.
83  */
load_module(char * filename,char * progname)84 static sepol_module_package_t *load_module(char *filename, char *progname)
85 {
86 	int ret;
87 	FILE *fp = NULL;
88 	struct sepol_policy_file *pf = NULL;
89 	sepol_module_package_t *p = NULL;
90 
91 	if (sepol_module_package_create(&p)) {
92 		fprintf(stderr, "%s:  Out of memory\n", progname);
93 		goto bad;
94 	}
95 	if (sepol_policy_file_create(&pf)) {
96 		fprintf(stderr, "%s:  Out of memory\n", progname);
97 		goto bad;
98 	}
99 	fp = fopen(filename, "r");
100 	if (!fp) {
101 		fprintf(stderr, "%s:  Could not open package %s:  %s", progname,
102 			filename, strerror(errno));
103 		goto bad;
104 	}
105 	sepol_policy_file_set_fp(pf, fp);
106 
107 	ret = sepol_module_package_read(p, pf, 0);
108 	if (ret) {
109 		fprintf(stderr, "%s:  Error while reading package from %s\n",
110 			progname, filename);
111 		goto bad;
112 	}
113 	fclose(fp);
114 	sepol_policy_file_free(pf);
115 	return p;
116       bad:
117 	sepol_module_package_free(p);
118 	sepol_policy_file_free(pf);
119 	if (fp)
120 		fclose(fp);
121 	return NULL;
122 }
123 
124 /* This function generates the requirements graph and stores it in
125  * a set of nested hashtables. The top level hash table stores modules
126  * keyed by name. The value of that module is a hashtable storing all
127  * of the requirements keyed by name. There is no value for the requirements
128  * hashtable.
129  *
130  * This only tracks symbols that are _required_ - optional symbols
131  * are completely ignored. A future version might look at this.
132  *
133  * This requirement generation only looks at booleans and types because:
134  *  - object classes: (for now) only present in bases
135  *  - roles: since they are multiply declared it is not clear how
136  *           to present these requirements as they will be satisfied
137  *           by multiple modules.
138  *  - users: same problem as roles plus they are usually defined outside
139  *           of the policy.
140  *  - levels / cats: can't be required or used in modules.
141  */
generate_requires(policydb_t * p)142 static hashtab_t generate_requires(policydb_t * p)
143 {
144 	avrule_block_t *block;
145 	avrule_decl_t *decl;
146 	char *mod_name, *req_name, *id;
147 	ebitmap_t *b;
148 	ebitmap_node_t *node;
149 	uint32_t i, j;
150 	int ret;
151 	scope_datum_t *scope;
152 	hashtab_t mods;
153 	hashtab_t reqs;
154 
155 	mods = hashtab_create(reqsymhash, reqsymcmp, 64);
156 	if (mods == NULL)
157 		return NULL;
158 
159 	for (block = p->global; block != NULL; block = block->next) {
160 		if (block->flags & AVRULE_OPTIONAL)
161 			continue;
162 		for (decl = block->branch_list; decl != NULL; decl = decl->next) {
163 			mod_name =
164 			    decl->module_name ? decl->module_name : BASE_NAME;
165 			for (i = 0; i < SYM_NUM; i++) {
166 				if (!(i == SYM_TYPES || i == SYM_BOOLS))
167 					continue;
168 				b = &decl->required.scope[i];
169 				ebitmap_for_each_bit(b, node, j) {
170 					if (!ebitmap_node_get_bit(node, j))
171 						continue;
172 					id = p->sym_val_to_name[i][j];
173 					scope =
174 					    (scope_datum_t *) hashtab_search(p->
175 									     scope
176 									     [i].
177 									     table,
178 									     id);
179 					/* since this is only called after a successful link,
180 					 * this should never happen */
181 					assert(scope->scope == SCOPE_DECL);
182 					req_name =
183 					    p->decl_val_to_struct[scope->
184 								  decl_ids[0]]->
185 					    module_name ? p->
186 					    decl_val_to_struct[scope->
187 							       decl_ids[0]]->
188 					    module_name : BASE_NAME;
189 
190 					reqs =
191 					    (hashtab_t) hashtab_search(mods,
192 								       mod_name);
193 					if (!reqs) {
194 						reqs =
195 						    hashtab_create(reqsymhash,
196 								   reqsymcmp,
197 								   64);
198 						if (reqs == NULL) {
199 							return NULL;
200 						}
201 						ret =
202 						    hashtab_insert(mods,
203 								   mod_name,
204 								   reqs);
205 						if (ret != SEPOL_OK)
206 							return NULL;
207 					}
208 					ret =
209 					    hashtab_insert(reqs, req_name,
210 							   NULL);
211 					if (!
212 					    (ret == SEPOL_EEXIST
213 					     || ret == SEPOL_OK))
214 						return NULL;
215 				}
216 			}
217 
218 		}
219 	}
220 
221 	return mods;
222 }
223 
free_requires(hashtab_t req)224 static void free_requires(hashtab_t req)
225 {
226 	unsigned int i;
227 	hashtab_ptr_t cur;
228 
229 	/* We steal memory for everything stored in the hash tables
230 	 * from the policydb, so this only looks like it leaks.
231 	 */
232 	for (i = 0; i < req->size; i++) {
233 		cur = req->htable[i];
234 		while (cur != NULL) {
235 			hashtab_destroy((hashtab_t) cur->datum);
236 			cur = cur->next;
237 		}
238 	}
239 	hashtab_destroy(req);
240 }
241 
output_graphviz(hashtab_t mods,int exclude_base,FILE * f)242 static void output_graphviz(hashtab_t mods, int exclude_base, FILE * f)
243 {
244 	unsigned int i, j;
245 	hashtab_ptr_t cur, cur2;
246 	hashtab_t reqs;
247 
248 	fprintf(f, "digraph mod_deps {\n");
249 	fprintf(f, "\toverlap=false\n");
250 
251 	for (i = 0; i < mods->size; i++) {
252 		cur = mods->htable[i];
253 		while (cur != NULL) {
254 			reqs = (hashtab_t) cur->datum;
255 			assert(reqs);
256 			for (j = 0; j < reqs->size; j++) {
257 				cur2 = reqs->htable[j];
258 				while (cur2 != NULL) {
259 					if (exclude_base
260 					    && strcmp(cur2->key,
261 						      BASE_NAME) == 0) {
262 						cur2 = cur2->next;
263 						continue;
264 					}
265 					fprintf(f, "\t%s -> %s\n", cur->key,
266 						cur2->key);
267 					cur2 = cur2->next;
268 				}
269 			}
270 			cur = cur->next;
271 		}
272 	}
273 	fprintf(f, "}\n");
274 }
275 
output_requirements(hashtab_t mods,int exclude_base,FILE * f)276 static void output_requirements(hashtab_t mods, int exclude_base, FILE * f)
277 {
278 	unsigned int i, j;
279 	hashtab_ptr_t cur, cur2;
280 	hashtab_t reqs;
281 	int found_req;
282 
283 	for (i = 0; i < mods->size; i++) {
284 		cur = mods->htable[i];
285 		while (cur != NULL) {
286 			reqs = (hashtab_t) cur->datum;
287 			assert(reqs);
288 			fprintf(f, "module: %s\n", cur->key);
289 			found_req = 0;
290 			for (j = 0; j < reqs->size; j++) {
291 				cur2 = reqs->htable[j];
292 				while (cur2 != NULL) {
293 					if (exclude_base
294 					    && strcmp(cur2->key,
295 						      BASE_NAME) == 0) {
296 						cur2 = cur2->next;
297 						continue;
298 					}
299 					found_req = 1;
300 					fprintf(f, "\t%s\n", cur2->key);
301 					cur2 = cur2->next;
302 				}
303 			}
304 			if (!found_req)
305 				fprintf(f, "\t[no dependencies]\n");
306 			cur = cur->next;
307 		}
308 	}
309 	fprintf(f, "}\n");
310 }
311 
312 /* Possible commands - see the command variable in
313  * main below and the man page for more info.
314  */
315 #define SHOW_DEPS    1
316 #define GEN_GRAPHVIZ 2
317 
main(int argc,char ** argv)318 int main(int argc, char **argv)
319 {
320 	int ch, i, num_mods;
321 	int verbose = 0, exclude_base = 1, command = SHOW_DEPS;
322 	char *basename;
323 	sepol_module_package_t *base, **mods;
324 	policydb_t *p;
325 	hashtab_t req;
326 
327 	while ((ch = getopt(argc, argv, "vgb")) != EOF) {
328 		switch (ch) {
329 		case 'v':
330 			verbose = 1;
331 			break;
332 		case 'g':
333 			command = GEN_GRAPHVIZ;
334 			break;
335 		case 'b':
336 			exclude_base = 0;
337 			break;
338 		default:
339 			usage(argv[0]);
340 		}
341 	}
342 
343 	/* check args */
344 	if (argc < 3 || !(optind != (argc - 1))) {
345 		fprintf(stderr,
346 			"%s:  You must provide the base module package and at least one other module package\n",
347 			argv[0]);
348 		usage(argv[0]);
349 	}
350 
351 	basename = argv[optind++];
352 	base = load_module(basename, argv[0]);
353 	if (!base) {
354 		fprintf(stderr,
355 			"%s:  Could not load base module from file %s\n",
356 			argv[0], basename);
357 		exit(1);
358 	}
359 
360 	num_mods = argc - optind;
361 	mods =
362 	    (sepol_module_package_t **) malloc(sizeof(sepol_module_package_t *)
363 					       * num_mods);
364 	if (!mods) {
365 		fprintf(stderr, "%s:  Out of memory\n", argv[0]);
366 		exit(1);
367 	}
368 	memset(mods, 0, sizeof(sepol_module_package_t *) * num_mods);
369 
370 	for (i = 0; optind < argc; optind++, i++) {
371 		mods[i] = load_module(argv[optind], argv[0]);
372 		if (!mods[i]) {
373 			fprintf(stderr,
374 				"%s:  Could not load module from file %s\n",
375 				argv[0], argv[optind]);
376 			exit(1);
377 		}
378 	}
379 
380 	if (sepol_link_packages(NULL, base, mods, num_mods, verbose)) {
381 		fprintf(stderr, "%s:  Error while linking packages\n", argv[0]);
382 		exit(1);
383 	}
384 
385 	p = (policydb_t *) sepol_module_package_get_policy(base);
386 	if (p == NULL)
387 		exit(1);
388 
389 	req = generate_requires(p);
390 	if (req == NULL)
391 		exit(1);
392 
393 	if (command == SHOW_DEPS)
394 		output_requirements(req, exclude_base, stdout);
395 	else
396 		output_graphviz(req, exclude_base, stdout);
397 
398 	sepol_module_package_free(base);
399 	for (i = 0; i < num_mods; i++)
400 		sepol_module_package_free(mods[i]);
401 
402 	free_requires(req);
403 
404 	exit(0);
405 }
406