1 /* Authors: Karl MacMillan <kmacmillan (at) tresys.com> 2 * Joshua Brindle <jbrindle (at) tresys.com> 3 * Jason Tang <jtang (at) tresys.com> 4 * Christopher Ashworth <cashworth (at) tresys.com> 5 * Chris PeBenito <cpebenito (at) tresys.com> 6 * Caleb Case <ccase (at) tresys.com> 7 * 8 * Copyright (C) 2004-2006,2009 Tresys Technology, LLC 9 * Copyright (C) 2005 Red Hat, Inc. 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2.1 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with this library; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 */ 25 26 /* This file contains semanage routines that manipulate the files on a 27 * local module store. Sandbox routines, used by both source and 28 * direct connections, are here as well. 29 */ 30 31 struct dbase_policydb; 32 typedef struct dbase_policydb dbase_t; 33 #define DBASE_DEFINED 34 35 #include "semanage_store.h" 36 #include "database_policydb.h" 37 #include "handle.h" 38 39 #include <selinux/selinux.h> 40 #include <sepol/policydb.h> 41 #include <sepol/module.h> 42 43 #include <assert.h> 44 #include <ctype.h> 45 #include <dirent.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <stdio.h> 49 #include <stdio_ext.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <sys/file.h> 54 #include <sys/stat.h> 55 #include <sys/types.h> 56 #include <sys/wait.h> 57 #include <limits.h> 58 #include <libgen.h> 59 60 #include "debug.h" 61 #include "utilities.h" 62 63 #define SEMANAGE_CONF_FILE "semanage.conf" 64 /* relative path names to enum semanage_paths to special files and 65 * directories for the module store */ 66 67 #define TRUE 1 68 69 enum semanage_file_defs { 70 SEMANAGE_ROOT, 71 SEMANAGE_TRANS_LOCK, 72 SEMANAGE_READ_LOCK, 73 SEMANAGE_NUM_FILES 74 }; 75 76 static char *semanage_paths[SEMANAGE_NUM_STORES][SEMANAGE_STORE_NUM_PATHS]; 77 static char *semanage_files[SEMANAGE_NUM_FILES] = { NULL }; 78 static int semanage_paths_initialized = 0; 79 80 /* These are paths relative to the bottom of the module store */ 81 static const char *semanage_relative_files[SEMANAGE_NUM_FILES] = { 82 "", 83 "/semanage.trans.LOCK", 84 "/semanage.read.LOCK" 85 }; 86 87 static const char *semanage_store_paths[SEMANAGE_NUM_STORES] = { 88 "/active", 89 "/previous", 90 "/tmp" 91 }; 92 93 /* relative path names to enum sandbox_paths for special files within 94 * a sandbox */ 95 static const char *semanage_sandbox_paths[SEMANAGE_STORE_NUM_PATHS] = { 96 "", 97 "/modules", 98 "/base.linked", 99 "/homedir_template", 100 "/file_contexts.template", 101 "/commit_num", 102 "/ports.local", 103 "/interfaces.local", 104 "/nodes.local", 105 "/booleans.local", 106 "/seusers.local", 107 "/users.local", 108 "/users_extra.local", 109 "/users_extra", 110 "/disable_dontaudit", 111 "/preserve_tunables", 112 "/modules/disabled", 113 "/policy.kern", 114 "/file_contexts.local", 115 "/file_contexts", 116 "/seusers" 117 }; 118 119 static char const * const semanage_final_prefix[SEMANAGE_FINAL_NUM] = { 120 "/final", 121 "", 122 }; 123 124 static char *semanage_final[SEMANAGE_FINAL_NUM] = { NULL }; 125 static char *semanage_final_suffix[SEMANAGE_FINAL_PATH_NUM] = { NULL }; 126 static char *semanage_final_paths[SEMANAGE_FINAL_NUM][SEMANAGE_FINAL_PATH_NUM] = {{ NULL }}; 127 128 /* A node used in a linked list of file contexts; used for sorting. 129 */ 130 typedef struct semanage_file_context_node { 131 char *path; 132 char *file_type; 133 char *context; 134 int path_len; 135 int effective_len; 136 int type_len; 137 int context_len; 138 int meta; /* position of first meta char in path, -1 if none */ 139 struct semanage_file_context_node *next; 140 } semanage_file_context_node_t; 141 142 /* A node used in a linked list of buckets that contain 143 * semanage_file_context_node lists. Used for sorting. 144 */ 145 typedef struct semanage_file_context_bucket { 146 semanage_file_context_node_t *data; 147 struct semanage_file_context_bucket *next; 148 } semanage_file_context_bucket_t; 149 150 /* A node used in a linked list of netfilter rules. 151 */ 152 typedef struct semanage_netfilter_context_node { 153 char *rule; 154 size_t rule_len; 155 struct semanage_netfilter_context_node *next; 156 } semanage_netfilter_context_node_t; 157 158 /* Initialize the paths to config file, lock files and store root. 159 */ 160 static int semanage_init_paths(const char *root) 161 { 162 size_t len, prefix_len; 163 int i; 164 165 if (!root) 166 return -1; 167 168 prefix_len = strlen(root); 169 170 for (i = 0; i < SEMANAGE_NUM_FILES; i++) { 171 len = (strlen(semanage_relative_files[i]) + prefix_len); 172 semanage_files[i] = calloc(len + 1, sizeof(char)); 173 if (!semanage_files[i]) 174 return -1; 175 sprintf(semanage_files[i], "%s%s", root, 176 semanage_relative_files[i]); 177 } 178 179 return 0; 180 } 181 182 /* This initializes the paths inside the stores, this is only necessary 183 * when directly accessing the store 184 */ 185 static int semanage_init_store_paths(const char *root) 186 { 187 int i, j; 188 size_t len; 189 size_t prefix_len; 190 191 if (!root) 192 return -1; 193 194 prefix_len = strlen(root); 195 196 for (i = 0; i < SEMANAGE_NUM_STORES; i++) { 197 for (j = 0; j < SEMANAGE_STORE_NUM_PATHS; j++) { 198 len = prefix_len + strlen(semanage_store_paths[i]) 199 + strlen(semanage_sandbox_paths[j]); 200 semanage_paths[i][j] = calloc(len + 1, sizeof(char)); 201 if (!semanage_paths[i][j]) 202 goto cleanup; 203 sprintf(semanage_paths[i][j], "%s%s%s", root, 204 semanage_store_paths[i], 205 semanage_sandbox_paths[j]); 206 } 207 } 208 209 cleanup: 210 return 0; 211 } 212 213 static int semanage_init_final(semanage_handle_t *sh, const char *prefix) 214 { 215 assert(sh); 216 assert(prefix); 217 218 int status = 0; 219 size_t len; 220 const char *store_path = sh->conf->store_path; 221 size_t store_len = strlen(store_path); 222 223 /* SEMANAGE_FINAL_TMP */ 224 len = strlen(semanage_root()) + 225 strlen(prefix) + 226 strlen("/") + 227 strlen(semanage_final_prefix[SEMANAGE_FINAL_TMP]) + 228 store_len; 229 semanage_final[SEMANAGE_FINAL_TMP] = malloc(len + 1); 230 if (semanage_final[SEMANAGE_FINAL_TMP] == NULL) { 231 status = -1; 232 goto cleanup; 233 } 234 235 sprintf(semanage_final[SEMANAGE_FINAL_TMP], 236 "%s%s%s/%s", 237 semanage_root(), 238 prefix, 239 semanage_final_prefix[SEMANAGE_FINAL_TMP], 240 store_path); 241 242 /* SEMANAGE_FINAL_SELINUX */ 243 const char *selinux_root = selinux_path(); 244 len = strlen(semanage_root()) + 245 strlen(selinux_root) + 246 strlen(semanage_final_prefix[SEMANAGE_FINAL_SELINUX]) + 247 store_len; 248 semanage_final[SEMANAGE_FINAL_SELINUX] = malloc(len + 1); 249 if (semanage_final[SEMANAGE_FINAL_SELINUX] == NULL) { 250 status = -1; 251 goto cleanup; 252 } 253 254 sprintf(semanage_final[SEMANAGE_FINAL_SELINUX], 255 "%s%s%s%s", 256 semanage_root(), 257 selinux_root, 258 semanage_final_prefix[SEMANAGE_FINAL_SELINUX], 259 store_path); 260 261 cleanup: 262 if (status != 0) { 263 int i; 264 for (i = 0; i < SEMANAGE_FINAL_NUM; i++) { 265 free(semanage_final[i]); 266 semanage_final[i] = NULL; 267 } 268 } 269 270 return status; 271 } 272 273 static int semanage_init_final_suffix(semanage_handle_t *sh) 274 { 275 int ret = 0; 276 int status = 0; 277 char path[PATH_MAX]; 278 size_t offset = strlen(selinux_policy_root()); 279 280 semanage_final_suffix[SEMANAGE_FINAL_TOPLEVEL] = strdup(""); 281 if (semanage_final_suffix[SEMANAGE_FINAL_TOPLEVEL] == NULL) { 282 ERR(sh, "Unable to allocate space for policy top level path."); 283 status = -1; 284 goto cleanup; 285 } 286 287 semanage_final_suffix[SEMANAGE_FC] = 288 strdup(selinux_file_context_path() + offset); 289 if (semanage_final_suffix[SEMANAGE_FC] == NULL) { 290 ERR(sh, "Unable to allocate space for file context path."); 291 status = -1; 292 goto cleanup; 293 } 294 295 semanage_final_suffix[SEMANAGE_FC_HOMEDIRS] = 296 strdup(selinux_file_context_homedir_path() + offset); 297 if (semanage_final_suffix[SEMANAGE_FC_HOMEDIRS] == NULL) { 298 ERR(sh, "Unable to allocate space for file context home directory path."); 299 status = -1; 300 goto cleanup; 301 } 302 303 semanage_final_suffix[SEMANAGE_FC_LOCAL] = 304 strdup(selinux_file_context_local_path() + offset); 305 if (semanage_final_suffix[SEMANAGE_FC_LOCAL] == NULL) { 306 ERR(sh, "Unable to allocate space for local file context path."); 307 status = -1; 308 goto cleanup; 309 } 310 311 semanage_final_suffix[SEMANAGE_NC] = 312 strdup(selinux_netfilter_context_path() + offset); 313 if (semanage_final_suffix[SEMANAGE_NC] == NULL) { 314 ERR(sh, "Unable to allocate space for netfilter context path."); 315 status = -1; 316 goto cleanup; 317 } 318 319 semanage_final_suffix[SEMANAGE_SEUSERS] = 320 strdup(selinux_usersconf_path() + offset); 321 if (semanage_final_suffix[SEMANAGE_SEUSERS] == NULL) { 322 ERR(sh, "Unable to allocate space for userconf path."); 323 status = -1; 324 goto cleanup; 325 } 326 327 ret = snprintf(path, 328 sizeof(path), 329 "%s.%d", 330 selinux_binary_policy_path() + offset, 331 sh->conf->policyvers); 332 if (ret < 0 || ret >= (int)sizeof(path)) { 333 ERR(sh, "Unable to compose policy binary path."); 334 status = -1; 335 goto cleanup; 336 } 337 338 semanage_final_suffix[SEMANAGE_KERNEL] = strdup(path); 339 if (semanage_final_suffix[SEMANAGE_KERNEL] == NULL) { 340 ERR(sh, "Unable to allocate space for policy binary path."); 341 status = -1; 342 goto cleanup; 343 } 344 345 cleanup: 346 if (status != 0) { 347 int i; 348 for (i = 0; i < SEMANAGE_FINAL_PATH_NUM; i++) { 349 free(semanage_final_suffix[i]); 350 semanage_final_suffix[i] = NULL; 351 } 352 } 353 354 return status; 355 } 356 357 /* Initialize final paths. */ 358 static int semanage_init_final_paths(semanage_handle_t *sh) 359 { 360 int status = 0; 361 int i, j; 362 size_t len; 363 364 for (i = 0; i < SEMANAGE_FINAL_NUM; i++) { 365 for (j = 0; j < SEMANAGE_FINAL_PATH_NUM; j++) { 366 len = strlen(semanage_final[i]) 367 + strlen(semanage_final_suffix[j]); 368 369 semanage_final_paths[i][j] = malloc(len + 1); 370 if (semanage_final_paths[i][j] == NULL) { 371 ERR(sh, "Unable to allocate space for policy final path."); 372 status = -1; 373 goto cleanup; 374 } 375 376 sprintf(semanage_final_paths[i][j], 377 "%s%s", 378 semanage_final[i], 379 semanage_final_suffix[j]); 380 } 381 } 382 383 cleanup: 384 if (status != 0) { 385 for (i = 0; i < SEMANAGE_FINAL_NUM; i++) { 386 for (j = 0; j < SEMANAGE_FINAL_PATH_NUM; j++) { 387 free(semanage_final_paths[i][j]); 388 semanage_final_paths[i][j] = NULL; 389 } 390 } 391 } 392 393 return status; 394 } 395 396 /* THIS MUST BE THE FIRST FUNCTION CALLED IN THIS LIBRARY. If the 397 * library has nnot been initialized yet then call the functions that 398 * initialize the path variables. This function does nothing if it 399 * was previously called and that call was successful. Return 0 on 400 * success, -1 on error. 401 * 402 * Note that this function is NOT thread-safe. 403 */ 404 int semanage_check_init(semanage_handle_t *sh, const char *prefix) 405 { 406 int rc; 407 if (semanage_paths_initialized == 0) { 408 char root[PATH_MAX]; 409 410 rc = snprintf(root, 411 sizeof(root), 412 "%s%s/%s", 413 semanage_root(), 414 prefix, 415 sh->conf->store_path); 416 if (rc < 0 || rc >= (int)sizeof(root)) 417 return -1; 418 419 rc = semanage_init_paths(root); 420 if (rc) 421 return rc; 422 423 rc = semanage_init_store_paths(root); 424 if (rc) 425 return rc; 426 427 rc = semanage_init_final(sh, prefix); 428 if (rc) 429 return rc; 430 431 rc = semanage_init_final_suffix(sh); 432 if (rc) 433 return rc; 434 435 rc = semanage_init_final_paths(sh); 436 if (rc) 437 return rc; 438 439 semanage_paths_initialized = 1; 440 } 441 return 0; 442 } 443 444 /* Given a definition number, return a file name from the paths array */ 445 const char *semanage_fname(enum semanage_sandbox_defs file_enum) 446 { 447 return semanage_sandbox_paths[file_enum]; 448 } 449 450 /* Given a store location (active/previous/tmp) and a definition 451 * number, return a fully-qualified path to that file or directory. 452 * The caller must not alter the string returned (and hence why this 453 * function return type is const). 454 * 455 * This function shall never return a NULL, assuming that 456 * semanage_check_init() was previously called. 457 */ 458 const char *semanage_path(enum semanage_store_defs store, 459 enum semanage_sandbox_defs path_name) 460 { 461 assert(semanage_paths[store][path_name]); 462 return semanage_paths[store][path_name]; 463 } 464 465 /* Given a store location (tmp or selinux) and a definition 466 * number, return a fully-qualified path to that file or directory. 467 * The caller must not alter the string returned (and hence why this 468 * function return type is const). 469 * 470 * This function shall never return a NULL, assuming that 471 * semanage_check_init() was previously called. 472 */ 473 const char *semanage_final_path(enum semanage_final_defs store, 474 enum semanage_final_path_defs path_name) 475 { 476 assert(semanage_final_paths[store][path_name]); 477 return semanage_final_paths[store][path_name]; 478 } 479 480 /* Return a fully-qualified path + filename to the semanage 481 * configuration file. If semanage.conf file in the semanage 482 * root is cannot be read, use the default semanage.conf as a 483 * fallback. 484 * 485 * The caller is responsible for freeing the returned string. 486 */ 487 char *semanage_conf_path(void) 488 { 489 char *semanage_conf = NULL; 490 int len; 491 492 len = strlen(semanage_root()) + strlen(selinux_path()) + strlen(SEMANAGE_CONF_FILE); 493 semanage_conf = calloc(len + 1, sizeof(char)); 494 if (!semanage_conf) 495 return NULL; 496 snprintf(semanage_conf, len + 1, "%s%s%s", semanage_root(), selinux_path(), 497 SEMANAGE_CONF_FILE); 498 499 if (access(semanage_conf, R_OK) != 0) { 500 snprintf(semanage_conf, len + 1, "%s%s", selinux_path(), SEMANAGE_CONF_FILE); 501 } 502 503 return semanage_conf; 504 } 505 506 /**************** functions that create module store ***************/ 507 508 /* Check that the semanage store exists. If 'create' is non-zero then 509 * create the directories. Returns 0 if module store exists (either 510 * already or just created), -1 if does not exist or could not be 511 * read, or -2 if it could not create the store. */ 512 int semanage_create_store(semanage_handle_t * sh, int create) 513 { 514 struct stat sb; 515 int mode_mask = R_OK | W_OK | X_OK; 516 const char *path = semanage_files[SEMANAGE_ROOT]; 517 int fd; 518 519 if (stat(path, &sb) == -1) { 520 if (errno == ENOENT && create) { 521 if (mkdir(path, S_IRWXU) == -1) { 522 ERR(sh, "Could not create module store at %s.", 523 path); 524 return -2; 525 } 526 } else { 527 if (create) 528 ERR(sh, 529 "Could not read from module store at %s.", 530 path); 531 return -1; 532 } 533 } else { 534 if (!S_ISDIR(sb.st_mode) || access(path, mode_mask) == -1) { 535 ERR(sh, 536 "Could not access module store at %s, or it is not a directory.", 537 path); 538 return -1; 539 } 540 } 541 path = semanage_path(SEMANAGE_ACTIVE, SEMANAGE_TOPLEVEL); 542 if (stat(path, &sb) == -1) { 543 if (errno == ENOENT && create) { 544 if (mkdir(path, S_IRWXU) == -1) { 545 ERR(sh, 546 "Could not create module store, active subdirectory at %s.", 547 path); 548 return -2; 549 } 550 } else { 551 ERR(sh, 552 "Could not read from module store, active subdirectory at %s.", 553 path); 554 return -1; 555 } 556 } else { 557 if (!S_ISDIR(sb.st_mode) || access(path, mode_mask) == -1) { 558 ERR(sh, 559 "Could not access module store active subdirectory at %s, or it is not a directory.", 560 path); 561 return -1; 562 } 563 } 564 path = semanage_path(SEMANAGE_ACTIVE, SEMANAGE_MODULES); 565 if (stat(path, &sb) == -1) { 566 if (errno == ENOENT && create) { 567 if (mkdir(path, S_IRWXU) == -1) { 568 ERR(sh, 569 "Could not create module store, active modules subdirectory at %s.", 570 path); 571 return -2; 572 } 573 } else { 574 ERR(sh, 575 "Could not read from module store, active modules subdirectory at %s.", 576 path); 577 return -1; 578 } 579 } else { 580 if (!S_ISDIR(sb.st_mode) || access(path, mode_mask) == -1) { 581 ERR(sh, 582 "Could not access module store active modules subdirectory at %s, or it is not a directory.", 583 path); 584 return -1; 585 } 586 } 587 path = semanage_files[SEMANAGE_READ_LOCK]; 588 if (stat(path, &sb) == -1) { 589 if (errno == ENOENT && create) { 590 if ((fd = creat(path, S_IRUSR | S_IWUSR)) == -1) { 591 ERR(sh, "Could not create lock file at %s.", 592 path); 593 return -2; 594 } 595 close(fd); 596 } else { 597 ERR(sh, "Could not read lock file at %s.", path); 598 return -1; 599 } 600 } else { 601 if (!S_ISREG(sb.st_mode) || access(path, R_OK | W_OK) == -1) { 602 ERR(sh, "Could not access lock file at %s.", path); 603 return -1; 604 } 605 } 606 return 0; 607 } 608 609 /* returns <0 if the active store cannot be read or doesn't exist 610 * 0 if the store exists but the lock file cannot be accessed 611 * SEMANAGE_CAN_READ if the store can be read and the lock file used 612 * SEMANAGE_CAN_WRITE if the modules directory and binary policy dir can be written to 613 */ 614 int semanage_store_access_check(void) 615 { 616 const char *path; 617 int rc = -1; 618 619 /* read access on active store */ 620 path = semanage_path(SEMANAGE_ACTIVE, SEMANAGE_TOPLEVEL); 621 if (access(path, R_OK | X_OK) != 0) 622 goto out; 623 624 /* we can read the active store meaning it is managed 625 * so now we return 0 to indicate no error */ 626 rc = 0; 627 628 /* read access on lock file required for locking 629 * write access necessary if the lock file does not exist 630 */ 631 path = semanage_files[SEMANAGE_READ_LOCK]; 632 if (access(path, R_OK) != 0) { 633 if (access(path, F_OK) == 0) { 634 goto out; 635 } 636 637 path = semanage_files[SEMANAGE_ROOT]; 638 if (access(path, R_OK | W_OK | X_OK) != 0) { 639 goto out; 640 } 641 } 642 643 /* everything needed for reading has been checked */ 644 rc = SEMANAGE_CAN_READ; 645 646 /* check the modules directory */ 647 path = semanage_path(SEMANAGE_ACTIVE, SEMANAGE_MODULES); 648 if (access(path, R_OK | W_OK | X_OK) != 0) 649 goto out; 650 651 rc = SEMANAGE_CAN_WRITE; 652 653 out: 654 return rc; 655 } 656 657 /********************* other I/O functions *********************/ 658 659 /* Callback used by scandir() to select files. */ 660 static int semanage_filename_select(const struct dirent *d) 661 { 662 if (d->d_name[0] == '.' 663 && (d->d_name[1] == '\0' 664 || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) 665 return 0; 666 return 1; 667 } 668 669 /* Copies a file from src to dst. If dst already exists then 670 * overwrite it. Returns 0 on success, -1 on error. */ 671 int semanage_copy_file(const char *src, const char *dst, mode_t mode) 672 { 673 int in, out, retval = 0, amount_read, n, errsv = errno; 674 char tmp[PATH_MAX]; 675 char buf[4192]; 676 mode_t mask; 677 678 n = snprintf(tmp, PATH_MAX, "%s.tmp", dst); 679 if (n < 0 || n >= PATH_MAX) 680 return -1; 681 682 if ((in = open(src, O_RDONLY)) == -1) { 683 return -1; 684 } 685 686 if (!mode) 687 mode = S_IRUSR | S_IWUSR; 688 689 mask = umask(0); 690 if ((out = open(tmp, O_WRONLY | O_CREAT | O_TRUNC, mode)) == -1) { 691 umask(mask); 692 errsv = errno; 693 close(in); 694 retval = -1; 695 goto out; 696 } 697 umask(mask); 698 while (retval == 0 && (amount_read = read(in, buf, sizeof(buf))) > 0) { 699 if (write(out, buf, amount_read) < 0) { 700 errsv = errno; 701 retval = -1; 702 } 703 } 704 if (amount_read < 0) { 705 errsv = errno; 706 retval = -1; 707 } 708 close(in); 709 if (close(out) < 0) { 710 errsv = errno; 711 retval = -1; 712 } 713 714 if (!retval && rename(tmp, dst) == -1) 715 return -1; 716 717 out: 718 errno = errsv; 719 return retval; 720 } 721 722 static int semanage_copy_dir_flags(const char *src, const char *dst, int flag); 723 724 /* Copies all of the files from src to dst, recursing into 725 * subdirectories. Returns 0 on success, -1 on error. */ 726 static int semanage_copy_dir(const char *src, const char *dst) 727 { 728 return semanage_copy_dir_flags(src, dst, 1); 729 } 730 731 /* Copies all of the dirs from src to dst, recursing into 732 * subdirectories. If flag == 1, then copy regular files as 733 * well. Returns 0 on success, -1 on error. */ 734 static int semanage_copy_dir_flags(const char *src, const char *dst, int flag) 735 { 736 int i, len = 0, retval = -1; 737 struct stat sb; 738 struct dirent **names = NULL; 739 char path[PATH_MAX], path2[PATH_MAX]; 740 741 if ((len = scandir(src, &names, semanage_filename_select, NULL)) == -1) { 742 fprintf(stderr, "Could not read the contents of %s: %s\n", src, strerror(errno)); 743 return -1; 744 } 745 746 if (stat(dst, &sb) != 0) { 747 if (mkdir(dst, S_IRWXU) != 0) { 748 fprintf(stderr, "Could not create %s: %s\n", dst, strerror(errno)); 749 goto cleanup; 750 } 751 } 752 753 for (i = 0; i < len; i++) { 754 snprintf(path, sizeof(path), "%s/%s", src, names[i]->d_name); 755 /* stat() to see if this entry is a file or not since 756 * d_type isn't set properly on XFS */ 757 if (stat(path, &sb)) { 758 goto cleanup; 759 } 760 snprintf(path2, sizeof(path2), "%s/%s", dst, names[i]->d_name); 761 if (S_ISDIR(sb.st_mode)) { 762 if (mkdir(path2, 0700) == -1 || 763 semanage_copy_dir_flags(path, path2, flag) == -1) { 764 goto cleanup; 765 } 766 } else if (S_ISREG(sb.st_mode) && flag == 1) { 767 if (semanage_copy_file(path, path2, sb.st_mode) < 0) { 768 goto cleanup; 769 } 770 } 771 } 772 retval = 0; 773 cleanup: 774 for (i = 0; names != NULL && i < len; i++) { 775 free(names[i]); 776 } 777 free(names); 778 return retval; 779 } 780 781 /* Recursively removes the contents of a directory along with the 782 * directory itself. Returns 0 on success, non-zero on error. */ 783 int semanage_remove_directory(const char *path) 784 { 785 struct dirent **namelist = NULL; 786 int num_entries, i; 787 if ((num_entries = scandir(path, &namelist, semanage_filename_select, 788 NULL)) == -1) { 789 return -1; 790 } 791 for (i = 0; i < num_entries; i++) { 792 char s[NAME_MAX]; 793 struct stat buf; 794 snprintf(s, sizeof(s), "%s/%s", path, namelist[i]->d_name); 795 if (stat(s, &buf) == -1) { 796 return -2; 797 } 798 if (S_ISDIR(buf.st_mode)) { 799 int retval; 800 if ((retval = semanage_remove_directory(s)) != 0) { 801 return retval; 802 } 803 } else { 804 if (remove(s) == -1) { 805 return -3; 806 } 807 } 808 free(namelist[i]); 809 } 810 free(namelist); 811 if (rmdir(path) == -1) { 812 return -4; 813 } 814 return 0; 815 } 816 817 int semanage_mkpath(semanage_handle_t *sh, const char *path) 818 { 819 char fn[PATH_MAX]; 820 char *c; 821 int rc = 0; 822 823 if (strlen(path) >= PATH_MAX) { 824 return -1; 825 } 826 827 for (c = strcpy(fn, path) + 1; *c != '\0'; c++) { 828 if (*c != '/') { 829 continue; 830 } 831 832 *c = '\0'; 833 rc = semanage_mkdir(sh, fn); 834 if (rc < 0) { 835 goto cleanup; 836 } 837 *c = '/'; 838 } 839 rc = semanage_mkdir(sh, fn); 840 841 cleanup: 842 return rc; 843 } 844 845 int semanage_mkdir(semanage_handle_t *sh, const char *path) 846 { 847 int status = 0; 848 struct stat sb; 849 850 /* check if directory already exists */ 851 if (stat(path, &sb) != 0) { 852 /* make the modules directory */ 853 if (mkdir(path, S_IRWXU) != 0) { 854 ERR(sh, "Cannot make directory at %s", path); 855 status = -1; 856 goto cleanup; 857 858 } 859 } 860 else { 861 /* check that it really is a directory */ 862 if (!S_ISDIR(sb.st_mode)) { 863 ERR(sh, "Directory path taken by non-directory file at %s.", path); 864 status = -1; 865 goto cleanup; 866 } 867 } 868 869 cleanup: 870 return status; 871 } 872 873 /********************* sandbox management routines *********************/ 874 875 /* Creates a sandbox for a single client. Returns 0 if a 876 * sandbox was created, -1 on error. 877 */ 878 int semanage_make_sandbox(semanage_handle_t * sh) 879 { 880 const char *sandbox = semanage_path(SEMANAGE_TMP, SEMANAGE_TOPLEVEL); 881 struct stat buf; 882 int errsv; 883 884 if (stat(sandbox, &buf) == -1) { 885 if (errno != ENOENT) { 886 ERR(sh, "Error scanning directory %s.", sandbox); 887 return -1; 888 } 889 errno = 0; 890 } else { 891 /* remove the old sandbox */ 892 if (semanage_remove_directory(sandbox) != 0) { 893 ERR(sh, "Error removing old sandbox directory %s.", 894 sandbox); 895 return -1; 896 } 897 } 898 899 if (mkdir(sandbox, S_IRWXU) == -1 || 900 semanage_copy_dir(semanage_path(SEMANAGE_ACTIVE, SEMANAGE_TOPLEVEL), 901 sandbox) == -1) { 902 ERR(sh, "Could not copy files to sandbox %s.", sandbox); 903 goto cleanup; 904 } 905 return 0; 906 907 cleanup: 908 errsv = errno; 909 semanage_remove_directory(sandbox); 910 errno = errsv; 911 return -1; 912 } 913 914 /* Create final temporary space. Returns -1 on error 0 on success. */ 915 int semanage_make_final(semanage_handle_t *sh) 916 { 917 int status = 0; 918 int ret = 0; 919 char fn[PATH_MAX]; 920 921 /* Create tmp dir if it does not exist. */ 922 ret = snprintf(fn, 923 sizeof(fn), 924 "%s%s%s", 925 semanage_root(), 926 sh->conf->store_root_path, 927 semanage_final_prefix[SEMANAGE_FINAL_TMP]); 928 if (ret < 0 || ret >= (int)sizeof(fn)) { 929 ERR(sh, "Unable to compose the final tmp path."); 930 status = -1; 931 goto cleanup; 932 } 933 934 ret = semanage_mkdir(sh, fn); 935 if (ret != 0) { 936 ERR(sh, "Unable to create temporary directory for final files at %s", fn); 937 status = -1; 938 goto cleanup; 939 } 940 941 /* Delete store specific dir if it exists. */ 942 ret = semanage_remove_directory( 943 semanage_final_path(SEMANAGE_FINAL_TMP, 944 SEMANAGE_FINAL_TOPLEVEL)); 945 if (ret < -1) { 946 status = -1; 947 goto cleanup; 948 } 949 950 // Build final directory structure 951 int i; 952 for (i = 1; i < SEMANAGE_FINAL_PATH_NUM; i++) { 953 if (strlen(semanage_final_path(SEMANAGE_FINAL_TMP, i)) >= sizeof(fn)) { 954 ERR(sh, "Unable to compose the final paths."); 955 status = -1; 956 goto cleanup; 957 } 958 strcpy(fn, semanage_final_path(SEMANAGE_FINAL_TMP, i)); 959 ret = semanage_mkpath(sh, dirname(fn)); 960 if (ret < 0) { 961 status = -1; 962 goto cleanup; 963 } 964 } 965 966 cleanup: 967 return status; 968 } 969 970 /* qsort comparison function for semanage_get_active_modules. */ 971 static int semanage_get_active_modules_cmp(const void *a, const void *b) 972 { 973 semanage_module_info_t *aa = (semanage_module_info_t *)a; 974 semanage_module_info_t *bb = (semanage_module_info_t *)b; 975 976 return strcmp(aa->name, bb->name); 977 } 978 979 int semanage_get_cil_paths(semanage_handle_t * sh, 980 semanage_module_info_t *modinfos, 981 int num_modinfos, 982 char *** filenames) 983 { 984 char path[PATH_MAX]; 985 char **names = NULL; 986 987 int ret; 988 int status = 0; 989 int i = 0; 990 991 names = calloc(num_modinfos, sizeof(*names)); 992 if (names == NULL) { 993 ERR(sh, "Error allocating space for filenames."); 994 status = -1; 995 goto cleanup; 996 } 997 998 for (i = 0; i < num_modinfos; i++) { 999 ret = semanage_module_get_path( 1000 sh, 1001 &modinfos[i], 1002 SEMANAGE_MODULE_PATH_CIL, 1003 path, 1004 sizeof(path)); 1005 if (ret != 0) { 1006 status = -1; 1007 goto cleanup; 1008 } 1009 1010 names[i] = strdup(path); 1011 1012 if (names[i] == NULL) { 1013 status = -1; 1014 goto cleanup; 1015 } 1016 } 1017 1018 cleanup: 1019 if (status != 0) { 1020 for (i = 0; i < num_modinfos; i++) { 1021 free(names[i]); 1022 } 1023 free(names); 1024 } else { 1025 *filenames = names; 1026 } 1027 1028 return status; 1029 } 1030 1031 /* Scans the modules directory for the current semanage handler. This 1032 * might be the active directory or sandbox, depending upon if the 1033 * handler has a transaction lock. Allocates and fills in *modinfos 1034 * with an array of module infos; length of array is stored in 1035 * *num_modules. The caller is responsible for free()ing *modinfos and its 1036 * individual elements. Upon success returns 0, -1 on error. 1037 */ 1038 int semanage_get_active_modules(semanage_handle_t * sh, 1039 semanage_module_info_t ** modinfo, 1040 int *num_modules) 1041 { 1042 assert(sh); 1043 assert(modinfo); 1044 assert(num_modules); 1045 *modinfo = NULL; 1046 *num_modules = 0; 1047 1048 int status = 0; 1049 int ret = 0; 1050 1051 int i = 0; 1052 int j = 0; 1053 1054 semanage_list_t *list = NULL; 1055 semanage_list_t *found = NULL; 1056 1057 semanage_module_info_t *all_modinfos = NULL; 1058 int all_modinfos_len = 0; 1059 1060 void *tmp = NULL; 1061 1062 /* get all modules */ 1063 ret = semanage_module_list_all(sh, &all_modinfos, &all_modinfos_len); 1064 if (ret != 0) { 1065 status = -1; 1066 goto cleanup; 1067 } 1068 1069 if (all_modinfos_len == 0) { 1070 goto cleanup; 1071 } 1072 1073 /* allocate enough for worst case */ 1074 (*modinfo) = calloc(all_modinfos_len, sizeof(**modinfo)); 1075 if ((*modinfo) == NULL) { 1076 ERR(sh, "Error allocating space for module information."); 1077 status = -1; 1078 goto cleanup; 1079 } 1080 1081 /* for each highest priority, enabled module get its path */ 1082 semanage_list_destroy(&list); 1083 j = 0; 1084 for (i = 0; i < all_modinfos_len; i++) { 1085 /* check if enabled */ 1086 if (all_modinfos[i].enabled != 1) continue; 1087 1088 /* check if we've seen this before (i.e. highest priority) */ 1089 found = semanage_list_find(list, all_modinfos[i].name); 1090 if (found == NULL) { 1091 ret = semanage_list_push(&list, all_modinfos[i].name); 1092 if (ret != 0) { 1093 ERR(sh, "Failed to add module name to list of known names."); 1094 status = -1; 1095 goto cleanup; 1096 } 1097 } 1098 else continue; 1099 1100 if (semanage_module_info_clone(sh, &all_modinfos[i], &(*modinfo)[j]) != 0) { 1101 status = -1; 1102 goto cleanup; 1103 } 1104 1105 j += 1; 1106 } 1107 1108 *num_modules = j; 1109 1110 if (j == 0) { 1111 free(*modinfo); 1112 *modinfo = NULL; 1113 goto cleanup; 1114 } 1115 1116 /* realloc the array to its min size */ 1117 tmp = realloc(*modinfo, j * sizeof(**modinfo)); 1118 if (tmp == NULL) { 1119 ERR(sh, "Error allocating space for filenames."); 1120 status = -1; 1121 goto cleanup; 1122 } 1123 *modinfo = tmp; 1124 1125 /* sort array on module name */ 1126 qsort(*modinfo, 1127 *num_modules, 1128 sizeof(**modinfo), 1129 semanage_get_active_modules_cmp); 1130 1131 cleanup: 1132 semanage_list_destroy(&list); 1133 1134 for (i = 0; i < all_modinfos_len; i++) { 1135 semanage_module_info_destroy(sh, &all_modinfos[i]); 1136 } 1137 free(all_modinfos); 1138 1139 if (status != 0) { 1140 for (i = 0; i < j; j++) { 1141 semanage_module_info_destroy(sh, &(*modinfo)[i]); 1142 } 1143 free(*modinfo); 1144 } 1145 1146 return status; 1147 } 1148 1149 /******************* routines that run external programs *******************/ 1150 1151 /* Appends a single character to a string. Returns a pointer to the 1152 * realloc()ated string. If out of memory return NULL; original 1153 * string will remain untouched. 1154 */ 1155 static char *append(char *s, char c) 1156 { 1157 size_t len = (s == NULL ? 0 : strlen(s)); 1158 char *new_s = realloc(s, len + 2); 1159 if (new_s == NULL) { 1160 return NULL; 1161 } 1162 s = new_s; 1163 s[len] = c; 1164 s[len + 1] = '\0'; 1165 return s; 1166 } 1167 1168 /* Append string 't' to string 's', realloc()ating 's' as needed. 't' 1169 * may be safely free()d afterwards. Returns a pointer to the 1170 * realloc()ated 's'. If out of memory return NULL; original strings 1171 * will remain untouched. 1172 */ 1173 static char *append_str(char *s, const char *t) 1174 { 1175 size_t s_len = (s == NULL ? 0 : strlen(s)); 1176 size_t t_len = (t == NULL ? 0 : strlen(t)); 1177 char *new_s = realloc(s, s_len + t_len + 1); 1178 if (new_s == NULL) { 1179 return NULL; 1180 } 1181 s = new_s; 1182 memcpy(s + s_len, t, t_len); 1183 s[s_len + t_len] = '\0'; 1184 return s; 1185 } 1186 1187 /* 1188 * Append an argument string to an argument vector. Replaces the 1189 * argument pointer passed in. Returns -1 on error. Increments 1190 * 'num_args' on success. 1191 */ 1192 static int append_arg(char ***argv, int *num_args, const char *arg) 1193 { 1194 char **a; 1195 1196 a = realloc(*argv, sizeof(**argv) * (*num_args + 1)); 1197 if (a == NULL) 1198 return -1; 1199 1200 *argv = a; 1201 a[*num_args] = NULL; 1202 1203 if (arg) { 1204 a[*num_args] = strdup(arg); 1205 if (!a[*num_args]) 1206 return -1; 1207 } 1208 (*num_args)++; 1209 return 0; 1210 } 1211 1212 /* free()s all strings within a null-terminated argument vector, as 1213 * well as the pointer itself. */ 1214 static void free_argv(char **argv) 1215 { 1216 int i; 1217 for (i = 0; argv != NULL && argv[i] != NULL; i++) { 1218 free(argv[i]); 1219 } 1220 free(argv); 1221 } 1222 1223 /* Take an argument string and split and place into an argument 1224 * vector. Respect normal quoting, double-quoting, and backslash 1225 * conventions. Perform substitutions on $@ and $< symbols. Returns 1226 * a NULL-terminated argument vector; caller is responsible for 1227 * free()ing the vector and its elements. */ 1228 static char **split_args(const char *arg0, char *arg_string, 1229 const char *new_name, const char *old_name) 1230 { 1231 char **argv = NULL, *s, *arg = NULL, *targ; 1232 int num_args = 0, in_quote = 0, in_dquote = 0, rc; 1233 1234 rc = append_arg(&argv, &num_args, arg0); 1235 if (rc) 1236 goto cleanup; 1237 s = arg_string; 1238 /* parse the argument string one character at a time, 1239 * repsecting quotes and other special characters */ 1240 while (s != NULL && *s != '\0') { 1241 switch (*s) { 1242 case '\\':{ 1243 if (*(s + 1) == '\0') { 1244 targ = append(arg, '\\'); 1245 if (targ == NULL) 1246 goto cleanup; 1247 arg = targ; 1248 } else { 1249 targ = append(arg, *(s + 1)); 1250 if (targ == NULL) 1251 goto cleanup; 1252 arg = targ; 1253 s++; 1254 } 1255 break; 1256 } 1257 case '\'':{ 1258 if (in_dquote) { 1259 targ = append(arg, *s); 1260 if (targ == NULL) 1261 goto cleanup; 1262 arg = targ; 1263 } else if (in_quote) { 1264 in_quote = 0; 1265 } else { 1266 in_quote = 1; 1267 targ = append(arg, '\0'); 1268 if (targ == NULL) 1269 goto cleanup; 1270 arg = targ; 1271 } 1272 break; 1273 } 1274 case '\"':{ 1275 if (in_quote) { 1276 targ = append(arg, *s); 1277 if (targ == NULL) 1278 goto cleanup; 1279 arg = targ; 1280 } else if (in_dquote) { 1281 in_dquote = 0; 1282 } else { 1283 in_dquote = 1; 1284 targ = append(arg, '\0'); 1285 if (targ == NULL) 1286 goto cleanup; 1287 arg = targ; 1288 } 1289 break; 1290 } 1291 case '$':{ 1292 switch (*(s + 1)) { 1293 case '@':{ 1294 targ = append_str(arg, new_name); 1295 if (targ == NULL) 1296 goto cleanup; 1297 arg = targ; 1298 s++; 1299 break; 1300 } 1301 case '<':{ 1302 targ = append_str(arg, old_name); 1303 if (targ == NULL) 1304 goto cleanup; 1305 arg = targ; 1306 s++; 1307 break; 1308 } 1309 default:{ 1310 targ = append(arg, *s); 1311 if (targ == NULL) 1312 goto cleanup; 1313 arg = targ; 1314 } 1315 } 1316 break; 1317 } 1318 default:{ 1319 if (isspace(*s) && !in_quote && !in_dquote) { 1320 if (arg != NULL) { 1321 rc = append_arg(&argv, &num_args, arg); 1322 free(arg); 1323 arg = NULL; 1324 } 1325 } else { 1326 if ((targ = append(arg, *s)) == NULL) { 1327 goto cleanup; 1328 } else { 1329 arg = targ; 1330 } 1331 } 1332 } 1333 } 1334 s++; 1335 } 1336 if (arg != NULL) { 1337 rc = append_arg(&argv, &num_args, arg); 1338 free(arg); 1339 arg = NULL; 1340 } 1341 /* explicitly add a NULL at the end */ 1342 rc = append_arg(&argv, &num_args, NULL); 1343 if (rc) 1344 goto cleanup; 1345 return argv; 1346 cleanup: 1347 free_argv(argv); 1348 free(arg); 1349 return NULL; 1350 } 1351 1352 /* Take the arguments given in v->args and expand any $ macros within. 1353 * Split the arguments into different strings (argv). Next fork and 1354 * execute the process. BE SURE THAT ALL FILE DESCRIPTORS ARE SET TO 1355 * CLOSE-ON-EXEC. Take the return value of the child process and 1356 * return it, -1 on error. 1357 */ 1358 static int semanage_exec_prog(semanage_handle_t * sh, 1359 external_prog_t * e, const char *new_name, 1360 const char *old_name) 1361 { 1362 char **argv; 1363 pid_t forkval; 1364 int status = 0; 1365 1366 argv = split_args(e->path, e->args, new_name, old_name); 1367 if (argv == NULL) { 1368 ERR(sh, "Out of memory!"); 1369 return -1; 1370 } 1371 1372 /* no need to use pthread_atfork() -- child will not be using 1373 * any mutexes. */ 1374 forkval = vfork(); 1375 if (forkval == 0) { 1376 /* child process. file descriptors will be closed 1377 * because they were set as close-on-exec. */ 1378 execve(e->path, argv, NULL); 1379 _exit(EXIT_FAILURE); /* if execve() failed */ 1380 } 1381 1382 free_argv(argv); 1383 1384 if (forkval == -1) { 1385 ERR(sh, "Error while forking process."); 1386 return -1; 1387 } 1388 1389 /* parent process. wait for child to finish */ 1390 if (waitpid(forkval, &status, 0) == -1 || !WIFEXITED(status)) { 1391 ERR(sh, "Child process %s did not exit cleanly.", 1392 e->path); 1393 return -1; 1394 } 1395 return WEXITSTATUS(status); 1396 } 1397 1398 /* reloads the policy pointed to by the handle, used locally by install 1399 * and exported for user reload requests */ 1400 int semanage_reload_policy(semanage_handle_t * sh) 1401 { 1402 int r = 0; 1403 1404 if (!sh) 1405 return -1; 1406 1407 if ((r = semanage_exec_prog(sh, sh->conf->load_policy, "", "")) != 0) { 1408 ERR(sh, "load_policy returned error code %d.", r); 1409 } 1410 return r; 1411 } 1412 1413 hidden_def(semanage_reload_policy) 1414 1415 /* This expands the file_context.tmpl file to file_context and homedirs.template */ 1416 int semanage_split_fc(semanage_handle_t * sh) 1417 { 1418 FILE *file_con = NULL; 1419 int fc = -1, hd = -1, retval = -1; 1420 char buf[PATH_MAX] = { 0 }; 1421 1422 /* I use fopen here instead of open so that I can use fgets which only reads a single line */ 1423 file_con = fopen(semanage_path(SEMANAGE_TMP, SEMANAGE_FC_TMPL), "r"); 1424 if (!file_con) { 1425 ERR(sh, "Could not open %s for reading.", 1426 semanage_path(SEMANAGE_TMP, SEMANAGE_FC_TMPL)); 1427 goto cleanup; 1428 } 1429 1430 fc = open(semanage_path(SEMANAGE_TMP, SEMANAGE_STORE_FC), 1431 O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); 1432 if (fc < 0) { 1433 ERR(sh, "Could not open %s for writing.", 1434 semanage_path(SEMANAGE_TMP, SEMANAGE_STORE_FC)); 1435 goto cleanup; 1436 } 1437 hd = open(semanage_path(SEMANAGE_TMP, SEMANAGE_HOMEDIR_TMPL), 1438 O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); 1439 if (hd < 0) { 1440 ERR(sh, "Could not open %s for writing.", 1441 semanage_path(SEMANAGE_TMP, SEMANAGE_HOMEDIR_TMPL)); 1442 goto cleanup; 1443 } 1444 1445 while (fgets_unlocked(buf, PATH_MAX, file_con)) { 1446 if (!strncmp(buf, "HOME_DIR", 8) || 1447 !strncmp(buf, "HOME_ROOT", 9) || strstr(buf, "ROLE") || 1448 strstr(buf, "USER")) { 1449 /* This contains one of the template variables, write it to homedir.template */ 1450 if (write(hd, buf, strlen(buf)) < 0) { 1451 ERR(sh, "Write to %s failed.", 1452 semanage_path(SEMANAGE_TMP, 1453 SEMANAGE_HOMEDIR_TMPL)); 1454 goto cleanup; 1455 } 1456 } else { 1457 if (write(fc, buf, strlen(buf)) < 0) { 1458 ERR(sh, "Write to %s failed.", 1459 semanage_path(SEMANAGE_TMP, SEMANAGE_STORE_FC)); 1460 goto cleanup; 1461 } 1462 } 1463 } 1464 1465 retval = 0; 1466 cleanup: 1467 if (file_con) 1468 fclose(file_con); 1469 if (fc >= 0) 1470 close(fc); 1471 if (hd >= 0) 1472 close(hd); 1473 1474 return retval; 1475 1476 } 1477 1478 static int sefcontext_compile(semanage_handle_t * sh, const char *path) { 1479 1480 int r; 1481 1482 if (access(path, F_OK) != 0) { 1483 return 0; 1484 } 1485 1486 if ((r = semanage_exec_prog(sh, sh->conf->sefcontext_compile, path, "")) != 0) { 1487 ERR(sh, "sefcontext_compile returned error code %d. Compiling %s", r, path); 1488 return -1; 1489 } 1490 1491 return 0; 1492 } 1493 1494 /* Load the contexts of the final tmp into the final selinux directory. 1495 * Return 0 on success, -3 on error. 1496 */ 1497 static int semanage_install_final_tmp(semanage_handle_t * sh) 1498 { 1499 int status = -3; 1500 int ret = 0; 1501 int i = 0; 1502 const char *src = NULL; 1503 const char *dst = NULL; 1504 struct stat sb; 1505 char fn[PATH_MAX]; 1506 1507 /* For each of the final files install it if it exists. 1508 * i = 1 to avoid copying the top level directory. 1509 */ 1510 for (i = 1; i < SEMANAGE_FINAL_PATH_NUM; i++) { 1511 src = semanage_final_path(SEMANAGE_FINAL_TMP, i); 1512 dst = semanage_final_path(SEMANAGE_FINAL_SELINUX, i); 1513 1514 /* skip file if src doesn't exist */ 1515 if (stat(src, &sb) != 0) continue; 1516 1517 /* skip genhomedircon if configured */ 1518 if (sh->conf->disable_genhomedircon && 1519 i == SEMANAGE_FC_HOMEDIRS) continue; 1520 1521 strcpy(fn, dst); 1522 ret = semanage_mkpath(sh, dirname(fn)); 1523 if (ret < 0) { 1524 goto cleanup; 1525 } 1526 1527 ret = semanage_copy_file(src, dst, sh->conf->file_mode); 1528 if (ret < 0) { 1529 ERR(sh, "Could not copy %s to %s.", src, dst); 1530 goto cleanup; 1531 } 1532 } 1533 1534 if (!sh->do_reload) 1535 goto skip_reload; 1536 1537 /* This stats what libselinux says the active store is (according to config) 1538 * and what we are installing to, to decide if they are the same store. If 1539 * they are not then we do not reload policy. 1540 */ 1541 const char *really_active_store = selinux_policy_root(); 1542 struct stat astore; 1543 struct stat istore; 1544 const char *storepath = semanage_final_path(SEMANAGE_FINAL_SELINUX, 1545 SEMANAGE_FINAL_TOPLEVEL); 1546 1547 if (stat(really_active_store, &astore) == 0) { 1548 if (stat(storepath, &istore)) { 1549 ERR(sh, "Could not stat store path %s.", storepath); 1550 goto cleanup; 1551 } 1552 1553 if (!(astore.st_ino == istore.st_ino && 1554 astore.st_dev == istore.st_dev)) { 1555 /* They are not the same store */ 1556 goto skip_reload; 1557 } 1558 } else if (errno == ENOENT && 1559 strcmp(really_active_store, storepath) != 0) { 1560 errno = 0; 1561 goto skip_reload; 1562 } 1563 1564 if (semanage_reload_policy(sh)) { 1565 goto cleanup; 1566 } 1567 1568 skip_reload: 1569 if (sh->do_check_contexts) { 1570 ret = semanage_exec_prog( 1571 sh, 1572 sh->conf->setfiles, 1573 semanage_final_path(SEMANAGE_FINAL_SELINUX, 1574 SEMANAGE_KERNEL), 1575 semanage_final_path(SEMANAGE_FINAL_SELINUX, 1576 SEMANAGE_FC)); 1577 if (ret != 0) { 1578 ERR(sh, "setfiles returned error code %d.", ret); 1579 goto cleanup; 1580 } 1581 } 1582 1583 if (sefcontext_compile(sh, 1584 semanage_final_path(SEMANAGE_FINAL_SELINUX, SEMANAGE_FC)) != 0) { 1585 goto cleanup; 1586 } 1587 1588 if (sefcontext_compile(sh, 1589 semanage_final_path(SEMANAGE_FINAL_SELINUX, SEMANAGE_FC_LOCAL)) != 0) { 1590 goto cleanup; 1591 } 1592 1593 if (sefcontext_compile(sh, 1594 semanage_final_path(SEMANAGE_FINAL_SELINUX, SEMANAGE_FC_HOMEDIRS)) != 0) { 1595 goto cleanup; 1596 } 1597 1598 status = 0; 1599 cleanup: 1600 return status; 1601 } 1602 1603 /* Prepare the sandbox to be installed by making a backup of the 1604 * current active directory. Then copy the sandbox to the active 1605 * directory. Return the new commit number on success, negative 1606 * values on error. */ 1607 static int semanage_commit_sandbox(semanage_handle_t * sh) 1608 { 1609 int commit_number, fd, retval; 1610 char write_buf[32]; 1611 const char *commit_filename = 1612 semanage_path(SEMANAGE_TMP, SEMANAGE_COMMIT_NUM_FILE); 1613 ssize_t amount_written; 1614 const char *active = semanage_path(SEMANAGE_ACTIVE, SEMANAGE_TOPLEVEL); 1615 const char *backup = 1616 semanage_path(SEMANAGE_PREVIOUS, SEMANAGE_TOPLEVEL); 1617 const char *sandbox = semanage_path(SEMANAGE_TMP, SEMANAGE_TOPLEVEL); 1618 struct stat buf; 1619 1620 /* update the commit number */ 1621 if ((commit_number = semanage_direct_get_serial(sh)) < 0) { 1622 return -1; 1623 } 1624 commit_number++; 1625 memset(write_buf, 0, sizeof(write_buf)); 1626 snprintf(write_buf, sizeof(write_buf), "%d", commit_number); 1627 if ((fd = 1628 open(commit_filename, O_WRONLY | O_CREAT | O_TRUNC, 1629 S_IRUSR | S_IWUSR)) == -1) { 1630 ERR(sh, "Could not open commit number file %s for writing.", 1631 commit_filename); 1632 return -1; 1633 } 1634 amount_written = write(fd, write_buf, sizeof(write_buf)); 1635 if (amount_written == -1) { 1636 ERR(sh, "Error while writing commit number to %s.", 1637 commit_filename); 1638 close(fd); 1639 return -1; 1640 } 1641 close(fd); 1642 1643 retval = commit_number; 1644 1645 if (semanage_get_active_lock(sh) < 0) { 1646 return -1; 1647 } 1648 /* make the backup of the current active directory */ 1649 if (stat(backup, &buf) == 0) { 1650 if (S_ISDIR(buf.st_mode) && 1651 semanage_remove_directory(backup) != 0) { 1652 ERR(sh, "Could not remove previous backup %s.", backup); 1653 retval = -1; 1654 goto cleanup; 1655 } 1656 } else if (errno != ENOENT) { 1657 ERR(sh, "Could not stat directory %s.", backup); 1658 retval = -1; 1659 goto cleanup; 1660 } 1661 1662 if (rename(active, backup) == -1) { 1663 ERR(sh, "Error while renaming %s to %s.", active, backup); 1664 retval = -1; 1665 goto cleanup; 1666 } 1667 1668 /* clean up some files from the sandbox before install */ 1669 /* remove homedir_template from sandbox */ 1670 1671 if (rename(sandbox, active) == -1) { 1672 ERR(sh, "Error while renaming %s to %s.", sandbox, active); 1673 /* note that if an error occurs during the next 1674 * function then the store will be left in an 1675 * inconsistent state */ 1676 if (rename(backup, active) < 0) 1677 ERR(sh, "Error while renaming %s back to %s.", backup, 1678 active); 1679 retval = -1; 1680 goto cleanup; 1681 } 1682 if (semanage_install_final_tmp(sh) != 0) { 1683 /* note that if an error occurs during the next three 1684 * function then the store will be left in an 1685 * inconsistent state */ 1686 int errsv = errno; 1687 if (rename(active, sandbox) < 0) 1688 ERR(sh, "Error while renaming %s back to %s.", active, 1689 sandbox); 1690 else if (rename(backup, active) < 0) 1691 ERR(sh, "Error while renaming %s back to %s.", backup, 1692 active); 1693 else 1694 semanage_install_final_tmp(sh); 1695 errno = errsv; 1696 retval = -1; 1697 goto cleanup; 1698 } 1699 1700 if (!sh->conf->save_previous) { 1701 int errsv = errno; 1702 retval = semanage_remove_directory(backup); 1703 if (retval < 0) { 1704 ERR(sh, "Could not delete previous directory %s.", backup); 1705 goto cleanup; 1706 } 1707 errno = errsv; 1708 } 1709 1710 cleanup: 1711 semanage_release_active_lock(sh); 1712 return retval; 1713 } 1714 1715 /* Takes the kernel policy in a sandbox, move it to the active 1716 * directory, copy it to the binary policy path, then load it. Upon 1717 * error move the active directory back to the sandbox. This function 1718 * should be placed within a mutex lock to ensure that it runs 1719 * atomically. Returns commit number on success, -1 on error. 1720 */ 1721 int semanage_install_sandbox(semanage_handle_t * sh) 1722 { 1723 int retval = -1, commit_num = -1; 1724 1725 if (sh->conf->load_policy == NULL) { 1726 ERR(sh, 1727 "No load_policy program specified in configuration file."); 1728 goto cleanup; 1729 } 1730 if (sh->conf->setfiles == NULL) { 1731 ERR(sh, "No setfiles program specified in configuration file."); 1732 goto cleanup; 1733 } 1734 1735 if (sh->conf->sefcontext_compile == NULL) { 1736 ERR(sh, "No sefcontext_compile program specified in configuration file."); 1737 goto cleanup; 1738 } 1739 1740 if ((commit_num = semanage_commit_sandbox(sh)) < 0) { 1741 retval = commit_num; 1742 goto cleanup; 1743 } 1744 1745 retval = commit_num; 1746 1747 cleanup: 1748 return retval; 1749 1750 } 1751 1752 /********************* functions that manipulate lock *********************/ 1753 1754 static int semanage_get_lock(semanage_handle_t * sh, 1755 const char *lock_name, const char *lock_file) 1756 { 1757 int fd; 1758 struct timeval origtime, curtime; 1759 int got_lock = 0; 1760 1761 if ((fd = open(lock_file, O_RDONLY)) == -1) { 1762 if ((fd = 1763 open(lock_file, O_RDWR | O_CREAT | O_TRUNC, 1764 S_IRUSR | S_IWUSR)) == -1) { 1765 ERR(sh, "Could not open direct %s at %s.", lock_name, 1766 lock_file); 1767 return -1; 1768 } 1769 } 1770 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { 1771 ERR(sh, "Could not set close-on-exec for %s at %s.", lock_name, 1772 lock_file); 1773 close(fd); 1774 return -1; 1775 } 1776 1777 if (sh->timeout == 0) { 1778 /* return immediately */ 1779 origtime.tv_sec = 0; 1780 } else { 1781 origtime.tv_sec = sh->timeout; 1782 } 1783 origtime.tv_usec = 0; 1784 do { 1785 curtime.tv_sec = 1; 1786 curtime.tv_usec = 0; 1787 if (flock(fd, LOCK_EX | LOCK_NB) == 0) { 1788 got_lock = 1; 1789 break; 1790 } else if (errno != EAGAIN) { 1791 ERR(sh, "Error obtaining direct %s at %s.", lock_name, 1792 lock_file); 1793 close(fd); 1794 return -1; 1795 } 1796 if (origtime.tv_sec > 0 || sh->timeout == -1) { 1797 if (select(0, NULL, NULL, NULL, &curtime) == -1) { 1798 if (errno == EINTR) { 1799 continue; 1800 } 1801 ERR(sh, 1802 "Error while waiting to get direct %s at %s.", 1803 lock_name, lock_file); 1804 close(fd); 1805 return -1; 1806 } 1807 origtime.tv_sec--; 1808 } 1809 } while (origtime.tv_sec > 0 || sh->timeout == -1); 1810 if (!got_lock) { 1811 ERR(sh, "Could not get direct %s at %s.", lock_name, lock_file); 1812 close(fd); 1813 return -1; 1814 } 1815 return fd; 1816 } 1817 1818 /* Locking for the module store for transactions. This is very basic 1819 * locking of the module store and doesn't do anything if the module 1820 * store is being manipulated with a program not using this library 1821 * (but the policy should prevent that). Returns 0 on success, -1 if 1822 * it could not obtain a lock. 1823 */ 1824 int semanage_get_trans_lock(semanage_handle_t * sh) 1825 { 1826 const char *lock_file = semanage_files[SEMANAGE_TRANS_LOCK]; 1827 1828 if (sh->u.direct.translock_file_fd >= 0) 1829 return 0; 1830 1831 sh->u.direct.translock_file_fd = 1832 semanage_get_lock(sh, "transaction lock", lock_file); 1833 if (sh->u.direct.translock_file_fd >= 0) { 1834 return 0; 1835 } else { 1836 return -1; 1837 } 1838 } 1839 1840 /* Locking for the module store for active store reading; this also includes 1841 * the file containing the commit number. This is very basic locking 1842 * of the module store and doesn't do anything if the module store is 1843 * being manipulated with a program not using this library (but the 1844 * policy should prevent that). Returns 0 on success, -1 if it could 1845 * not obtain a lock. 1846 */ 1847 int semanage_get_active_lock(semanage_handle_t * sh) 1848 { 1849 const char *lock_file = semanage_files[SEMANAGE_READ_LOCK]; 1850 1851 if (sh->u.direct.activelock_file_fd >= 0) 1852 return 0; 1853 1854 sh->u.direct.activelock_file_fd = 1855 semanage_get_lock(sh, "read lock", lock_file); 1856 if (sh->u.direct.activelock_file_fd >= 0) { 1857 return 0; 1858 } else { 1859 return -1; 1860 } 1861 } 1862 1863 /* Releases the transaction lock. Does nothing if there was not one already 1864 * there. */ 1865 void semanage_release_trans_lock(semanage_handle_t * sh) 1866 { 1867 int errsv = errno; 1868 if (sh->u.direct.translock_file_fd >= 0) { 1869 flock(sh->u.direct.translock_file_fd, LOCK_UN); 1870 close(sh->u.direct.translock_file_fd); 1871 sh->u.direct.translock_file_fd = -1; 1872 } 1873 errno = errsv; 1874 } 1875 1876 /* Releases the read lock. Does nothing if there was not one already 1877 * there. */ 1878 void semanage_release_active_lock(semanage_handle_t * sh) 1879 { 1880 int errsv = errno; 1881 if (sh->u.direct.activelock_file_fd >= 0) { 1882 flock(sh->u.direct.activelock_file_fd, LOCK_UN); 1883 close(sh->u.direct.activelock_file_fd); 1884 sh->u.direct.activelock_file_fd = -1; 1885 } 1886 errno = errsv; 1887 } 1888 1889 /* Read the current commit number from the commit number file which 1890 * the handle is pointing, resetting the file pointer afterwards. 1891 * Return it (a non-negative number), or -1 on error. */ 1892 int semanage_direct_get_serial(semanage_handle_t * sh) 1893 { 1894 char buf[32]; 1895 int fd, commit_number; 1896 ssize_t amount_read; 1897 const char *commit_filename; 1898 memset(buf, 0, sizeof(buf)); 1899 1900 if (sh->is_in_transaction) { 1901 commit_filename = 1902 semanage_path(SEMANAGE_TMP, SEMANAGE_COMMIT_NUM_FILE); 1903 } else { 1904 commit_filename = 1905 semanage_path(SEMANAGE_ACTIVE, SEMANAGE_COMMIT_NUM_FILE); 1906 } 1907 1908 if ((fd = open(commit_filename, O_RDONLY)) == -1) { 1909 if (errno == ENOENT) { 1910 /* the commit number file does not exist yet, 1911 * so assume that the number is 0 */ 1912 errno = 0; 1913 return 0; 1914 } else { 1915 ERR(sh, "Could not open commit number file %s.", 1916 commit_filename); 1917 return -1; 1918 } 1919 } 1920 1921 amount_read = read(fd, buf, sizeof(buf)); 1922 if (amount_read == -1) { 1923 ERR(sh, "Error while reading commit number from %s.", 1924 commit_filename); 1925 commit_number = -1; 1926 } else if (sscanf(buf, "%d", &commit_number) != 1) { 1927 /* if nothing was read, assume that the commit number is 0 */ 1928 commit_number = 0; 1929 } else if (commit_number < 0) { 1930 /* read file ought never have negative values */ 1931 ERR(sh, 1932 "Commit number file %s is corrupted; it should only contain a non-negative integer.", 1933 commit_filename); 1934 commit_number = -1; 1935 } 1936 1937 close(fd); 1938 return commit_number; 1939 } 1940 1941 /* HIGHER LEVEL COMMIT FUNCTIONS */ 1942 1943 int semanage_load_files(semanage_handle_t * sh, cil_db_t *cildb, char **filenames, int numfiles) 1944 { 1945 int retval = 0; 1946 FILE *fp; 1947 ssize_t size; 1948 char *data = NULL; 1949 char *filename; 1950 int i; 1951 1952 for (i = 0; i < numfiles; i++) { 1953 filename = filenames[i]; 1954 1955 if ((fp = fopen(filename, "rb")) == NULL) { 1956 ERR(sh, "Could not open module file %s for reading.", filename); 1957 goto cleanup; 1958 } 1959 1960 if ((size = bunzip(sh, fp, &data)) <= 0) { 1961 rewind(fp); 1962 __fsetlocking(fp, FSETLOCKING_BYCALLER); 1963 1964 if (fseek(fp, 0, SEEK_END) != 0) { 1965 ERR(sh, "Failed to determine size of file %s.", filename); 1966 goto cleanup; 1967 } 1968 size = ftell(fp); 1969 rewind(fp); 1970 1971 data = malloc(size); 1972 if (fread(data, size, 1, fp) != 1) { 1973 ERR(sh, "Failed to read file %s.", filename); 1974 goto cleanup; 1975 } 1976 } 1977 1978 fclose(fp); 1979 fp = NULL; 1980 1981 retval = cil_add_file(cildb, filename, data, size); 1982 if (retval != SEPOL_OK) { 1983 ERR(sh, "Error while reading from file %s.", filename); 1984 goto cleanup; 1985 } 1986 1987 free(data); 1988 data = NULL; 1989 } 1990 1991 return retval; 1992 1993 cleanup: 1994 if (fp != NULL) { 1995 fclose(fp); 1996 } 1997 free(data); 1998 return -1; 1999 } 2000 2001 /* 2002 * Expands the policy contained within *base 2003 */ 2004 2005 /** 2006 * Read the policy from the sandbox (kernel) 2007 */ 2008 int semanage_read_policydb(semanage_handle_t * sh, sepol_policydb_t * in) 2009 { 2010 2011 int retval = STATUS_ERR; 2012 const char *kernel_filename = NULL; 2013 struct sepol_policy_file *pf = NULL; 2014 FILE *infile = NULL; 2015 2016 if ((kernel_filename = 2017 semanage_path(SEMANAGE_ACTIVE, SEMANAGE_STORE_KERNEL)) == NULL) { 2018 goto cleanup; 2019 } 2020 if ((infile = fopen(kernel_filename, "r")) == NULL) { 2021 ERR(sh, "Could not open kernel policy %s for reading.", 2022 kernel_filename); 2023 goto cleanup; 2024 } 2025 __fsetlocking(infile, FSETLOCKING_BYCALLER); 2026 if (sepol_policy_file_create(&pf)) { 2027 ERR(sh, "Out of memory!"); 2028 goto cleanup; 2029 } 2030 sepol_policy_file_set_fp(pf, infile); 2031 sepol_policy_file_set_handle(pf, sh->sepolh); 2032 if (sepol_policydb_read(in, pf) == -1) { 2033 ERR(sh, "Error while reading kernel policy from %s.", 2034 kernel_filename); 2035 goto cleanup; 2036 } 2037 retval = STATUS_SUCCESS; 2038 2039 cleanup: 2040 if (infile != NULL) { 2041 fclose(infile); 2042 } 2043 sepol_policy_file_free(pf); 2044 return retval; 2045 } 2046 /** 2047 * Writes the final policy to the sandbox (kernel) 2048 */ 2049 int semanage_write_policydb(semanage_handle_t * sh, sepol_policydb_t * out) 2050 { 2051 2052 int retval = STATUS_ERR; 2053 const char *kernel_filename = NULL; 2054 struct sepol_policy_file *pf = NULL; 2055 FILE *outfile = NULL; 2056 2057 if ((kernel_filename = 2058 semanage_path(SEMANAGE_TMP, SEMANAGE_STORE_KERNEL)) == NULL) { 2059 goto cleanup; 2060 } 2061 if ((outfile = fopen(kernel_filename, "wb")) == NULL) { 2062 ERR(sh, "Could not open kernel policy %s for writing.", 2063 kernel_filename); 2064 goto cleanup; 2065 } 2066 __fsetlocking(outfile, FSETLOCKING_BYCALLER); 2067 if (sepol_policy_file_create(&pf)) { 2068 ERR(sh, "Out of memory!"); 2069 goto cleanup; 2070 } 2071 sepol_policy_file_set_fp(pf, outfile); 2072 sepol_policy_file_set_handle(pf, sh->sepolh); 2073 if (sepol_policydb_write(out, pf) == -1) { 2074 ERR(sh, "Error while writing kernel policy to %s.", 2075 kernel_filename); 2076 goto cleanup; 2077 } 2078 retval = STATUS_SUCCESS; 2079 2080 cleanup: 2081 if (outfile != NULL) { 2082 fclose(outfile); 2083 } 2084 sepol_policy_file_free(pf); 2085 return retval; 2086 } 2087 2088 /* Execute the module verification programs for each source module. 2089 * Returns 0 if every verifier returned success, -1 on error. 2090 */ 2091 int semanage_verify_modules(semanage_handle_t * sh, 2092 char **module_filenames, int num_modules) 2093 { 2094 int i, retval; 2095 semanage_conf_t *conf = sh->conf; 2096 if (conf->mod_prog == NULL) { 2097 return 0; 2098 } 2099 for (i = 0; i < num_modules; i++) { 2100 char *module = module_filenames[i]; 2101 external_prog_t *e; 2102 for (e = conf->mod_prog; e != NULL; e = e->next) { 2103 if ((retval = 2104 semanage_exec_prog(sh, e, module, "$<")) != 0) { 2105 return -1; 2106 } 2107 } 2108 } 2109 return 0; 2110 } 2111 2112 /* Execute the linker verification programs for the linked (but not 2113 * expanded) base. Returns 0 if every verifier returned success, -1 2114 * on error. 2115 */ 2116 int semanage_verify_linked(semanage_handle_t * sh) 2117 { 2118 external_prog_t *e; 2119 semanage_conf_t *conf = sh->conf; 2120 const char *linked_filename = 2121 semanage_path(SEMANAGE_TMP, SEMANAGE_LINKED); 2122 int retval = -1; 2123 if (conf->linked_prog == NULL) { 2124 return 0; 2125 } 2126 for (e = conf->linked_prog; e != NULL; e = e->next) { 2127 if (semanage_exec_prog(sh, e, linked_filename, "$<") != 0) { 2128 goto cleanup; 2129 } 2130 } 2131 retval = 0; 2132 cleanup: 2133 return retval; 2134 } 2135 2136 /* Execute each of the kernel verification programs. Returns 0 if 2137 * every verifier returned success, -1 on error. 2138 */ 2139 int semanage_verify_kernel(semanage_handle_t * sh) 2140 { 2141 int retval = -1; 2142 const char *kernel_filename = 2143 semanage_path(SEMANAGE_FINAL_TMP, SEMANAGE_KERNEL); 2144 semanage_conf_t *conf = sh->conf; 2145 external_prog_t *e; 2146 if (conf->kernel_prog == NULL) { 2147 return 0; 2148 } 2149 for (e = conf->kernel_prog; e != NULL; e = e->next) { 2150 if (semanage_exec_prog(sh, e, kernel_filename, "$<") != 0) { 2151 goto cleanup; 2152 } 2153 } 2154 retval = 0; 2155 cleanup: 2156 return retval; 2157 } 2158 2159 /********************* functions that sort file contexts *********************/ 2160 2161 /* Free the given node. */ 2162 static void semanage_fc_node_destroy(semanage_file_context_node_t * x) 2163 { 2164 free(x->path); 2165 free(x->file_type); 2166 free(x->context); 2167 free(x); 2168 } 2169 2170 /* Free the linked list of nodes starting at the given node. */ 2171 static void semanage_fc_node_list_destroy(semanage_file_context_node_t * x) 2172 { 2173 semanage_file_context_node_t *temp; 2174 2175 while (x) { 2176 temp = x; 2177 x = x->next; 2178 semanage_fc_node_destroy(temp); 2179 } 2180 } 2181 2182 /* Free the linked list of buckets (and their node lists) 2183 * starting at the given bucket. */ 2184 static void semanage_fc_bucket_list_destroy(semanage_file_context_bucket_t * x) 2185 { 2186 semanage_file_context_bucket_t *temp; 2187 2188 while (x) { 2189 temp = x; 2190 x = x->next; 2191 semanage_fc_node_list_destroy(temp->data); 2192 free(temp); 2193 } 2194 } 2195 2196 /* Compares two file contexts' regular expressions and returns: 2197 * -1 if a is less specific than b 2198 * 0 if a and be are equally specific 2199 * 1 if a is more specific than b 2200 * The comparison is based on the following heuristics, 2201 * in order from most important to least important, given a and b: 2202 * If a is a regular expression and b is not, 2203 * -> a is less specific than b. 2204 * If a's stem length is shorter than b's stem length, 2205 * -> a is less specific than b. 2206 * If a's string length is shorter than b's string length, 2207 * -> a is less specific than b. 2208 * If a does not have a specified type and b does not, 2209 * -> a is less specific than b. 2210 * FIXME: These heuristics are imperfect, but good enough for 2211 * now. A proper comparison would determine which (if either) 2212 * regular expression is a subset of the other. 2213 */ 2214 static int semanage_fc_compare(semanage_file_context_node_t * a, 2215 semanage_file_context_node_t * b) 2216 { 2217 int a_has_meta = (a->meta >= 0); 2218 int b_has_meta = (b->meta >= 0); 2219 2220 /* Check to see if either a or b are regexes 2221 * and the other isn't. */ 2222 if (a_has_meta && !b_has_meta) 2223 return -1; 2224 if (b_has_meta && !a_has_meta) 2225 return 1; 2226 2227 /* Check to see if either a or b have a shorter stem 2228 * length than the other. */ 2229 if (a->meta < b->meta) 2230 return -1; 2231 if (b->meta < a->meta) 2232 return 1; 2233 2234 /* Check to see if either a or b have a shorter string 2235 * length than the other. */ 2236 if (a->effective_len < b->effective_len) 2237 return -1; 2238 if (b->effective_len < a->effective_len) 2239 return 1; 2240 2241 /* Check to see if either a or b has a specified type 2242 * and the other doesn't. */ 2243 if (!a->file_type && b->file_type) 2244 return -1; 2245 if (!b->file_type && a->file_type) 2246 return 1; 2247 2248 /* If none of the above conditions were satisfied, 2249 * then a and b are equally specific. */ 2250 return 0; 2251 } 2252 2253 /* Merges two sorted file context linked lists into a single sorted one. 2254 * The left list is assumed to represent nodes that came first in the original ordering. 2255 * The final sorted list is returned. 2256 */ 2257 static semanage_file_context_node_t 2258 * semanage_fc_merge(semanage_file_context_node_t * left, 2259 semanage_file_context_node_t * right) 2260 { 2261 semanage_file_context_node_t *head; 2262 semanage_file_context_node_t *current; 2263 semanage_file_context_node_t *tail; 2264 2265 if (!left) 2266 return right; 2267 2268 if (!right) 2269 return left; 2270 2271 if (semanage_fc_compare(left, right) == 1) { 2272 head = tail = right; 2273 right = right->next; 2274 } else { 2275 head = tail = left; 2276 left = left->next; 2277 } 2278 2279 while (left && right) { 2280 /* if left was more specific than right, 2281 * insert right before left. Otherwise leave order alone. */ 2282 if (semanage_fc_compare(left, right) == 1) { 2283 current = right; 2284 right = right->next; 2285 } else { 2286 current = left; 2287 left = left->next; 2288 } 2289 2290 tail = tail->next = current; 2291 } 2292 2293 tail->next = (left != NULL) ? left : right; 2294 2295 return head; 2296 } 2297 2298 /* Sorts file contexts from least specific to most specific. 2299 * A bucket linked list is passed in. Upon completion, 2300 * there is only one bucket (pointed to by master) that 2301 * contains a linked list of all the file contexts in sorted order. 2302 * Explanation of the algorithm: 2303 * This is a stable implementation of an iterative merge sort. 2304 * Each bucket initially has a linked list of file contexts 2305 * that are 1 node long. 2306 * Each pass, buckets (and the nodes they contain) are merged 2307 * two at time. 2308 * Buckets are merged until there is only one bucket left, 2309 * containing the list of file contexts, sorted. 2310 */ 2311 static void semanage_fc_merge_sort(semanage_file_context_bucket_t * master) 2312 { 2313 semanage_file_context_bucket_t *current; 2314 semanage_file_context_bucket_t *temp; 2315 2316 /* Loop until master is the only bucket left. 2317 * When we stop master contains the sorted list. */ 2318 while (master->next) { 2319 current = master; 2320 2321 /* Merge buckets two-by-two. 2322 * If there is an odd number of buckets, the last 2323 * bucket will be left alone, which corresponds 2324 * to the operation of merging it with an empty bucket. */ 2325 while (current) { 2326 if (current->next) { 2327 current->data = 2328 semanage_fc_merge(current->data, 2329 current->next->data); 2330 temp = current->next; 2331 current->next = current->next->next; 2332 2333 /* Free the (now empty) second bucket. 2334 * (This does not touch the node list 2335 * in the bucket because it has been 2336 * shifted over to the first bucket. */ 2337 free(temp); 2338 } 2339 current = current->next; 2340 } 2341 } 2342 } 2343 2344 /* Compute the location of the first regular expression 2345 * meta character in the path of the given node, if it exists. 2346 * On return: 2347 * fc_node->meta = position of meta character, if it exists 2348 * (-1 corresponds to no character) 2349 */ 2350 static void semanage_fc_find_meta(semanage_file_context_node_t * fc_node) 2351 { 2352 int c = 0; 2353 int escape_chars = 0; 2354 2355 fc_node->meta = -1; 2356 2357 /* Note: this while loop has been adapted from 2358 * spec_hasMetaChars in matchpathcon.c from 2359 * libselinux-1.22. */ 2360 while (fc_node->path[c] != '\0') { 2361 switch (fc_node->path[c]) { 2362 case '.': 2363 case '^': 2364 case '$': 2365 case '?': 2366 case '*': 2367 case '+': 2368 case '|': 2369 case '[': 2370 case '(': 2371 case '{': 2372 fc_node->meta = c - escape_chars; 2373 return; 2374 case '\\': 2375 /* If an escape character is found, 2376 * skip the next character. */ 2377 c++; 2378 escape_chars++; 2379 break; 2380 } 2381 2382 c++; 2383 } 2384 } 2385 2386 /* Replicates strchr, but limits search to buf_len characters. */ 2387 static char *semanage_strnchr(const char *buf, size_t buf_len, char c) 2388 { 2389 size_t idx = 0; 2390 2391 if (buf == NULL) 2392 return NULL; 2393 if (buf_len <= 0) 2394 return NULL; 2395 2396 while (idx < buf_len) { 2397 if (buf[idx] == c) 2398 return (char *)buf + idx; 2399 idx++; 2400 } 2401 2402 return NULL; 2403 } 2404 2405 /* Returns a pointer to the end of line character in the given buffer. 2406 * Used in the context of a file context char buffer that we will be 2407 * parsing and sorting. 2408 */ 2409 static char *semanage_get_line_end(const char *buf, size_t buf_len) 2410 { 2411 char *line_end = NULL; 2412 2413 if (buf == NULL) 2414 return NULL; 2415 if (buf_len <= 0) 2416 return NULL; 2417 2418 line_end = semanage_strnchr(buf, buf_len, '\n'); 2419 if (!line_end) 2420 line_end = semanage_strnchr(buf, buf_len, '\r'); 2421 if (!line_end) 2422 line_end = semanage_strnchr(buf, buf_len, EOF); 2423 2424 return line_end; 2425 } 2426 2427 /* Entry function for sorting a set of file context lines. 2428 * Returns 0 on success, -1 on failure. 2429 * Allocates a buffer pointed to by sorted_buf that contains the sorted lines. 2430 * sorted_buf_len is set to the size of this buffer. 2431 * This buffer is guaranteed to have a final \0 character. 2432 * This buffer must be released by the caller. 2433 */ 2434 int semanage_fc_sort(semanage_handle_t * sh, const char *buf, size_t buf_len, 2435 char **sorted_buf, size_t * sorted_buf_len) 2436 { 2437 size_t start, finish, regex_len, type_len, context_len; 2438 size_t line_len, buf_remainder, i; 2439 ssize_t sanity_check; 2440 const char *line_buf, *line_end; 2441 char *sorted_buf_pos; 2442 int escape_chars, just_saw_escape; 2443 2444 semanage_file_context_node_t *temp; 2445 semanage_file_context_node_t *head; 2446 semanage_file_context_node_t *current; 2447 semanage_file_context_bucket_t *master; 2448 semanage_file_context_bucket_t *bcurrent; 2449 2450 i = 0; 2451 2452 if (sh == NULL) { 2453 return -1; 2454 } 2455 if (buf == NULL) { 2456 ERR(sh, "Received NULL buffer."); 2457 return -1; 2458 } 2459 if (buf_len <= 0) { 2460 ERR(sh, "Received buffer of length 0."); 2461 return -1; 2462 } 2463 2464 /* Initialize the head of the linked list 2465 * that will contain a node for each file context line. */ 2466 head = current = 2467 (semanage_file_context_node_t *) calloc(1, 2468 sizeof 2469 (semanage_file_context_node_t)); 2470 if (!head) { 2471 ERR(sh, "Failure allocating memory."); 2472 return -1; 2473 } 2474 2475 /* Parse the char buffer into a semanage_file_context_node_t linked list. */ 2476 line_buf = buf; 2477 buf_remainder = buf_len; 2478 while ((line_end = semanage_get_line_end(line_buf, buf_remainder))) { 2479 line_len = line_end - line_buf + 1; 2480 sanity_check = buf_remainder - line_len; 2481 buf_remainder = buf_remainder - line_len; 2482 2483 if (sanity_check < 0) { 2484 ERR(sh, "Failure parsing file context buffer."); 2485 semanage_fc_node_list_destroy(head); 2486 return -1; 2487 } 2488 2489 if (line_len == 0 || line_len == 1) { 2490 line_buf = line_end + 1; 2491 continue; 2492 } 2493 2494 /* Skip the whitespace at the front of the line. */ 2495 for (i = 0; i < line_len; i++) { 2496 if (!isspace(line_buf[i])) 2497 break; 2498 } 2499 2500 /* Check for a blank line. */ 2501 if (i >= line_len) { 2502 line_buf = line_end + 1; 2503 continue; 2504 } 2505 2506 /* Check if the line is a comment. */ 2507 if (line_buf[i] == '#') { 2508 line_buf = line_end + 1; 2509 continue; 2510 } 2511 2512 /* Allocate a new node. */ 2513 temp = 2514 (semanage_file_context_node_t *) calloc(1, 2515 sizeof 2516 (semanage_file_context_node_t)); 2517 if (!temp) { 2518 ERR(sh, "Failure allocating memory."); 2519 semanage_fc_node_list_destroy(head); 2520 return -1; 2521 } 2522 temp->next = NULL; 2523 2524 /* Extract the regular expression from the line. */ 2525 escape_chars = 0; 2526 just_saw_escape = 0; 2527 start = i; 2528 while (i < line_len && (!isspace(line_buf[i]))) { 2529 if (line_buf[i] == '\\') { 2530 if (!just_saw_escape) { 2531 escape_chars++; 2532 just_saw_escape = 1; 2533 } else { 2534 /* We're looking at an escaped 2535 escape. Reset our flag. */ 2536 just_saw_escape = 0; 2537 } 2538 } else { 2539 just_saw_escape = 0; 2540 } 2541 i++; 2542 } 2543 finish = i; 2544 regex_len = finish - start; 2545 2546 if (regex_len == 0) { 2547 ERR(sh, 2548 "WARNING: semanage_fc_sort: Regex of length 0."); 2549 semanage_fc_node_destroy(temp); 2550 line_buf = line_end + 1; 2551 continue; 2552 } 2553 2554 temp->path = (char *)strndup(&line_buf[start], regex_len); 2555 if (!temp->path) { 2556 ERR(sh, "Failure allocating memory."); 2557 semanage_fc_node_destroy(temp); 2558 semanage_fc_node_list_destroy(head); 2559 return -1; 2560 } 2561 2562 /* Skip the whitespace after the regular expression. */ 2563 for (; i < line_len; i++) { 2564 if (!isspace(line_buf[i])) 2565 break; 2566 } 2567 if (i == line_len) { 2568 ERR(sh, 2569 "WARNING: semanage_fc_sort: Incomplete context. %s", temp->path); 2570 semanage_fc_node_destroy(temp); 2571 line_buf = line_end + 1; 2572 continue; 2573 } 2574 2575 /* Extract the inode type from the line (if it exists). */ 2576 if (line_buf[i] == '-') { 2577 type_len = 2; /* defined as '--', '-d', '-f', etc. */ 2578 2579 if (i + type_len >= line_len) { 2580 ERR(sh, 2581 "WARNING: semanage_fc_sort: Incomplete context. %s", temp->path); 2582 semanage_fc_node_destroy(temp); 2583 line_buf = line_end + 1; 2584 continue; 2585 } 2586 2587 /* Record the inode type. */ 2588 temp->file_type = 2589 (char *)strndup(&line_buf[i], type_len); 2590 if (!temp->file_type) { 2591 ERR(sh, "Failure allocating memory."); 2592 semanage_fc_node_destroy(temp); 2593 semanage_fc_node_list_destroy(head); 2594 return -1; 2595 } 2596 2597 i += type_len; 2598 2599 /* Skip the whitespace after the type. */ 2600 for (; i < line_len; i++) { 2601 if (!isspace(line_buf[i])) 2602 break; 2603 } 2604 if (i == line_len) { 2605 ERR(sh, 2606 "WARNING: semanage_fc_sort: Incomplete context. %s", temp->path); 2607 semanage_fc_node_destroy(temp); 2608 line_buf = line_end + 1; 2609 continue; 2610 } 2611 } else { 2612 type_len = 0; /* inode type did not exist in the file context */ 2613 } 2614 2615 /* Extract the context from the line. */ 2616 start = i; 2617 while (i < line_len && (!isspace(line_buf[i]))) 2618 i++; 2619 finish = i; 2620 context_len = finish - start; 2621 2622 temp->context = (char *)strndup(&line_buf[start], context_len); 2623 if (!temp->context) { 2624 ERR(sh, "Failure allocating memory."); 2625 semanage_fc_node_destroy(temp); 2626 semanage_fc_node_list_destroy(head); 2627 return -1; 2628 } 2629 2630 /* Initialize the data about the file context. */ 2631 temp->path_len = regex_len; 2632 temp->effective_len = regex_len - escape_chars; 2633 temp->type_len = type_len; 2634 temp->context_len = context_len; 2635 semanage_fc_find_meta(temp); 2636 2637 /* Add this node to the end of the linked list. */ 2638 current->next = temp; 2639 current = current->next; 2640 2641 line_buf = line_end + 1; 2642 } 2643 2644 /* Create the bucket linked list from the node linked list. */ 2645 current = head->next; 2646 bcurrent = master = (semanage_file_context_bucket_t *) 2647 calloc(1, sizeof(semanage_file_context_bucket_t)); 2648 if (!master) { 2649 ERR(sh, "Failure allocating memory."); 2650 semanage_fc_node_list_destroy(head); 2651 return -1; 2652 } 2653 2654 /* Free the head node, as it is no longer used. */ 2655 semanage_fc_node_destroy(head); 2656 head = NULL; 2657 2658 /* Place each node into a bucket. */ 2659 while (current) { 2660 bcurrent->data = current; 2661 current = current->next; 2662 2663 /* Detach the node in the bucket from the old list. */ 2664 bcurrent->data->next = NULL; 2665 2666 /* If we need another bucket, add one to the end. */ 2667 if (current) { 2668 bcurrent->next = (semanage_file_context_bucket_t *) 2669 calloc(1, sizeof(semanage_file_context_bucket_t)); 2670 if (!(bcurrent->next)) { 2671 ERR(sh, "Failure allocating memory."); 2672 semanage_fc_bucket_list_destroy(master); 2673 return -1; 2674 } 2675 2676 bcurrent = bcurrent->next; 2677 } 2678 } 2679 2680 /* Sort the bucket list. */ 2681 semanage_fc_merge_sort(master); 2682 2683 /* First, calculate how much space we'll need for 2684 * the newly sorted block of data. (We don't just 2685 * use buf_len for this because we have extracted 2686 * comments and whitespace.) */ 2687 i = 0; 2688 current = master->data; 2689 while (current) { 2690 i += current->path_len + 1; /* +1 for a tab */ 2691 if (current->file_type) { 2692 i += current->type_len + 1; /* +1 for a tab */ 2693 } 2694 i += current->context_len + 1; /* +1 for a newline */ 2695 current = current->next; 2696 } 2697 i = i + 1; /* +1 for trailing \0 */ 2698 2699 /* Allocate the buffer for the sorted list. */ 2700 *sorted_buf = calloc(i, sizeof(char)); 2701 if (!*sorted_buf) { 2702 ERR(sh, "Failure allocating memory."); 2703 semanage_fc_bucket_list_destroy(master); 2704 return -1; 2705 } 2706 *sorted_buf_len = i; 2707 2708 /* Output the sorted semanage_file_context linked list to the char buffer. */ 2709 sorted_buf_pos = *sorted_buf; 2710 current = master->data; 2711 while (current) { 2712 /* Output the path. */ 2713 i = current->path_len + 1; /* +1 for tab */ 2714 snprintf(sorted_buf_pos, i + 1, "%s\t", current->path); 2715 sorted_buf_pos = sorted_buf_pos + i; 2716 2717 /* Output the type, if there is one. */ 2718 if (current->file_type) { 2719 i = strlen(current->file_type) + 1; /* +1 for tab */ 2720 snprintf(sorted_buf_pos, i + 1, "%s\t", 2721 current->file_type); 2722 sorted_buf_pos = sorted_buf_pos + i; 2723 } 2724 2725 /* Output the context. */ 2726 i = strlen(current->context) + 1; /* +1 for newline */ 2727 snprintf(sorted_buf_pos, i + 1, "%s\n", current->context); 2728 sorted_buf_pos = sorted_buf_pos + i; 2729 2730 current = current->next; 2731 } 2732 2733 /* Clean up. */ 2734 semanage_fc_bucket_list_destroy(master); 2735 2736 /* Sanity check. */ 2737 sorted_buf_pos++; 2738 if ((sorted_buf_pos - *sorted_buf) != (ssize_t) * sorted_buf_len) { 2739 ERR(sh, "Failure writing sorted buffer."); 2740 free(*sorted_buf); 2741 *sorted_buf = NULL; 2742 return -1; 2743 } 2744 2745 return 0; 2746 } 2747 2748 /********************* functions that sort netfilter contexts *********************/ 2749 #define NC_SORT_NAMES { "pre", "base", "module", "local", "post" } 2750 #define NC_SORT_NAMES_LEN { 3, 4, 6, 5, 4 } 2751 #define NC_SORT_NEL 5 2752 static void semanage_nc_destroy_ruletab(semanage_netfilter_context_node_t * 2753 ruletab[NC_SORT_NEL][2]) 2754 { 2755 semanage_netfilter_context_node_t *curr, *next; 2756 int i; 2757 2758 for (i = 0; i < NC_SORT_NEL; i++) { 2759 for (curr = ruletab[i][0]; curr != NULL; curr = next) { 2760 next = curr->next; 2761 free(curr->rule); 2762 free(curr); 2763 } 2764 } 2765 } 2766 2767 /* Entry function for sorting a set of netfilter context lines. 2768 * Returns 0 on success, -1 on failure. 2769 * Allocates a buffer pointed to by sorted_buf that contains the sorted lines. 2770 * sorted_buf_len is set to the size of this buffer. 2771 * This buffer is guaranteed to have a final \0 character. 2772 * This buffer must be released by the caller. 2773 */ 2774 int semanage_nc_sort(semanage_handle_t * sh, const char *buf, size_t buf_len, 2775 char **sorted_buf, size_t * sorted_buf_len) 2776 { 2777 2778 /* parsing bits */ 2779 const char *priority_names[] = NC_SORT_NAMES; 2780 const int priority_names_len[] = NC_SORT_NAMES_LEN; 2781 size_t line_len, buf_remainder, i, offset; 2782 const char *line_buf, *line_end; 2783 2784 /* ruletab bits */ 2785 /* keep track of the head (index 0) and tail (index 1) with this array */ 2786 semanage_netfilter_context_node_t *ruletab[NC_SORT_NEL][2]; 2787 semanage_netfilter_context_node_t *curr, *node; 2788 int priority; 2789 2790 /* sorted buffer bits */ 2791 char *sorted_buf_pos; 2792 size_t count; 2793 2794 /* initialize ruletab */ 2795 memset(ruletab, 0, 2796 NC_SORT_NEL * 2 * sizeof(semanage_netfilter_context_node_t *)); 2797 2798 /* while lines to be read */ 2799 line_buf = buf; 2800 buf_remainder = buf_len; 2801 while ((line_end = semanage_get_line_end(line_buf, buf_remainder))) { 2802 line_len = line_end - line_buf + 1; 2803 buf_remainder = buf_remainder - line_len; 2804 2805 if (line_len == 0 || line_len == 1) { 2806 line_buf = line_end + 1; 2807 continue; 2808 } 2809 2810 /* Skip the whitespace at the front of the line. */ 2811 for (i = 0; i < line_len; i++) { 2812 if (!isspace(line_buf[i])) 2813 break; 2814 } 2815 2816 /* Check for a blank line. */ 2817 if (i >= line_len) { 2818 line_buf = line_end + 1; 2819 continue; 2820 } 2821 2822 /* Check if the line is a comment. */ 2823 if (line_buf[i] == '#') { 2824 line_buf = line_end + 1; 2825 continue; 2826 } 2827 2828 /* extract priority */ 2829 priority = -1; 2830 offset = 0; 2831 for (i = 0; i < NC_SORT_NEL; i++) { 2832 if (strncmp 2833 (line_buf, priority_names[i], 2834 priority_names_len[i]) == 0) { 2835 priority = i; 2836 offset = priority_names_len[i]; 2837 break; 2838 } 2839 } 2840 2841 if (priority < 0) { 2842 ERR(sh, "Netfilter context line missing priority."); 2843 semanage_nc_destroy_ruletab(ruletab); 2844 return -1; 2845 } 2846 2847 /* skip over whitespace */ 2848 for (; offset < line_len && isspace(line_buf[offset]); 2849 offset++) ; 2850 2851 /* load rule into node */ 2852 node = (semanage_netfilter_context_node_t *) 2853 malloc(sizeof(semanage_netfilter_context_node_t)); 2854 if (!node) { 2855 ERR(sh, "Failure allocating memory."); 2856 semanage_nc_destroy_ruletab(ruletab); 2857 return -1; 2858 } 2859 2860 node->rule = 2861 (char *)strndup(line_buf + offset, line_len - offset); 2862 node->rule_len = line_len - offset; 2863 node->next = NULL; 2864 2865 if (!node->rule) { 2866 ERR(sh, "Failure allocating memory."); 2867 free(node); 2868 semanage_nc_destroy_ruletab(ruletab); 2869 return -1; 2870 } 2871 2872 /* add node to rule table */ 2873 if (ruletab[priority][0] && ruletab[priority][1]) { 2874 /* add to end of list, update tail pointer */ 2875 ruletab[priority][1]->next = node; 2876 ruletab[priority][1] = node; 2877 } else { 2878 /* this list is empty, make head and tail point to the node */ 2879 ruletab[priority][0] = ruletab[priority][1] = node; 2880 } 2881 2882 line_buf = line_end + 1; 2883 } 2884 2885 /* First, calculate how much space we'll need for 2886 * the newly sorted block of data. (We don't just 2887 * use buf_len for this because we have extracted 2888 * comments and whitespace.) Start at 1 for trailing \0 */ 2889 count = 1; 2890 for (i = 0; i < NC_SORT_NEL; i++) 2891 for (curr = ruletab[i][0]; curr != NULL; curr = curr->next) 2892 count += curr->rule_len; 2893 2894 /* Allocate the buffer for the sorted list. */ 2895 *sorted_buf = calloc(count, sizeof(char)); 2896 if (!*sorted_buf) { 2897 ERR(sh, "Failure allocating memory."); 2898 semanage_nc_destroy_ruletab(ruletab); 2899 return -1; 2900 } 2901 *sorted_buf_len = count; 2902 2903 /* write out rule buffer */ 2904 sorted_buf_pos = *sorted_buf; 2905 for (i = 0; i < NC_SORT_NEL; i++) { 2906 for (curr = ruletab[i][0]; curr != NULL; curr = curr->next) { 2907 /* put rule into buffer */ 2908 snprintf(sorted_buf_pos, curr->rule_len + 1, "%s\n", curr->rule); /* +1 for newline */ 2909 sorted_buf_pos = sorted_buf_pos + curr->rule_len; 2910 } 2911 } 2912 2913 /* free ruletab */ 2914 semanage_nc_destroy_ruletab(ruletab); 2915 2916 return 0; 2917 } 2918