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