Home | History | Annotate | Download | only in src
      1 /* Author: Mark Goldman	  <mgoldman (at) tresys.com>
      2  * 	   Paul Rosenfeld <prosenfeld (at) tresys.com>
      3  * 	   Todd C. Miller <tmiller (at) tresys.com>
      4  *
      5  * Copyright (C) 2007 Tresys Technology, LLC
      6  *
      7  *  This library is free software; you can redistribute it and/or modify
      8  *  it under the terms of the GNU Lesser General Public License as
      9  *  published by the Free Software Foundation; either version 2.1 of the
     10  *  License, or (at your option) any later version.
     11  *
     12  *  This library is distributed in the hope that it will be useful, but
     13  *  WITHOUT ANY WARRANTY; without even the implied warranty of
     14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  *  Lesser General Public License for more details.
     16  *
     17  *  You should have received a copy of the GNU Lesser General Public
     18  *  License along with this library; if not, write to the Free Software
     19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
     20  *  02110-1301  USA
     21  */
     22 
     23 #include <semanage/handle.h>
     24 #include <semanage/seusers_policy.h>
     25 #include <semanage/users_policy.h>
     26 #include <semanage/user_record.h>
     27 #include <semanage/fcontext_record.h>
     28 #include <semanage/fcontexts_policy.h>
     29 #include <sepol/context.h>
     30 #include <sepol/context_record.h>
     31 #include "semanage_store.h"
     32 #include "seuser_internal.h"
     33 #include "debug.h"
     34 
     35 #include "utilities.h"
     36 #include "genhomedircon.h"
     37 
     38 #include <assert.h>
     39 #include <ctype.h>
     40 #include <limits.h>
     41 #include <stdio.h>
     42 #include <stdlib.h>
     43 #include <string.h>
     44 #include <sys/types.h>
     45 #include <sys/stat.h>
     46 #include <fcntl.h>
     47 #include <pwd.h>
     48 #include <errno.h>
     49 #include <unistd.h>
     50 #include <regex.h>
     51 #include <grp.h>
     52 #include <search.h>
     53 
     54 /* paths used in get_home_dirs() */
     55 #define PATH_ETC_USERADD "/etc/default/useradd"
     56 #define PATH_ETC_LIBUSER "/etc/libuser.conf"
     57 #define PATH_DEFAULT_HOME "/home"
     58 #define PATH_EXPORT_HOME "/export/home"
     59 #define PATH_ETC_LOGIN_DEFS "/etc/login.defs"
     60 
     61 /* other paths */
     62 #define PATH_SHELLS_FILE "/etc/shells"
     63 #define PATH_NOLOGIN_SHELL "/sbin/nologin"
     64 
     65 /* comments written to context file */
     66 #define COMMENT_FILE_CONTEXT_HEADER "#\n#\n# " \
     67 			"User-specific file contexts, generated via libsemanage\n" \
     68 			"# use semanage command to manage system users to change" \
     69 			" the file_context\n#\n#\n"
     70 
     71 #define COMMENT_USER_HOME_CONTEXT "\n\n#\n# Home Context for user %s" \
     72 			"\n#\n\n"
     73 
     74 /* placeholders used in the template file
     75    which are searched for and replaced */
     76 #define TEMPLATE_HOME_ROOT "HOME_ROOT"
     77 #define TEMPLATE_HOME_DIR "HOME_DIR"
     78 /* these are legacy */
     79 #define TEMPLATE_USER "USER"
     80 #define TEMPLATE_ROLE "ROLE"
     81 /* new names */
     82 #define TEMPLATE_USERNAME "%{USERNAME}"
     83 #define TEMPLATE_USERID "%{USERID}"
     84 
     85 #define FALLBACK_SENAME "user_u"
     86 #define FALLBACK_PREFIX "user"
     87 #define FALLBACK_LEVEL "s0"
     88 #define FALLBACK_NAME "[^/]+"
     89 #define FALLBACK_UIDGID "[0-9]+"
     90 #define DEFAULT_LOGIN "__default__"
     91 
     92 #define CONTEXT_NONE "<<none>>"
     93 
     94 typedef struct user_entry {
     95 	char *name;
     96 	char *uid;
     97 	char *gid;
     98 	char *sename;
     99 	char *prefix;
    100 	char *home;
    101 	char *level;
    102 	char *login;
    103 	char *homedir_role;
    104 	struct user_entry *next;
    105 } genhomedircon_user_entry_t;
    106 
    107 typedef struct {
    108 	const char *fcfilepath;
    109 	int usepasswd;
    110 	const char *homedir_template_path;
    111 	genhomedircon_user_entry_t *fallback;
    112 	semanage_handle_t *h_semanage;
    113 	sepol_policydb_t *policydb;
    114 } genhomedircon_settings_t;
    115 
    116 typedef struct {
    117 	const char *search_for;
    118 	const char *replace_with;
    119 } replacement_pair_t;
    120 
    121 typedef struct {
    122 	const char *dir;
    123 	int matched;
    124 } fc_match_handle_t;
    125 
    126 typedef struct IgnoreDir {
    127 	struct IgnoreDir *next;
    128 	char *dir;
    129 } ignoredir_t;
    130 
    131 ignoredir_t *ignore_head = NULL;
    132 
    133 static void ignore_free(void) {
    134 	ignoredir_t *next;
    135 
    136 	while (ignore_head) {
    137 		next = ignore_head->next;
    138 		free(ignore_head->dir);
    139 		free(ignore_head);
    140 		ignore_head = next;
    141 	}
    142 }
    143 
    144 static int ignore_setup(char *ignoredirs) {
    145 	char *tok;
    146 	ignoredir_t *ptr = NULL;
    147 
    148 	tok = strtok(ignoredirs, ";");
    149 	while(tok) {
    150 		ptr = calloc(sizeof(ignoredir_t),1);
    151 		if (!ptr)
    152 			goto err;
    153 		ptr->dir = strdup(tok);
    154 		if (!ptr->dir)
    155 			goto err;
    156 
    157 		ptr->next = ignore_head;
    158 		ignore_head = ptr;
    159 
    160 		tok = strtok(NULL, ";");
    161 	}
    162 
    163 	return 0;
    164 err:
    165 	free(ptr);
    166 	ignore_free();
    167 	return -1;
    168 }
    169 
    170 static int ignore(const char *homedir) {
    171 	ignoredir_t *ptr = ignore_head;
    172 	while (ptr) {
    173 		if (strcmp(ptr->dir, homedir) == 0) {
    174 			return 1;
    175 		}
    176 		ptr = ptr->next;
    177 	}
    178 	return 0;
    179 }
    180 
    181 static int prefix_is_homedir_role(const semanage_user_t *user,
    182 				  const char *prefix)
    183 {
    184 	return strcmp(OBJECT_R, prefix) == 0 ||
    185 		semanage_user_has_role(user, prefix);
    186 }
    187 
    188 static semanage_list_t *default_shell_list(void)
    189 {
    190 	semanage_list_t *list = NULL;
    191 
    192 	if (semanage_list_push(&list, "/bin/csh")
    193 	    || semanage_list_push(&list, "/bin/tcsh")
    194 	    || semanage_list_push(&list, "/bin/ksh")
    195 	    || semanage_list_push(&list, "/bin/bsh")
    196 	    || semanage_list_push(&list, "/bin/ash")
    197 	    || semanage_list_push(&list, "/usr/bin/ksh")
    198 	    || semanage_list_push(&list, "/usr/bin/pdksh")
    199 	    || semanage_list_push(&list, "/bin/zsh")
    200 	    || semanage_list_push(&list, "/bin/sh")
    201 	    || semanage_list_push(&list, "/bin/bash"))
    202 		goto fail;
    203 
    204 	return list;
    205 
    206       fail:
    207 	semanage_list_destroy(&list);
    208 	return NULL;
    209 }
    210 
    211 static semanage_list_t *get_shell_list(void)
    212 {
    213 	FILE *shells;
    214 	char *temp = NULL;
    215 	semanage_list_t *list = NULL;
    216 	size_t buff_len = 0;
    217 	ssize_t len;
    218 
    219 	shells = fopen(PATH_SHELLS_FILE, "r");
    220 	if (!shells)
    221 		return default_shell_list();
    222 	while ((len = getline(&temp, &buff_len, shells)) > 0) {
    223 		if (temp[len-1] == '\n') temp[len-1] = 0;
    224 		if (strcmp(temp, PATH_NOLOGIN_SHELL)) {
    225 			if (semanage_list_push(&list, temp)) {
    226 				free(temp);
    227 				semanage_list_destroy(&list);
    228 				return default_shell_list();
    229 			}
    230 		}
    231 	}
    232 	free(temp);
    233 
    234 	return list;
    235 }
    236 
    237 /* Helper function called via semanage_fcontext_iterate() */
    238 static int fcontext_matches(const semanage_fcontext_t *fcontext, void *varg)
    239 {
    240 	const char *oexpr = semanage_fcontext_get_expr(fcontext);
    241 	fc_match_handle_t *handp = varg;
    242 	char *expr = NULL;
    243 	regex_t re;
    244 	int type, retval = -1;
    245 	size_t len;
    246 
    247 	/* Only match ALL or DIR */
    248 	type = semanage_fcontext_get_type(fcontext);
    249 	if (type != SEMANAGE_FCONTEXT_ALL && type != SEMANAGE_FCONTEXT_DIR)
    250 		return 0;
    251 
    252 	len = strlen(oexpr);
    253 	/* Define a macro to strip a literal string from the end of oexpr */
    254 #define rstrip_oexpr_len(cstr, cstrlen) \
    255 	do { \
    256 		if (len >= (cstrlen) && !strncmp(oexpr + len - (cstrlen), (cstr), (cstrlen))) \
    257 			len -= (cstrlen); \
    258 	} while (0)
    259 #define rstrip_oexpr(cstr) rstrip_oexpr_len(cstr, sizeof(cstr) - 1)
    260 
    261 	rstrip_oexpr(".+");
    262 	rstrip_oexpr(".*");
    263 	rstrip_oexpr("(/.*)?");
    264 	rstrip_oexpr("/");
    265 
    266 #undef rstrip_oexpr_len
    267 #undef rstrip_oexpr
    268 
    269 	/* Anchor oexpr at the beginning and append pattern to eat up trailing slashes */
    270 	if (asprintf(&expr, "^%.*s/*$", (int)len, oexpr) < 0)
    271 		return -1;
    272 
    273 	/* Check dir against expr */
    274 	if (regcomp(&re, expr, REG_EXTENDED) != 0)
    275 		goto done;
    276 	if (regexec(&re, handp->dir, 0, NULL, 0) == 0)
    277 		handp->matched = 1;
    278 	regfree(&re);
    279 
    280 	retval = 0;
    281 
    282 done:
    283 	free(expr);
    284 
    285 	return retval;
    286 }
    287 
    288 static semanage_list_t *get_home_dirs(genhomedircon_settings_t * s)
    289 {
    290 	semanage_list_t *homedir_list = NULL;
    291 	semanage_list_t *shells = NULL;
    292 	fc_match_handle_t hand;
    293 	char *path = NULL;
    294 	uid_t temp, minuid = 500, maxuid = 60000;
    295 	int minuid_set = 0;
    296 	struct passwd *pwbuf;
    297 	struct stat buf;
    298 
    299 	path = semanage_findval(PATH_ETC_USERADD, "HOME", "=");
    300 	if (path && *path) {
    301 		if (semanage_list_push(&homedir_list, path))
    302 			goto fail;
    303 	}
    304 	free(path);
    305 
    306 	path = semanage_findval(PATH_ETC_LIBUSER, "LU_HOMEDIRECTORY", "=");
    307 	if (path && *path) {
    308 		if (semanage_list_push(&homedir_list, path))
    309 			goto fail;
    310 	}
    311 	free(path);
    312 	path = NULL;
    313 
    314 	if (!homedir_list) {
    315 		if (semanage_list_push(&homedir_list, PATH_DEFAULT_HOME)) {
    316 			goto fail;
    317 		}
    318 	}
    319 
    320 	if (!stat(PATH_EXPORT_HOME, &buf)) {
    321 		if (S_ISDIR(buf.st_mode)) {
    322 			if (semanage_list_push(&homedir_list, PATH_EXPORT_HOME)) {
    323 				goto fail;
    324 			}
    325 		}
    326 	}
    327 
    328 	if (!(s->usepasswd))
    329 		return homedir_list;
    330 
    331 	shells = get_shell_list();
    332 	assert(shells);
    333 
    334 	path = semanage_findval(PATH_ETC_LOGIN_DEFS, "UID_MIN", NULL);
    335 	if (path && *path) {
    336 		temp = atoi(path);
    337 		minuid = temp;
    338 		minuid_set = 1;
    339 	}
    340 	free(path);
    341 	path = NULL;
    342 
    343 	path = semanage_findval(PATH_ETC_LOGIN_DEFS, "UID_MAX", NULL);
    344 	if (path && *path) {
    345 		temp = atoi(path);
    346 		maxuid = temp;
    347 	}
    348 	free(path);
    349 	path = NULL;
    350 
    351 	path = semanage_findval(PATH_ETC_LIBUSER, "LU_UIDNUMBER", "=");
    352 	if (path && *path) {
    353 		temp = atoi(path);
    354 		if (!minuid_set || temp < minuid) {
    355 			minuid = temp;
    356 			minuid_set = 1;
    357 		}
    358 	}
    359 	free(path);
    360 	path = NULL;
    361 
    362 	errno = 0;
    363 	setpwent();
    364 	while ((pwbuf = getpwent()) != NULL) {
    365 		if (pwbuf->pw_uid < minuid || pwbuf->pw_uid > maxuid)
    366 			continue;
    367 		if (!semanage_list_find(shells, pwbuf->pw_shell))
    368 			continue;
    369 		int len = strlen(pwbuf->pw_dir) -1;
    370 		for(; len > 0 && pwbuf->pw_dir[len] == '/'; len--) {
    371 			pwbuf->pw_dir[len] = '\0';
    372 		}
    373 		if (strcmp(pwbuf->pw_dir, "/") == 0)
    374 			continue;
    375 		if (ignore(pwbuf->pw_dir))
    376 			continue;
    377 		if (semanage_str_count(pwbuf->pw_dir, '/') <= 1)
    378 			continue;
    379 		if (!(path = strdup(pwbuf->pw_dir))) {
    380 			break;
    381 		}
    382 
    383 		semanage_rtrim(path, '/');
    384 
    385 		if (!semanage_list_find(homedir_list, path)) {
    386 			/*
    387 			 * Now check for an existing file context that matches
    388 			 * so we don't label a non-homedir as a homedir.
    389 			 */
    390 			hand.dir = path;
    391 			hand.matched = 0;
    392 			if (semanage_fcontext_iterate(s->h_semanage,
    393 			    fcontext_matches, &hand) == STATUS_ERR)
    394 				goto fail;
    395 
    396 			/* NOTE: old genhomedircon printed a warning on match */
    397 			if (hand.matched) {
    398 				WARN(s->h_semanage, "%s homedir %s or its parent directory conflicts with a file context already specified in the policy.  This usually indicates an incorrectly defined system account.  If it is a system account please make sure its uid is less than %u or greater than %u or its login shell is /sbin/nologin.", pwbuf->pw_name, pwbuf->pw_dir, minuid, maxuid);
    399 			} else {
    400 				if (semanage_list_push(&homedir_list, path))
    401 					goto fail;
    402 			}
    403 		}
    404 		free(path);
    405 		path = NULL;
    406 		errno = 0;
    407 	}
    408 
    409 	if (errno) {
    410 		WARN(s->h_semanage, "Error while fetching users.  "
    411 		     "Returning list so far.");
    412 	}
    413 
    414 	if (semanage_list_sort(&homedir_list))
    415 		goto fail;
    416 
    417 	endpwent();
    418 	semanage_list_destroy(&shells);
    419 
    420 	return homedir_list;
    421 
    422       fail:
    423 	endpwent();
    424 	free(path);
    425 	semanage_list_destroy(&homedir_list);
    426 	semanage_list_destroy(&shells);
    427 	return NULL;
    428 }
    429 
    430 /**
    431  * @param	out	the FILE to put all the output in.
    432  * @return	0 on success
    433  */
    434 static int write_file_context_header(FILE * out)
    435 {
    436 	if (fprintf(out, COMMENT_FILE_CONTEXT_HEADER) < 0) {
    437 		return STATUS_ERR;
    438 	}
    439 
    440 	return STATUS_SUCCESS;
    441 }
    442 
    443 /* Predicates for use with semanage_slurp_file_filter() the homedir_template
    444  * file currently contains lines that serve as the template for a user's
    445  * homedir.
    446  *
    447  * It also contains lines that are the template for the parent of a
    448  * user's home directory.
    449  *
    450  * Currently, the only lines that apply to the the root of a user's home
    451  * directory are all prefixed with the string "HOME_ROOT".  All other
    452  * lines apply to a user's home directory.  If this changes the
    453  * following predicates need to change to reflect that.
    454  */
    455 static int HOME_ROOT_PRED(const char *string)
    456 {
    457 	return semanage_is_prefix(string, TEMPLATE_HOME_ROOT);
    458 }
    459 
    460 static int HOME_DIR_PRED(const char *string)
    461 {
    462 	return semanage_is_prefix(string, TEMPLATE_HOME_DIR);
    463 }
    464 
    465 /* new names */
    466 static int USERNAME_CONTEXT_PRED(const char *string)
    467 {
    468 	return (int)(
    469 		(strstr(string, TEMPLATE_USERNAME) != NULL) ||
    470 		(strstr(string, TEMPLATE_USERID) != NULL)
    471 	);
    472 }
    473 
    474 /* This will never match USER if USERNAME or USERID are found. */
    475 static int USER_CONTEXT_PRED(const char *string)
    476 {
    477 	if (USERNAME_CONTEXT_PRED(string))
    478 		return 0;
    479 
    480 	return (int)(strstr(string, TEMPLATE_USER) != NULL);
    481 }
    482 
    483 static int STR_COMPARATOR(const void *a, const void *b)
    484 {
    485 	return strcmp((const char *) a, (const char *) b);
    486 }
    487 
    488 /* make_tempate
    489  * @param	s	  the settings holding the paths to various files
    490  * @param	pred	function pointer to function to use as filter for slurp
    491  * 					file filter
    492  * @return   a list of lines from the template file with inappropriate
    493  *	    lines filtered out.
    494  */
    495 static semanage_list_t *make_template(genhomedircon_settings_t * s,
    496 				      int (*pred) (const char *))
    497 {
    498 	FILE *template_file = NULL;
    499 	semanage_list_t *template_data = NULL;
    500 
    501 	template_file = fopen(s->homedir_template_path, "r");
    502 	if (!template_file)
    503 		return NULL;
    504 	template_data = semanage_slurp_file_filter(template_file, pred);
    505 	fclose(template_file);
    506 
    507 	return template_data;
    508 }
    509 
    510 static char *replace_all(const char *str, const replacement_pair_t * repl)
    511 {
    512 	char *retval, *retval2;
    513 	int i;
    514 
    515 	if (!str || !repl)
    516 		return NULL;
    517 
    518 	retval = strdup(str);
    519 	for (i = 0; retval != NULL && repl[i].search_for; i++) {
    520 		retval2 = semanage_str_replace(repl[i].search_for,
    521 					       repl[i].replace_with, retval, 0);
    522 		free(retval);
    523 		retval = retval2;
    524 	}
    525 	return retval;
    526 }
    527 
    528 static const char *extract_context(const char *line)
    529 {
    530 	const char *p = line;
    531 	size_t off;
    532 
    533 	off = strlen(p);
    534 	p += off;
    535 	/* consider trailing whitespaces */
    536 	while (off > 0) {
    537 		p--;
    538 		off--;
    539 		if (!isspace(*p))
    540 			break;
    541 	}
    542 	if (off == 0)
    543 		return NULL;
    544 
    545 	/* find the last field in line */
    546 	while (off > 0 && !isspace(*(p - 1))) {
    547 		p--;
    548 		off--;
    549 	}
    550 	return p;
    551 }
    552 
    553 static int check_line(genhomedircon_settings_t * s, const char *line)
    554 {
    555 	sepol_context_t *ctx_record = NULL;
    556 	const char *ctx_str;
    557 	int result;
    558 
    559 	ctx_str = extract_context(line);
    560 	if (!ctx_str)
    561 		return STATUS_ERR;
    562 
    563 	result = sepol_context_from_string(s->h_semanage->sepolh,
    564 					   ctx_str, &ctx_record);
    565 	if (result == STATUS_SUCCESS && ctx_record != NULL) {
    566 		result = sepol_context_check(s->h_semanage->sepolh,
    567 					     s->policydb, ctx_record);
    568 		sepol_context_free(ctx_record);
    569 	}
    570 	return result;
    571 }
    572 
    573 static int write_replacements(genhomedircon_settings_t * s, FILE * out,
    574 			      const semanage_list_t * tpl,
    575 			      const replacement_pair_t *repl)
    576 {
    577 	char *line;
    578 
    579 	for (; tpl; tpl = tpl->next) {
    580 		line = replace_all(tpl->data, repl);
    581 		if (!line)
    582 			goto fail;
    583 		if (check_line(s, line) == STATUS_SUCCESS) {
    584 			if (fprintf(out, "%s\n", line) < 0)
    585 				goto fail;
    586 		}
    587 		free(line);
    588 	}
    589 	return STATUS_SUCCESS;
    590 
    591       fail:
    592 	free(line);
    593 	return STATUS_ERR;
    594 }
    595 
    596 static int write_contexts(genhomedircon_settings_t *s, FILE *out,
    597 			  semanage_list_t *tpl, const replacement_pair_t *repl,
    598 			  const genhomedircon_user_entry_t *user)
    599 {
    600 	char *line, *temp;
    601 	sepol_context_t *context;
    602 	char *new_context_str;
    603 
    604 	for (; tpl; tpl = tpl->next) {
    605 		context = NULL;
    606 		new_context_str = NULL;
    607 		line = replace_all(tpl->data, repl);
    608 		if (!line) {
    609 			goto fail;
    610 		}
    611 
    612 		const char *old_context_str = extract_context(line);
    613 		if (!old_context_str) {
    614 			goto fail;
    615 		}
    616 
    617 		if (strcmp(old_context_str, CONTEXT_NONE) == 0) {
    618 			if (check_line(s, line) == STATUS_SUCCESS &&
    619 			    fprintf(out, "%s\n", line) < 0) {
    620 				goto fail;
    621 			}
    622 			free(line);
    623 			continue;
    624 		}
    625 
    626 		sepol_handle_t *sepolh = s->h_semanage->sepolh;
    627 
    628 		if (sepol_context_from_string(sepolh, old_context_str,
    629 					      &context) < 0) {
    630 			goto fail;
    631 		}
    632 
    633 		if (sepol_context_set_user(sepolh, context, user->sename) < 0) {
    634 			goto fail;
    635 		}
    636 
    637 		if (sepol_policydb_mls_enabled(s->policydb) &&
    638 		    sepol_context_set_mls(sepolh, context, user->level) < 0) {
    639 			goto fail;
    640 		}
    641 
    642 		if (user->homedir_role &&
    643 		    sepol_context_set_role(sepolh, context, user->homedir_role) < 0) {
    644 			goto fail;
    645 		}
    646 
    647 		if (sepol_context_to_string(sepolh, context,
    648 					    &new_context_str) < 0) {
    649 			goto fail;
    650 		}
    651 
    652 		temp = semanage_str_replace(old_context_str, new_context_str,
    653 					    line, 1);
    654 		if (!temp) {
    655 			goto fail;
    656 		}
    657 		free(line);
    658 		line = temp;
    659 
    660 		if (check_line(s, line) == STATUS_SUCCESS) {
    661 			if (fprintf(out, "%s\n", line) < 0)
    662 				goto fail;
    663 		}
    664 
    665 		free(line);
    666 		sepol_context_free(context);
    667 		free(new_context_str);
    668 	}
    669 
    670 	return STATUS_SUCCESS;
    671 fail:
    672 	free(line);
    673 	sepol_context_free(context);
    674 	free(new_context_str);
    675 	return STATUS_ERR;
    676 }
    677 
    678 static int write_home_dir_context(genhomedircon_settings_t * s, FILE * out,
    679 				  semanage_list_t * tpl, const genhomedircon_user_entry_t *user)
    680 {
    681 	replacement_pair_t repl[] = {
    682 		{.search_for = TEMPLATE_HOME_DIR,.replace_with = user->home},
    683 		{.search_for = TEMPLATE_ROLE,.replace_with = user->prefix},
    684 		{NULL, NULL}
    685 	};
    686 
    687 	if (strcmp(user->name, FALLBACK_NAME) == 0) {
    688 		if (fprintf(out, COMMENT_USER_HOME_CONTEXT, FALLBACK_SENAME) < 0)
    689 			return STATUS_ERR;
    690 	} else {
    691 		if (fprintf(out, COMMENT_USER_HOME_CONTEXT, user->name) < 0)
    692 			return STATUS_ERR;
    693 	}
    694 
    695 	return write_contexts(s, out, tpl, repl, user);
    696 }
    697 
    698 static int write_home_root_context(genhomedircon_settings_t * s, FILE * out,
    699 				   semanage_list_t * tpl, char *homedir)
    700 {
    701 	replacement_pair_t repl[] = {
    702 		{.search_for = TEMPLATE_HOME_ROOT,.replace_with = homedir},
    703 		{NULL, NULL}
    704 	};
    705 
    706 	return write_replacements(s, out, tpl, repl);
    707 }
    708 
    709 static int write_username_context(genhomedircon_settings_t * s, FILE * out,
    710 				  semanage_list_t * tpl,
    711 				  const genhomedircon_user_entry_t *user)
    712 {
    713 	replacement_pair_t repl[] = {
    714 		{.search_for = TEMPLATE_USERNAME,.replace_with = user->name},
    715 		{.search_for = TEMPLATE_USERID,.replace_with = user->uid},
    716 		{.search_for = TEMPLATE_ROLE,.replace_with = user->prefix},
    717 		{NULL, NULL}
    718 	};
    719 
    720 	return write_contexts(s, out, tpl, repl, user);
    721 }
    722 
    723 static int write_user_context(genhomedircon_settings_t * s, FILE * out,
    724 			      semanage_list_t * tpl, const genhomedircon_user_entry_t *user)
    725 {
    726 	replacement_pair_t repl[] = {
    727 		{.search_for = TEMPLATE_USER,.replace_with = user->name},
    728 		{.search_for = TEMPLATE_ROLE,.replace_with = user->prefix},
    729 		{NULL, NULL}
    730 	};
    731 
    732 	return write_contexts(s, out, tpl, repl, user);
    733 }
    734 
    735 static int seuser_sort_func(const void *arg1, const void *arg2)
    736 {
    737 	const semanage_seuser_t **u1 = (const semanage_seuser_t **) arg1;
    738 	const semanage_seuser_t **u2 = (const semanage_seuser_t **) arg2;;
    739 	const char *name1 = semanage_seuser_get_name(*u1);
    740 	const char *name2 = semanage_seuser_get_name(*u2);
    741 
    742 	if (name1[0] == '%' && name2[0] == '%') {
    743 		return 0;
    744 	} else if (name1[0] == '%') {
    745 		return 1;
    746 	} else if (name2[0] == '%') {
    747 		return -1;
    748 	}
    749 
    750 	return strcmp(name1, name2);
    751 }
    752 
    753 static int user_sort_func(semanage_user_t ** arg1, semanage_user_t ** arg2)
    754 {
    755 	return strcmp(semanage_user_get_name(*arg1),
    756 		      semanage_user_get_name(*arg2));
    757 }
    758 
    759 static int name_user_cmp(char *key, semanage_user_t ** val)
    760 {
    761 	return strcmp(key, semanage_user_get_name(*val));
    762 }
    763 
    764 static int push_user_entry(genhomedircon_user_entry_t ** list, const char *n,
    765 			   const char *u, const char *g, const char *sen,
    766 			   const char *pre, const char *h, const char *l,
    767 			   const char *ln, const char *hd_role)
    768 {
    769 	genhomedircon_user_entry_t *temp = NULL;
    770 	char *name = NULL;
    771 	char *uid = NULL;
    772 	char *gid = NULL;
    773 	char *sename = NULL;
    774 	char *prefix = NULL;
    775 	char *home = NULL;
    776 	char *level = NULL;
    777 	char *lname = NULL;
    778 	char *homedir_role = NULL;
    779 
    780 	temp = malloc(sizeof(genhomedircon_user_entry_t));
    781 	if (!temp)
    782 		goto cleanup;
    783 	name = strdup(n);
    784 	if (!name)
    785 		goto cleanup;
    786 	uid = strdup(u);
    787 	if (!uid)
    788 		goto cleanup;
    789 	gid = strdup(g);
    790 	if (!gid)
    791 		goto cleanup;
    792 	sename = strdup(sen);
    793 	if (!sename)
    794 		goto cleanup;
    795 	prefix = strdup(pre);
    796 	if (!prefix)
    797 		goto cleanup;
    798 	home = strdup(h);
    799 	if (!home)
    800 		goto cleanup;
    801 	level = strdup(l);
    802 	if (!level)
    803 		goto cleanup;
    804 	lname = strdup(ln);
    805 	if (!lname)
    806 		goto cleanup;
    807 	if (hd_role) {
    808 		homedir_role = strdup(hd_role);
    809 		if (!homedir_role)
    810 			goto cleanup;
    811 	}
    812 
    813 	temp->name = name;
    814 	temp->uid = uid;
    815 	temp->gid = gid;
    816 	temp->sename = sename;
    817 	temp->prefix = prefix;
    818 	temp->home = home;
    819 	temp->level = level;
    820 	temp->login = lname;
    821 	temp->homedir_role = homedir_role;
    822 	temp->next = (*list);
    823 	(*list) = temp;
    824 
    825 	return STATUS_SUCCESS;
    826 
    827       cleanup:
    828 	free(name);
    829 	free(uid);
    830 	free(gid);
    831 	free(sename);
    832 	free(prefix);
    833 	free(home);
    834 	free(level);
    835 	free(lname);
    836 	free(homedir_role);
    837 	free(temp);
    838 	return STATUS_ERR;
    839 }
    840 
    841 static void pop_user_entry(genhomedircon_user_entry_t ** list)
    842 {
    843 	genhomedircon_user_entry_t *temp;
    844 
    845 	if (!list || !(*list))
    846 		return;
    847 
    848 	temp = *list;
    849 	*list = temp->next;
    850 	free(temp->name);
    851 	free(temp->uid);
    852 	free(temp->gid);
    853 	free(temp->sename);
    854 	free(temp->prefix);
    855 	free(temp->home);
    856 	free(temp->level);
    857 	free(temp->login);
    858 	free(temp->homedir_role);
    859 	free(temp);
    860 }
    861 
    862 static int setup_fallback_user(genhomedircon_settings_t * s)
    863 {
    864 	semanage_seuser_t **seuser_list = NULL;
    865 	unsigned int nseusers = 0;
    866 	semanage_user_key_t *key = NULL;
    867 	semanage_user_t *u = NULL;
    868 	const char *name = NULL;
    869 	const char *seuname = NULL;
    870 	const char *prefix = NULL;
    871 	const char *level = NULL;
    872 	const char *homedir_role = NULL;
    873 	unsigned int i;
    874 	int retval;
    875 	int errors = 0;
    876 
    877 	retval = semanage_seuser_list(s->h_semanage, &seuser_list, &nseusers);
    878 	if (retval < 0 || (nseusers < 1)) {
    879 		/* if there are no users, this function can't do any other work */
    880 		return errors;
    881 	}
    882 
    883 	for (i = 0; i < nseusers; i++) {
    884 		name = semanage_seuser_get_name(seuser_list[i]);
    885 		if (strcmp(name, DEFAULT_LOGIN) == 0) {
    886 			seuname = semanage_seuser_get_sename(seuser_list[i]);
    887 
    888 			/* find the user structure given the name */
    889 			if (semanage_user_key_create(s->h_semanage, seuname,
    890 						     &key) < 0) {
    891 				errors = STATUS_ERR;
    892 				break;
    893 			}
    894 			if (semanage_user_query(s->h_semanage, key, &u) < 0)
    895 			{
    896 				prefix = name;
    897 				level = FALLBACK_LEVEL;
    898 			}
    899 			else
    900 			{
    901 				prefix = semanage_user_get_prefix(u);
    902 				level = semanage_user_get_mlslevel(u);
    903 				if (!level)
    904 					level = FALLBACK_LEVEL;
    905 			}
    906 
    907 			if (prefix_is_homedir_role(u, prefix)) {
    908 				homedir_role = prefix;
    909 			}
    910 
    911 			if (push_user_entry(&(s->fallback), FALLBACK_NAME,
    912 					    FALLBACK_UIDGID, FALLBACK_UIDGID,
    913 					    seuname, prefix, "", level,
    914 					    FALLBACK_NAME, homedir_role) != 0)
    915 				errors = STATUS_ERR;
    916 			semanage_user_key_free(key);
    917 			if (u)
    918 				semanage_user_free(u);
    919 			break;
    920 		}
    921 	}
    922 
    923 	for (i = 0; i < nseusers; i++)
    924 		semanage_seuser_free(seuser_list[i]);
    925 	free(seuser_list);
    926 
    927 	return errors;
    928 }
    929 
    930 static genhomedircon_user_entry_t *find_user(genhomedircon_user_entry_t *head,
    931 					     const char *name)
    932 {
    933 	for(; head; head = head->next) {
    934 		if (strcmp(head->name, name) == 0) {
    935 			return head;
    936 		}
    937 	}
    938 
    939 	return NULL;
    940 }
    941 
    942 static int add_user(genhomedircon_settings_t * s,
    943 		    genhomedircon_user_entry_t **head,
    944 		    semanage_user_t *user,
    945 		    const char *name,
    946 		    const char *sename,
    947 		    const char *selogin)
    948 {
    949 	if (selogin[0] == '%') {
    950 		genhomedircon_user_entry_t *orig = find_user(*head, name);
    951 		if (orig != NULL && orig->login[0] == '%') {
    952 			ERR(s->h_semanage, "User %s is already mapped to"
    953 			    " group %s, but also belongs to group %s. Add an"
    954 			    " explicit mapping for this user to"
    955 			    " override group mappings.",
    956 			    name, orig->login + 1, selogin + 1);
    957 			return STATUS_ERR;
    958 		} else if (orig != NULL) {
    959 			// user mappings take precedence
    960 			return STATUS_SUCCESS;
    961 		}
    962 	}
    963 
    964 	int retval = STATUS_ERR;
    965 
    966 	char *rbuf = NULL;
    967 	long rbuflen;
    968 	struct passwd pwstorage, *pwent = NULL;
    969 	const char *prefix = NULL;
    970 	const char *level = NULL;
    971 	const char *homedir_role = NULL;
    972 	char uid[11];
    973 	char gid[11];
    974 
    975 	/* Allocate space for the getpwnam_r buffer */
    976 	rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
    977 	if (rbuflen <= 0)
    978 		goto cleanup;
    979 	rbuf = malloc(rbuflen);
    980 	if (rbuf == NULL)
    981 		goto cleanup;
    982 
    983 	if (user) {
    984 		prefix = semanage_user_get_prefix(user);
    985 		level = semanage_user_get_mlslevel(user);
    986 
    987 		if (!level) {
    988 			level = FALLBACK_LEVEL;
    989 		}
    990 	} else {
    991 		prefix = name;
    992 		level = FALLBACK_LEVEL;
    993 	}
    994 
    995 	if (prefix_is_homedir_role(user, prefix)) {
    996 		homedir_role = prefix;
    997 	}
    998 
    999 	retval = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent);
   1000 	if (retval != 0 || pwent == NULL) {
   1001 		if (retval != 0 && retval != ENOENT) {
   1002 			goto cleanup;
   1003 		}
   1004 
   1005 		WARN(s->h_semanage,
   1006 		     "user %s not in password file", name);
   1007 		retval = STATUS_SUCCESS;
   1008 		goto cleanup;
   1009 	}
   1010 
   1011 	int len = strlen(pwent->pw_dir) -1;
   1012 	for(; len > 0 && pwent->pw_dir[len] == '/'; len--) {
   1013 		pwent->pw_dir[len] = '\0';
   1014 	}
   1015 
   1016 	if (strcmp(pwent->pw_dir, "/") == 0) {
   1017 		/* don't relabel / genhomdircon checked to see if root
   1018 		 * was the user and if so, set his home directory to
   1019 		 * /root */
   1020 		retval = STATUS_SUCCESS;
   1021 		goto cleanup;
   1022 	}
   1023 
   1024 	if (ignore(pwent->pw_dir)) {
   1025 		retval = STATUS_SUCCESS;
   1026 		goto cleanup;
   1027 	}
   1028 
   1029 	len = snprintf(uid, sizeof(uid), "%u", pwent->pw_uid);
   1030 	if (len < 0 || len >= (int)sizeof(uid)) {
   1031 		goto cleanup;
   1032 	}
   1033 
   1034 	len = snprintf(gid, sizeof(gid), "%u", pwent->pw_gid);
   1035 	if (len < 0 || len >= (int)sizeof(gid)) {
   1036 		goto cleanup;
   1037 	}
   1038 
   1039 	retval = push_user_entry(head, name, uid, gid, sename, prefix,
   1040 				pwent->pw_dir, level, selogin, homedir_role);
   1041 cleanup:
   1042 	free(rbuf);
   1043 	return retval;
   1044 }
   1045 
   1046 static int get_group_users(genhomedircon_settings_t * s,
   1047 			  genhomedircon_user_entry_t **head,
   1048 			  semanage_user_t *user,
   1049 			  const char *sename,
   1050 			  const char *selogin)
   1051 {
   1052 	int retval = STATUS_ERR;
   1053 	unsigned int i;
   1054 
   1055 	long grbuflen;
   1056 	char *grbuf = NULL;
   1057 	struct group grstorage, *group = NULL;
   1058 	struct passwd *pw = NULL;
   1059 
   1060 	grbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
   1061 	if (grbuflen <= 0)
   1062 		goto cleanup;
   1063 	grbuf = malloc(grbuflen);
   1064 	if (grbuf == NULL)
   1065 		goto cleanup;
   1066 
   1067 	const char *grname = selogin + 1;
   1068 
   1069 	if (getgrnam_r(grname, &grstorage, grbuf,
   1070 			(size_t) grbuflen, &group) != 0) {
   1071 		goto cleanup;
   1072 	}
   1073 
   1074 	if (group == NULL) {
   1075 		ERR(s->h_semanage, "Can't find group named %s\n", grname);
   1076 		goto cleanup;
   1077 	}
   1078 
   1079 	size_t nmembers = 0;
   1080 	char **members = group->gr_mem;
   1081 
   1082 	while (*members != NULL) {
   1083 		nmembers++;
   1084 		members++;
   1085 	}
   1086 
   1087 	for (i = 0; i < nmembers; i++) {
   1088 		const char *uname = group->gr_mem[i];
   1089 
   1090 		if (add_user(s, head, user, uname, sename, selogin) < 0) {
   1091 			goto cleanup;
   1092 		}
   1093 	}
   1094 
   1095 	setpwent();
   1096 	while ((pw = getpwent()) != NULL) {
   1097 		// skip users who also have this group as their
   1098 		// primary group
   1099 		if (lfind(pw->pw_name, group->gr_mem, &nmembers,
   1100 			  sizeof(char *), &STR_COMPARATOR)) {
   1101 			continue;
   1102 		}
   1103 
   1104 		if (group->gr_gid == pw->pw_gid) {
   1105 			if (add_user(s, head, user, pw->pw_name,
   1106 				     sename, selogin) < 0) {
   1107 				goto cleanup;
   1108 			}
   1109 		}
   1110 	}
   1111 
   1112 	retval = STATUS_SUCCESS;
   1113 cleanup:
   1114 	endpwent();
   1115 	free(grbuf);
   1116 
   1117 	return retval;
   1118 }
   1119 
   1120 static genhomedircon_user_entry_t *get_users(genhomedircon_settings_t * s,
   1121 					     int *errors)
   1122 {
   1123 	genhomedircon_user_entry_t *head = NULL;
   1124 	semanage_seuser_t **seuser_list = NULL;
   1125 	unsigned int nseusers = 0;
   1126 	semanage_user_t **user_list = NULL;
   1127 	unsigned int nusers = 0;
   1128 	semanage_user_t **u = NULL;
   1129 	const char *name = NULL;
   1130 	const char *seuname = NULL;
   1131 	unsigned int i;
   1132 	int retval;
   1133 
   1134 	*errors = 0;
   1135 	retval = semanage_seuser_list(s->h_semanage, &seuser_list, &nseusers);
   1136 	if (retval < 0 || (nseusers < 1)) {
   1137 		/* if there are no users, this function can't do any other work */
   1138 		return NULL;
   1139 	}
   1140 
   1141 	if (semanage_user_list(s->h_semanage, &user_list, &nusers) < 0) {
   1142 		nusers = 0;
   1143 	}
   1144 
   1145 	qsort(seuser_list, nseusers, sizeof(semanage_seuser_t *),
   1146 	      &seuser_sort_func);
   1147 	qsort(user_list, nusers, sizeof(semanage_user_t *),
   1148 	      (int (*)(const void *, const void *))&user_sort_func);
   1149 
   1150 	for (i = 0; i < nseusers; i++) {
   1151 		seuname = semanage_seuser_get_sename(seuser_list[i]);
   1152 		name = semanage_seuser_get_name(seuser_list[i]);
   1153 
   1154 		if (strcmp(name, DEFAULT_LOGIN) == 0)
   1155 			continue;
   1156 
   1157 		/* find the user structure given the name */
   1158 		u = bsearch(seuname, user_list, nusers, sizeof(semanage_user_t *),
   1159 			    (int (*)(const void *, const void *))
   1160 			    &name_user_cmp);
   1161 
   1162 		/* %groupname syntax */
   1163 		if (name[0] == '%') {
   1164 			retval = get_group_users(s, &head, *u, seuname,
   1165 						name);
   1166 		} else {
   1167 			retval = add_user(s, &head, *u, name,
   1168 					  seuname, name);
   1169 		}
   1170 
   1171 		if (retval != 0) {
   1172 			*errors = STATUS_ERR;
   1173 			goto cleanup;
   1174 		}
   1175 	}
   1176 
   1177       cleanup:
   1178 	if (*errors) {
   1179 		for (; head; pop_user_entry(&head)) {
   1180 			/* the pop function takes care of all the cleanup
   1181 			   so the loop body is just empty */
   1182 		}
   1183 	}
   1184 	for (i = 0; i < nseusers; i++) {
   1185 		semanage_seuser_free(seuser_list[i]);
   1186 	}
   1187 	free(seuser_list);
   1188 
   1189 	for (i = 0; i < nusers; i++) {
   1190 		semanage_user_free(user_list[i]);
   1191 	}
   1192 	free(user_list);
   1193 
   1194 	return head;
   1195 }
   1196 
   1197 static int write_gen_home_dir_context(genhomedircon_settings_t * s, FILE * out,
   1198 				      semanage_list_t * username_context_tpl,
   1199 				      semanage_list_t * user_context_tpl,
   1200 				      semanage_list_t * homedir_context_tpl)
   1201 {
   1202 	genhomedircon_user_entry_t *users;
   1203 	int errors = 0;
   1204 
   1205 	users = get_users(s, &errors);
   1206 	if (!users && errors) {
   1207 		return STATUS_ERR;
   1208 	}
   1209 
   1210 	for (; users; pop_user_entry(&users)) {
   1211 		if (write_home_dir_context(s, out, homedir_context_tpl, users))
   1212 			goto err;
   1213 		if (write_username_context(s, out, username_context_tpl, users))
   1214 			goto err;
   1215 		if (write_user_context(s, out, user_context_tpl, users))
   1216 			goto err;
   1217 	}
   1218 
   1219 	return STATUS_SUCCESS;
   1220 err:
   1221 	for (; users; pop_user_entry(&users)) {
   1222 	/* the pop function takes care of all the cleanup
   1223 	 * so the loop body is just empty */
   1224 	}
   1225 
   1226 	return STATUS_ERR;
   1227 }
   1228 
   1229 /**
   1230  * @param	s	settings structure, stores various paths etc. Must never be NULL
   1231  * @param	out	the FILE to put all the output in.
   1232  * @return	0 on success
   1233  */
   1234 static int write_context_file(genhomedircon_settings_t * s, FILE * out)
   1235 {
   1236 	semanage_list_t *homedirs = NULL;
   1237 	semanage_list_t *h = NULL;
   1238 	semanage_list_t *homedir_context_tpl = NULL;
   1239 	semanage_list_t *homeroot_context_tpl = NULL;
   1240 	semanage_list_t *username_context_tpl = NULL;
   1241 	semanage_list_t *user_context_tpl = NULL;
   1242 	int retval = STATUS_SUCCESS;
   1243 
   1244 	homedir_context_tpl = make_template(s, &HOME_DIR_PRED);
   1245 	homeroot_context_tpl = make_template(s, &HOME_ROOT_PRED);
   1246 	username_context_tpl = make_template(s, &USERNAME_CONTEXT_PRED);
   1247 	user_context_tpl = make_template(s, &USER_CONTEXT_PRED);
   1248 
   1249 	if (!homedir_context_tpl
   1250 	 && !homeroot_context_tpl
   1251 	 && !username_context_tpl
   1252 	 && !user_context_tpl)
   1253 		goto done;
   1254 
   1255 	if (write_file_context_header(out) != STATUS_SUCCESS) {
   1256 		retval = STATUS_ERR;
   1257 		goto done;
   1258 	}
   1259 
   1260 	if (setup_fallback_user(s) != 0) {
   1261 		retval = STATUS_ERR;
   1262 		goto done;
   1263 	}
   1264 
   1265 	if (homedir_context_tpl || homeroot_context_tpl) {
   1266 		homedirs = get_home_dirs(s);
   1267 		if (!homedirs) {
   1268 			WARN(s->h_semanage,
   1269 			     "no home directories were available, exiting without writing");
   1270 			goto done;
   1271 		}
   1272 
   1273 		for (h = homedirs; h; h = h->next) {
   1274 			char *temp = NULL;
   1275 
   1276 			if (asprintf(&temp, "%s/%s", h->data, FALLBACK_NAME) < 0) {
   1277 				retval = STATUS_ERR;
   1278 				goto done;
   1279 			}
   1280 
   1281 			free(s->fallback->home);
   1282 			s->fallback->home = temp;
   1283 
   1284 			if (write_home_dir_context(s, out, homedir_context_tpl,
   1285 						   s->fallback) != STATUS_SUCCESS) {
   1286 				free(temp);
   1287 				s->fallback->home = NULL;
   1288 				retval = STATUS_ERR;
   1289 				goto done;
   1290 			}
   1291 			if (write_home_root_context(s, out,
   1292 						    homeroot_context_tpl,
   1293 						    h->data) != STATUS_SUCCESS) {
   1294 				free(temp);
   1295 				s->fallback->home = NULL;
   1296 				retval = STATUS_ERR;
   1297 				goto done;
   1298 			}
   1299 
   1300 			free(temp);
   1301 			s->fallback->home = NULL;
   1302 		}
   1303 	}
   1304 	if (user_context_tpl || username_context_tpl) {
   1305 		if (write_username_context(s, out, username_context_tpl,
   1306 					   s->fallback) != STATUS_SUCCESS) {
   1307 			retval = STATUS_ERR;
   1308 			goto done;
   1309 		}
   1310 
   1311 		if (write_user_context(s, out, user_context_tpl,
   1312 				       s->fallback) != STATUS_SUCCESS) {
   1313 			retval = STATUS_ERR;
   1314 			goto done;
   1315 		}
   1316 
   1317 		if (write_gen_home_dir_context(s, out, username_context_tpl,
   1318 					       user_context_tpl, homedir_context_tpl)
   1319 				!= STATUS_SUCCESS) {
   1320 			retval = STATUS_ERR;
   1321 		}
   1322 	}
   1323 
   1324 done:
   1325 	/* Cleanup */
   1326 	semanage_list_destroy(&homedirs);
   1327 	semanage_list_destroy(&username_context_tpl);
   1328 	semanage_list_destroy(&user_context_tpl);
   1329 	semanage_list_destroy(&homedir_context_tpl);
   1330 	semanage_list_destroy(&homeroot_context_tpl);
   1331 
   1332 	return retval;
   1333 }
   1334 
   1335 int semanage_genhomedircon(semanage_handle_t * sh,
   1336 			   sepol_policydb_t * policydb,
   1337 			   int usepasswd,
   1338 			   char *ignoredirs)
   1339 {
   1340 	genhomedircon_settings_t s;
   1341 	FILE *out = NULL;
   1342 	int retval = 0;
   1343 
   1344 	assert(sh);
   1345 
   1346 	s.homedir_template_path =
   1347 	    semanage_path(SEMANAGE_TMP, SEMANAGE_HOMEDIR_TMPL);
   1348 	s.fcfilepath =
   1349 		semanage_path(SEMANAGE_TMP, SEMANAGE_STORE_FC_HOMEDIRS);
   1350 
   1351 	s.fallback = calloc(1, sizeof(genhomedircon_user_entry_t));
   1352 	if (s.fallback == NULL) {
   1353 		retval = STATUS_ERR;
   1354 		goto done;
   1355 	}
   1356 
   1357 	s.fallback->name = strdup(FALLBACK_NAME);
   1358 	s.fallback->sename = strdup(FALLBACK_SENAME);
   1359 	s.fallback->prefix = strdup(FALLBACK_PREFIX);
   1360 	s.fallback->level = strdup(FALLBACK_LEVEL);
   1361 	if (s.fallback->name == NULL
   1362 	 || s.fallback->sename == NULL
   1363 	 || s.fallback->prefix == NULL
   1364 	 || s.fallback->level == NULL) {
   1365 		retval = STATUS_ERR;
   1366 		goto done;
   1367 	}
   1368 
   1369 	if (ignoredirs) ignore_setup(ignoredirs);
   1370 
   1371 	s.usepasswd = usepasswd;
   1372 	s.h_semanage = sh;
   1373 	s.policydb = policydb;
   1374 
   1375 	if (!(out = fopen(s.fcfilepath, "w"))) {
   1376 		/* couldn't open output file */
   1377 		ERR(sh, "Could not open the file_context file for writing");
   1378 		retval = STATUS_ERR;
   1379 		goto done;
   1380 	}
   1381 
   1382 	retval = write_context_file(&s, out);
   1383 
   1384 done:
   1385 	if (out != NULL)
   1386 		fclose(out);
   1387 
   1388 	while (s.fallback)
   1389 		pop_user_entry(&(s.fallback));
   1390 
   1391 	ignore_free();
   1392 
   1393 	return retval;
   1394 }
   1395