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