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