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