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