• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Author: Mark Goldman	  <mgoldman@tresys.com>
2  * 	   Paul Rosenfeld <prosenfeld@tresys.com>
3  * 	   Todd C. Miller <tmiller@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 
ignore_free(void)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 
ignore_setup(char * ignoredirs)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 
ignore(const char * homedir)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 
prefix_is_homedir_role(const semanage_user_t * user,const char * prefix)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 
default_shell_list(void)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 
get_shell_list(void)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() */
fcontext_matches(const semanage_fcontext_t * fcontext,void * varg)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 
get_home_dirs(genhomedircon_settings_t * s)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 *rbuf = NULL;
294 	char *path = NULL;
295 	long rbuflen;
296 	uid_t temp, minuid = 500, maxuid = 60000;
297 	int minuid_set = 0;
298 	struct passwd pwstorage, *pwbuf;
299 	struct stat buf;
300 	int retval;
301 
302 	path = semanage_findval(PATH_ETC_USERADD, "HOME", "=");
303 	if (path && *path) {
304 		if (semanage_list_push(&homedir_list, path))
305 			goto fail;
306 	}
307 	free(path);
308 
309 	path = semanage_findval(PATH_ETC_LIBUSER, "LU_HOMEDIRECTORY", "=");
310 	if (path && *path) {
311 		if (semanage_list_push(&homedir_list, path))
312 			goto fail;
313 	}
314 	free(path);
315 	path = NULL;
316 
317 	if (!homedir_list) {
318 		if (semanage_list_push(&homedir_list, PATH_DEFAULT_HOME)) {
319 			goto fail;
320 		}
321 	}
322 
323 	if (!stat(PATH_EXPORT_HOME, &buf)) {
324 		if (S_ISDIR(buf.st_mode)) {
325 			if (semanage_list_push(&homedir_list, PATH_EXPORT_HOME)) {
326 				goto fail;
327 			}
328 		}
329 	}
330 
331 	if (!(s->usepasswd))
332 		return homedir_list;
333 
334 	shells = get_shell_list();
335 	assert(shells);
336 
337 	path = semanage_findval(PATH_ETC_LOGIN_DEFS, "UID_MIN", NULL);
338 	if (path && *path) {
339 		temp = atoi(path);
340 		minuid = temp;
341 		minuid_set = 1;
342 	}
343 	free(path);
344 	path = NULL;
345 
346 	path = semanage_findval(PATH_ETC_LOGIN_DEFS, "UID_MAX", NULL);
347 	if (path && *path) {
348 		temp = atoi(path);
349 		maxuid = temp;
350 	}
351 	free(path);
352 	path = NULL;
353 
354 	path = semanage_findval(PATH_ETC_LIBUSER, "LU_UIDNUMBER", "=");
355 	if (path && *path) {
356 		temp = atoi(path);
357 		if (!minuid_set || temp < minuid) {
358 			minuid = temp;
359 			minuid_set = 1;
360 		}
361 	}
362 	free(path);
363 	path = NULL;
364 
365 	rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
366 	if (rbuflen <= 0)
367 		goto fail;
368 	rbuf = malloc(rbuflen);
369 	if (rbuf == NULL)
370 		goto fail;
371 	setpwent();
372 	while ((retval = getpwent_r(&pwstorage, rbuf, rbuflen, &pwbuf)) == 0) {
373 		if (pwbuf->pw_uid < minuid || pwbuf->pw_uid > maxuid)
374 			continue;
375 		if (!semanage_list_find(shells, pwbuf->pw_shell))
376 			continue;
377 		int len = strlen(pwbuf->pw_dir) -1;
378 		for(; len > 0 && pwbuf->pw_dir[len] == '/'; len--) {
379 			pwbuf->pw_dir[len] = '\0';
380 		}
381 		if (strcmp(pwbuf->pw_dir, "/") == 0)
382 			continue;
383 		if (ignore(pwbuf->pw_dir))
384 			continue;
385 		if (semanage_str_count(pwbuf->pw_dir, '/') <= 1)
386 			continue;
387 		if (!(path = strdup(pwbuf->pw_dir))) {
388 			break;
389 		}
390 
391 		semanage_rtrim(path, '/');
392 
393 		if (!semanage_list_find(homedir_list, path)) {
394 			/*
395 			 * Now check for an existing file context that matches
396 			 * so we don't label a non-homedir as a homedir.
397 			 */
398 			hand.dir = path;
399 			hand.matched = 0;
400 			if (semanage_fcontext_iterate(s->h_semanage,
401 			    fcontext_matches, &hand) == STATUS_ERR)
402 				goto fail;
403 
404 			/* NOTE: old genhomedircon printed a warning on match */
405 			if (hand.matched) {
406 				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);
407 			} else {
408 				if (semanage_list_push(&homedir_list, path))
409 					goto fail;
410 			}
411 		}
412 		free(path);
413 		path = NULL;
414 	}
415 
416 	if (retval && retval != ENOENT) {
417 		WARN(s->h_semanage, "Error while fetching users.  "
418 		     "Returning list so far.");
419 	}
420 
421 	if (semanage_list_sort(&homedir_list))
422 		goto fail;
423 
424 	endpwent();
425 	free(rbuf);
426 	semanage_list_destroy(&shells);
427 
428 	return homedir_list;
429 
430       fail:
431 	endpwent();
432 	free(rbuf);
433 	free(path);
434 	semanage_list_destroy(&homedir_list);
435 	semanage_list_destroy(&shells);
436 	return NULL;
437 }
438 
439 /**
440  * @param	out	the FILE to put all the output in.
441  * @return	0 on success
442  */
write_file_context_header(FILE * out)443 static int write_file_context_header(FILE * out)
444 {
445 	if (fprintf(out, COMMENT_FILE_CONTEXT_HEADER) < 0) {
446 		return STATUS_ERR;
447 	}
448 
449 	return STATUS_SUCCESS;
450 }
451 
452 /* Predicates for use with semanage_slurp_file_filter() the homedir_template
453  * file currently contains lines that serve as the template for a user's
454  * homedir.
455  *
456  * It also contains lines that are the template for the parent of a
457  * user's home directory.
458  *
459  * Currently, the only lines that apply to the the root of a user's home
460  * directory are all prefixed with the string "HOME_ROOT".  All other
461  * lines apply to a user's home directory.  If this changes the
462  * following predicates need to change to reflect that.
463  */
HOME_ROOT_PRED(const char * string)464 static int HOME_ROOT_PRED(const char *string)
465 {
466 	return semanage_is_prefix(string, TEMPLATE_HOME_ROOT);
467 }
468 
HOME_DIR_PRED(const char * string)469 static int HOME_DIR_PRED(const char *string)
470 {
471 	return semanage_is_prefix(string, TEMPLATE_HOME_DIR);
472 }
473 
474 /* new names */
USERNAME_CONTEXT_PRED(const char * string)475 static int USERNAME_CONTEXT_PRED(const char *string)
476 {
477 	return (int)(
478 		(strstr(string, TEMPLATE_USERNAME) != NULL) ||
479 		(strstr(string, TEMPLATE_USERID) != NULL)
480 	);
481 }
482 
483 /* This will never match USER if USERNAME or USERID are found. */
USER_CONTEXT_PRED(const char * string)484 static int USER_CONTEXT_PRED(const char *string)
485 {
486 	if (USERNAME_CONTEXT_PRED(string))
487 		return 0;
488 
489 	return (int)(strstr(string, TEMPLATE_USER) != NULL);
490 }
491 
STR_COMPARATOR(const void * a,const void * b)492 static int STR_COMPARATOR(const void *a, const void *b)
493 {
494 	return strcmp((const char *) a, (const char *) b);
495 }
496 
497 /* make_tempate
498  * @param	s	  the settings holding the paths to various files
499  * @param	pred	function pointer to function to use as filter for slurp
500  * 					file filter
501  * @return   a list of lines from the template file with inappropriate
502  *	    lines filtered out.
503  */
make_template(genhomedircon_settings_t * s,int (* pred)(const char *))504 static semanage_list_t *make_template(genhomedircon_settings_t * s,
505 				      int (*pred) (const char *))
506 {
507 	FILE *template_file = NULL;
508 	semanage_list_t *template_data = NULL;
509 
510 	template_file = fopen(s->homedir_template_path, "r");
511 	if (!template_file)
512 		return NULL;
513 	template_data = semanage_slurp_file_filter(template_file, pred);
514 	fclose(template_file);
515 
516 	return template_data;
517 }
518 
replace_all(const char * str,const replacement_pair_t * repl)519 static char *replace_all(const char *str, const replacement_pair_t * repl)
520 {
521 	char *retval, *retval2;
522 	int i;
523 
524 	if (!str || !repl)
525 		return NULL;
526 
527 	retval = strdup(str);
528 	for (i = 0; retval != NULL && repl[i].search_for; i++) {
529 		retval2 = semanage_str_replace(repl[i].search_for,
530 					       repl[i].replace_with, retval, 0);
531 		free(retval);
532 		retval = retval2;
533 	}
534 	return retval;
535 }
536 
extract_context(const char * line)537 static const char *extract_context(const char *line)
538 {
539 	const char *p = line;
540 	size_t off;
541 
542 	off = strlen(p);
543 	p += off;
544 	/* consider trailing whitespaces */
545 	while (off > 0) {
546 		p--;
547 		off--;
548 		if (!isspace(*p))
549 			break;
550 	}
551 	if (off == 0)
552 		return NULL;
553 
554 	/* find the last field in line */
555 	while (off > 0 && !isspace(*(p - 1))) {
556 		p--;
557 		off--;
558 	}
559 	return p;
560 }
561 
check_line(genhomedircon_settings_t * s,const char * line)562 static int check_line(genhomedircon_settings_t * s, const char *line)
563 {
564 	sepol_context_t *ctx_record = NULL;
565 	const char *ctx_str;
566 	int result;
567 
568 	ctx_str = extract_context(line);
569 	if (!ctx_str)
570 		return STATUS_ERR;
571 
572 	result = sepol_context_from_string(s->h_semanage->sepolh,
573 					   ctx_str, &ctx_record);
574 	if (result == STATUS_SUCCESS && ctx_record != NULL) {
575 		result = sepol_context_check(s->h_semanage->sepolh,
576 					     s->policydb, ctx_record);
577 		sepol_context_free(ctx_record);
578 	}
579 	return result;
580 }
581 
write_replacements(genhomedircon_settings_t * s,FILE * out,const semanage_list_t * tpl,const replacement_pair_t * repl)582 static int write_replacements(genhomedircon_settings_t * s, FILE * out,
583 			      const semanage_list_t * tpl,
584 			      const replacement_pair_t *repl)
585 {
586 	char *line;
587 
588 	for (; tpl; tpl = tpl->next) {
589 		line = replace_all(tpl->data, repl);
590 		if (!line)
591 			goto fail;
592 		if (check_line(s, line) == STATUS_SUCCESS) {
593 			if (fprintf(out, "%s\n", line) < 0)
594 				goto fail;
595 		}
596 		free(line);
597 	}
598 	return STATUS_SUCCESS;
599 
600       fail:
601 	free(line);
602 	return STATUS_ERR;
603 }
604 
write_contexts(genhomedircon_settings_t * s,FILE * out,semanage_list_t * tpl,const replacement_pair_t * repl,const genhomedircon_user_entry_t * user)605 static int write_contexts(genhomedircon_settings_t *s, FILE *out,
606 			  semanage_list_t *tpl, const replacement_pair_t *repl,
607 			  const genhomedircon_user_entry_t *user)
608 {
609 	char *line, *temp;
610 	sepol_context_t *context = NULL;
611 	char *new_context_str = NULL;
612 
613 	for (; tpl; tpl = tpl->next) {
614 		line = replace_all(tpl->data, repl);
615 		if (!line) {
616 			goto fail;
617 		}
618 
619 		const char *old_context_str = extract_context(line);
620 		if (!old_context_str) {
621 			goto fail;
622 		}
623 
624 		if (strcmp(old_context_str, CONTEXT_NONE) == 0) {
625 			if (check_line(s, line) == STATUS_SUCCESS &&
626 			    fprintf(out, "%s\n", line) < 0) {
627 				goto fail;
628 			}
629 			free(line);
630 			continue;
631 		}
632 
633 		sepol_handle_t *sepolh = s->h_semanage->sepolh;
634 
635 		if (sepol_context_from_string(sepolh, old_context_str,
636 					      &context) < 0) {
637 			goto fail;
638 		}
639 
640 		if (sepol_context_set_user(sepolh, context, user->sename) < 0) {
641 			goto fail;
642 		}
643 
644 		if (sepol_policydb_mls_enabled(s->policydb) &&
645 		    sepol_context_set_mls(sepolh, context, user->level) < 0) {
646 			goto fail;
647 		}
648 
649 		if (user->homedir_role &&
650 		    sepol_context_set_role(sepolh, context, user->homedir_role) < 0) {
651 			goto fail;
652 		}
653 
654 		if (sepol_context_to_string(sepolh, context,
655 					    &new_context_str) < 0) {
656 			goto fail;
657 		}
658 
659 		temp = semanage_str_replace(old_context_str, new_context_str,
660 					    line, 1);
661 		if (!temp) {
662 			goto fail;
663 		}
664 		free(line);
665 		line = temp;
666 
667 		if (check_line(s, line) == STATUS_SUCCESS) {
668 			if (fprintf(out, "%s\n", line) < 0)
669 				goto fail;
670 		}
671 
672 		free(line);
673 		sepol_context_free(context);
674 		free(new_context_str);
675 	}
676 
677 	return STATUS_SUCCESS;
678 fail:
679 	free(line);
680 	sepol_context_free(context);
681 	free(new_context_str);
682 	return STATUS_ERR;
683 }
684 
write_home_dir_context(genhomedircon_settings_t * s,FILE * out,semanage_list_t * tpl,const genhomedircon_user_entry_t * user)685 static int write_home_dir_context(genhomedircon_settings_t * s, FILE * out,
686 				  semanage_list_t * tpl, const genhomedircon_user_entry_t *user)
687 {
688 	replacement_pair_t repl[] = {
689 		{.search_for = TEMPLATE_HOME_DIR,.replace_with = user->home},
690 		{.search_for = TEMPLATE_ROLE,.replace_with = user->prefix},
691 		{NULL, NULL}
692 	};
693 
694 	if (strcmp(user->name, FALLBACK_NAME) == 0) {
695 		if (fprintf(out, COMMENT_USER_HOME_CONTEXT, FALLBACK_SENAME) < 0)
696 			return STATUS_ERR;
697 	} else {
698 		if (fprintf(out, COMMENT_USER_HOME_CONTEXT, user->name) < 0)
699 			return STATUS_ERR;
700 	}
701 
702 	return write_contexts(s, out, tpl, repl, user);
703 }
704 
write_home_root_context(genhomedircon_settings_t * s,FILE * out,semanage_list_t * tpl,char * homedir)705 static int write_home_root_context(genhomedircon_settings_t * s, FILE * out,
706 				   semanage_list_t * tpl, char *homedir)
707 {
708 	replacement_pair_t repl[] = {
709 		{.search_for = TEMPLATE_HOME_ROOT,.replace_with = homedir},
710 		{NULL, NULL}
711 	};
712 
713 	return write_replacements(s, out, tpl, repl);
714 }
715 
write_username_context(genhomedircon_settings_t * s,FILE * out,semanage_list_t * tpl,const genhomedircon_user_entry_t * user)716 static int write_username_context(genhomedircon_settings_t * s, FILE * out,
717 				  semanage_list_t * tpl,
718 				  const genhomedircon_user_entry_t *user)
719 {
720 	replacement_pair_t repl[] = {
721 		{.search_for = TEMPLATE_USERNAME,.replace_with = user->name},
722 		{.search_for = TEMPLATE_USERID,.replace_with = user->uid},
723 		{.search_for = TEMPLATE_ROLE,.replace_with = user->prefix},
724 		{NULL, NULL}
725 	};
726 
727 	return write_contexts(s, out, tpl, repl, user);
728 }
729 
write_user_context(genhomedircon_settings_t * s,FILE * out,semanage_list_t * tpl,const genhomedircon_user_entry_t * user)730 static int write_user_context(genhomedircon_settings_t * s, FILE * out,
731 			      semanage_list_t * tpl, const genhomedircon_user_entry_t *user)
732 {
733 	replacement_pair_t repl[] = {
734 		{.search_for = TEMPLATE_USER,.replace_with = user->name},
735 		{.search_for = TEMPLATE_ROLE,.replace_with = user->prefix},
736 		{NULL, NULL}
737 	};
738 
739 	return write_contexts(s, out, tpl, repl, user);
740 }
741 
seuser_sort_func(const void * arg1,const void * arg2)742 static int seuser_sort_func(const void *arg1, const void *arg2)
743 {
744 	const semanage_seuser_t **u1 = (const semanage_seuser_t **) arg1;
745 	const semanage_seuser_t **u2 = (const semanage_seuser_t **) arg2;;
746 	const char *name1 = semanage_seuser_get_name(*u1);
747 	const char *name2 = semanage_seuser_get_name(*u2);
748 
749 	if (name1[0] == '%' && name2[0] == '%') {
750 		return 0;
751 	} else if (name1[0] == '%') {
752 		return 1;
753 	} else if (name2[0] == '%') {
754 		return -1;
755 	}
756 
757 	return strcmp(name1, name2);
758 }
759 
user_sort_func(semanage_user_t ** arg1,semanage_user_t ** arg2)760 static int user_sort_func(semanage_user_t ** arg1, semanage_user_t ** arg2)
761 {
762 	return strcmp(semanage_user_get_name(*arg1),
763 		      semanage_user_get_name(*arg2));
764 }
765 
name_user_cmp(char * key,semanage_user_t ** val)766 static int name_user_cmp(char *key, semanage_user_t ** val)
767 {
768 	return strcmp(key, semanage_user_get_name(*val));
769 }
770 
push_user_entry(genhomedircon_user_entry_t ** list,const char * n,const char * u,const char * g,const char * sen,const char * pre,const char * h,const char * l,const char * ln,const char * hd_role)771 static int push_user_entry(genhomedircon_user_entry_t ** list, const char *n,
772 			   const char *u, const char *g, const char *sen,
773 			   const char *pre, const char *h, const char *l,
774 			   const char *ln, const char *hd_role)
775 {
776 	genhomedircon_user_entry_t *temp = NULL;
777 	char *name = NULL;
778 	char *uid = NULL;
779 	char *gid = NULL;
780 	char *sename = NULL;
781 	char *prefix = NULL;
782 	char *home = NULL;
783 	char *level = NULL;
784 	char *lname = NULL;
785 	char *homedir_role = NULL;
786 
787 	temp = malloc(sizeof(genhomedircon_user_entry_t));
788 	if (!temp)
789 		goto cleanup;
790 	name = strdup(n);
791 	if (!name)
792 		goto cleanup;
793 	uid = strdup(u);
794 	if (!uid)
795 		goto cleanup;
796 	gid = strdup(g);
797 	if (!gid)
798 		goto cleanup;
799 	sename = strdup(sen);
800 	if (!sename)
801 		goto cleanup;
802 	prefix = strdup(pre);
803 	if (!prefix)
804 		goto cleanup;
805 	home = strdup(h);
806 	if (!home)
807 		goto cleanup;
808 	level = strdup(l);
809 	if (!level)
810 		goto cleanup;
811 	lname = strdup(ln);
812 	if (!lname)
813 		goto cleanup;
814 	if (hd_role) {
815 		homedir_role = strdup(hd_role);
816 		if (!homedir_role)
817 			goto cleanup;
818 	}
819 
820 	temp->name = name;
821 	temp->uid = uid;
822 	temp->gid = gid;
823 	temp->sename = sename;
824 	temp->prefix = prefix;
825 	temp->home = home;
826 	temp->level = level;
827 	temp->login = lname;
828 	temp->homedir_role = homedir_role;
829 	temp->next = (*list);
830 	(*list) = temp;
831 
832 	return STATUS_SUCCESS;
833 
834       cleanup:
835 	free(name);
836 	free(uid);
837 	free(gid);
838 	free(sename);
839 	free(prefix);
840 	free(home);
841 	free(level);
842 	free(lname);
843 	free(homedir_role);
844 	free(temp);
845 	return STATUS_ERR;
846 }
847 
pop_user_entry(genhomedircon_user_entry_t ** list)848 static void pop_user_entry(genhomedircon_user_entry_t ** list)
849 {
850 	genhomedircon_user_entry_t *temp;
851 
852 	if (!list || !(*list))
853 		return;
854 
855 	temp = *list;
856 	*list = temp->next;
857 	free(temp->name);
858 	free(temp->uid);
859 	free(temp->gid);
860 	free(temp->sename);
861 	free(temp->prefix);
862 	free(temp->home);
863 	free(temp->level);
864 	free(temp->login);
865 	free(temp->homedir_role);
866 	free(temp);
867 }
868 
setup_fallback_user(genhomedircon_settings_t * s)869 static int setup_fallback_user(genhomedircon_settings_t * s)
870 {
871 	semanage_seuser_t **seuser_list = NULL;
872 	unsigned int nseusers = 0;
873 	semanage_user_key_t *key = NULL;
874 	semanage_user_t *u = NULL;
875 	const char *name = NULL;
876 	const char *seuname = NULL;
877 	const char *prefix = NULL;
878 	const char *level = NULL;
879 	const char *homedir_role = NULL;
880 	unsigned int i;
881 	int retval;
882 	int errors = 0;
883 
884 	retval = semanage_seuser_list(s->h_semanage, &seuser_list, &nseusers);
885 	if (retval < 0 || (nseusers < 1)) {
886 		/* if there are no users, this function can't do any other work */
887 		return errors;
888 	}
889 
890 	for (i = 0; i < nseusers; i++) {
891 		name = semanage_seuser_get_name(seuser_list[i]);
892 		if (strcmp(name, DEFAULT_LOGIN) == 0) {
893 			seuname = semanage_seuser_get_sename(seuser_list[i]);
894 
895 			/* find the user structure given the name */
896 			if (semanage_user_key_create(s->h_semanage, seuname,
897 						     &key) < 0) {
898 				errors = STATUS_ERR;
899 				break;
900 			}
901 			if (semanage_user_query(s->h_semanage, key, &u) < 0)
902 			{
903 				prefix = name;
904 				level = FALLBACK_LEVEL;
905 			}
906 			else
907 			{
908 				prefix = semanage_user_get_prefix(u);
909 				level = semanage_user_get_mlslevel(u);
910 				if (!level)
911 					level = FALLBACK_LEVEL;
912 			}
913 
914 			if (prefix_is_homedir_role(u, prefix)) {
915 				homedir_role = prefix;
916 			}
917 
918 			if (push_user_entry(&(s->fallback), FALLBACK_NAME,
919 					    FALLBACK_UIDGID, FALLBACK_UIDGID,
920 					    seuname, prefix, "", level,
921 					    FALLBACK_NAME, homedir_role) != 0)
922 				errors = STATUS_ERR;
923 			semanage_user_key_free(key);
924 			if (u)
925 				semanage_user_free(u);
926 			break;
927 		}
928 	}
929 
930 	for (i = 0; i < nseusers; i++)
931 		semanage_seuser_free(seuser_list[i]);
932 	free(seuser_list);
933 
934 	return errors;
935 }
936 
find_user(genhomedircon_user_entry_t * head,const char * name)937 static genhomedircon_user_entry_t *find_user(genhomedircon_user_entry_t *head,
938 					     const char *name)
939 {
940 	for(; head; head = head->next) {
941 		if (strcmp(head->name, name) == 0) {
942 			return head;
943 		}
944 	}
945 
946 	return NULL;
947 }
948 
add_user(genhomedircon_settings_t * s,genhomedircon_user_entry_t ** head,semanage_user_t * user,const char * name,const char * sename,const char * selogin)949 static int add_user(genhomedircon_settings_t * s,
950 		    genhomedircon_user_entry_t **head,
951 		    semanage_user_t *user,
952 		    const char *name,
953 		    const char *sename,
954 		    const char *selogin)
955 {
956 	if (selogin[0] == '%') {
957 		genhomedircon_user_entry_t *orig = find_user(*head, name);
958 		if (orig != NULL && orig->login[0] == '%') {
959 			ERR(s->h_semanage, "User %s is already mapped to"
960 			    " group %s, but also belongs to group %s. Add an"
961 			    " explicit mapping for this user to"
962 			    " override group mappings.",
963 			    name, orig->login + 1, selogin + 1);
964 			return STATUS_ERR;
965 		} else if (orig != NULL) {
966 			// user mappings take precedence
967 			return STATUS_SUCCESS;
968 		}
969 	}
970 
971 	int retval = STATUS_ERR;
972 
973 	char *rbuf = NULL;
974 	long rbuflen;
975 	struct passwd pwstorage, *pwent = NULL;
976 	const char *prefix = NULL;
977 	const char *level = NULL;
978 	const char *homedir_role = NULL;
979 	char uid[11];
980 	char gid[11];
981 
982 	/* Allocate space for the getpwnam_r buffer */
983 	rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
984 	if (rbuflen <= 0)
985 		goto cleanup;
986 	rbuf = malloc(rbuflen);
987 	if (rbuf == NULL)
988 		goto cleanup;
989 
990 	if (user) {
991 		prefix = semanage_user_get_prefix(user);
992 		level = semanage_user_get_mlslevel(user);
993 
994 		if (!level) {
995 			level = FALLBACK_LEVEL;
996 		}
997 	} else {
998 		prefix = name;
999 		level = FALLBACK_LEVEL;
1000 	}
1001 
1002 	if (prefix_is_homedir_role(user, prefix)) {
1003 		homedir_role = prefix;
1004 	}
1005 
1006 	retval = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent);
1007 	if (retval != 0 || pwent == NULL) {
1008 		if (retval != 0 && retval != ENOENT) {
1009 			goto cleanup;
1010 		}
1011 
1012 		WARN(s->h_semanage,
1013 		     "user %s not in password file", name);
1014 		retval = STATUS_SUCCESS;
1015 		goto cleanup;
1016 	}
1017 
1018 	int len = strlen(pwent->pw_dir) -1;
1019 	for(; len > 0 && pwent->pw_dir[len] == '/'; len--) {
1020 		pwent->pw_dir[len] = '\0';
1021 	}
1022 
1023 	if (strcmp(pwent->pw_dir, "/") == 0) {
1024 		/* don't relabel / genhomdircon checked to see if root
1025 		 * was the user and if so, set his home directory to
1026 		 * /root */
1027 		retval = STATUS_SUCCESS;
1028 		goto cleanup;
1029 	}
1030 
1031 	if (ignore(pwent->pw_dir)) {
1032 		retval = STATUS_SUCCESS;
1033 		goto cleanup;
1034 	}
1035 
1036 	len = snprintf(uid, sizeof(uid), "%u", pwent->pw_uid);
1037 	if (len < 0 || len >= (int)sizeof(uid)) {
1038 		goto cleanup;
1039 	}
1040 
1041 	len = snprintf(gid, sizeof(gid), "%u", pwent->pw_gid);
1042 	if (len < 0 || len >= (int)sizeof(gid)) {
1043 		goto cleanup;
1044 	}
1045 
1046 	retval = push_user_entry(head, name, uid, gid, sename, prefix,
1047 				pwent->pw_dir, level, selogin, homedir_role);
1048 cleanup:
1049 	free(rbuf);
1050 	return retval;
1051 }
1052 
get_group_users(genhomedircon_settings_t * s,genhomedircon_user_entry_t ** head,semanage_user_t * user,const char * sename,const char * selogin)1053 static int get_group_users(genhomedircon_settings_t * s,
1054 			  genhomedircon_user_entry_t **head,
1055 			  semanage_user_t *user,
1056 			  const char *sename,
1057 			  const char *selogin)
1058 {
1059 	int retval = STATUS_ERR;
1060 	unsigned int i;
1061 
1062 	long grbuflen;
1063 	char *grbuf = NULL;
1064 	struct group grstorage, *group = NULL;
1065 
1066 	long prbuflen;
1067 	char *pwbuf = NULL;
1068 	struct passwd pwstorage, *pw = NULL;
1069 
1070 	grbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
1071 	if (grbuflen <= 0)
1072 		goto cleanup;
1073 	grbuf = malloc(grbuflen);
1074 	if (grbuf == NULL)
1075 		goto cleanup;
1076 
1077 	const char *grname = selogin + 1;
1078 
1079 	if (getgrnam_r(grname, &grstorage, grbuf,
1080 			(size_t) grbuflen, &group) != 0) {
1081 		goto cleanup;
1082 	}
1083 
1084 	if (group == NULL) {
1085 		ERR(s->h_semanage, "Can't find group named %s\n", grname);
1086 		goto cleanup;
1087 	}
1088 
1089 	size_t nmembers = 0;
1090 	char **members = group->gr_mem;
1091 
1092 	while (*members != NULL) {
1093 		nmembers++;
1094 		members++;
1095 	}
1096 
1097 	for (i = 0; i < nmembers; i++) {
1098 		const char *uname = group->gr_mem[i];
1099 
1100 		if (add_user(s, head, user, uname, sename, selogin) < 0) {
1101 			goto cleanup;
1102 		}
1103 	}
1104 
1105 	prbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
1106 	if (prbuflen <= 0)
1107 		goto cleanup;
1108 	pwbuf = malloc(prbuflen);
1109 	if (pwbuf == NULL)
1110 		goto cleanup;
1111 
1112 	setpwent();
1113 	while ((retval = getpwent_r(&pwstorage, pwbuf, prbuflen, &pw)) == 0) {
1114 		// skip users who also have this group as their
1115 		// primary group
1116 		if (lfind(pw->pw_name, group->gr_mem, &nmembers,
1117 			  sizeof(char *), &STR_COMPARATOR)) {
1118 			continue;
1119 		}
1120 
1121 		if (group->gr_gid == pw->pw_gid) {
1122 			if (add_user(s, head, user, pw->pw_name,
1123 				     sename, selogin) < 0) {
1124 				goto cleanup;
1125 			}
1126 		}
1127 	}
1128 
1129 	retval = STATUS_SUCCESS;
1130 cleanup:
1131 	endpwent();
1132 	free(pwbuf);
1133 	free(grbuf);
1134 
1135 	return retval;
1136 }
1137 
get_users(genhomedircon_settings_t * s,int * errors)1138 static genhomedircon_user_entry_t *get_users(genhomedircon_settings_t * s,
1139 					     int *errors)
1140 {
1141 	genhomedircon_user_entry_t *head = NULL;
1142 	semanage_seuser_t **seuser_list = NULL;
1143 	unsigned int nseusers = 0;
1144 	semanage_user_t **user_list = NULL;
1145 	unsigned int nusers = 0;
1146 	semanage_user_t **u = NULL;
1147 	const char *name = NULL;
1148 	const char *seuname = NULL;
1149 	unsigned int i;
1150 	int retval;
1151 
1152 	*errors = 0;
1153 	retval = semanage_seuser_list(s->h_semanage, &seuser_list, &nseusers);
1154 	if (retval < 0 || (nseusers < 1)) {
1155 		/* if there are no users, this function can't do any other work */
1156 		return NULL;
1157 	}
1158 
1159 	if (semanage_user_list(s->h_semanage, &user_list, &nusers) < 0) {
1160 		nusers = 0;
1161 	}
1162 
1163 	qsort(seuser_list, nseusers, sizeof(semanage_seuser_t *),
1164 	      &seuser_sort_func);
1165 	qsort(user_list, nusers, sizeof(semanage_user_t *),
1166 	      (int (*)(const void *, const void *))&user_sort_func);
1167 
1168 	for (i = 0; i < nseusers; i++) {
1169 		seuname = semanage_seuser_get_sename(seuser_list[i]);
1170 		name = semanage_seuser_get_name(seuser_list[i]);
1171 
1172 		if (strcmp(name, DEFAULT_LOGIN) == 0)
1173 			continue;
1174 
1175 		/* find the user structure given the name */
1176 		u = bsearch(seuname, user_list, nusers, sizeof(semanage_user_t *),
1177 			    (int (*)(const void *, const void *))
1178 			    &name_user_cmp);
1179 
1180 		/* %groupname syntax */
1181 		if (name[0] == '%') {
1182 			retval = get_group_users(s, &head, *u, seuname,
1183 						name);
1184 		} else {
1185 			retval = add_user(s, &head, *u, name,
1186 					  seuname, name);
1187 		}
1188 
1189 		if (retval != 0) {
1190 			*errors = STATUS_ERR;
1191 			goto cleanup;
1192 		}
1193 	}
1194 
1195       cleanup:
1196 	if (*errors) {
1197 		for (; head; pop_user_entry(&head)) {
1198 			/* the pop function takes care of all the cleanup
1199 			   so the loop body is just empty */
1200 		}
1201 	}
1202 	for (i = 0; i < nseusers; i++) {
1203 		semanage_seuser_free(seuser_list[i]);
1204 	}
1205 	free(seuser_list);
1206 
1207 	for (i = 0; i < nusers; i++) {
1208 		semanage_user_free(user_list[i]);
1209 	}
1210 	free(user_list);
1211 
1212 	return head;
1213 }
1214 
write_gen_home_dir_context(genhomedircon_settings_t * s,FILE * out,semanage_list_t * username_context_tpl,semanage_list_t * user_context_tpl,semanage_list_t * homedir_context_tpl)1215 static int write_gen_home_dir_context(genhomedircon_settings_t * s, FILE * out,
1216 				      semanage_list_t * username_context_tpl,
1217 				      semanage_list_t * user_context_tpl,
1218 				      semanage_list_t * homedir_context_tpl)
1219 {
1220 	genhomedircon_user_entry_t *users;
1221 	int errors = 0;
1222 
1223 	users = get_users(s, &errors);
1224 	if (!users && errors) {
1225 		return STATUS_ERR;
1226 	}
1227 
1228 	for (; users; pop_user_entry(&users)) {
1229 		if (write_home_dir_context(s, out, homedir_context_tpl, users))
1230 			goto err;
1231 		if (write_username_context(s, out, username_context_tpl, users))
1232 			goto err;
1233 		if (write_user_context(s, out, user_context_tpl, users))
1234 			goto err;
1235 	}
1236 
1237 	return STATUS_SUCCESS;
1238 err:
1239 	for (; users; pop_user_entry(&users)) {
1240 	/* the pop function takes care of all the cleanup
1241 	 * so the loop body is just empty */
1242 	}
1243 
1244 	return STATUS_ERR;
1245 }
1246 
1247 /**
1248  * @param	s	settings structure, stores various paths etc. Must never be NULL
1249  * @param	out	the FILE to put all the output in.
1250  * @return	0 on success
1251  */
write_context_file(genhomedircon_settings_t * s,FILE * out)1252 static int write_context_file(genhomedircon_settings_t * s, FILE * out)
1253 {
1254 	semanage_list_t *homedirs = NULL;
1255 	semanage_list_t *h = NULL;
1256 	semanage_list_t *homedir_context_tpl = NULL;
1257 	semanage_list_t *homeroot_context_tpl = NULL;
1258 	semanage_list_t *username_context_tpl = NULL;
1259 	semanage_list_t *user_context_tpl = NULL;
1260 	int retval = STATUS_SUCCESS;
1261 
1262 	homedir_context_tpl = make_template(s, &HOME_DIR_PRED);
1263 	homeroot_context_tpl = make_template(s, &HOME_ROOT_PRED);
1264 	username_context_tpl = make_template(s, &USERNAME_CONTEXT_PRED);
1265 	user_context_tpl = make_template(s, &USER_CONTEXT_PRED);
1266 
1267 	if (!homedir_context_tpl
1268 	 && !homeroot_context_tpl
1269 	 && !username_context_tpl
1270 	 && !user_context_tpl)
1271 		goto done;
1272 
1273 	if (write_file_context_header(out) != STATUS_SUCCESS) {
1274 		retval = STATUS_ERR;
1275 		goto done;
1276 	}
1277 
1278 	if (setup_fallback_user(s) != 0) {
1279 		retval = STATUS_ERR;
1280 		goto done;
1281 	}
1282 
1283 	if (homedir_context_tpl || homeroot_context_tpl) {
1284 		homedirs = get_home_dirs(s);
1285 		if (!homedirs) {
1286 			WARN(s->h_semanage,
1287 			     "no home directories were available, exiting without writing");
1288 			goto done;
1289 		}
1290 
1291 		for (h = homedirs; h; h = h->next) {
1292 			char *temp = NULL;
1293 
1294 			if (asprintf(&temp, "%s/%s", h->data, FALLBACK_NAME) < 0) {
1295 				retval = STATUS_ERR;
1296 				goto done;
1297 			}
1298 
1299 			free(s->fallback->home);
1300 			s->fallback->home = temp;
1301 
1302 			if (write_home_dir_context(s, out, homedir_context_tpl,
1303 						   s->fallback) != STATUS_SUCCESS) {
1304 				free(temp);
1305 				s->fallback->home = NULL;
1306 				retval = STATUS_ERR;
1307 				goto done;
1308 			}
1309 			if (write_home_root_context(s, out,
1310 						    homeroot_context_tpl,
1311 						    h->data) != STATUS_SUCCESS) {
1312 				free(temp);
1313 				s->fallback->home = NULL;
1314 				retval = STATUS_ERR;
1315 				goto done;
1316 			}
1317 
1318 			free(temp);
1319 			s->fallback->home = NULL;
1320 		}
1321 	}
1322 	if (user_context_tpl || username_context_tpl) {
1323 		if (write_username_context(s, out, username_context_tpl,
1324 					   s->fallback) != STATUS_SUCCESS) {
1325 			retval = STATUS_ERR;
1326 			goto done;
1327 		}
1328 
1329 		if (write_user_context(s, out, user_context_tpl,
1330 				       s->fallback) != STATUS_SUCCESS) {
1331 			retval = STATUS_ERR;
1332 			goto done;
1333 		}
1334 
1335 		if (write_gen_home_dir_context(s, out, username_context_tpl,
1336 					       user_context_tpl, homedir_context_tpl)
1337 				!= STATUS_SUCCESS) {
1338 			retval = STATUS_ERR;
1339 		}
1340 	}
1341 
1342 done:
1343 	/* Cleanup */
1344 	semanage_list_destroy(&homedirs);
1345 	semanage_list_destroy(&username_context_tpl);
1346 	semanage_list_destroy(&user_context_tpl);
1347 	semanage_list_destroy(&homedir_context_tpl);
1348 	semanage_list_destroy(&homeroot_context_tpl);
1349 
1350 	return retval;
1351 }
1352 
semanage_genhomedircon(semanage_handle_t * sh,sepol_policydb_t * policydb,int usepasswd,char * ignoredirs)1353 int semanage_genhomedircon(semanage_handle_t * sh,
1354 			   sepol_policydb_t * policydb,
1355 			   int usepasswd,
1356 			   char *ignoredirs)
1357 {
1358 	genhomedircon_settings_t s;
1359 	FILE *out = NULL;
1360 	int retval = 0;
1361 
1362 	assert(sh);
1363 
1364 	s.homedir_template_path =
1365 	    semanage_path(SEMANAGE_TMP, SEMANAGE_HOMEDIR_TMPL);
1366 	s.fcfilepath = semanage_final_path(SEMANAGE_FINAL_TMP,
1367 					   SEMANAGE_FC_HOMEDIRS);
1368 
1369 	s.fallback = calloc(1, sizeof(genhomedircon_user_entry_t));
1370 	if (s.fallback == NULL) {
1371 		retval = STATUS_ERR;
1372 		goto done;
1373 	}
1374 
1375 	s.fallback->name = strdup(FALLBACK_NAME);
1376 	s.fallback->sename = strdup(FALLBACK_SENAME);
1377 	s.fallback->prefix = strdup(FALLBACK_PREFIX);
1378 	s.fallback->level = strdup(FALLBACK_LEVEL);
1379 	if (s.fallback->name == NULL
1380 	 || s.fallback->sename == NULL
1381 	 || s.fallback->prefix == NULL
1382 	 || s.fallback->level == NULL) {
1383 		retval = STATUS_ERR;
1384 		goto done;
1385 	}
1386 
1387 	if (ignoredirs) ignore_setup(ignoredirs);
1388 
1389 	s.usepasswd = usepasswd;
1390 	s.h_semanage = sh;
1391 	s.policydb = policydb;
1392 
1393 	if (!(out = fopen(s.fcfilepath, "w"))) {
1394 		/* couldn't open output file */
1395 		ERR(sh, "Could not open the file_context file for writing");
1396 		retval = STATUS_ERR;
1397 		goto done;
1398 	}
1399 
1400 	retval = write_context_file(&s, out);
1401 
1402 done:
1403 	if (out != NULL)
1404 		fclose(out);
1405 
1406 	pop_user_entry(&(s.fallback));
1407 	ignore_free();
1408 
1409 	return retval;
1410 }
1411