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