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