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