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 */
35extern int semanage_lex_destroy(void);        /* defined in conf-scan.c */
36int semanage_error(const char *msg);
37
38extern FILE *semanage_in;
39extern char *semanage_text;
40
41static int parse_module_store(char *arg);
42static int parse_store_root_path(char *arg);
43static int parse_compiler_path(char *arg);
44static void semanage_conf_external_prog_destroy(external_prog_t *ep);
45static int new_external_prog(external_prog_t **chain);
46
47static semanage_conf_t *current_conf;
48static external_prog_t *new_external;
49static 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
72config_file:    config_line config_file
73        |       /* empty */
74        ;
75
76config_line:    single_opt
77        |       command_block
78        |       verify_block
79        ;
80
81single_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
100module_store:   MODULE_STORE '=' ARG {
101                        if (parse_module_store($3) != 0) {
102                                parse_errors++;
103                                YYABORT;
104                        }
105                        free($3);
106                }
107
108        ;
109
110store_root:     STORE_ROOT '=' ARG  {
111                        if (parse_store_root_path($3) != 0) {
112                                parse_errors++;
113                                YYABORT;
114                        }
115                        free($3);
116                }
117        ;
118
119compiler_dir:       COMPILER_DIR '=' ARG  {
120                        if (parse_compiler_path($3) != 0) {
121                                parse_errors++;
122                                YYABORT;
123                        }
124                        free($3);
125                }
126        ;
127
128ignore_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
140version:        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
151target_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
163expand_check:   EXPAND_CHECK '=' ARG  {
164                        current_conf->expand_check = atoi($3);
165                        free($3);
166                }
167        ;
168
169file_mode:   FILE_MODE '=' ARG  {
170                        current_conf->file_mode = strtoul($3, NULL, 8);
171                        free($3);
172                }
173        ;
174
175save_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
188save_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
200disable_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
211usepasswd: 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
222ignoredirs: IGNOREDIRS '=' ARG {
223	current_conf->ignoredirs = strdup($3);
224	free($3);
225 }
226
227handle_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
240bzip_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
249bzip_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
260remove_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
271command_block:
272                command_start external_opts BLOCK_END  {
273                        if (new_external->path == NULL) {
274                                parse_errors++;
275                                YYABORT;
276                        }
277                }
278        ;
279
280command_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
307verify_block:   verify_start external_opts BLOCK_END  {
308                        if (new_external->path == NULL) {
309                                parse_errors++;
310                                YYABORT;
311                        }
312                }
313        ;
314
315verify_start:   verify_start_tok {
316                        if ($1 == -1) {
317                                parse_errors++;
318                                YYABORT;
319                        }
320                }
321        ;
322
323verify_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
328external_opts:  external_opt external_opts
329        |       /* empty */
330        ;
331
332external_opt:   PROG_PATH '=' ARG  { PASSIGN(new_external->path, $3); }
333        |       PROG_ARGS '=' ARG  { PASSIGN(new_external->args, $3); }
334        ;
335
336%%
337
338static 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 */
411semanage_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
438static 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. */
451void 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
468int 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 */
486static 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
521static 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
532static 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 */
546static 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