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