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