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