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