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