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