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