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