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