#! /usr/bin/env python
# Copyright (C) 2004 Tresys Technology, LLC
# see file 'COPYING' for use and warranty information

#
# genhomedircon - Replace HOME_ROOT, HOME_DIR, and ROLE macros in .fc files
#  with generic and user-specific values.
#
# Based off original script by Dan Walsh, <dwalsh@redhat.com>
#
# ASSUMPTIONS:
#
# If a user has more than one role in FILECONTEXTDIR/users, genhomedircon uses
#  the first role in the list.
#
# If a user is not listed in FILECONTEXTDIR/users, genhomedircon assumes that
#  the user's home dir will be found in one of the HOME_ROOTs.
#
# "Real" users (as opposed to system users) are those whose UID is greater than
#  or equal STARTING_UID (usually 100) and whose login is not a member of
#  EXCLUDE_LOGINS.  Users who are explicitly defined in FILECONTEXTDIR/users
#  are always "real" (including root, in the default configuration).
#

import commands, sys, os, pwd, string

EXCLUDE_LOGINS=["/sbin/nologin", "/bin/false"]
STARTING_UID=100

def getPrefixes():
	ulist = pwd.getpwall()
	prefixes = {}
	for u in ulist:
		if u[2] >= STARTING_UID and \
		   not u[6] in EXCLUDE_LOGINS and \
		   u[5] != "/" and \
		   string.count(u[5], "/") > 1:
			prefix = u[5][:string.rfind(u[5], "/")]
			if not prefixes.has_key(prefix):
				prefixes[prefix] = ""
	return prefixes
 
def getUsers():
	rc = commands.getstatusoutput("grep ^user %s/users" % FILECONTEXTDIR)
	udict = {}
	if rc[0] == 0:
		ulist = rc[1].strip().split("\n")
		for u in ulist:
			user = u.split()
			try:
				if user[1] == "user_u" or user[1] == "system_u":
					continue
				# !!! chooses first role in the list to use in the file context !!!
				role = user[4].split("_r")[0]
				home = pwd.getpwnam(user[1])[5]
				if home == "/":
					continue
				prefs = {}
				prefs["role"] = role
				prefs["home"] = home
				udict[user[1]] = prefs
			except KeyError:
				sys.stderr.write("The user \"%s\" is not present in the passwd file, skipping...\n" % (user[1],))
	return udict

def usage(error = ""):
	if error != "":
		sys.stderr.write("%s\n" % (error,))
	sys.stderr.write("Usage: %s POLICYSOURCEDIR FILE_CONTEXTS\n" % sys.argv[0])
	sys.stderr.flush()
	sys.exit(1)
	
def update(filecontext, user, prefs):
	rc=commands.getstatusoutput("grep -h '^HOME_DIR' %s | grep -v vmware | sed -e 's|HOME_DIR|%s|' -e 's/ROLE/%s/' -e 's/system_u/%s/'" % (filecontext, prefs["home"], prefs["role"], user))
	if rc[0] == 0:
		print rc[1]
	else:
		usage(rc[1])
	return rc

try:
	if len(sys.argv) != 3:
		print len(sys.argv)
		usage()

	FILECONTEXTDIR=sys.argv[1]
	prefixes = getPrefixes()

	rc=commands.getstatusoutput("grep -h '^HOME' /etc/default/useradd")
	if rc[0] == 0:
		homedir = rc[1].split("=")[1]
	else:
		sys.stderr.write("%s\n" % (rc[1],))
		sys.stderr.write("You do not have access to /etc/default/useradd, default /home\n")
		sys.stderr.flush()
		homedir = "/home"
		

	if not prefixes.has_key(homedir):
		prefixes[homedir] = ""

	# There may be a more elegant sed script to expand a macro to multiple lines, but this works
	sed_root = "h; s|^HOME_ROOT|%s|" % (string.join(prefixes.keys(), "|; p; g; s|^HOME_ROOT|"),)
	sed_dir = "h; s|^HOME_DIR|%s/[^/]+|; s|ROLE_|user_|" % (string.join(prefixes.keys(), "/[^/]+|; s|ROLE_|user_|; p; g; s|^HOME_DIR|"),)

	# Fill in HOME_ROOT, HOME_DIR, and ROLE for users not explicitly defined in /etc/security/selinux/src/policy/users
	rc=commands.getstatusoutput("sed -e \"/^HOME_ROOT/{%s}\" -e \"/^HOME_DIR/{%s}\" %s" % (sed_root, sed_dir, sys.argv[2]))
	if rc[0] == 0:
		print rc[1]
	else:
		usage(rc[1])

	users = getUsers()
	print "\n#\n# User-specific file contexts\n#\n"

	# Fill in HOME and ROLE for users that are defined
	for u in users.keys():
		update(sys.argv[2], u, users[u]) 
except ValueError, error:
	usage(error)
except IndexError, error:
	usage()
