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