1 /* Authors: Jason Tang <jtang (at) tresys.com> 2 * James Athey <jathey (at) 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 */ 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 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. */ 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 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 */ 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 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 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 */ 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