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