#!/usr/bin/python -E
#
# build-sepolicy - Build a basic SELinux policy using the Reference Policy 
#                  security_classes, access_vector and initial_sids files.
#
# The program will output either CIL or kernel policy language formats
# suitable for building basic non-MLS and MLS binary policies. It will
# also build a header file containing the class/permission mapping
# structure for selinux_set_mapping(3).
#
# It uses the Reference Policy source tree /policy/flask/initial_sids,
# /policy/flask/security_classes and /policy/flask/access_vectors files
# to build the basic policy. The -r parameter will point to the base
# of the source tree, for example to build an MLS CIL policy:
#
#  build-sepolicy -c -M -r /etc/selinux/targeted/src/policy -o base.cil
#
# The policies are built as follows:
# Kernel policy language:
#    users - unconfined_u and system_u with one type
#    roles - unconfined_r and object_r
#    types - unconfined_t
#
# CIL:
#    namespaces - global, unconfined and system
#    users      - unconfined.user and system.user
#    roles      - unconfined.role and object_r
#    types      - unconfined.process and unconfined.object
#
# Note that the code has been adapted from the Python script "flask.py" written
# by Caleb Case <ccase@tresys.com> that is supplied with the Reference Policy
# to build the libselinux header files.
#

import getopt
import os
import sys
import re

class ParseError(Exception):
	def __init__(self, type, file, line):
		self.type = type
		self.file = file
		self.line = line
	def __str__(self):
		typeS = self.type
		if type(self.type) is not str: typeS = Flask.CONSTANT_S[self.type]
		return "Parse Error: Unexpected %s on line %d of %s." % (typeS, self.line, self.file)

class DuplicateError(Exception):
	def __init__(self, type, file, line, symbol):
		self.type = type
		self.file = file
		self.line = line
		self.symbol = symbol
	def __str__(self):
		typeS = self.type
		if type(self.type) is not str: typeS = Flask.CONSTANT_S[self.type]
		return "Duplicate Error: Duplicate %s '%s' on line %d of %s." % (typeS, self.symbol, self.line, self.file)

class UndefinedError(Exception):
	def __init__(self, type, file, line, symbol):
		self.type = type
		self.file = file
		self.line = line
		self.symbol = symbol
	def __str__(self):
		typeS = self.type
		if type(self.type) is not str: typeS = Flask.CONSTANT_S[self.type]
		return "Undefined Error: %s '%s' is not defined but used on line %d of %s." % (typeS, self.symbol, self.line, self.file)

class UnusedError(Exception):
	def __init__(self, info):
		self.info = info
	def __str__(self):
		return "Unused Error: %s" % self.info

class Flask:
	'''
	FLASK container class with utilities for parsing definition
	files and creating source policy files (CIL and policy language versions).
	'''

	#Constants used in definitions parsing.
	WHITE    = re.compile(r'^\s*$')
	COMMENT  = re.compile(r'^\s*#')
	USERFLAG = re.compile(r'# userspace')
	CLASS    = re.compile(r'^class (?P<name>\w+)')
	COMMON   = re.compile(r'^common (?P<name>\w+)')
	INHERITS = re.compile(r'^inherits (?P<name>\w+)')
	OPENB    = re.compile(r'^{')
	VECTOR   = re.compile(r'^\s*(?P<name>\w+)')
	CLOSEB   = re.compile(r'^}')
	SID      = re.compile(r'^sid (?P<name>\w+)')
	EOF      = "end of file"

	#Constants used in header generation.
	USERSPACE = 0
	KERNEL    = 1

	CONSTANT_S = { \
		#parsing constants
		WHITE    : "whitespace", \
		COMMENT  : "comment", \
		USERFLAG : "userspace flag", \
		CLASS    : "class definition", \
		COMMON   : "common definition", \
		INHERITS : "inherits definition", \
		OPENB    : "'{'", \
		VECTOR   : "access vector definition", \
		CLOSEB   : "'}'", \
		SID      : "security identifier", \
		EOF      : "end of file", \
		#generation constants
		USERSPACE : "userspace mode", \
		KERNEL    : "kernel mode", \
	}

	def __init__(self):
		self.commons   = []
		self.user_commons = []
		self.common    = {}
		self.classes   = []
		self.vectors   = []
		self.vector    = {}
		self.userspace = {}
		self.sids      = []
		self.inherits  = {}
	
	def parseClasses(self, path):
		'''
		Parses security class definitions from the given path.
		'''
		classes = []
		input = open(path, 'r')

		number = 0
		for line in input:
			number += 1
			m = self.COMMENT.search(line)
			if m: continue

			m = self.WHITE.search(line)
			if m: continue

			m = self.CLASS.search(line)
			if m:
				g = m.groupdict()
				c = g['name']
				if c in classes: raise DuplicateError, (self.CLASS, path, number, c)
				classes.append(c)
				if self.USERFLAG.search(line):
					self.userspace[c] = True
				else:
					self.userspace[c] = False
				continue

			raise ParseError, ("data.  Was expecting either a comment, whitespace, or class definition. ", path, number)

		self.classes = classes
		return classes


	def parseSids(self, path):
		'''
		Parses initial SID definitions from the given path.
		'''
		sids = []
		input = open(path, 'r')
		for line in input:
			m = self.COMMENT.search(line)
			if m: continue

			m = self.WHITE.search(line)
			if m: continue

			m = self.SID.search(line)
			if m:
				g = m.groupdict()
				s = g['name']
				if s in sids: raise DuplicateError, (self.SID, path, number, s)
				sids.append(s)
				continue
			
			raise ParseError, ("data. Was expecting either a comment, whitespace, or security identifier. ", path, number)

		self.sids = sids
		return sids

	def parseVectors(self, path):
		'''
		Parses access vector definitions from the given path.
		'''
		vectors = []
		vector  = {}
		commons = []
		common = {}
		inherits = {}
		user_commons = {}
		input = open(path, 'r')

		# states
		NONE    = 0
		COMMON  = 1
		CLASS   = 2
		INHERIT = 3
		OPEN    = 4

		state = NONE
		state2 = NONE
		number = 0
		for line in input:
			number += 1
			m = self.COMMENT.search(line)
			if m: continue

			m = self.WHITE.search(line)
			if m: 
				if state == INHERIT:
					state = NONE
				continue

			m = self.COMMON.search(line)
			if m:
				if state != NONE: raise ParseError, (self.COMMON, path, number)
				g = m.groupdict()
				c = g['name']
				if c in commons: raise DuplicateError, (self.COMMON, path, number, c)
				commons.append(c)
				common[c] = []
				user_commons[c] = True
				state = COMMON
				continue

			m = self.CLASS.search(line)
			if m:
				if state != NONE: raise ParseError, (self.CLASS, number)
				g = m.groupdict()
				c = g['name']
				if c in vectors: raise DuplicateError, (self.CLASS, path, number, c)
				if c not in self.classes: raise UndefinedError, (self.CLASS, path, number, c)
				vectors.append(c)
				vector[c] = []
				state = CLASS
				continue
			
			m = self.INHERITS.search(line)
			if m:
				if state != CLASS: raise ParseError, (self.INHERITS, number)
				g = m.groupdict()
				i = g['name']
				if c in inherits: raise DuplicateError, (self.INHERITS, path, number, c)
				if i not in common: raise UndefinedError, (self.COMMON, path, number, i)
				inherits[c] = i
				state = INHERIT
				if not self.userspace[c]: user_commons[i] = False
				continue

			m = self.OPENB.search(line)
			if m:
				if (state != CLASS \
				and state != INHERIT \
				and state != COMMON) \
				or state2 != NONE: 
					raise ParseError, (self.OPENB, path, number)
				state2 = OPEN
				continue

			m = self.VECTOR.search(line)
			if m:
				if state2 != OPEN: raise ParseError, (self.VECTOR, path, number)
				g = m.groupdict()
				v = g['name']
				if state == CLASS or state == INHERIT:
					if v in vector[c]: raise DuplicateError, (self.VECTOR, path, number, v)
					vector[c].append(v)
				elif state == COMMON:
					if v in common[c]: raise DuplicateError, (self.VECTOR, path, number, v)
					common[c].append(v)
				continue

			m = self.CLOSEB.search(line)
			if m:
				if state2 != OPEN: raise ParseError, (self.CLOSEB, path, number)
				state = NONE
				state2 = NONE
				c = None
				continue
			
			raise ParseError, ("data", path, number)

		if state != NONE and state2 != NONE: raise ParseError, (self.EOF, path, number)

		cvdiff = set(self.classes) - set(vectors)
		if cvdiff: raise UnusedError, "Not all security classes were used in access vectors: %s" % cvdiff # the inverse of this will be caught as an undefined class error

# These arrays contain the classes, permissions and initial SIDs after 
# processing the Reference Policy security_classes, access_vector and 
# initial_sids files. The buildXxxPolicy Routines will then output the 
# requested policy file.
		self.commons = commons
		self.user_commons = user_commons
		self.common = common
		self.vectors = vectors
		self.vector = vector
		self.inherits = inherits
		return vector


	def createPolicyHdr(self, mode = USERSPACE):
		'''
		Creates a header file for processing by the libselinux examples.
		'''
		results = []
		results.append("/*\n * libselinux header file for selinux_set_mapping(3)\n")

		if mode == self.KERNEL:
			results.append(" * containing Kernel classes and permissions only\n */\n")
		else:
			results.append(" * containing Kernel + Userspace classes and permissions\n */\n")

#	print out the class + permissions
		results.append("static struct security_class_mapping map[] = {\n\n")

		for c in self.classes:
			if mode == self.KERNEL and self.userspace[c]:
				continue
			else:
				results.append("\t{ \"%s\", { " % c)
				count = 0
				ps = []
				if self.inherits.has_key(c):
					ps += self.common[self.inherits[c]]
				ps += self.vector[c]
				for p in ps: 
					results.append("\"%s\", " % ps[count])
					count += 1
				results.append("NULL } },\n\n")
		results.append("\t{ NULL }\n};\n")

		return results

#
# Create a CIL Policy
#
	def createCilPolicy(self, mode = USERSPACE):
		'''
		Creates a CIL language formatted policy file for processing by the CIL compiler.
		'''
		results = []
		if mls:
			results.append(";\n; MLS CIL policy ")
		else:
			results.append(";\n; CIL policy ")

		if mode == self.KERNEL:
			results.append("- Kernel objects only\n;\n")
		else:
			results.append("- Kernel + Userspace objects\n;\n")

		results.append("; This policy has been generated using the initial_sids, security_classes\n; and access_vectors flask files from: %s \n" % (flask_dir))
		results.append(";\n; The CIL policy file is built with three namespaces:\n;\tglobal - contains classes, sids, policycap, fsuse and object_r role.\n;\tunconfined - contains the unconfined user / role and allow rules.\n;\tsystem - contains the system user.\n\n")
		results.append(";\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
		results.append(";;;;;;;;;;;;;;;;;;;;;; global namespace ;;;;;;;;;;;;;;;;;;;;;\n")
		results.append(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;\n")
		#
		results.append("(handleunknown allow)\n\n")
		#
		# Set up the required default MLS information. This will be discarded 
		# by the CIL policy build process for a non-MLS policy. It will
		# allow the generation of a suitable default context.
		#
		if mls:
			results.append("(mls true)\n")
		else:
			results.append("(mls false)\n")

		results.append("(category c0)\n")
		results.append("(categoryorder (c0))\n")
		results.append("(sensitivity s0)\n")
		results.append("(sensitivityorder (s0))\n")
		results.append("(sensitivitycategory s0 (c0))\n")
		results.append("(levelrange low_low (systemlow systemlow))\n")
		results.append("(level systemlow (s0))\n\n")

		# print out the sids 
		for s in self.sids:
			results.append("(sid %s)\n" % (s))
		results.append("\n")

		results.append("(sidorder (")
		for s in self.sids:
			results.append("%s " % (s))
		results.append("))\n\n")

		# print out the commons
		for c in self.commons:
			results.append("(common %s (" % c)
			count = 0
			ps = []
			ps += self.common[c]
			for p in ps:
				results.append("%s " % ps[count])
				count += 1
			results.append("))\n\n")
		results.append("\n")

        # print out classorder
		results.append("(classorder (")
		for c in self.classes:
			if mode == self.KERNEL and self.userspace[c]:
				continue
			else:
				results.append("%s " % c)
		results.append("))\n\n")

		# print out the classes + permissions
		for c in self.classes:
			if mode == self.KERNEL and self.userspace[c]:
				continue
			else:
				if self.inherits.has_key(c):
					# Inherited so do (classcommon class inherit class)
					ps = []
					ps += self.common[self.inherits[c]]
					results.append("(classcommon %s %s)\n" % (c, self.inherits[c]))
					# Now only fill in non-inherited perms
					count = 0
					all_perms = []
					all_perms += self.vector[c]
					for a in all_perms:
						if count == 0:
							results.append("(class %s (%s " % (c, all_perms[count]))
						else:
							results.append("%s " % all_perms[count])
						count += 1

					if count == 0:
						results.append("(class %s ())\n" % c)
					else:
						results.append("))\n")
				else:
					# Do (class %s ( perm...)) that do not use inherited perms
					count = 0
					ps = []
					results.append("(class %s (" % c)
					ps += self.vector[c]
					for p in ps:
						results.append("%s " % ps[count])
						count += 1
					results.append("))\n")
			results.append("\n")

		# Add object_r
		results.append("; Declare role for object_r:\n(role  object_r)\n\n")

		# The context strings
		results.append("(context system_context (system.user unconfined.role unconfined.process low_low))\n")
		results.append("(context object_context (system.user object_r unconfined.object low_low))\n\n")

		# Initial sids with contexts
		for s in self.sids:
			if s == "kernel":
				results.append("(sidcontext %s system_context)\n" % (s))
			else:
				results.append("(sidcontext %s object_context)\n" % (s))
		results.append("\n")

		results.append("(fsuse xattr ext2 object_context)\n")
		results.append("(fsuse xattr ext3 object_context)\n")
		results.append("(fsuse xattr ext4 object_context)\n")
		results.append("(fsuse xattr jfs object_context)\n")
		results.append("(fsuse xattr xfs object_context)\n")
		results.append("(fsuse xattr reiserfs object_context)\n")
		results.append("(fsuse xattr jffs2 object_context)\n")
		results.append("(fsuse xattr gfs2 object_context)\n")
		results.append("(fsuse xattr lustre object_context)\n\n")

		results.append("(fsuse task eventpollfs object_context)\n")
		results.append("(fsuse task pipefs object_context)\n")
		results.append("(fsuse task sockfs object_context)\n\n")

		results.append("(fsuse trans devtmpfs object_context)\n")
		results.append("(fsuse trans mqueue object_context)\n")
		results.append("(fsuse trans devpts object_context)\n")
		results.append("(fsuse trans hugetlbfs object_context)\n")
		results.append("(fsuse trans tmpfs object_context)\n")
		results.append("(fsuse trans shm object_context)\n\n")

		results.append("(genfscon selinuxfs / object_context)\n")
		results.append("(genfscon securityfs / object_context)\n")
		results.append("(genfscon sysfs / object_context)\n")
		results.append("(genfscon cgroup / object_context)\n")
		results.append("(genfscon proc / object_context)\n\n")

		# CIL outputs these filecon statements, however currently use file_contexts file for demo.
		results.append("(filecon \"/\" any object_context)\n")
		results.append("(filecon \"/.*\" any object_context)\n")
		results.append("(filecon \"/boot\" any object_context)\n")
		results.append("(filecon \"/tmp\" any object_context)\n")
		results.append("(filecon \"/run\" any object_context)\n")
		results.append("(filecon \"/sys\" any object_context)\n")
		results.append("(filecon \"/dev\" any object_context)\n")
		results.append("(filecon \"/dev/shm\" any object_context)\n")
		results.append("(filecon \"/proc\" dir	object_context)\n\n")

		results.append(";\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
		results.append(";;;;;;; The global attribute \"unconfineddomain\" holds ;;;;;;;;;;;\n")
		results.append(";;;;; the unconfined.process and unconfined.object types ;;;;;;;;\n")
		results.append(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;\n")
		results.append("(typeattribute unconfineddomain)\n;\n")

		# Start unconfined namespace
		results.append(";\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
		results.append(";;;;;;;;;;;;;;;;;;;;; unconfined namespace ;;;;;;;;;;;;;;;;;;;;;;\n")
		results.append(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
		results.append("(block unconfined\n")
		results.append("\t(type process)\n")
		results.append("\t(type object)\n")
		results.append("\t(typeattributeset unconfineddomain (process object))\n")

		results.append("\t(role role)\n")
		results.append("\t(roletype role process)\n")
		results.append("\t(roletype object_r object)\n")

		results.append("\t(user user)\n")
		results.append("\t(userrole user role)\n")
		results.append("\t(userrange user (systemlow systemlow))\n")
		results.append("\t(userlevel user systemlow)\n")

		results.append("\t; Define SELinux user:\n")
		results.append("\t(selinuxuser name user low_low)\n")
		results.append("\t(selinuxuserdefault user low_low)\n")

		# print out an allow all rule for each class
		for c in self.classes:
			if mode == self.KERNEL and self.userspace[c]:
				continue
			else:
				results.append("\t(allow unconfineddomain self (%s (all)))\n" % (c))

		results.append(") ; End unconfined namespace\n\n")

		# Start system namespace
		results.append(";\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
		results.append(";;;;;;;;;;;;;;;;;;;;; system namespace ;;;;;;;;;;;;;;;;;;;;\n")
		results.append(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;\n")
		results.append("(block system\n")
		results.append("\t(user user)\n")
		results.append("\t(userrange user (systemlow systemlow))\n")
		results.append("\t(userlevel user systemlow)\n")
		results.append("\t(userrole user object_r)\n")
		results.append("\t(userrole user unconfined.role)\n")
		results.append(") ; End system namespace\n\n")
		results.append(";\n;;;;;;;;;;;;;;;;;;;;;;; End CIL Policy ;;;;;;;;;;;;;;;;;;;;;\n;\n")
		return results

#
# Create initial SID statements for a CIL Policy
#
	def createCilInitialSIDS(self, mode = USERSPACE):
		'''
		Create the initial SID statements for a CIL Policy.
		'''
		results = []

		results.append("; This file has been generated using the initial_sids flask file from:\n;\t%s\n" % (flask_dir))
		results.append(";\n; Create the initial SID statements and their order for a CIL Policy.\n;\n\n")

		results.append(";\n;;;;;;;;;;;;; Initial SIDs order from initial_sids ;;;;;;;;;;;;;\n;\n")
		results.append("(sidorder (")
		for s in self.sids:
			results.append("%s " % (s))
		results.append("))\n\n")

		# print out the sids 
		results.append(";\n;;;;;;;;;;;;; Initial SIDs ;;;;;;;;;;;;;\n;\n")
		for s in self.sids:
			results.append("(sid %s)\n" % (s))
		results.append("\n")

		return results

#
# Create the classes, permissions and classpermissionset statements for a CIL Policy
#
	def createCilClassPerms(self, mode = USERSPACE):
		'''
		Create the classes, permissions and classpermissionset statements for a CIL Policy.
		'''
		results = []

		if mode == self.KERNEL:
			results.append("; Kernel classes only\n;\n")
		else:
			results.append("; Kernel + Userspace classes\n;\n")

		results.append("; This file has been generated using the security_classes and access_vectors\n; flask files from: %s \n" % (flask_dir))
		results.append(";\n; Create the classes, permissions and classpermissionset statements for a CIL Policy.\n; The classpermissionsets generated are for all the permissions within each class i.e.:\n;\t(classpermission security_all_perms)\n;\t(classpermissionset security_all_perms (security (all)))\n;\n\n")

        # print out classorder
		results.append(";\n;;;;;;;;;;;;; Class order from security_classes ;;;;;;;;;;;;;\n;\n")
		results.append("(classorder (")
		for c in self.classes:
			if mode == self.KERNEL and self.userspace[c]:
				continue
			else:
				results.append("%s " % c)
		results.append("))\n\n")

		# print out the commons
		results.append(";\n;;;;;;;;;;;;; Common permissions ;;;;;;;;;;;;;\n;\n")
		for c in self.commons:
			results.append("(common %s (" % c)
			count = 0
			ps = []
			ps += self.common[c]
			for p in ps:
				results.append("%s " % ps[count])
				count += 1
			results.append("))\n\n")
		results.append("\n")

		# print out the classes + permissions
		results.append(";\n;;;;;;;;;; Classes, permissions and classpermissionsets ;;;;;;;;;;\n;\n")
		for c in self.classes:
			if mode == self.KERNEL and self.userspace[c]:
				continue
			else:
				if self.inherits.has_key(c):
					# Inherited so do (classcommon class inherit class)
					ps = []
					ps += self.common[self.inherits[c]]
					results.append("(classcommon %s %s)\n" % (c, self.inherits[c]))
					# Now only fill in non-inherited perms
					count = 0
					all_perms = []
					all_perms += self.vector[c]
					for a in all_perms:
						if count == 0:
							results.append("(class %s (%s " % (c, all_perms[count]))
						else:
							results.append("%s " % all_perms[count])
						count += 1

					if count == 0:
						results.append("(class %s ())\n" % c)
					else:
						results.append("))\n")
				else:
					# Do (class %s ( perm...)) that do not use inherited perms
					count = 0
					ps = []
					results.append("(class %s (" % c)
					ps += self.vector[c]
					for p in ps:
						results.append("%s " % ps[count])
						count += 1
					results.append("))\n")
				results.append("(classpermission %s_all_perms)\n" % c)
				results.append("(classpermissionset %s_all_perms (%s (all)))\n" % (c, c))
			results.append("\n")
		return results

#
# Create a Kernel Policy Language Policy
#
	def createPolicy(self, mode = USERSPACE):
		'''
		Creates a non-MLS or MLS Kernel Policy Langauge file for processing by checkpolicy
		'''
		results = []
		se_user = "unconfined_u"
		se_role = "unconfined_r"
		se_type = "unconfined_t"
		sys_user = "system_u"
		obj_role = "object_r"

		if mls:
			results.append("#\n# MLS Kernel Policy Language policy ")
		else:
			results.append("#\n# Kernel Policy Language policy ")

		if mode == self.KERNEL:
			results.append("- Kernel objects only\n#\n")
		else:
			results.append("- Kernel + Userspace objects\n#\n")

		results.append("# This policy has been generated using the initial_sids, security_classes\n# and access_vectors flask files from: %s\n\n" % (flask_dir))

#	print out the classes
		for c in self.classes:
			if mode == self.KERNEL and self.userspace[c]:
				continue
			else:
				results.append("class %s\n" % c)

		results.append("\n")

#	 print out the sids 
		for s in self.sids:
			results.append("sid %s\n" % (s))
		results.append("\n")

		# print out the commons
		for c in self.commons:
			results.append("common %s {" % c)
			count = 0
			ps = []
			ps += self.common[c]
			for p in ps:
				results.append("%s " % ps[count])
				count += 1
			results.append("}\n\n")
		results.append("\n")

		# print out the classes + permissions
		for c in self.classes:
			if mode == self.KERNEL and self.userspace[c]:
				continue
			else:
				if self.inherits.has_key(c):
					# Inherited so do (classcommon class inherit class)
					ps = []
					ps += self.common[self.inherits[c]]
					results.append("class %s inherits %s" % (c, self.inherits[c]))
					# Now only fill in non-inherited perms
					count = 0
					all_perms = []
					all_perms += self.vector[c]
					for a in all_perms:
						if count == 0:
							results.append(" { %s " % all_perms[count])
						else:
							results.append("%s " % all_perms[count])
						count += 1

					if count == 0:
						results.append("\n")
					else:
						results.append("}\n")
				else:
					# Do (class %s ( perm...)) that do not use inherited perms
					count = 0
					ps = []
					results.append("class %s { " % c)
					ps += self.vector[c]
					for p in ps:
						results.append("%s " % ps[count])
						count += 1
					results.append("}\n")
			results.append("\n")

#	Check for MLS policy
		if mls:
			results.append("sensitivity s0;\n")
			results.append("dominance { s0 }\n")
			results.append("category c0;\n")
			results.append("level s0:c0;\n")
			results.append("# checkpolicy always wants an mlsconstrain or "
				"fails to build.\n#This is a simple one:\n")
			results.append("mlsconstrain { filesystem } { relabelto } ( h1 dom h2 );\n\n")

#	Example policycap for demo
		results.append("#\n# This policycap statement will be used in a "
				"netlabel module exercise\n# to show network_peer_controls. "
				"For now comment out:\n#policycap network_peer_controls;\n\n")

#	types and roles
		results.append("type  %s;\n" % (se_type))
		results.append("role  %s;\n" % (se_role))
		results.append("role %s types { %s };\n\n" % (se_role, se_type))

#	print out allow rules
		for c in self.classes:
			if mode == self.KERNEL and self.userspace[c]:
				continue
			else:
				results.append("allow %s %s:%s { " % (se_type, se_type, c))
				count = 0
				ps = []
				if self.inherits.has_key(c):
					ps += self.common[self.inherits[c]]
				ps += self.vector[c]
				for p in ps: 
					results.append("%s " % ps[count])
					count += 1
				results.append("};\n")
		results.append("\n")

#	print out user statements
		if mls:
			results.append("user %s roles { %s } level s0 range s0;\n" % (se_user, se_role))
			results.append("user %s roles { %s } level s0 range s0;\n" % (sys_user, se_role))
		else:
			results.append("user %s roles { %s };\n" % (se_user, se_role))
			results.append("user %s roles { %s };\n" % (sys_user, se_role))
		results.append("\n");

#	An example constrain for demo
		results.append("#\n# This role constraint statement will be used to show "
			"limiting\n# a role transition in the external gateway. For now "
			"comment out:\n#constrain process transition (r1 == r2);\n\n")

		if mls:
#			Initial sids with contexts
			count = 0
			for s in self.sids:
				count += 1
				if s == "kernel":
					results.append("sid %s %s:%s:%s:s0\n" % (s, sys_user, se_role, se_type))
				else:
					results.append("sid %s %s:%s:%s:s0\n" % (s, sys_user, obj_role, se_type))

			results.append("\n")

			results.append("fs_use_xattr ext2 %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr ext3 %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr ext4 %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr jfs %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr xfs %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr reiserfs %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr jffs2 %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr gfs2 %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr lustre %s:%s:%s:s0;\n\n" % (sys_user, obj_role, se_type))
	
			results.append("fs_use_task eventpollfs %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_task pipefs %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_task sockfs %s:%s:%s:s0;\n\n" % (sys_user, obj_role, se_type))

			results.append("fs_use_trans mqueue %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_trans devpts %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_trans hugetlbfs %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_trans tmpfs %s:%s:%s:s0;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_trans shm %s:%s:%s:s0;\n\n" % (sys_user, obj_role, se_type))
			results.append("genfscon selinuxfs / %s:%s:%s:s0\n" % (sys_user, obj_role, se_type))
			results.append("genfscon proc / %s:%s:%s:s0\n" % (sys_user, obj_role, se_type))

		else:
#			 default sids 
			count = 0
			for s in self.sids:
				count += 1
				if s == "kernel":
					results.append("sid %s %s:%s:%s\n" % (s, sys_user, se_role, se_type))
				else:
					results.append("sid %s %s:%s:%s\n" % (s, sys_user, obj_role, se_type))
			results.append("\n")


			results.append("fs_use_xattr ext2 %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr ext3 %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr ext4 %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr jfs %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr xfs %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr reiserfs %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr jffs2 %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr gfs2 %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_xattr lustre %s:%s:%s;\n\n" % (sys_user, obj_role, se_type))
	
			results.append("fs_use_task eventpollfs %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_task pipefs %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_task sockfs %s:%s:%s;\n\n" % (sys_user, obj_role, se_type))

			results.append("fs_use_trans mqueue %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_trans devpts %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_trans hugetlbfs %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_trans tmpfs %s:%s:%s;\n" % (sys_user, obj_role, se_type))
			results.append("fs_use_trans shm %s:%s:%s;\n\n" % (sys_user, obj_role, se_type))
			results.append("genfscon selinuxfs / %s:%s:%s\n" % (sys_user, obj_role, se_type))
			results.append("genfscon proc / %s:%s:%s\n" % (sys_user, obj_role, se_type))

		return results


def usage():
	'''
	Returns the usage string.
	'''
	usage  = 'Usage: %s [-k] [-M] [-c|-i|-p|-s] -d flask_directory -o output_file\n' % os.path.basename(sys.argv[0])
	usage += '\n'
	usage += ' -k\tOutput kernel classes only (exclude # userspace entries in the\n\tsecurity_classes file).\n'
	usage += ' -M\tOutput an MLS policy.\n'
	usage += ' -c\tOutput a policy in CIL language (otherwise gererate a kernel policy\n\tlanguage policy).\n'
	usage += ' -p\tOutput a file containing class and classpermissionsets + their order\n\tfor use by CIL policies.\n'
	usage += ' -s\tOutput a file containing initial SIDs + their order for use by\n\tCIL policies.\n'
	usage += ' -i\tOutput a header file containing class/permissions for use by\n\tselinux_set_mapping(3).\n'
	usage += ' -o\tThe output file that will contain the policy source or header file.\n'
#	usage += ' -r\tReference Policy source base directory, for example:\n\t\t/etc/selinux/targeted/src/policy\n\tas the path to the initial_sids, security_classes and access_vectors\n\tfiles will be added to this base.\n\n'
	usage += ' -d\tDirectory containing the initial_sids, security_classes and\n\taccess_vectors Flask files.\n\n'
	return usage

########## MAIN ##########
if __name__ == '__main__':
	
	# Parse command line args
	try:
		opts, args = getopt.getopt(sys.argv[1:], 'o:d:Mckhips')
	except getopt.GetoptError:
		print(usage())
		sys.exit(2)
	
	mls = 0
	cil = 0
	include = 0
	class_perm = 0
	sids = 0
	flask_dir = None
	outf = None
	mode = None
	for o, a in opts:
		if o in ('-h'):
			print(usage())
			sys.exit(0)
		elif o in ('-M'):
			mls = 1
		elif o in ('-c'):
			cil = 1
		elif o in ('-i'):
			include = 1
		elif o in ('-p'):
			class_perm = 1
		elif o in ('-s'):
			sids = 1
		elif o in ('-o'):
			outf = a
		elif o in ('-d'):
			flask_dir = a
		elif o in ('-k'):
			mode = Flask.KERNEL
		else:
			print(usage())
			sys.exit(2)

	if	flask_dir == None or \
		outf == None:
		print(usage())
		sys.exit(2)

	try:
		f = Flask()
#		f.parseSids(flask_dir + "/policy/flask/initial_sids")
#		f.parseClasses(flask_dir + "/policy/flask/security_classes")
#		f.parseVectors(flask_dir + "/policy/flask/access_vectors")
		f.parseSids(flask_dir + "/initial_sids")
		f.parseClasses(flask_dir + "/security_classes")
		f.parseVectors(flask_dir + "/access_vectors")

	# Open the file and then create the requested policy source file
		of = open(outf, 'w')
		if include == 1:
			of.writelines(f.createPolicyHdr(mode))
			print("Output header file")
		elif cil == 1:
			of.writelines(f.createCilPolicy(mode))
			print("Output CIL policy")
		elif class_perm == 1:
			of.writelines(f.createCilClassPerms(mode))
			print("Output CIL class permission sets")
		elif sids == 1:
			of.writelines(f.createCilInitialSIDS(mode))
			print("Output CIL initial SIDs")
		else:
			of.writelines(f.createPolicy(mode))
			print("Output Kernel Language policy")
		of.close()

	except Exception, e:
		print(e)
		sys.exit(2)
