1 /* Authors: Jason Tang     <jtang@tresys.com>
2  *          James Athey    <jathey@tresys.com>
3  *
4  * Copyright (C) 2004-2006 Tresys Technology, LLC
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2.1 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 %{
22 
23 #include "semanage_conf.h"
24 
25 #include <sepol/policydb.h>
26 #include <selinux/selinux.h>
27 #include <semanage/handle.h>
28 
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 extern int semanage_lex(void);                /* defined in conf-scan.c */
35 extern int semanage_lex_destroy(void);        /* defined in conf-scan.c */
36 int semanage_error(const char *msg);
37 
38 extern FILE *semanage_in;
39 extern char *semanage_text;
40 
41 static int parse_module_store(char *arg);
42 static int parse_store_root_path(char *arg);
43 static int parse_compiler_path(char *arg);
44 static void semanage_conf_external_prog_destroy(external_prog_t *ep);
45 static int new_external_prog(external_prog_t **chain);
46 
47 static semanage_conf_t *current_conf;
48 static external_prog_t *new_external;
49 static int parse_errors;
50 
51 #define PASSIGN(p1,p2) { free(p1); p1 = p2; }
52 
53 %}
54 
55 %name-prefix "semanage_"
56 
57 %union {
58         int d;
59         char *s;
60 }
61 
62 %token MODULE_STORE VERSION EXPAND_CHECK FILE_MODE SAVE_PREVIOUS SAVE_LINKED TARGET_PLATFORM COMPILER_DIR IGNORE_MODULE_CACHE STORE_ROOT OPTIMIZE_POLICY
63 %token LOAD_POLICY_START SETFILES_START SEFCONTEXT_COMPILE_START DISABLE_GENHOMEDIRCON HANDLE_UNKNOWN USEPASSWD IGNOREDIRS
64 %token BZIP_BLOCKSIZE BZIP_SMALL REMOVE_HLL
65 %token VERIFY_MOD_START VERIFY_LINKED_START VERIFY_KERNEL_START BLOCK_END
66 %token PROG_PATH PROG_ARGS
67 %token <s> ARG
68 %type <d> verify_start_tok
69 
70 %%
71 
72 config_file:    config_line config_file
73         |       /* empty */
74         ;
75 
76 config_line:    single_opt
77         |       command_block
78         |       verify_block
79         ;
80 
81 single_opt:     module_store
82         |       version
83         |       target_platform
84         |       store_root
85         |       compiler_dir
86         |       ignore_module_cache
87         |       expand_check
88         |       file_mode
89         |       save_previous
90         |       save_linked
91         |       disable_genhomedircon
92         |       usepasswd
93         |       ignoredirs
94         |       handle_unknown
95 	|	bzip_blocksize
96 	|	bzip_small
97 	|	remove_hll
98 	|	optimize_policy
99         ;
100 
101 module_store:   MODULE_STORE '=' ARG {
102                         if (parse_module_store($3) != 0) {
103                                 parse_errors++;
104                                 YYABORT;
105                         }
106                         free($3);
107                 }
108 
109         ;
110 
111 store_root:     STORE_ROOT '=' ARG  {
112                         if (parse_store_root_path($3) != 0) {
113                                 parse_errors++;
114                                 YYABORT;
115                         }
116                         free($3);
117                 }
118         ;
119 
120 compiler_dir:       COMPILER_DIR '=' ARG  {
121                         if (parse_compiler_path($3) != 0) {
122                                 parse_errors++;
123                                 YYABORT;
124                         }
125                         free($3);
126                 }
127         ;
128 
129 ignore_module_cache:	IGNORE_MODULE_CACHE '=' ARG  {
130 							if (strcasecmp($3, "true") == 0)
131 								current_conf->ignore_module_cache = 1;
132 							else if (strcasecmp($3, "false") == 0)
133 								current_conf->ignore_module_cache = 0;
134 							else {
135 								yyerror("disable-caching can only be 'true' or 'false'");
136 							}
137 							free($3);
138 						}
139         ;
140 
141 version:        VERSION '=' ARG  {
142                         current_conf->policyvers = atoi($3);
143                         free($3);
144                         if (current_conf->policyvers < sepol_policy_kern_vers_min() ||
145                             current_conf->policyvers > sepol_policy_kern_vers_max()) {
146                                 parse_errors++;
147                                 YYABORT;
148                         }
149                 }
150         ;
151 
152 target_platform: TARGET_PLATFORM '=' ARG  {
153                         if (strcasecmp($3, "selinux") == 0)
154                                 current_conf->target_platform = SEPOL_TARGET_SELINUX;
155                         else if (strcasecmp($3, "xen") == 0)
156                                 current_conf->target_platform = SEPOL_TARGET_XEN;
157                         else {
158                                 yyerror("target_platform can only be 'selinux' or 'xen'");
159                         }
160                         free($3);
161                 }
162         ;
163 
164 expand_check:   EXPAND_CHECK '=' ARG  {
165                         current_conf->expand_check = atoi($3);
166                         free($3);
167                 }
168         ;
169 
170 file_mode:   FILE_MODE '=' ARG  {
171                         current_conf->file_mode = strtoul($3, NULL, 8);
172                         free($3);
173                 }
174         ;
175 
176 save_previous:    SAVE_PREVIOUS '=' ARG {
177 	                if (strcasecmp($3, "true") == 0)
178 		                current_conf->save_previous = 1;
179 			else if (strcasecmp($3, "false") == 0)
180 				current_conf->save_previous = 0;
181 			else {
182 				yyerror("save-previous can only be 'true' or 'false'");
183 			}
184 			free($3);
185                 }
186         ;
187 
188 
189 save_linked:    SAVE_LINKED '=' ARG {
190 	                if (strcasecmp($3, "true") == 0)
191 		                current_conf->save_linked = 1;
192 			else if (strcasecmp($3, "false") == 0)
193 				current_conf->save_linked = 0;
194 			else {
195 				yyerror("save-linked can only be 'true' or 'false'");
196 			}
197 			free($3);
198                 }
199         ;
200 
201 disable_genhomedircon: DISABLE_GENHOMEDIRCON '=' ARG {
202 	if (strcasecmp($3, "false") == 0) {
203 		current_conf->disable_genhomedircon = 0;
204 	} else if (strcasecmp($3, "true") == 0) {
205 		current_conf->disable_genhomedircon = 1;
206 	} else {
207 		yyerror("disable-genhomedircon can only be 'true' or 'false'");
208 	}
209 	free($3);
210  }
211 
212 usepasswd: USEPASSWD '=' ARG {
213 	if (strcasecmp($3, "false") == 0) {
214 		current_conf->usepasswd = 0;
215 	} else if (strcasecmp($3, "true") == 0) {
216 		current_conf->usepasswd = 1;
217 	} else {
218 		yyerror("usepasswd can only be 'true' or 'false'");
219 	}
220 	free($3);
221  }
222 
223 ignoredirs: IGNOREDIRS '=' ARG {
224 	current_conf->ignoredirs = strdup($3);
225 	free($3);
226  }
227 
228 handle_unknown: HANDLE_UNKNOWN '=' ARG {
229 	if (strcasecmp($3, "deny") == 0) {
230 		current_conf->handle_unknown = SEPOL_DENY_UNKNOWN;
231 	} else if (strcasecmp($3, "reject") == 0) {
232 		current_conf->handle_unknown = SEPOL_REJECT_UNKNOWN;
233 	} else if (strcasecmp($3, "allow") == 0) {
234 		current_conf->handle_unknown = SEPOL_ALLOW_UNKNOWN;
235 	} else {
236 		yyerror("handle-unknown can only be 'deny', 'reject' or 'allow'");
237 	}
238 	free($3);
239  }
240 
241 bzip_blocksize:  BZIP_BLOCKSIZE '=' ARG {
242 	int blocksize = atoi($3);
243 	free($3);
244 	if (blocksize > 9)
245 		yyerror("bzip-blocksize can only be in the range 0-9");
246 	else
247 		current_conf->bzip_blocksize = blocksize;
248 }
249 
250 bzip_small:  BZIP_SMALL '=' ARG {
251 	if (strcasecmp($3, "false") == 0) {
252 		current_conf->bzip_small = 0;
253 	} else if (strcasecmp($3, "true") == 0) {
254 		current_conf->bzip_small = 1;
255 	} else {
256 		yyerror("bzip-small can only be 'true' or 'false'");
257 	}
258 	free($3);
259 }
260 
261 remove_hll:  REMOVE_HLL'=' ARG {
262 	if (strcasecmp($3, "false") == 0) {
263 		current_conf->remove_hll = 0;
264 	} else if (strcasecmp($3, "true") == 0) {
265 		current_conf->remove_hll = 1;
266 	} else {
267 		yyerror("remove-hll can only be 'true' or 'false'");
268 	}
269 	free($3);
270 }
271 
272 optimize_policy:  OPTIMIZE_POLICY '=' ARG {
273 	if (strcasecmp($3, "false") == 0) {
274 		current_conf->optimize_policy = 0;
275 	} else if (strcasecmp($3, "true") == 0) {
276 		current_conf->optimize_policy = 1;
277 	} else {
278 		yyerror("optimize-policy can only be 'true' or 'false'");
279 	}
280 	free($3);
281 }
282 
283 command_block:
284                 command_start external_opts BLOCK_END  {
285                         if (new_external->path == NULL) {
286                                 parse_errors++;
287                                 YYABORT;
288                         }
289                 }
290         ;
291 
292 command_start:
293                 LOAD_POLICY_START {
294                         semanage_conf_external_prog_destroy(current_conf->load_policy);
295                         current_conf->load_policy = NULL;
296                         if (new_external_prog(&current_conf->load_policy) == -1) {
297                                 parse_errors++;
298                                 YYABORT;
299                         }
300                 }
301         |       SETFILES_START {
302                         semanage_conf_external_prog_destroy(current_conf->setfiles);
303                         current_conf->setfiles = NULL;
304                         if (new_external_prog(&current_conf->setfiles) == -1) {
305                                 parse_errors++;
306                                 YYABORT;
307                         }
308                 }
309         |       SEFCONTEXT_COMPILE_START {
310                         semanage_conf_external_prog_destroy(current_conf->sefcontext_compile);
311                         current_conf->sefcontext_compile = NULL;
312                         if (new_external_prog(&current_conf->sefcontext_compile) == -1) {
313                                 parse_errors++;
314                                 YYABORT;
315                         }
316                 }
317         ;
318 
319 verify_block:   verify_start external_opts BLOCK_END  {
320                         if (new_external->path == NULL) {
321                                 parse_errors++;
322                                 YYABORT;
323                         }
324                 }
325         ;
326 
327 verify_start:   verify_start_tok {
328                         if ($1 == -1) {
329                                 parse_errors++;
330                                 YYABORT;
331                         }
332                 }
333         ;
334 
335 verify_start_tok: VERIFY_MOD_START  {$$ = new_external_prog(&current_conf->mod_prog);}
336         |       VERIFY_LINKED_START {$$ = new_external_prog(&current_conf->linked_prog);}
337         |       VERIFY_KERNEL_START {$$ = new_external_prog(&current_conf->kernel_prog);}
338         ;
339 
340 external_opts:  external_opt external_opts
341         |       /* empty */
342         ;
343 
344 external_opt:   PROG_PATH '=' ARG  { PASSIGN(new_external->path, $3); }
345         |       PROG_ARGS '=' ARG  { PASSIGN(new_external->args, $3); }
346         ;
347 
348 %%
349 
350 static int semanage_conf_init(semanage_conf_t * conf)
351 {
352 	conf->store_type = SEMANAGE_CON_DIRECT;
353 	conf->store_path = strdup(basename(selinux_policy_root()));
354 	conf->ignoredirs = NULL;
355 	conf->store_root_path = strdup("/var/lib/selinux");
356 	conf->compiler_directory_path = strdup("/usr/libexec/selinux/hll");
357 	conf->policyvers = sepol_policy_kern_vers_max();
358 	conf->target_platform = SEPOL_TARGET_SELINUX;
359 	conf->expand_check = 1;
360 	conf->handle_unknown = -1;
361 	conf->usepasswd = 1;
362 	conf->file_mode = 0644;
363 	conf->bzip_blocksize = 9;
364 	conf->bzip_small = 0;
365 	conf->ignore_module_cache = 0;
366 	conf->remove_hll = 0;
367 	conf->optimize_policy = 0;
368 
369 	conf->save_previous = 0;
370 	conf->save_linked = 0;
371 
372 	if ((conf->load_policy =
373 	     calloc(1, sizeof(*(current_conf->load_policy)))) == NULL) {
374 		return -1;
375 	}
376 
377 	if (access("/sbin/load_policy", X_OK) == 0) {
378 		conf->load_policy->path = strdup("/sbin/load_policy");
379 	} else {
380 		conf->load_policy->path = strdup("/usr/sbin/load_policy");
381 	}
382 	if (conf->load_policy->path == NULL) {
383 		return -1;
384 	}
385 	conf->load_policy->args = NULL;
386 
387 	if ((conf->setfiles =
388 	     calloc(1, sizeof(*(current_conf->setfiles)))) == NULL) {
389 		return -1;
390 	}
391 	if (access("/sbin/setfiles", X_OK) == 0) {
392 		conf->setfiles->path = strdup("/sbin/setfiles");
393 	} else {
394 		conf->setfiles->path = strdup("/usr/sbin/setfiles");
395 	}
396 	if ((conf->setfiles->path == NULL) ||
397 	    (conf->setfiles->args = strdup("-q -c $@ $<")) == NULL) {
398 		return -1;
399 	}
400 
401 	if ((conf->sefcontext_compile =
402 	     calloc(1, sizeof(*(current_conf->sefcontext_compile)))) == NULL) {
403 		return -1;
404 	}
405 	if (access("/sbin/sefcontext_compile", X_OK) == 0) {
406 		conf->sefcontext_compile->path = strdup("/sbin/sefcontext_compile");
407 	} else {
408 		conf->sefcontext_compile->path = strdup("/usr/sbin/sefcontext_compile");
409 	}
410 	if ((conf->sefcontext_compile->path == NULL) ||
411 	    (conf->sefcontext_compile->args = strdup("$@")) == NULL) {
412 		return -1;
413 	}
414 
415 	return 0;
416 }
417 
418 /* Parse a libsemanage configuration file.  THIS FUNCTION IS NOT
419  * THREAD-SAFE!	 Return a newly allocated semanage_conf_t *.  If the
420  * configuration file could be read, parse it; otherwise rely upon
421  * default values.  If the file could not be parsed correctly or if
422  * out of memory return NULL.
423  */
semanage_conf_parse(const char * config_filename)424 semanage_conf_t *semanage_conf_parse(const char *config_filename)
425 {
426 	if ((current_conf = calloc(1, sizeof(*current_conf))) == NULL) {
427 		return NULL;
428 	}
429 	if (semanage_conf_init(current_conf) == -1) {
430 		goto cleanup;
431 	}
432 	if ((semanage_in = fopen(config_filename, "r")) == NULL) {
433 		/* configuration file does not exist or could not be
434 		 * read.  THIS IS NOT AN ERROR.  just rely on the
435 		 * defaults. */
436 		return current_conf;
437 	}
438 	parse_errors = 0;
439 	semanage_parse();
440 	fclose(semanage_in);
441 	semanage_lex_destroy();
442 	if (parse_errors != 0) {
443 		goto cleanup;
444 	}
445 	return current_conf;
446       cleanup:
447 	semanage_conf_destroy(current_conf);
448 	return NULL;
449 }
450 
semanage_conf_external_prog_destroy(external_prog_t * ep)451 static void semanage_conf_external_prog_destroy(external_prog_t * ep)
452 {
453 	while (ep != NULL) {
454 		external_prog_t *next = ep->next;
455 		free(ep->path);
456 		free(ep->args);
457 		free(ep);
458 		ep = next;
459 	}
460 }
461 
462 /* Deallocates all space associated with a configuration struct,
463  * including the pointer itself. */
semanage_conf_destroy(semanage_conf_t * conf)464 void semanage_conf_destroy(semanage_conf_t * conf)
465 {
466 	if (conf != NULL) {
467 		free(conf->store_path);
468 		free(conf->ignoredirs);
469 		free(conf->store_root_path);
470 		free(conf->compiler_directory_path);
471 		semanage_conf_external_prog_destroy(conf->load_policy);
472 		semanage_conf_external_prog_destroy(conf->setfiles);
473 		semanage_conf_external_prog_destroy(conf->sefcontext_compile);
474 		semanage_conf_external_prog_destroy(conf->mod_prog);
475 		semanage_conf_external_prog_destroy(conf->linked_prog);
476 		semanage_conf_external_prog_destroy(conf->kernel_prog);
477 		free(conf);
478 	}
479 }
480 
semanage_error(const char * msg)481 int semanage_error(const char *msg)
482 {
483 	fprintf(stderr, "error parsing semanage configuration file: %s\n", msg);
484 	parse_errors++;
485 	return 0;
486 }
487 
488 /* Take the string argument for a module store.	 If it is exactly the
489  * word "direct" then have libsemanage directly manipulate the module
490  * store. The policy path will default to the active policy directory.
491  * Otherwise if it begins with a forward slash interpret it as
492  * an absolute path to a named socket, to which a policy server is
493  * listening on the other end.	Otherwise treat it as the host name to
494  * an external server; if there is a colon in the name then everything
495  * after gives a port number.  The default port number is 4242.
496  * Returns 0 on success, -1 if out of memory, -2 if a port number is
497  * illegal.
498  */
parse_module_store(char * arg)499 static int parse_module_store(char *arg)
500 {
501 	/* arg is already a strdup()ed copy of yytext */
502 	if (arg == NULL) {
503 		return -1;
504 	}
505 	free(current_conf->store_path);
506 	if (strcmp(arg, "direct") == 0) {
507 		current_conf->store_type = SEMANAGE_CON_DIRECT;
508 		current_conf->store_path =
509 		    strdup(basename(selinux_policy_root()));
510 		current_conf->server_port = -1;
511 	} else if (*arg == '/') {
512 		current_conf->store_type = SEMANAGE_CON_POLSERV_LOCAL;
513 		current_conf->store_path = strdup(arg);
514 		current_conf->server_port = -1;
515 	} else {
516 		char *s;
517 		current_conf->store_type = SEMANAGE_CON_POLSERV_REMOTE;
518 		if ((s = strchr(arg, ':')) == NULL) {
519 			current_conf->store_path = arg;
520 			current_conf->server_port = 4242;
521 		} else {
522 			char *endptr;
523 			*s = '\0';
524 			current_conf->store_path = arg;
525 			current_conf->server_port = strtol(s + 1, &endptr, 10);
526 			if (*(s + 1) == '\0' || *endptr != '\0') {
527 				return -2;
528 			}
529 		}
530 	}
531 	return 0;
532 }
533 
parse_store_root_path(char * arg)534 static int parse_store_root_path(char *arg)
535 {
536 	if (arg == NULL) {
537 		return -1;
538 	}
539 
540 	free(current_conf->store_root_path);
541 	current_conf->store_root_path = strdup(arg);
542 	return 0;
543 }
544 
parse_compiler_path(char * arg)545 static int parse_compiler_path(char *arg)
546 {
547 	if (arg == NULL) {
548 		return -1;
549 	}
550 	free(current_conf->compiler_directory_path);
551 	current_conf->compiler_directory_path = strdup(arg);
552 	return 0;
553 }
554 
555 /* Helper function; called whenever configuration file specifies
556  * another external program.  Returns 0 on success, -1 if out of
557  * memory.
558  */
new_external_prog(external_prog_t ** chain)559 static int new_external_prog(external_prog_t ** chain)
560 {
561 	if ((new_external = calloc(1, sizeof(*new_external))) == NULL) {
562 		return -1;
563 	}
564 	/* hook this new external program to the end of the chain */
565 	if (*chain == NULL) {
566 		*chain = new_external;
567 	} else {
568 		external_prog_t *prog = *chain;
569 		while (prog->next != NULL) {
570 			prog = prog->next;
571 		}
572 		prog->next = new_external;
573 	}
574 	return 0;
575 }
576