#! /usr/bin/python -E # Copyright (C) 2004 Tresys Technology, LLC # see file 'COPYING' for use and warranty information # # genhomedircon - this script is used to generate file context # configuration entries for user home directories based on their # default prefixes and is run when building the policy. Specifically, we # replace HOME_ROOT, HOME_DIR, and ROLE macros in .fc files with # generic and user-specific values. # # Based off original script by Dan Walsh, # # ASSUMPTIONS: # # The file CONTEXTDIR/files/homedir_template exists. This file is used to # set up the home directory context for each real user. # # If a user is not listed in CONTEXTDIR/seusers, he will default to user_u, prefix user # # "Real" users (as opposed to system users) are those whose UID is greater than # or equal STARTING_UID (usually 500) and whose login is not a member of # EXCLUDE_LOGINS. Users who are explicitly defined in CONTEXTDIR/seusers # are always "real" (including root, in the default configuration). # # import sys, os, pwd, string, getopt, re from semanage import *; import selinux import gettext gettext.install('policycoreutils') def grep(file, var): ret = "" fd = open(file, 'r') for i in fd.readlines(): if re.search(var, i, 0) != None: ret = i break fd.close() return ret def findval(file, var, delim = ""): val = "" try: fd = open(file, 'r') for i in fd.readlines(): if i.startswith(var) == 1: if delim == "": val = i.split()[1] else: val = i.split(delim)[1] val = val.split("#")[0] val = val.strip() fd.close() except: val = "" return val def getStartingUID(): starting_uid = sys.maxint uid_min = findval("/etc/login.defs", "UID_MIN") if uid_min != "": uid_min = uid_min.split("#")[0] uid_min = uid_min.strip() if int(uid_min) < starting_uid: starting_uid = int(uid_min) uid_min = findval("/etc/libuser.conf", "LU_UIDNUMBER", "=") if uid_min != "": uid_min = uid_min.split("#")[0] uid_min = uid_min.strip() if int(uid_min) < starting_uid: starting_uid = int(uid_min) if starting_uid == sys.maxint: starting_uid = 500 return starting_uid def getDefaultHomeDir(): ret = [] homedir = findval("/etc/default/useradd", "HOME", "=") if homedir != "" and not homedir in ret: ret.append(homedir) homedir = findval("/etc/libuser.conf", "LU_HOMEDIRECTORY", "=") if homedir != "" and not homedir in ret: ret.append(homedir) if ret == []: ret.append("/home") # Add /export/home if it exists # Some customers use this for automounted homedirs if os.path.exists("/export/home"): ret.append("/export/home") return ret def getSELinuxType(directory): val = findval(directory+"/config", "SELINUXTYPE", "=") if val != "": return val return "targeted" def usage(rc=0, error = ""): if error != "": sys.stderr.write("%s\n" % error) rc = 1 sys.stderr.write("Usage: %s [ -d selinuxdir ] [-n | --nopasswd] [-t selinuxtype ]\n" % sys.argv[0]) sys.stderr.flush() sys.exit(rc) def warning(warning = ""): sys.stderr.write("%s\n" % warning) sys.stderr.flush() def errorExit(error): sys.stderr.write("%s exiting for: " % sys.argv[0]) sys.stderr.write("%s\n" % error) sys.stderr.flush() sys.exit(1) class selinuxConfig: def __init__(self, selinuxdir = "/etc/selinux", type = "targeted", usepwd = 1): self.semanageHandle = semanage_handle_create() self.semanaged = semanage_is_managed(self.semanageHandle) if self.semanaged: rc = semanage_connect(self.semanageHandle) if rc: errorExit("Unable to connect to semanage") (status, self.ulist) = semanage_user_list(self.semanageHandle) self.type = type self.selinuxdir = selinuxdir +"/" self.contextdir = "/contexts" self.filecontextdir = self.contextdir+"/files" self.usepwd = usepwd self.default_user = "user_u" self.default_prefix = "user" self.users = self.getUsers() def getFileContextDir(self): return self.selinuxdir+self.type+self.filecontextdir def getFileContextFile(self): return self.getFileContextDir()+"/file_contexts" def getContextDir(self): return self.selinuxdir+self.type+self.contextdir def getHomeDirTemplate(self): return self.getFileContextDir()+"/homedir_template" def getHomeRootContext(self, homedir): ret = "" fd = open(self.getHomeDirTemplate(), 'r') for i in fd.readlines(): if i.find("HOME_ROOT") == 0: i = i.replace("HOME_ROOT", homedir) ret += i fd.close() if ret == "": errorExit("No Home Root Context Found") return ret def heading(self): ret = "\n#\n#\n# User-specific file contexts, generated via %s\n" % sys.argv[0] if self.semanaged: ret += "# use semanage command to manage system users in order to change the file_context\n#\n#\n" else: ret += "# edit %s to change file_context\n#\n#\n" % (self.selinuxdir+self.type+"/seusers") return ret def get_default_prefix(self, name): for user in self.ulist: if semanage_user_get_name(user) == name: return semanage_user_get_prefix(user) return name def get_old_prefix(self, user): rc = grep(self.selinuxdir+self.type+"/users/system.users", "^user %s" % user) if rc == "": rc = grep(self.selinuxdir+self.type+"/users/local.users", "^user %s" % user) if rc != "": user = rc.split() prefix = user[3] if prefix == "{": prefix = user[4] if len(prefix) > 2 and (prefix[-2:] == "_r" or prefix[-2:] == "_u"): prefix = prefix[:-2] return prefix def adduser(self, udict, user, seuser, prefix): if seuser == self.default_user or user == "__default__" or user == "system_u": return # !!! chooses first prefix in the list to use in the file context !!! try: home = pwd.getpwnam(user)[5] if home == "/": # Probably install so hard code to /root if user == "root": home = "/root" else: return except KeyError: if user == "root": home = "/root" else: sys.stderr.write("The user \"%s\" is not present in the passwd file, skipping...\n" % user) return prefs = {} prefs["seuser"] = seuser prefs["prefix"] = prefix prefs["home"] = home udict[user] = prefs def setDefaultUser(self, user, prefix): self.default_user = user self.default_prefix = prefix def getUsers(self): udict = {} if self.semanaged: (status, list) = semanage_seuser_list(self.semanageHandle) for seuser in list: user = [] seusername = semanage_seuser_get_sename(seuser) prefix = self.get_default_prefix(seusername) if semanage_seuser_get_name(seuser) == "__default__": self.setDefaultUser(seusername, prefix) self.adduser(udict, semanage_seuser_get_name(seuser), seusername, prefix) else: try: fd = open(self.selinuxdir+self.type+"/seusers") for u in fd.readlines(): u = u.strip() if len(u) == 0 or u[0] == "#": continue user = u.split(":") if len(user) < 2: continue prefix = self.get_old_prefix(user[1]) self.adduser(udict, user[0], user[1], prefix) fd.close() except IOError, error: # Must be install so force add of root self.adduser(udict, "root", "root", "root") return udict def getHomeDirContext(self, user, seuser, home, prefix): ret = "\n\n#\n# Home Context for user %s\n#\n\n" % user fd = open(self.getHomeDirTemplate(), 'r') for i in fd.readlines(): if i.startswith("HOME_DIR") == 1: i = i.replace("HOME_DIR", home) i = i.replace("ROLE", prefix) i = i.replace("system_u", seuser) # Validate if the generated context exists. Some user types may not exist scon = i.split()[-1] if selinux.is_selinux_enabled() < 1 or scon == '<>' or selinux.security_check_context(scon) == 0: ret = ret+i fd.close() return ret def getUserContext(self, user, sel_user, prefix): ret = "" fd = open(self.getHomeDirTemplate(), 'r') for i in fd.readlines(): if i.find("USER") == 1: i = i.replace("USER", user) i = i.replace("ROLE", prefix) i = i.replace("system_u", sel_user) ret = ret+i fd.close() return ret def genHomeDirContext(self): ret = "" # Fill in HOME and prefix for users that are defined for u in self.users.keys(): ret += self.getHomeDirContext (u, self.users[u]["seuser"], self.users[u]["home"], self.users[u]["prefix"]) ret += self.getUserContext (u, self.users[u]["seuser"], self.users[u]["prefix"]) return ret+"\n" def checkExists(self, home): fd = open(self.getFileContextFile()) for i in fd.readlines(): if len(i) == 0: continue try: regex = i.split()[0] #match a trailing .+ regex = re.sub("\.+$", "", regex) regex = re.sub("\.\*$", "", regex) #strip a (/.*)? which matches anything trailing to a /*$ which matches trailing /'s regex = re.sub("\(\/\.\*\)\?", "", regex) regex = regex + "/*$" if re.match(home, regex): return 1 except: continue return 0 def getHomeDirs(self): homedirs = getDefaultHomeDir() starting_uid = getStartingUID() if self.usepwd == 0: return homedirs ulist = pwd.getpwall() for u in ulist: if u[2] >= starting_uid and \ u[6] in VALID_SHELLS and \ u[5] != "/" and \ string.count(u[5], "/") > 1: homedir = u[5][:string.rfind(u[5], "/")] if not homedir in homedirs: if self.checkExists(homedir) == 1: warning("%s homedir %s or its parent directory conflicts with a\ndefined context in %s,\n%s will not create a new context. This usually indicates an incorrectly defined system account. If it is a system account please make sure its login shell is /sbin/nologin." % (u[0], u[5], self.getFileContextFile(), sys.argv[0])) else: homedirs.append(homedir) homedirs.sort() return homedirs def genoutput(self): ret = self.heading() for h in self.getHomeDirs(): ret += self.getHomeDirContext (self.default_user, self.default_user, h+'/[^/]*', self.default_prefix) ret += self.getHomeRootContext(h) ret += self.getUserContext(".*", self.default_user, self.default_prefix) + "\n" ret += self.genHomeDirContext() return ret def printout(self): print self.genoutput() def write(self): fd = open(self.getFileContextDir()+"/file_contexts.homedirs", "w") fd.write(self.genoutput()) fd.close() if os.getuid() > 0 or os.geteuid() > 0: print _("You must be root to run %s.") % sys.argv[0] sys.exit(1) try: fd = open("/etc/shells", 'r') VALID_SHELLS = fd.read().split("\n") fd.close() if "/sbin/nologin" in VALID_SHELLS: VALID_SHELLS.remove("/sbin/nologin") if "" in VALID_SHELLS: VALID_SHELLS.remove("") except: VALID_SHELLS = ['/bin/sh', '/bin/bash', '/bin/ash', '/bin/bsh', '/bin/ksh', '/usr/bin/ksh', '/usr/bin/pdksh', '/bin/tcsh', '/bin/csh', '/bin/zsh'] # # This script will generate home dir file context # based off the homedir_template file, entries in the password file, and # try: usepwd = 1 directory = "/etc/selinux" type = None gopts, cmds = getopt.getopt(sys.argv[1:], 'hnd:t:', ['help', 'type=', 'nopasswd', 'dir=']) for o,a in gopts: if o == '--type' or o == "-t": type = a if o == '--nopasswd' or o == "-n": usepwd = 0 if o == '--dir' or o == "-d": directory = a if o == '--help' or o == "-h": usage() except getopt.error, error: errorExit(_("Options Error %s ") % error) if type == None: type = getSELinuxType(directory) if len(cmds) != 0: usage(1) selconf = selinuxConfig(directory, type, usepwd) try: selconf.write() except IOError, error: sys.stderr.write("%s: %s\n" % ( sys.argv[0], error )) sys.exit(1)