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