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