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