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