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