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
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         ;
99 
100 module_store:   MODULE_STORE '=' ARG {
101                         if (parse_module_store($3) != 0) {
102                                 parse_errors++;
103                                 YYABORT;
104                         }
105                         free($3);
106                 }
107 
108         ;
109 
110 store_root:     STORE_ROOT '=' ARG  {
111                         if (parse_store_root_path($3) != 0) {
112                                 parse_errors++;
113                                 YYABORT;
114                         }
115                         free($3);
116                 }
117         ;
118 
119 compiler_dir:       COMPILER_DIR '=' ARG  {
120                         if (parse_compiler_path($3) != 0) {
121                                 parse_errors++;
122                                 YYABORT;
123                         }
124                         free($3);
125                 }
126         ;
127 
128 ignore_module_cache:	IGNORE_MODULE_CACHE '=' ARG  {
129 							if (strcasecmp($3, "true") == 0)
130 								current_conf->ignore_module_cache = 1;
131 							else if (strcasecmp($3, "false") == 0)
132 								current_conf->ignore_module_cache = 0;
133 							else {
134 								yyerror("disable-caching can only be 'true' or 'false'");
135 							}
136 							free($3);
137 						}
138         ;
139 
140 version:        VERSION '=' ARG  {
141                         current_conf->policyvers = atoi($3);
142                         free($3);
143                         if (current_conf->policyvers < sepol_policy_kern_vers_min() ||
144                             current_conf->policyvers > sepol_policy_kern_vers_max()) {
145                                 parse_errors++;
146                                 YYABORT;
147                         }
148                 }
149         ;
150 
151 target_platform: TARGET_PLATFORM '=' ARG  {
152                         if (strcasecmp($3, "selinux") == 0)
153                                 current_conf->target_platform = SEPOL_TARGET_SELINUX;
154                         else if (strcasecmp($3, "xen") == 0)
155                                 current_conf->target_platform = SEPOL_TARGET_XEN;
156                         else {
157                                 yyerror("target_platform can only be 'selinux' or 'xen'");
158                         }
159                         free($3);
160                 }
161         ;
162 
163 expand_check:   EXPAND_CHECK '=' ARG  {
164                         current_conf->expand_check = atoi($3);
165                         free($3);
166                 }
167         ;
168 
169 file_mode:   FILE_MODE '=' ARG  {
170                         current_conf->file_mode = strtoul($3, NULL, 8);
171                         free($3);
172                 }
173         ;
174 
175 save_previous:    SAVE_PREVIOUS '=' ARG {
176 	                if (strcasecmp($3, "true") == 0)
177 		                current_conf->save_previous = 1;
178 			else if (strcasecmp($3, "false") == 0)
179 				current_conf->save_previous = 0;
180 			else {
181 				yyerror("save-previous can only be 'true' or 'false'");
182 			}
183 			free($3);
184                 }
185         ;
186 
187 
188 save_linked:    SAVE_LINKED '=' ARG {
189 	                if (strcasecmp($3, "true") == 0)
190 		                current_conf->save_linked = 1;
191 			else if (strcasecmp($3, "false") == 0)
192 				current_conf->save_linked = 0;
193 			else {
194 				yyerror("save-linked can only be 'true' or 'false'");
195 			}
196 			free($3);
197                 }
198         ;
199 
200 disable_genhomedircon: DISABLE_GENHOMEDIRCON '=' ARG {
201 	if (strcasecmp($3, "false") == 0) {
202 		current_conf->disable_genhomedircon = 0;
203 	} else if (strcasecmp($3, "true") == 0) {
204 		current_conf->disable_genhomedircon = 1;
205 	} else {
206 		yyerror("disable-genhomedircon can only be 'true' or 'false'");
207 	}
208 	free($3);
209  }
210 
211 usepasswd: USEPASSWD '=' ARG {
212 	if (strcasecmp($3, "false") == 0) {
213 		current_conf->usepasswd = 0;
214 	} else if (strcasecmp($3, "true") == 0) {
215 		current_conf->usepasswd = 1;
216 	} else {
217 		yyerror("usepasswd can only be 'true' or 'false'");
218 	}
219 	free($3);
220  }
221 
222 ignoredirs: IGNOREDIRS '=' ARG {
223 	current_conf->ignoredirs = strdup($3);
224 	free($3);
225  }
226 
227 handle_unknown: HANDLE_UNKNOWN '=' ARG {
228 	if (strcasecmp($3, "deny") == 0) {
229 		current_conf->handle_unknown = SEPOL_DENY_UNKNOWN;
230 	} else if (strcasecmp($3, "reject") == 0) {
231 		current_conf->handle_unknown = SEPOL_REJECT_UNKNOWN;
232 	} else if (strcasecmp($3, "allow") == 0) {
233 		current_conf->handle_unknown = SEPOL_ALLOW_UNKNOWN;
234 	} else {
235 		yyerror("handle-unknown can only be 'deny', 'reject' or 'allow'");
236 	}
237 	free($3);
238  }
239 
240 bzip_blocksize:  BZIP_BLOCKSIZE '=' ARG {
241 	int blocksize = atoi($3);
242 	free($3);
243 	if (blocksize > 9)
244 		yyerror("bzip-blocksize can only be in the range 0-9");
245 	else
246 		current_conf->bzip_blocksize = blocksize;
247 }
248 
249 bzip_small:  BZIP_SMALL '=' ARG {
250 	if (strcasecmp($3, "false") == 0) {
251 		current_conf->bzip_small = 0;
252 	} else if (strcasecmp($3, "true") == 0) {
253 		current_conf->bzip_small = 1;
254 	} else {
255 		yyerror("bzip-small can only be 'true' or 'false'");
256 	}
257 	free($3);
258 }
259 
260 remove_hll:  REMOVE_HLL'=' ARG {
261 	if (strcasecmp($3, "false") == 0) {
262 		current_conf->remove_hll = 0;
263 	} else if (strcasecmp($3, "true") == 0) {
264 		current_conf->remove_hll = 1;
265 	} else {
266 		yyerror("remove-hll can only be 'true' or 'false'");
267 	}
268 	free($3);
269 }
270 
271 command_block:
272                 command_start external_opts BLOCK_END  {
273                         if (new_external->path == NULL) {
274                                 parse_errors++;
275                                 YYABORT;
276                         }
277                 }
278         ;
279 
280 command_start:
281                 LOAD_POLICY_START {
282                         semanage_conf_external_prog_destroy(current_conf->load_policy);
283                         current_conf->load_policy = NULL;
284                         if (new_external_prog(&current_conf->load_policy) == -1) {
285                                 parse_errors++;
286                                 YYABORT;
287                         }
288                 }
289         |       SETFILES_START {
290                         semanage_conf_external_prog_destroy(current_conf->setfiles);
291                         current_conf->setfiles = NULL;
292                         if (new_external_prog(&current_conf->setfiles) == -1) {
293                                 parse_errors++;
294                                 YYABORT;
295                         }
296                 }
297         |       SEFCONTEXT_COMPILE_START {
298                         semanage_conf_external_prog_destroy(current_conf->sefcontext_compile);
299                         current_conf->sefcontext_compile = NULL;
300                         if (new_external_prog(&current_conf->sefcontext_compile) == -1) {
301                                 parse_errors++;
302                                 YYABORT;
303                         }
304                 }
305         ;
306 
307 verify_block:   verify_start external_opts BLOCK_END  {
308                         if (new_external->path == NULL) {
309                                 parse_errors++;
310                                 YYABORT;
311                         }
312                 }
313         ;
314 
315 verify_start:   verify_start_tok {
316                         if ($1 == -1) {
317                                 parse_errors++;
318                                 YYABORT;
319                         }
320                 }
321         ;
322 
323 verify_start_tok: VERIFY_MOD_START  {$$ = new_external_prog(&current_conf->mod_prog);}
324         |       VERIFY_LINKED_START {$$ = new_external_prog(&current_conf->linked_prog);}
325         |       VERIFY_KERNEL_START {$$ = new_external_prog(&current_conf->kernel_prog);}
326         ;
327 
328 external_opts:  external_opt external_opts
329         |       /* empty */
330         ;
331 
332 external_opt:   PROG_PATH '=' ARG  { PASSIGN(new_external->path, $3); }
333         |       PROG_ARGS '=' ARG  { PASSIGN(new_external->args, $3); }
334         ;
335 
336 %%
337 
338 static int semanage_conf_init(semanage_conf_t * conf)
339 {
340 	conf->store_type = SEMANAGE_CON_DIRECT;
341 	conf->store_path = strdup(basename(selinux_policy_root()));
342 	conf->ignoredirs = NULL;
343 	conf->store_root_path = strdup("/var/lib/selinux");
344 	conf->compiler_directory_path = strdup("/usr/libexec/selinux/hll");
345 	conf->policyvers = sepol_policy_kern_vers_max();
346 	conf->target_platform = SEPOL_TARGET_SELINUX;
347 	conf->expand_check = 1;
348 	conf->handle_unknown = -1;
349 	conf->usepasswd = 1;
350 	conf->file_mode = 0644;
351 	conf->bzip_blocksize = 9;
352 	conf->bzip_small = 0;
353 	conf->ignore_module_cache = 0;
354 	conf->remove_hll = 0;
355 
356 	conf->save_previous = 0;
357 	conf->save_linked = 0;
358 
359 	if ((conf->load_policy =
360 	     calloc(1, sizeof(*(current_conf->load_policy)))) == NULL) {
361 		return -1;
362 	}
363 
364 	if (access("/sbin/load_policy", X_OK) == 0) {
365 		conf->load_policy->path = strdup("/sbin/load_policy");
366 	} else {
367 		conf->load_policy->path = strdup("/usr/sbin/load_policy");
368 	}
369 	if (conf->load_policy->path == NULL) {
370 		return -1;
371 	}
372 	conf->load_policy->args = NULL;
373 
374 	if ((conf->setfiles =
375 	     calloc(1, sizeof(*(current_conf->setfiles)))) == NULL) {
376 		return -1;
377 	}
378 	if (access("/sbin/setfiles", X_OK) == 0) {
379 		conf->setfiles->path = strdup("/sbin/setfiles");
380 	} else {
381 		conf->setfiles->path = strdup("/usr/sbin/setfiles");
382 	}
383 	if ((conf->setfiles->path == NULL) ||
384 	    (conf->setfiles->args = strdup("-q -c $@ $<")) == NULL) {
385 		return -1;
386 	}
387 
388 	if ((conf->sefcontext_compile =
389 	     calloc(1, sizeof(*(current_conf->sefcontext_compile)))) == NULL) {
390 		return -1;
391 	}
392 	if (access("/sbin/sefcontext_compile", X_OK) == 0) {
393 		conf->sefcontext_compile->path = strdup("/sbin/sefcontext_compile");
394 	} else {
395 		conf->sefcontext_compile->path = strdup("/usr/sbin/sefcontext_compile");
396 	}
397 	if ((conf->sefcontext_compile->path == NULL) ||
398 	    (conf->sefcontext_compile->args = strdup("$@")) == NULL) {
399 		return -1;
400 	}
401 
402 	return 0;
403 }
404 
405 /* Parse a libsemanage configuration file.  THIS FUNCTION IS NOT
406  * THREAD-SAFE!	 Return a newly allocated semanage_conf_t *.  If the
407  * configuration file could be read, parse it; otherwise rely upon
408  * default values.  If the file could not be parsed correctly or if
409  * out of memory return NULL.
410  */
semanage_conf_parse(const char * config_filename)411 semanage_conf_t *semanage_conf_parse(const char *config_filename)
412 {
413 	if ((current_conf = calloc(1, sizeof(*current_conf))) == NULL) {
414 		return NULL;
415 	}
416 	if (semanage_conf_init(current_conf) == -1) {
417 		goto cleanup;
418 	}
419 	if ((semanage_in = fopen(config_filename, "r")) == NULL) {
420 		/* configuration file does not exist or could not be
421 		 * read.  THIS IS NOT AN ERROR.  just rely on the
422 		 * defaults. */
423 		return current_conf;
424 	}
425 	parse_errors = 0;
426 	semanage_parse();
427 	fclose(semanage_in);
428 	semanage_lex_destroy();
429 	if (parse_errors != 0) {
430 		goto cleanup;
431 	}
432 	return current_conf;
433       cleanup:
434 	semanage_conf_destroy(current_conf);
435 	return NULL;
436 }
437 
semanage_conf_external_prog_destroy(external_prog_t * ep)438 static void semanage_conf_external_prog_destroy(external_prog_t * ep)
439 {
440 	while (ep != NULL) {
441 		external_prog_t *next = ep->next;
442 		free(ep->path);
443 		free(ep->args);
444 		free(ep);
445 		ep = next;
446 	}
447 }
448 
449 /* Deallocates all space associated with a configuration struct,
450  * including the pointer itself. */
semanage_conf_destroy(semanage_conf_t * conf)451 void semanage_conf_destroy(semanage_conf_t * conf)
452 {
453 	if (conf != NULL) {
454 		free(conf->store_path);
455 		free(conf->ignoredirs);
456 		free(conf->store_root_path);
457 		free(conf->compiler_directory_path);
458 		semanage_conf_external_prog_destroy(conf->load_policy);
459 		semanage_conf_external_prog_destroy(conf->setfiles);
460 		semanage_conf_external_prog_destroy(conf->sefcontext_compile);
461 		semanage_conf_external_prog_destroy(conf->mod_prog);
462 		semanage_conf_external_prog_destroy(conf->linked_prog);
463 		semanage_conf_external_prog_destroy(conf->kernel_prog);
464 		free(conf);
465 	}
466 }
467 
semanage_error(const char * msg)468 int semanage_error(const char *msg)
469 {
470 	fprintf(stderr, "error parsing semanage configuration file: %s\n", msg);
471 	parse_errors++;
472 	return 0;
473 }
474 
475 /* Take the string argument for a module store.	 If it is exactly the
476  * word "direct" then have libsemanage directly manipulate the module
477  * store. The policy path will default to the active policy directory.
478  * Otherwise if it begins with a forward slash interpret it as
479  * an absolute path to a named socket, to which a policy server is
480  * listening on the other end.	Otherwise treat it as the host name to
481  * an external server; if there is a colon in the name then everything
482  * after gives a port number.  The default port number is 4242.
483  * Returns 0 on success, -1 if out of memory, -2 if a port number is
484  * illegal.
485  */
parse_module_store(char * arg)486 static int parse_module_store(char *arg)
487 {
488 	/* arg is already a strdup()ed copy of yytext */
489 	if (arg == NULL) {
490 		return -1;
491 	}
492 	free(current_conf->store_path);
493 	if (strcmp(arg, "direct") == 0) {
494 		current_conf->store_type = SEMANAGE_CON_DIRECT;
495 		current_conf->store_path =
496 		    strdup(basename(selinux_policy_root()));
497 		current_conf->server_port = -1;
498 	} else if (*arg == '/') {
499 		current_conf->store_type = SEMANAGE_CON_POLSERV_LOCAL;
500 		current_conf->store_path = strdup(arg);
501 		current_conf->server_port = -1;
502 	} else {
503 		char *s;
504 		current_conf->store_type = SEMANAGE_CON_POLSERV_REMOTE;
505 		if ((s = strchr(arg, ':')) == NULL) {
506 			current_conf->store_path = arg;
507 			current_conf->server_port = 4242;
508 		} else {
509 			char *endptr;
510 			*s = '\0';
511 			current_conf->store_path = arg;
512 			current_conf->server_port = strtol(s + 1, &endptr, 10);
513 			if (*(s + 1) == '\0' || *endptr != '\0') {
514 				return -2;
515 			}
516 		}
517 	}
518 	return 0;
519 }
520 
parse_store_root_path(char * arg)521 static int parse_store_root_path(char *arg)
522 {
523 	if (arg == NULL) {
524 		return -1;
525 	}
526 
527 	free(current_conf->store_root_path);
528 	current_conf->store_root_path = strdup(arg);
529 	return 0;
530 }
531 
parse_compiler_path(char * arg)532 static int parse_compiler_path(char *arg)
533 {
534 	if (arg == NULL) {
535 		return -1;
536 	}
537 	free(current_conf->compiler_directory_path);
538 	current_conf->compiler_directory_path = strdup(arg);
539 	return 0;
540 }
541 
542 /* Helper function; called whenever configuration file specifies
543  * another external program.  Returns 0 on success, -1 if out of
544  * memory.
545  */
new_external_prog(external_prog_t ** chain)546 static int new_external_prog(external_prog_t ** chain)
547 {
548 	if ((new_external = calloc(1, sizeof(*new_external))) == NULL) {
549 		return -1;
550 	}
551 	/* hook this new external program to the end of the chain */
552 	if (*chain == NULL) {
553 		*chain = new_external;
554 	} else {
555 		external_prog_t *prog = *chain;
556 		while (prog->next != NULL) {
557 			prog = prog->next;
558 		}
559 		prog->next = new_external;
560 	}
561 	return 0;
562 }
563