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