Home | History | Annotate | Download | only in src
      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