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