xref: /aosp_15_r20/external/selinux/checkpolicy/checkmodule.c (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
1 /*
2  * Authors: Joshua Brindle <[email protected]>
3  *	    Karl MacMillan <[email protected]>
4  *          Jason Tang     <[email protected]>
5  *
6  *
7  * Copyright (C) 2004-5 Tresys Technology, LLC
8  *	This program is free software; you can redistribute it and/or modify
9  *  	it under the terms of the GNU General Public License as published by
10  *	the Free Software Foundation, version 2.
11  */
12 
13 #include <getopt.h>
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <errno.h>
21 #include <sys/mman.h>
22 #include <libgen.h>
23 
24 #include <sepol/module_to_cil.h>
25 #include <sepol/policydb/policydb.h>
26 #include <sepol/policydb/services.h>
27 #include <sepol/policydb/conditional.h>
28 #include <sepol/policydb/hierarchy.h>
29 #include <sepol/policydb/expand.h>
30 #include <sepol/policydb/link.h>
31 #include <sepol/policydb/sidtab.h>
32 
33 #include "queue.h"
34 #include "parse_util.h"
35 
36 static sidtab_t sidtab;
37 
38 extern int mlspol;
39 extern int werror;
40 
41 static int handle_unknown = SEPOL_DENY_UNKNOWN;
42 static const char *txtfile = "policy.conf";
43 static const char *binfile = "policy";
44 
read_binary_policy(policydb_t * p,const char * file,const char * progname)45 static int read_binary_policy(policydb_t * p, const char *file, const char *progname)
46 {
47 	int fd;
48 	struct stat sb;
49 	void *map;
50 	struct policy_file f, *fp;
51 
52 	fd = open(file, O_RDONLY);
53 	if (fd < 0) {
54 		fprintf(stderr, "Can't open '%s':  %s\n",
55 			file, strerror(errno));
56 		return -1;
57 	}
58 	if (fstat(fd, &sb) < 0) {
59 		fprintf(stderr, "Can't stat '%s':  %s\n",
60 			file, strerror(errno));
61 		close(fd);
62 		return -1;
63 	}
64 	map =
65 	    mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
66 	close(fd);
67 	if (map == MAP_FAILED) {
68 		fprintf(stderr, "Can't map '%s':  %s\n", file, strerror(errno));
69 		return -1;
70 	}
71 	policy_file_init(&f);
72 	f.type = PF_USE_MEMORY;
73 	f.data = map;
74 	f.len = sb.st_size;
75 	fp = &f;
76 
77 	if (policydb_init(p)) {
78 		fprintf(stderr, "%s:  policydb_init:  Out of memory!\n",
79 			progname);
80 		return -1;
81 	}
82 	if (policydb_read(p, fp, 1)) {
83 		fprintf(stderr,
84 			"%s:  error(s) encountered while parsing configuration\n",
85 			progname);
86 		return -1;
87 	}
88 
89 	/* Check Policy Consistency */
90 	if (p->mls) {
91 		if (!mlspol) {
92 			fprintf(stderr, "%s:  MLS policy, but non-MLS"
93 				" is specified\n", progname);
94 			return -1;
95 		}
96 	} else {
97 		if (mlspol) {
98 			fprintf(stderr, "%s:  non-MLS policy, but MLS"
99 				" is specified\n", progname);
100 			return -1;
101 		}
102 	}
103 	return 0;
104 }
105 
write_binary_policy(policydb_t * p,FILE * outfp,unsigned int policy_type,unsigned int policyvers)106 static int write_binary_policy(policydb_t * p, FILE *outfp, unsigned int policy_type, unsigned int policyvers)
107 {
108 	struct policy_file pf;
109 
110 	p->policy_type = policy_type;
111 	p->policyvers = policyvers;
112 	p->handle_unknown = handle_unknown;
113 
114 	policy_file_init(&pf);
115 	pf.type = PF_USE_STDIO;
116 	pf.fp = outfp;
117 	return policydb_write(p, &pf);
118 }
119 
usage(const char * progname)120 static __attribute__((__noreturn__)) void usage(const char *progname)
121 {
122 	printf("usage:  %s [-h] [-V] [-b] [-C] [-E] [-U handle_unknown] [-m] [-M] [-N] [-o FILE] [-c VERSION] [INPUT]\n", progname);
123 	printf("Build base and policy modules.\n");
124 	printf("Options:\n");
125 	printf("  INPUT      build module from INPUT (else read from \"%s\")\n",
126 	       txtfile);
127 	printf("  -V         show policy versions created by this program\n");
128 	printf("  -b         treat input as a binary policy file\n");
129 	printf("  -C         output CIL policy instead of binary policy\n");
130 	printf("  -E         treat warnings as errors\n");
131 	printf("  -h         print usage\n");
132 	printf("  -U OPTION  How to handle unknown classes and permissions\n");
133 	printf("               deny: Deny unknown kernel checks\n");
134 	printf("               reject: Reject loading of policy with unknowns\n");
135 	printf("               allow: Allow unknown kernel checks\n");
136 	printf("  -m         build a policy module instead of a base module\n");
137 	printf("  -M         enable MLS policy\n");
138 	printf("  -N         do not check neverallow rules\n");
139 	printf("  -o FILE    write module to FILE (else just check syntax)\n");
140 	printf("  -c VERSION build a policy module targeting a modular policy version (%d-%d)\n",
141 	       MOD_POLICYDB_VERSION_MIN, MOD_POLICYDB_VERSION_MAX);
142 	exit(1);
143 }
144 
main(int argc,char ** argv)145 int main(int argc, char **argv)
146 {
147 	const char *file = txtfile, *outfile = NULL;
148 	unsigned int binary = 0, cil = 0, disable_neverallow = 0;
149 	unsigned int policy_type = POLICY_BASE;
150 	unsigned int policyvers = MOD_POLICYDB_VERSION_MAX;
151 	int ch;
152 	int show_version = 0;
153 	policydb_t modpolicydb;
154 	const struct option long_options[] = {
155 		{"help", no_argument, NULL, 'h'},
156 		{"output", required_argument, NULL, 'o'},
157 		{"binary", no_argument, NULL, 'b'},
158 		{"version", no_argument, NULL, 'V'},
159 		{"handle-unknown", required_argument, NULL, 'U'},
160 		{"mls", no_argument, NULL, 'M'},
161 		{"disable-neverallow", no_argument, NULL, 'N'},
162 		{"cil", no_argument, NULL, 'C'},
163 		{"werror", no_argument, NULL, 'E'},
164 		{NULL, 0, NULL, 0}
165 	};
166 
167 	while ((ch = getopt_long(argc, argv, "ho:bVEU:mMNCc:", long_options, NULL)) != -1) {
168 		switch (ch) {
169 		case 'h':
170 			usage(argv[0]);
171 			break;
172 		case 'o':
173 			outfile = optarg;
174 			break;
175 		case 'b':
176 			binary = 1;
177 			file = binfile;
178 			break;
179 		case 'V':
180 			show_version = 1;
181 			break;
182 		case 'E':
183 			werror = 1;
184 			break;
185 		case 'U':
186 			if (!strcasecmp(optarg, "deny")) {
187 				handle_unknown = DENY_UNKNOWN;
188 				break;
189 			}
190 			if (!strcasecmp(optarg, "reject")) {
191 				handle_unknown = REJECT_UNKNOWN;
192 				break;
193 			}
194 			if (!strcasecmp(optarg, "allow")) {
195 				handle_unknown = ALLOW_UNKNOWN;
196 				break;
197 			}
198 			usage(argv[0]);
199 		case 'm':
200 			policy_type = POLICY_MOD;
201 			break;
202 		case 'M':
203 			mlspol = 1;
204 			break;
205 		case 'N':
206 			disable_neverallow = 1;
207 			break;
208 		case 'C':
209 			cil = 1;
210 			break;
211 		case 'c': {
212 			long int n;
213 			errno = 0;
214 			n = strtol(optarg, NULL, 10);
215 			if (errno) {
216 				fprintf(stderr,
217 					"Invalid policyvers specified: %s\n",
218 					optarg);
219 				usage(argv[0]);
220 			}
221 
222 			if (n < MOD_POLICYDB_VERSION_MIN
223 			    || n > MOD_POLICYDB_VERSION_MAX) {
224 				fprintf(stderr,
225 					"policyvers value %ld not in range %d-%d\n",
226 					n, MOD_POLICYDB_VERSION_MIN,
227 					MOD_POLICYDB_VERSION_MAX);
228 				usage(argv[0]);
229 			}
230 
231 			policyvers = n;
232 			break;
233 		}
234 		default:
235 			usage(argv[0]);
236 		}
237 	}
238 
239 	if (show_version) {
240 		printf("Module versions %d-%d\n",
241 		       MOD_POLICYDB_VERSION_MIN, MOD_POLICYDB_VERSION_MAX);
242 		exit(0);
243 	}
244 
245 	if (handle_unknown && (policy_type != POLICY_BASE)) {
246 		fprintf(stderr, "%s:  Handling of unknown classes and permissions is only valid in the base module.\n", argv[0]);
247 		exit(1);
248 	}
249 
250 	if (binary && (policy_type != POLICY_BASE)) {
251 		fprintf(stderr, "%s:  -b and -m are incompatible with each other.\n", argv[0]);
252 		exit(1);
253 	}
254 
255 	if (optind != argc) {
256 		file = argv[optind++];
257 		if (optind != argc)
258 			usage(argv[0]);
259 	}
260 
261 	/* Set policydb and sidtab used by libsepol service functions
262 	   to my structures, so that I can directly populate and
263 	   manipulate them. */
264 	sepol_set_policydb(&modpolicydb);
265 	sepol_set_sidtab(&sidtab);
266 
267 	if (binary) {
268 		if (read_binary_policy(&modpolicydb, file, argv[0]) == -1) {
269 			exit(1);
270 		}
271 	} else {
272 		if (policydb_init(&modpolicydb)) {
273 			fprintf(stderr, "%s: out of memory!\n", argv[0]);
274 			exit(1);
275 		}
276 
277 		modpolicydb.policy_type = policy_type;
278 		modpolicydb.mls = mlspol;
279 		modpolicydb.handle_unknown = handle_unknown;
280 		modpolicydb.policyvers = policyvers;
281 
282 		if (read_source_policy(&modpolicydb, file, argv[0]) == -1) {
283 			exit(1);
284 		}
285 
286 		if (hierarchy_check_constraints(NULL, &modpolicydb)) {
287 			exit(1);
288 		}
289 	}
290 
291 	if (policy_type != POLICY_BASE && outfile) {
292 		char *out_name;
293 		char *separator;
294 		char *mod_name = modpolicydb.name;
295 		char *out_path = strdup(outfile);
296 		if (out_path == NULL) {
297 			fprintf(stderr, "%s:  out of memory\n", argv[0]);
298 			exit(1);
299 		}
300 		out_name = basename(out_path);
301 		separator = strrchr(out_name, '.');
302 		if (separator) {
303 			*separator = '\0';
304 		}
305 		if (strcmp(mod_name, out_name) != 0) {
306 			fprintf(stderr,	"%s:  Module name %s is different than the output base filename %s\n", argv[0], mod_name, out_name);
307 			exit(1);
308 		}
309 		free(out_path);
310 	}
311 
312 	if (modpolicydb.policy_type == POLICY_BASE && !cil) {
313 		/* Verify that we can successfully expand the base module. */
314 		policydb_t kernpolicydb;
315 
316 		if (policydb_init(&kernpolicydb)) {
317 			fprintf(stderr, "%s:  policydb_init failed\n", argv[0]);
318 			exit(1);
319 		}
320 		if (link_modules(NULL, &modpolicydb, NULL, 0, 0)) {
321 			fprintf(stderr, "%s:  link modules failed\n", argv[0]);
322 			exit(1);
323 		}
324 		if (expand_module(NULL, &modpolicydb, &kernpolicydb, /*verbose=*/0, !disable_neverallow)) {
325 			fprintf(stderr, "%s:  expand module failed\n", argv[0]);
326 			exit(1);
327 		}
328 		policydb_destroy(&kernpolicydb);
329 	}
330 
331 	if (policydb_load_isids(&modpolicydb, &sidtab))
332 		exit(1);
333 
334 	sepol_sidtab_destroy(&sidtab);
335 
336 	if (outfile) {
337 		FILE *outfp = fopen(outfile, "w");
338 
339 		if (!outfp) {
340 			fprintf(stderr, "%s:  error opening %s:  %s\n", argv[0], outfile, strerror(errno));
341 			exit(1);
342 		}
343 
344 		if (!cil) {
345 			if (write_binary_policy(&modpolicydb, outfp, policy_type, policyvers) != 0) {
346 				fprintf(stderr, "%s:  error writing %s\n", argv[0], outfile);
347 				exit(1);
348 			}
349 		} else {
350 			if (sepol_module_policydb_to_cil(outfp, &modpolicydb, 0) != 0) {
351 				fprintf(stderr, "%s:  error writing %s\n", argv[0], outfile);
352 				exit(1);
353 			}
354 		}
355 
356 		if (fclose(outfp)) {
357 			fprintf(stderr, "%s:  error closing %s:  %s\n", argv[0], outfile, strerror(errno));
358 			exit(1);
359 		}
360 	} else if (cil) {
361 		fprintf(stderr, "%s:  No file to write CIL was specified\n", argv[0]);
362 		exit(1);
363 	}
364 
365 	policydb_destroy(&modpolicydb);
366 
367 	return 0;
368 }
369 
370 /* FLASK */
371