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
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 ;
99
100 module_store: MODULE_STORE '=' ARG {
101 if (parse_module_store($3) != 0) {
102 parse_errors++;
103 YYABORT;
104 }
105 free($3);
106 }
107
108 ;
109
110 store_root: STORE_ROOT '=' ARG {
111 if (parse_store_root_path($3) != 0) {
112 parse_errors++;
113 YYABORT;
114 }
115 free($3);
116 }
117 ;
118
119 compiler_dir: COMPILER_DIR '=' ARG {
120 if (parse_compiler_path($3) != 0) {
121 parse_errors++;
122 YYABORT;
123 }
124 free($3);
125 }
126 ;
127
128 ignore_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
140 version: 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
151 target_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
163 expand_check: EXPAND_CHECK '=' ARG {
164 current_conf->expand_check = atoi($3);
165 free($3);
166 }
167 ;
168
169 file_mode: FILE_MODE '=' ARG {
170 current_conf->file_mode = strtoul($3, NULL, 8);
171 free($3);
172 }
173 ;
174
175 save_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
188 save_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
200 disable_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
211 usepasswd: 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
222 ignoredirs: IGNOREDIRS '=' ARG {
223 current_conf->ignoredirs = strdup($3);
224 free($3);
225 }
226
227 handle_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
240 bzip_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
249 bzip_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
260 remove_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
271 command_block:
272 command_start external_opts BLOCK_END {
273 if (new_external->path == NULL) {
274 parse_errors++;
275 YYABORT;
276 }
277 }
278 ;
279
280 command_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(¤t_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(¤t_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(¤t_conf->sefcontext_compile) == -1) {
301 parse_errors++;
302 YYABORT;
303 }
304 }
305 ;
306
307 verify_block: verify_start external_opts BLOCK_END {
308 if (new_external->path == NULL) {
309 parse_errors++;
310 YYABORT;
311 }
312 }
313 ;
314
315 verify_start: verify_start_tok {
316 if ($1 == -1) {
317 parse_errors++;
318 YYABORT;
319 }
320 }
321 ;
322
323 verify_start_tok: VERIFY_MOD_START {$$ = new_external_prog(¤t_conf->mod_prog);}
324 | VERIFY_LINKED_START {$$ = new_external_prog(¤t_conf->linked_prog);}
325 | VERIFY_KERNEL_START {$$ = new_external_prog(¤t_conf->kernel_prog);}
326 ;
327
328 external_opts: external_opt external_opts
329 | /* empty */
330 ;
331
332 external_opt: PROG_PATH '=' ARG { PASSIGN(new_external->path, $3); }
333 | PROG_ARGS '=' ARG { PASSIGN(new_external->args, $3); }
334 ;
335
336 %%
337
338 static 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 */
semanage_conf_parse(const char * config_filename)411 semanage_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
semanage_conf_external_prog_destroy(external_prog_t * ep)438 static 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. */
semanage_conf_destroy(semanage_conf_t * conf)451 void 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
semanage_error(const char * msg)468 int 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 */
parse_module_store(char * arg)486 static 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
parse_store_root_path(char * arg)521 static 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
parse_compiler_path(char * arg)532 static 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 */
new_external_prog(external_prog_t ** chain)546 static 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