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