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