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