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 #include <ustr.h>
     38 
     39 #include <assert.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 
     52 /* paths used in get_home_dirs() */
     53 #define PATH_ETC_USERADD "/etc/default/useradd"
     54 #define PATH_ETC_LIBUSER "/etc/libuser.conf"
     55 #define PATH_DEFAULT_HOME "/home"
     56 #define PATH_EXPORT_HOME "/export/home"
     57 #define PATH_ETC_LOGIN_DEFS "/etc/login.defs"
     58 
     59 /* other paths */
     60 #define PATH_SHELLS_FILE "/etc/shells"
     61 #define PATH_NOLOGIN_SHELL "/sbin/nologin"
     62 
     63 /* comments written to context file */
     64 #define COMMENT_FILE_CONTEXT_HEADER "#\n#\n# " \
     65 			"User-specific file contexts, generated via libsemanage\n" \
     66 			"# use semanage command to manage system users to change" \
     67 			" the file_context\n#\n#\n"
     68 
     69 #define COMMENT_USER_HOME_CONTEXT "\n\n#\n# Home Context for user %s" \
     70 			"\n#\n\n"
     71 
     72 /* placeholders used in the template file
     73    which are searched for and replaced */
     74 #define TEMPLATE_HOME_ROOT "HOME_ROOT"
     75 #define TEMPLATE_HOME_DIR "HOME_DIR"
     76 #define TEMPLATE_USER "USER"
     77 #define TEMPLATE_ROLE "ROLE"
     78 #define TEMPLATE_SEUSER "system_u"
     79 #define TEMPLATE_LEVEL "s0"
     80 
     81 #define FALLBACK_USER "user_u"
     82 #define FALLBACK_USER_PREFIX "user"
     83 #define FALLBACK_USER_LEVEL "s0"
     84 #define DEFAULT_LOGIN "__default__"
     85 
     86 typedef struct {
     87 	const char *fcfilepath;
     88 	int usepasswd;
     89 	const char *homedir_template_path;
     90 	char *fallback_user;
     91 	char *fallback_user_prefix;
     92 	char *fallback_user_level;
     93 	semanage_handle_t *h_semanage;
     94 	sepol_policydb_t *policydb;
     95 } genhomedircon_settings_t;
     96 
     97 typedef struct user_entry {
     98 	char *name;
     99 	char *sename;
    100 	char *prefix;
    101 	char *home;
    102 	char *level;
    103 	struct user_entry *next;
    104 } genhomedircon_user_entry_t;
    105 
    106 typedef struct {
    107 	const char *search_for;
    108 	const char *replace_with;
    109 } replacement_pair_t;
    110 
    111 typedef struct {
    112 	const char *dir;
    113 	int matched;
    114 } fc_match_handle_t;
    115 
    116 typedef struct IgnoreDir {
    117 	struct IgnoreDir *next;
    118 	char *dir;
    119 } ignoredir_t;
    120 
    121 ignoredir_t *ignore_head = NULL;
    122 
    123 static void ignore_free(void) {
    124 	ignoredir_t *next;
    125 
    126 	while (ignore_head) {
    127 		next = ignore_head->next;
    128 		free(ignore_head->dir);
    129 		free(ignore_head);
    130 		ignore_head = next;
    131 	}
    132 }
    133 
    134 static int ignore_setup(char *ignoredirs) {
    135 	char *tok;
    136 	ignoredir_t *ptr = NULL;
    137 
    138 	tok = strtok(ignoredirs, ";");
    139 	while(tok) {
    140 		ptr = calloc(sizeof(ignoredir_t),1);
    141 		if (!ptr)
    142 			goto err;
    143 		ptr->dir = strdup(tok);
    144 		if (!ptr->dir)
    145 			goto err;
    146 
    147 		ptr->next = ignore_head;
    148 		ignore_head = ptr;
    149 
    150 		tok = strtok(NULL, ";");
    151 	}
    152 
    153 	return 0;
    154 err:
    155 	free(ptr);
    156 	ignore_free();
    157 	return -1;
    158 }
    159 
    160 static int ignore(const char *homedir) {
    161 	ignoredir_t *ptr = ignore_head;
    162 	while (ptr) {
    163 		if (strcmp(ptr->dir, homedir) == 0) {
    164 			return 1;
    165 		}
    166 		ptr = ptr->next;
    167 	}
    168 	return 0;
    169 }
    170 
    171 static semanage_list_t *default_shell_list(void)
    172 {
    173 	semanage_list_t *list = NULL;
    174 
    175 	if (semanage_list_push(&list, "/bin/csh")
    176 	    || semanage_list_push(&list, "/bin/tcsh")
    177 	    || semanage_list_push(&list, "/bin/ksh")
    178 	    || semanage_list_push(&list, "/bin/bsh")
    179 	    || semanage_list_push(&list, "/bin/ash")
    180 	    || semanage_list_push(&list, "/usr/bin/ksh")
    181 	    || semanage_list_push(&list, "/usr/bin/pdksh")
    182 	    || semanage_list_push(&list, "/bin/zsh")
    183 	    || semanage_list_push(&list, "/bin/sh")
    184 	    || semanage_list_push(&list, "/bin/bash"))
    185 		goto fail;
    186 
    187 	return list;
    188 
    189       fail:
    190 	semanage_list_destroy(&list);
    191 	return NULL;
    192 }
    193 
    194 static semanage_list_t *get_shell_list(void)
    195 {
    196 	FILE *shells;
    197 	char *temp = NULL;
    198 	semanage_list_t *list = NULL;
    199 	size_t buff_len = 0;
    200 	ssize_t len;
    201 
    202 	shells = fopen(PATH_SHELLS_FILE, "r");
    203 	if (!shells)
    204 		return default_shell_list();
    205 	while ((len = getline(&temp, &buff_len, shells)) > 0) {
    206 		if (temp[len-1] == '\n') temp[len-1] = 0;
    207 		if (strcmp(temp, PATH_NOLOGIN_SHELL)) {
    208 			if (semanage_list_push(&list, temp)) {
    209 				free(temp);
    210 				semanage_list_destroy(&list);
    211 				return default_shell_list();
    212 			}
    213 		}
    214 	}
    215 	free(temp);
    216 
    217 	return list;
    218 }
    219 
    220 /* Helper function called via semanage_fcontext_iterate() */
    221 static int fcontext_matches(const semanage_fcontext_t *fcontext, void *varg)
    222 {
    223 	const char *oexpr = semanage_fcontext_get_expr(fcontext);
    224 	fc_match_handle_t *handp = varg;
    225 	struct Ustr *expr;
    226 	regex_t re;
    227 	int type, retval = -1;
    228 
    229 	/* Only match ALL or DIR */
    230 	type = semanage_fcontext_get_type(fcontext);
    231 	if (type != SEMANAGE_FCONTEXT_ALL && type != SEMANAGE_FCONTEXT_ALL)
    232 		return 0;
    233 
    234 	/* Convert oexpr into a Ustr and anchor it at the beginning */
    235 	expr = ustr_dup_cstr("^");
    236 	if (expr == USTR_NULL)
    237 		goto done;
    238 	if (!ustr_add_cstr(&expr, oexpr))
    239 		goto done;
    240 
    241 	/* Strip off trailing ".+" or ".*" */
    242 	if (ustr_cmp_suffix_cstr_eq(expr, ".+") ||
    243 	    ustr_cmp_suffix_cstr_eq(expr, ".*")) {
    244 		if (!ustr_del(&expr, 2))
    245 			goto done;
    246 	}
    247 
    248 	/* Strip off trailing "(/.*)?" */
    249 	if (ustr_cmp_suffix_cstr_eq(expr, "(/.*)?")) {
    250 		if (!ustr_del(&expr, 6))
    251 			goto done;
    252 	}
    253 
    254 	if (ustr_cmp_suffix_cstr_eq(expr, "/")) {
    255 		if (!ustr_del(&expr, 1))
    256 			goto done;
    257 	}
    258 
    259 	/* Append pattern to eat up trailing slashes */
    260 	if (!ustr_add_cstr(&expr, "/*$"))
    261 		goto done;
    262 
    263 	/* Check dir against expr */
    264 	if (regcomp(&re, ustr_cstr(expr), REG_EXTENDED) != 0)
    265 		goto done;
    266 	if (regexec(&re, handp->dir, 0, NULL, 0) == 0)
    267 		handp->matched = 1;
    268 	regfree(&re);
    269 
    270 	retval = 0;
    271 
    272 done:
    273 	ustr_free(expr);
    274 
    275 	return retval;
    276 }
    277 
    278 static semanage_list_t *get_home_dirs(genhomedircon_settings_t * s)
    279 {
    280 	semanage_list_t *homedir_list = NULL;
    281 	semanage_list_t *shells = NULL;
    282 	fc_match_handle_t hand;
    283 	char *rbuf = NULL;
    284 	char *path = NULL;
    285 	long rbuflen;
    286 	uid_t temp, minuid = 500, maxuid = 60000;
    287 	int minuid_set = 0;
    288 	struct passwd pwstorage, *pwbuf;
    289 	struct stat buf;
    290 	int retval;
    291 
    292 	path = semanage_findval(PATH_ETC_USERADD, "HOME", "=");
    293 	if (path && *path) {
    294 		if (semanage_list_push(&homedir_list, path))
    295 			goto fail;
    296 	}
    297 	free(path);
    298 
    299 	path = semanage_findval(PATH_ETC_LIBUSER, "LU_HOMEDIRECTORY", "=");
    300 	if (path && *path) {
    301 		if (semanage_list_push(&homedir_list, path))
    302 			goto fail;
    303 	}
    304 	free(path);
    305 	path = NULL;
    306 
    307 	if (!homedir_list) {
    308 		if (semanage_list_push(&homedir_list, PATH_DEFAULT_HOME)) {
    309 			goto fail;
    310 		}
    311 	}
    312 
    313 	if (!stat(PATH_EXPORT_HOME, &buf)) {
    314 		if (S_ISDIR(buf.st_mode)) {
    315 			if (semanage_list_push(&homedir_list, PATH_EXPORT_HOME)) {
    316 				goto fail;
    317 			}
    318 		}
    319 	}
    320 
    321 	if (!(s->usepasswd))
    322 		return homedir_list;
    323 
    324 	shells = get_shell_list();
    325 	assert(shells);
    326 
    327 	path = semanage_findval(PATH_ETC_LOGIN_DEFS, "UID_MIN", NULL);
    328 	if (path && *path) {
    329 		temp = atoi(path);
    330 		minuid = temp;
    331 		minuid_set = 1;
    332 	}
    333 	free(path);
    334 	path = NULL;
    335 
    336 	path = semanage_findval(PATH_ETC_LOGIN_DEFS, "UID_MAX", NULL);
    337 	if (path && *path) {
    338 		temp = atoi(path);
    339 		maxuid = temp;
    340 	}
    341 	free(path);
    342 	path = NULL;
    343 
    344 	path = semanage_findval(PATH_ETC_LIBUSER, "LU_UIDNUMBER", "=");
    345 	if (path && *path) {
    346 		temp = atoi(path);
    347 		if (!minuid_set || temp < minuid) {
    348 			minuid = temp;
    349 			minuid_set = 1;
    350 		}
    351 	}
    352 	free(path);
    353 	path = NULL;
    354 
    355 	rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
    356 	if (rbuflen <= 0)
    357 		goto fail;
    358 	rbuf = malloc(rbuflen);
    359 	if (rbuf == NULL)
    360 		goto fail;
    361 	setpwent();
    362 	while ((retval = getpwent_r(&pwstorage, rbuf, rbuflen, &pwbuf)) == 0) {
    363 		if (pwbuf->pw_uid < minuid || pwbuf->pw_uid > maxuid)
    364 			continue;
    365 		if (!semanage_list_find(shells, pwbuf->pw_shell))
    366 			continue;
    367 		int len = strlen(pwbuf->pw_dir) -1;
    368 		for(; len > 0 && pwbuf->pw_dir[len] == '/'; len--) {
    369 			pwbuf->pw_dir[len] = '\0';
    370 		}
    371 		if (strcmp(pwbuf->pw_dir, "/") == 0)
    372 			continue;
    373 		if (ignore(pwbuf->pw_dir))
    374 			continue;
    375 		if (semanage_str_count(pwbuf->pw_dir, '/') <= 1)
    376 			continue;
    377 		if (!(path = strdup(pwbuf->pw_dir))) {
    378 			break;
    379 		}
    380 
    381 		semanage_rtrim(path, '/');
    382 
    383 		if (!semanage_list_find(homedir_list, path)) {
    384 			/*
    385 			 * Now check for an existing file context that matches
    386 			 * so we don't label a non-homedir as a homedir.
    387 			 */
    388 			hand.dir = path;
    389 			hand.matched = 0;
    390 			if (semanage_fcontext_iterate(s->h_semanage,
    391 			    fcontext_matches, &hand) == STATUS_ERR)
    392 				goto fail;
    393 
    394 			/* NOTE: old genhomedircon printed a warning on match */
    395 			if (hand.matched) {
    396 				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);
    397 			} else {
    398 				if (semanage_list_push(&homedir_list, path))
    399 					goto fail;
    400 			}
    401 		}
    402 		free(path);
    403 		path = NULL;
    404 	}
    405 
    406 	if (retval && retval != ENOENT) {
    407 		WARN(s->h_semanage, "Error while fetching users.  "
    408 		     "Returning list so far.");
    409 	}
    410 
    411 	if (semanage_list_sort(&homedir_list))
    412 		goto fail;
    413 
    414 	endpwent();
    415 	free(rbuf);
    416 	semanage_list_destroy(&shells);
    417 
    418 	return homedir_list;
    419 
    420       fail:
    421 	endpwent();
    422 	free(rbuf);
    423 	free(path);
    424 	semanage_list_destroy(&homedir_list);
    425 	semanage_list_destroy(&shells);
    426 	return NULL;
    427 }
    428 
    429 /**
    430  * @param	out	the FILE to put all the output in.
    431  * @return	0 on success
    432  */
    433 static int write_file_context_header(FILE * out)
    434 {
    435 	if (fprintf(out, COMMENT_FILE_CONTEXT_HEADER) < 0) {
    436 		return STATUS_ERR;
    437 	}
    438 
    439 	return STATUS_SUCCESS;
    440 }
    441 
    442 /* Predicates for use with semanage_slurp_file_filter() the homedir_template
    443  * file currently contains lines that serve as the template for a user's
    444  * homedir.
    445  *
    446  * It also contains lines that are the template for the parent of a
    447  * user's home directory.
    448  *
    449  * Currently, the only lines that apply to the the root of a user's home
    450  * directory are all prefixed with the string "HOME_ROOT".  All other
    451  * lines apply to a user's home directory.  If this changes the
    452  * following predicates need to change to reflect that.
    453  */
    454 static int HOME_ROOT_PRED(const char *string)
    455 {
    456 	return semanage_is_prefix(string, TEMPLATE_HOME_ROOT);
    457 }
    458 
    459 static int HOME_DIR_PRED(const char *string)
    460 {
    461 	return semanage_is_prefix(string, TEMPLATE_HOME_DIR);
    462 }
    463 
    464 static int USER_CONTEXT_PRED(const char *string)
    465 {
    466 	return (int)(strstr(string, TEMPLATE_USER) != NULL);
    467 }
    468 
    469 /* make_tempate
    470  * @param	s	  the settings holding the paths to various files
    471  * @param	pred	function pointer to function to use as filter for slurp
    472  * 					file filter
    473  * @return   a list of lines from the template file with inappropriate
    474  *	    lines filtered out.
    475  */
    476 static semanage_list_t *make_template(genhomedircon_settings_t * s,
    477 				      int (*pred) (const char *))
    478 {
    479 	FILE *template_file = NULL;
    480 	semanage_list_t *template_data = NULL;
    481 
    482 	template_file = fopen(s->homedir_template_path, "r");
    483 	if (!template_file)
    484 		return NULL;
    485 	template_data = semanage_slurp_file_filter(template_file, pred);
    486 	fclose(template_file);
    487 
    488 	return template_data;
    489 }
    490 
    491 static Ustr *replace_all(const char *str, const replacement_pair_t * repl)
    492 {
    493 	Ustr *retval = USTR_NULL;
    494 	int i;
    495 
    496 	if (!str || !repl)
    497 		goto done;
    498 	if (!(retval = ustr_dup_cstr(str)))
    499 		goto done;
    500 
    501 	for (i = 0; repl[i].search_for; i++) {
    502 		ustr_replace_cstr(&retval, repl[i].search_for,
    503 				  repl[i].replace_with, 0);
    504 	}
    505 	if (ustr_enomem(retval))
    506 		ustr_sc_free(&retval);
    507 
    508       done:
    509 	return retval;
    510 }
    511 
    512 static const char * extract_context(Ustr *line)
    513 {
    514 	const char whitespace[] = " \t\n";
    515 	size_t off, len;
    516 
    517 	/* check for trailing whitespace */
    518 	off = ustr_spn_chrs_rev(line, 0, whitespace, strlen(whitespace));
    519 
    520 	/* find the length of the last field in line */
    521 	len = ustr_cspn_chrs_rev(line, off, whitespace, strlen(whitespace));
    522 
    523 	if (len == 0)
    524 		return NULL;
    525 	return ustr_cstr(line) + ustr_len(line) - (len + off);
    526 }
    527 
    528 static int check_line(genhomedircon_settings_t * s, Ustr *line)
    529 {
    530 	sepol_context_t *ctx_record = NULL;
    531 	const char *ctx_str;
    532 	int result;
    533 
    534 	ctx_str = extract_context(line);
    535 	if (!ctx_str)
    536 		return STATUS_ERR;
    537 
    538 	result = sepol_context_from_string(s->h_semanage->sepolh,
    539 					   ctx_str, &ctx_record);
    540 	if (result == STATUS_SUCCESS && ctx_record != NULL) {
    541 		sepol_msg_set_callback(s->h_semanage->sepolh, NULL, NULL);
    542 		result = sepol_context_check(s->h_semanage->sepolh,
    543 					     s->policydb, ctx_record);
    544 		sepol_msg_set_callback(s->h_semanage->sepolh,
    545 				       semanage_msg_relay_handler, s->h_semanage);
    546 		sepol_context_free(ctx_record);
    547 	}
    548 	return result;
    549 }
    550 
    551 static int write_home_dir_context(genhomedircon_settings_t * s, FILE * out,
    552 				  semanage_list_t * tpl, const char *user,
    553 				  const char *seuser, const char *home,
    554 				  const char *role_prefix, const char *level)
    555 {
    556 	replacement_pair_t repl[] = {
    557 		{.search_for = TEMPLATE_SEUSER,.replace_with = seuser},
    558 		{.search_for = TEMPLATE_HOME_DIR,.replace_with = home},
    559 		{.search_for = TEMPLATE_ROLE,.replace_with = role_prefix},
    560 		{.search_for = TEMPLATE_LEVEL,.replace_with = level},
    561 		{NULL, NULL}
    562 	};
    563 	Ustr *line = USTR_NULL;
    564 
    565 	if (fprintf(out, COMMENT_USER_HOME_CONTEXT, user) < 0)
    566 		return STATUS_ERR;
    567 
    568 	for (; tpl; tpl = tpl->next) {
    569 		line = replace_all(tpl->data, repl);
    570 		if (!line)
    571 			goto fail;
    572 		if (check_line(s, line) == STATUS_SUCCESS) {
    573 			if (!ustr_io_putfileline(&line, out))
    574 				goto fail;
    575 		}
    576 		ustr_sc_free(&line);
    577 	}
    578 	return STATUS_SUCCESS;
    579 
    580       fail:
    581 	ustr_sc_free(&line);
    582 	return STATUS_ERR;
    583 }
    584 
    585 static int write_home_root_context(genhomedircon_settings_t * s, FILE * out,
    586 				   semanage_list_t * tpl, char *homedir)
    587 {
    588 	replacement_pair_t repl[] = {
    589 		{.search_for = TEMPLATE_HOME_ROOT,.replace_with = homedir},
    590 		{NULL, NULL}
    591 	};
    592 	Ustr *line = USTR_NULL;
    593 
    594 	for (; tpl; tpl = tpl->next) {
    595 		line = replace_all(tpl->data, repl);
    596 		if (!line)
    597 			goto fail;
    598 		if (check_line(s, line) == STATUS_SUCCESS) {
    599 			if (!ustr_io_putfileline(&line, out))
    600 				goto fail;
    601 		}
    602 		ustr_sc_free(&line);
    603 	}
    604 	return STATUS_SUCCESS;
    605 
    606       fail:
    607 	ustr_sc_free(&line);
    608 	return STATUS_ERR;
    609 }
    610 
    611 static int write_user_context(genhomedircon_settings_t * s, FILE * out,
    612 			      semanage_list_t * tpl, const char *user,
    613 			      const char *seuser, const char *role_prefix)
    614 {
    615 	replacement_pair_t repl[] = {
    616 		{.search_for = TEMPLATE_USER,.replace_with = user},
    617 		{.search_for = TEMPLATE_ROLE,.replace_with = role_prefix},
    618 		{.search_for = TEMPLATE_SEUSER,.replace_with = seuser},
    619 		{NULL, NULL}
    620 	};
    621 	Ustr *line = USTR_NULL;
    622 
    623 	for (; tpl; tpl = tpl->next) {
    624 		line = replace_all(tpl->data, repl);
    625 		if (!line)
    626 			goto fail;
    627 		if (check_line(s, line) == STATUS_SUCCESS) {
    628 			if (!ustr_io_putfileline(&line, out))
    629 				goto fail;
    630 		}
    631 		ustr_sc_free(&line);
    632 	}
    633 	return STATUS_SUCCESS;
    634 
    635       fail:
    636 	ustr_sc_free(&line);
    637 	return STATUS_ERR;
    638 }
    639 
    640 static int user_sort_func(semanage_user_t ** arg1, semanage_user_t ** arg2)
    641 {
    642 	return strcmp(semanage_user_get_name(*arg1),
    643 		      semanage_user_get_name(*arg2));
    644 }
    645 
    646 static int name_user_cmp(char *key, semanage_user_t ** val)
    647 {
    648 	return strcmp(key, semanage_user_get_name(*val));
    649 }
    650 
    651 static int push_user_entry(genhomedircon_user_entry_t ** list, const char *n,
    652 			   const char *sen, const char *pre, const char *h,
    653 			   const char *l)
    654 {
    655 	genhomedircon_user_entry_t *temp = NULL;
    656 	char *name = NULL;
    657 	char *sename = NULL;
    658 	char *prefix = NULL;
    659 	char *home = NULL;
    660 	char *level = NULL;
    661 
    662 	temp = malloc(sizeof(genhomedircon_user_entry_t));
    663 	if (!temp)
    664 		goto cleanup;
    665 	name = strdup(n);
    666 	if (!name)
    667 		goto cleanup;
    668 	sename = strdup(sen);
    669 	if (!sename)
    670 		goto cleanup;
    671 	prefix = strdup(pre);
    672 	if (!prefix)
    673 		goto cleanup;
    674 	home = strdup(h);
    675 	if (!home)
    676 		goto cleanup;
    677 	level = strdup(l);
    678 	if (!level)
    679 		goto cleanup;
    680 
    681 	temp->name = name;
    682 	temp->sename = sename;
    683 	temp->prefix = prefix;
    684 	temp->home = home;
    685 	temp->level = level;
    686 	temp->next = (*list);
    687 	(*list) = temp;
    688 
    689 	return STATUS_SUCCESS;
    690 
    691       cleanup:
    692 	free(name);
    693 	free(sename);
    694 	free(prefix);
    695 	free(home);
    696 	free(level);
    697 	free(temp);
    698 	return STATUS_ERR;
    699 }
    700 
    701 static void pop_user_entry(genhomedircon_user_entry_t ** list)
    702 {
    703 	genhomedircon_user_entry_t *temp;
    704 
    705 	if (!list || !(*list))
    706 		return;
    707 
    708 	temp = *list;
    709 	*list = temp->next;
    710 	free(temp->name);
    711 	free(temp->sename);
    712 	free(temp->prefix);
    713 	free(temp->home);
    714 	free(temp->level);
    715 	free(temp);
    716 }
    717 
    718 static int set_fallback_user(genhomedircon_settings_t *s, const char *user,
    719 			     const char *prefix, const char *level)
    720 {
    721 	char *fallback_user = strdup(user);
    722 	char *fallback_user_prefix = strdup(prefix);
    723 	char *fallback_user_level = NULL;
    724 	if (level)
    725 		fallback_user_level = strdup(level);
    726 
    727 	if (fallback_user == NULL || fallback_user_prefix == NULL ||
    728 	    (fallback_user_level == NULL && level != NULL)) {
    729 		free(fallback_user);
    730 		free(fallback_user_prefix);
    731 		free(fallback_user_level);
    732 		return STATUS_ERR;
    733 	}
    734 
    735 	free(s->fallback_user);
    736 	free(s->fallback_user_prefix);
    737 	free(s->fallback_user_level);
    738 	s->fallback_user = fallback_user;
    739 	s->fallback_user_prefix = fallback_user_prefix;
    740 	s->fallback_user_level = fallback_user_level;
    741 	return STATUS_SUCCESS;
    742 }
    743 
    744 static int setup_fallback_user(genhomedircon_settings_t * s)
    745 {
    746 	semanage_seuser_t **seuser_list = NULL;
    747 	unsigned int nseusers = 0;
    748 	semanage_user_key_t *key = NULL;
    749 	semanage_user_t *u = NULL;
    750 	const char *name = NULL;
    751 	const char *seuname = NULL;
    752 	const char *prefix = NULL;
    753 	const char *level = NULL;
    754 	unsigned int i;
    755 	int retval;
    756 	int errors = 0;
    757 
    758 	retval = semanage_seuser_list(s->h_semanage, &seuser_list, &nseusers);
    759 	if (retval < 0 || (nseusers < 1)) {
    760 		/* if there are no users, this function can't do any other work */
    761 		return errors;
    762 	}
    763 
    764 	for (i = 0; i < nseusers; i++) {
    765 		name = semanage_seuser_get_name(seuser_list[i]);
    766 		if (strcmp(name, DEFAULT_LOGIN) == 0) {
    767 			seuname = semanage_seuser_get_sename(seuser_list[i]);
    768 
    769 			/* find the user structure given the name */
    770 			if (semanage_user_key_create(s->h_semanage, seuname,
    771 						     &key) < 0) {
    772 				errors = STATUS_ERR;
    773 				break;
    774 			}
    775 			if (semanage_user_query(s->h_semanage, key, &u) < 0)
    776 			{
    777 				prefix = name;
    778 				level = FALLBACK_USER_LEVEL;
    779 			}
    780 			else
    781 			{
    782 				prefix = semanage_user_get_prefix(u);
    783 				level = semanage_user_get_mlslevel(u);
    784 				if (!level)
    785 					level = FALLBACK_USER_LEVEL;
    786 			}
    787 
    788 			if (set_fallback_user(s, seuname, prefix, level) != 0)
    789 				errors = STATUS_ERR;
    790 			semanage_user_key_free(key);
    791 			if (u)
    792 				semanage_user_free(u);
    793 			break;
    794 		}
    795 	}
    796 
    797 	for (i = 0; i < nseusers; i++)
    798 		semanage_seuser_free(seuser_list[i]);
    799 	free(seuser_list);
    800 
    801 	return errors;
    802 }
    803 
    804 static genhomedircon_user_entry_t *get_users(genhomedircon_settings_t * s,
    805 					     int *errors)
    806 {
    807 	genhomedircon_user_entry_t *head = NULL;
    808 	semanage_seuser_t **seuser_list = NULL;
    809 	unsigned int nseusers = 0;
    810 	semanage_user_t **user_list = NULL;
    811 	unsigned int nusers = 0;
    812 	semanage_user_t **u = NULL;
    813 	const char *name = NULL;
    814 	const char *seuname = NULL;
    815 	const char *prefix = NULL;
    816 	const char *level = NULL;
    817 	struct passwd pwstorage, *pwent = NULL;
    818 	unsigned int i;
    819 	long rbuflen;
    820 	char *rbuf = NULL;
    821 	int retval;
    822 
    823 	*errors = 0;
    824 	retval = semanage_seuser_list(s->h_semanage, &seuser_list, &nseusers);
    825 	if (retval < 0 || (nseusers < 1)) {
    826 		/* if there are no users, this function can't do any other work */
    827 		return NULL;
    828 	}
    829 
    830 	if (semanage_user_list(s->h_semanage, &user_list, &nusers) < 0) {
    831 		nusers = 0;
    832 	}
    833 
    834 	qsort(user_list, nusers, sizeof(semanage_user_t *),
    835 	      (int (*)(const void *, const void *))&user_sort_func);
    836 
    837 	/* Allocate space for the getpwnam_r buffer */
    838 	rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
    839 	if (rbuflen <= 0)
    840 		goto cleanup;
    841 	rbuf = malloc(rbuflen);
    842 	if (rbuf == NULL)
    843 		goto cleanup;
    844 
    845 	for (i = 0; i < nseusers; i++) {
    846 		seuname = semanage_seuser_get_sename(seuser_list[i]);
    847 		name = semanage_seuser_get_name(seuser_list[i]);
    848 
    849 		if (strcmp(name,"root") && strcmp(seuname, s->fallback_user) == 0)
    850 			continue;
    851 
    852 		if (strcmp(name, DEFAULT_LOGIN) == 0)
    853 			continue;
    854 
    855 		if (strcmp(name, TEMPLATE_SEUSER) == 0)
    856 			continue;
    857 
    858 		/* %groupname syntax */
    859 		if (name[0] == '%')
    860 			continue;
    861 
    862 		/* find the user structure given the name */
    863 		u = bsearch(seuname, user_list, nusers, sizeof(semanage_user_t *),
    864 			    (int (*)(const void *, const void *))
    865 			    &name_user_cmp);
    866 		if (u) {
    867 			prefix = semanage_user_get_prefix(*u);
    868 			level = semanage_user_get_mlslevel(*u);
    869 			if (!level)
    870 				level = FALLBACK_USER_LEVEL;
    871 		} else {
    872 			prefix = name;
    873 			level = FALLBACK_USER_LEVEL;
    874 		}
    875 
    876 		retval = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent);
    877 		if (retval != 0 || pwent == NULL) {
    878 			if (retval != 0 && retval != ENOENT) {
    879 				*errors = STATUS_ERR;
    880 				goto cleanup;
    881 			}
    882 
    883 			WARN(s->h_semanage,
    884 			     "user %s not in password file", name);
    885 			continue;
    886 		}
    887 
    888 		int len = strlen(pwent->pw_dir) -1;
    889 		for(; len > 0 && pwent->pw_dir[len] == '/'; len--) {
    890 			pwent->pw_dir[len] = '\0';
    891 		}
    892 
    893 		if (strcmp(pwent->pw_dir, "/") == 0) {
    894 			/* don't relabel / genhomdircon checked to see if root
    895 			 * was the user and if so, set his home directory to
    896 			 * /root */
    897 			continue;
    898 		}
    899 		if (ignore(pwent->pw_dir))
    900 			continue;
    901 		if (push_user_entry(&head, name, seuname,
    902 				    prefix, pwent->pw_dir, level) != STATUS_SUCCESS) {
    903 			*errors = STATUS_ERR;
    904 			break;
    905 		}
    906 	}
    907 
    908       cleanup:
    909 	free(rbuf);
    910 	if (*errors) {
    911 		for (; head; pop_user_entry(&head)) {
    912 			/* the pop function takes care of all the cleanup
    913 			   so the loop body is just empty */
    914 		}
    915 	}
    916 	for (i = 0; i < nseusers; i++) {
    917 		semanage_seuser_free(seuser_list[i]);
    918 	}
    919 	free(seuser_list);
    920 
    921 	for (i = 0; i < nusers; i++) {
    922 		semanage_user_free(user_list[i]);
    923 	}
    924 	free(user_list);
    925 
    926 	return head;
    927 }
    928 
    929 static int write_gen_home_dir_context(genhomedircon_settings_t * s, FILE * out,
    930 				      semanage_list_t * user_context_tpl,
    931 				      semanage_list_t * homedir_context_tpl)
    932 {
    933 	genhomedircon_user_entry_t *users;
    934 	int errors = 0;
    935 
    936 	users = get_users(s, &errors);
    937 	if (!users && errors) {
    938 		return STATUS_ERR;
    939 	}
    940 
    941 	for (; users; pop_user_entry(&users)) {
    942 		if (write_home_dir_context(s, out, homedir_context_tpl,
    943 					   users->name,
    944 					   users->sename, users->home,
    945 					   users->prefix, users->level))
    946 			goto err;
    947 		if (write_user_context(s, out, user_context_tpl, users->name,
    948 				       users->sename, users->prefix))
    949 			goto err;
    950 	}
    951 
    952 	return STATUS_SUCCESS;
    953 err:
    954 	for (; users; pop_user_entry(&users)) {
    955 	/* the pop function takes care of all the cleanup
    956 	 * so the loop body is just empty */
    957 	}
    958 
    959 	return STATUS_ERR;
    960 }
    961 
    962 /**
    963  * @param	s	settings structure, stores various paths etc. Must never be NULL
    964  * @param	out	the FILE to put all the output in.
    965  * @return	0 on success
    966  */
    967 static int write_context_file(genhomedircon_settings_t * s, FILE * out)
    968 {
    969 	semanage_list_t *homedirs = NULL;
    970 	semanage_list_t *h = NULL;
    971 	semanage_list_t *user_context_tpl = NULL;
    972 	semanage_list_t *homedir_context_tpl = NULL;
    973 	semanage_list_t *homeroot_context_tpl = NULL;
    974 	int retval = STATUS_SUCCESS;
    975 
    976 	homedir_context_tpl = make_template(s, &HOME_DIR_PRED);
    977 	homeroot_context_tpl = make_template(s, &HOME_ROOT_PRED);
    978 	user_context_tpl = make_template(s, &USER_CONTEXT_PRED);
    979 
    980 	if (!homedir_context_tpl && !homeroot_context_tpl && !user_context_tpl)
    981 		goto done;
    982 
    983 	if (write_file_context_header(out) != STATUS_SUCCESS) {
    984 		retval = STATUS_ERR;
    985 		goto done;
    986 	}
    987 
    988 	if (setup_fallback_user(s) != 0) {
    989 		retval = STATUS_ERR;
    990 		goto done;
    991 	}
    992 
    993 	if (homedir_context_tpl || homeroot_context_tpl) {
    994 		homedirs = get_home_dirs(s);
    995 		if (!homedirs) {
    996 			WARN(s->h_semanage,
    997 			     "no home directories were available, exiting without writing");
    998 			goto done;
    999 		}
   1000 
   1001 		for (h = homedirs; h; h = h->next) {
   1002 			Ustr *temp = ustr_dup_cstr(h->data);
   1003 
   1004 			if (!temp || !ustr_add_cstr(&temp, "/[^/]*")) {
   1005 				ustr_sc_free(&temp);
   1006 				retval = STATUS_ERR;
   1007 				goto done;
   1008 			}
   1009 
   1010 			if (write_home_dir_context(s, out,
   1011 						   homedir_context_tpl,
   1012 						   s->fallback_user, s->fallback_user,
   1013 						   ustr_cstr(temp),
   1014 						   s->fallback_user_prefix, s->fallback_user_level) !=
   1015 			    STATUS_SUCCESS) {
   1016 				ustr_sc_free(&temp);
   1017 				retval = STATUS_ERR;
   1018 				goto done;
   1019 			}
   1020 			if (write_home_root_context(s, out,
   1021 						    homeroot_context_tpl,
   1022 						    h->data) != STATUS_SUCCESS) {
   1023 				ustr_sc_free(&temp);
   1024 				retval = STATUS_ERR;
   1025 				goto done;
   1026 			}
   1027 
   1028 			ustr_sc_free(&temp);
   1029 		}
   1030 	}
   1031 	if (user_context_tpl) {
   1032 		if (write_user_context(s, out, user_context_tpl,
   1033 				       ".*", s->fallback_user,
   1034 				       s->fallback_user_prefix) != STATUS_SUCCESS) {
   1035 			retval = STATUS_ERR;
   1036 			goto done;
   1037 		}
   1038 
   1039 		if (write_gen_home_dir_context(s, out, user_context_tpl,
   1040 					       homedir_context_tpl) != STATUS_SUCCESS) {
   1041 			retval = STATUS_ERR;
   1042 		}
   1043 	}
   1044 
   1045 done:
   1046 	/* Cleanup */
   1047 	semanage_list_destroy(&homedirs);
   1048 	semanage_list_destroy(&user_context_tpl);
   1049 	semanage_list_destroy(&homedir_context_tpl);
   1050 	semanage_list_destroy(&homeroot_context_tpl);
   1051 
   1052 	return retval;
   1053 }
   1054 
   1055 int semanage_genhomedircon(semanage_handle_t * sh,
   1056 			   sepol_policydb_t * policydb,
   1057 			   int usepasswd,
   1058 			   char *ignoredirs)
   1059 {
   1060 	genhomedircon_settings_t s;
   1061 	FILE *out = NULL;
   1062 	int retval = 0;
   1063 
   1064 	assert(sh);
   1065 
   1066 	s.homedir_template_path =
   1067 	    semanage_path(SEMANAGE_TMP, SEMANAGE_HOMEDIR_TMPL);
   1068 	s.fcfilepath = semanage_final_path(SEMANAGE_FINAL_TMP,
   1069 					   SEMANAGE_FC_HOMEDIRS);
   1070 
   1071 	s.fallback_user = strdup(FALLBACK_USER);
   1072 	s.fallback_user_prefix = strdup(FALLBACK_USER_PREFIX);
   1073 	s.fallback_user_level = strdup(FALLBACK_USER_LEVEL);
   1074 	if (s.fallback_user == NULL || s.fallback_user_prefix == NULL || s.fallback_user_level == NULL) {
   1075 		retval = STATUS_ERR;
   1076 		goto done;
   1077 	}
   1078 
   1079 	if (ignoredirs) ignore_setup(ignoredirs);
   1080 
   1081 	s.usepasswd = usepasswd;
   1082 	s.h_semanage = sh;
   1083 	s.policydb = policydb;
   1084 
   1085 	if (!(out = fopen(s.fcfilepath, "w"))) {
   1086 		/* couldn't open output file */
   1087 		ERR(sh, "Could not open the file_context file for writing");
   1088 		retval = STATUS_ERR;
   1089 		goto done;
   1090 	}
   1091 
   1092 	retval = write_context_file(&s, out);
   1093 
   1094 done:
   1095 	if (out != NULL)
   1096 		fclose(out);
   1097 
   1098 	free(s.fallback_user);
   1099 	free(s.fallback_user_prefix);
   1100 	free(s.fallback_user_level);
   1101 	ignore_free();
   1102 
   1103 	return retval;
   1104 }
   1105