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