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#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
86typedef 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
97typedef 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
106typedef struct {
107	const char *search_for;
108	const char *replace_with;
109} replacement_pair_t;
110
111typedef struct {
112	const char *dir;
113	int matched;
114} fc_match_handle_t;
115
116typedef struct IgnoreDir {
117	struct IgnoreDir *next;
118	char *dir;
119} ignoredir_t;
120
121ignoredir_t *ignore_head = NULL;
122
123static 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
134static 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;
154err:
155	free(ptr);
156	ignore_free();
157	return -1;
158}
159
160static 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
171static 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
194static 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() */
221static 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
272done:
273	ustr_free(expr);
274
275	return retval;
276}
277
278static 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 */
433static 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 */
454static int HOME_ROOT_PRED(const char *string)
455{
456	return semanage_is_prefix(string, TEMPLATE_HOME_ROOT);
457}
458
459static int HOME_DIR_PRED(const char *string)
460{
461	return semanage_is_prefix(string, TEMPLATE_HOME_DIR);
462}
463
464static 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 */
476static 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
491static 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
512static 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
528static 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
551static 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
585static 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
611static 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
640static 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
646static int name_user_cmp(char *key, semanage_user_t ** val)
647{
648	return strcmp(key, semanage_user_get_name(*val));
649}
650
651static 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
701static 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
718static 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
744static 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
804static 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
929static 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;
953err:
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 */
967static 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
1045done:
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
1055int 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
1094done:
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