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