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