Chris PeBenito c04f2a
#!/usr/bin/python
Chris PeBenito c04f2a
Chris PeBenito c04f2a
# Author: Donald Miner <dminer@tresys.com>
Chris PeBenito c04f2a
#
Chris PeBenito 6b873c
# Copyright (C) 2005 Tresys Technology, LLC
Chris PeBenito c04f2a
#      This program is free software; you can redistribute it and/or modify
Chris PeBenito c04f2a
#      it under the terms of the GNU General Public License as published by
Chris PeBenito c04f2a
#      the Free Software Foundation, version 2.
Chris PeBenito c04f2a
Chris PeBenito c04f2a
Chris PeBenito c04f2a
"""
Chris PeBenito c04f2a
	This script generates an object class perm definition file.
Chris PeBenito c04f2a
"""
Chris PeBenito c04f2a
Chris PeBenito c04f2a
import sys
Chris PeBenito c04f2a
Chris PeBenito c04f2a
USERSPACE_CLASS = "userspace"
Chris PeBenito c04f2a
Chris PeBenito c04f2a
class Class:
Chris PeBenito c04f2a
	"""
Chris PeBenito c04f2a
	This object stores an access vector class.
Chris PeBenito c04f2a
	"""
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	def __init__(self, name, perms, common):
Chris PeBenito c04f2a
		# The name of the class.
Chris PeBenito c04f2a
		self.name = name
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# A list of permissions the class contains.
Chris PeBenito c04f2a
		self.perms = perms
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# True if the class is declared as common, False if not.
Chris PeBenito c04f2a
		self.common = common
Chris PeBenito c04f2a
Chris PeBenito 6e0542
def get_perms(name, av_db, common):
Chris PeBenito c04f2a
	"""
Chris PeBenito c04f2a
	Returns the list of permissions contained within an access vector
Chris PeBenito c04f2a
	class that is stored in the access vector database av_db.
Chris PeBenito c04f2a
	Returns an empty list if the object name is not found.
Chris PeBenito 6e0542
	Specifiy whether get_perms is to return the class or the
Chris PeBenito 6e0542
	common set of permissions with the boolean value 'common',
Chris PeBenito 6e0542
	which is important in the case of having duplicate names (such as
Chris PeBenito 6e0542
	class file and common file).
Chris PeBenito c04f2a
	"""
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	# Traverse through the access vector database and try to find the
Chris PeBenito c04f2a
	#  object with the name passed.
Chris PeBenito c04f2a
	for obj in av_db:
Chris PeBenito 6e0542
		if obj.name == name and obj.common == common:
Chris PeBenito c04f2a
			return obj.perms
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	return []
Chris PeBenito c04f2a
Chris PeBenito c04f2a
def get_av_db(file_name):
Chris PeBenito c04f2a
	"""
Chris PeBenito c04f2a
	Returns an access vector database generated from the file file_name.
Chris PeBenito c04f2a
	"""
Chris PeBenito a6df70
	# This function takes a file, reads the data, parses it and returns
Chris PeBenito a6df70
	#  a list of access vector classes.
Chris PeBenito a6df70
	# Reading into av_data:
Chris PeBenito a6df70
	#  The file specified will be read line by line. Each line will have
Chris PeBenito a6df70
	#   its comments removed. Once comments are removed, each 'word' (text
Chris PeBenito a6df70
	#   seperated by whitespace) and braces will be split up into seperate
Chris PeBenito a6df70
	#   strings and appended to the av_data list, in the order they were
Chris PeBenito a6df70
	#   read.
Chris PeBenito a6df70
	# Parsing av_data:
Chris PeBenito a6df70
	#  Parsing is done using a queue implementation of the av_data list.
Chris PeBenito a6df70
	#   Each time a word is used, it is dequeued afterwards. Each loop in
Chris PeBenito a6df70
	#   the while loop below will read in key words and dequeue expected
Chris PeBenito a6df70
	#   words and values. At the end of each loop, a Class containing the
Chris PeBenito a6df70
	#   name, permissions and whether it is a common or not will be appended
Chris PeBenito a6df70
	#   to the database. Lots of errors are caught here, almost all checking
Chris PeBenito a6df70
	#   if a token is expected but EOF is reached.
Chris PeBenito a6df70
	# Now the list of Class objects is returned.
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	av_file = open(file_name, "r")
Chris PeBenito c04f2a
	av_data = []
Chris PeBenito c04f2a
	# Read the file and strip out comments on the way.
Chris PeBenito c04f2a
	# At the end of the loop, av_data will contain a list of individual
Chris PeBenito c04f2a
	#  words. i.e. ['common', 'file', '{', ...]. All comments and whitespace
Chris PeBenito c04f2a
	#  will be gone.
Chris PeBenito c04f2a
	while True:
Chris PeBenito c04f2a
		av_line = av_file.readline()
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# If EOF has been reached:
Chris PeBenito c04f2a
		if not av_line:
Chris PeBenito c04f2a
			break
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Check if there is a comment, and if there is, remove it.
Chris PeBenito c04f2a
		comment_index = av_line.find("#")
Chris PeBenito c04f2a
		if comment_index != -1:
Chris PeBenito c04f2a
			av_line = av_line[:comment_index]
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Pad the braces with whitespace so that they are split into
Chris PeBenito c04f2a
		#  their own word. It doesn't matter if there will be extra
Chris PeBenito c04f2a
		#  white space, it'll get thrown away when the string is split.
Chris PeBenito c04f2a
		av_line.replace("{"," { ")
Chris PeBenito c04f2a
		av_line.replace("}"," } ")		
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Split up the words on the line and add it to av_data.
Chris PeBenito c04f2a
		av_data += av_line.split()
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	av_file.close()
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	# Parsing the file:
Chris PeBenito c04f2a
	# The implementation of this parse is a queue. We use the list of words
Chris PeBenito c04f2a
	#  from av_data and use the front element, then dequeue it. Each
Chris PeBenito c04f2a
	#  loop of this while is a common or class declaration. Several
Chris PeBenito c04f2a
	#  expected tokens are parsed and dequeued out of av_data for each loop.
Chris PeBenito a6df70
	# At the end of the loop, database will contain a list of Class objects.
Chris PeBenito a6df70
	#  i.e. [Class('name',['perm1','perm2',...],'True'), ...]
Chris PeBenito c04f2a
	# Dequeue from the beginning of the list until av_data is empty:
Chris PeBenito c04f2a
	database = []
Chris PeBenito c04f2a
	while len(av_data) != 0:
Chris PeBenito c04f2a
		# At the beginning of every loop, the next word should be
Chris PeBenito c04f2a
		#  "common" or "class", meaning that each loop is a common
Chris PeBenito c04f2a
		#  or class declaration.
Chris PeBenito c04f2a
		# av_data = av_data[1:] removes the first element in the
Chris PeBenito c04f2a
		#  list, this is what is dequeueing data.
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Figure out whether the next class will be a common or a class.
Chris PeBenito c04f2a
		if av_data[0] == "class":
Chris PeBenito c04f2a
			common = False
Chris PeBenito c04f2a
		elif av_data[0] == "common":
Chris PeBenito c04f2a
			common = True
Chris PeBenito c04f2a
		else:
Chris PeBenito c04f2a
			error("Unexpected token in file " + file_name + ": "\
Chris PeBenito c04f2a
				+ av_data[0] + ".")
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Dequeue the "class" or "common" key word.
Chris PeBenito c04f2a
		av_data = av_data[1:]
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		if len(av_data) == 0:
Chris PeBenito c04f2a
			error("Missing token in file " + file_name + ".")
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Get and dequeue the name of the class or common.
Chris PeBenito c04f2a
		name = av_data[0]
Chris PeBenito c04f2a
		av_data = av_data[1:]
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Retrieve the permissions inherited from a common set:
Chris PeBenito c04f2a
		perms = []
Chris PeBenito c04f2a
		# If the object we are working with is a class, since only
Chris PeBenito c04f2a
		#  classes inherit:
Chris PeBenito c04f2a
		if common == False:
Chris PeBenito c04f2a
			if len(av_data) == 0:
Chris PeBenito c04f2a
				error("Missing token in file " + file_name + ".")
Chris PeBenito c04f2a
Chris PeBenito c04f2a
			# If the class inherits from something else:
Chris PeBenito c04f2a
			if av_data[0] == "inherits":
Chris PeBenito c04f2a
				# Dequeue the "inherits" key word.
Chris PeBenito c04f2a
				av_data = av_data[1:]
Chris PeBenito c04f2a
Chris PeBenito c04f2a
				if len(av_data) == 0:
Chris PeBenito c04f2a
					error("Missing token in file "\
Chris PeBenito c04f2a
						+ file_name + " for " +\
Chris PeBenito c04f2a
						keyword + " " + name + ".")
Chris PeBenito c04f2a
Chris PeBenito c04f2a
				# av_data[0] is the name of the parent.
Chris PeBenito c04f2a
				# Append the permissions of the parent to
Chris PeBenito c04f2a
				#  the current class' permissions.
Chris PeBenito 6e0542
				perms += get_perms(av_data[0], database, True)
Chris PeBenito 6e0542
Chris PeBenito c04f2a
				# Dequeue the name of the parent.
Chris PeBenito c04f2a
				av_data = av_data[1:]
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Retrieve the permissions defined with this set.
Chris PeBenito c04f2a
		if len(av_data) > 0 and av_data[0] == "{":
Chris PeBenito c04f2a
			# Dequeue the "{"
Chris PeBenito c04f2a
			av_data = av_data[1:]
Chris PeBenito c04f2a
Chris PeBenito c04f2a
			# Keep appending permissions until a close brace is
Chris PeBenito c04f2a
			#  found.
Chris PeBenito c04f2a
			while av_data[0] != "}":
Chris PeBenito c04f2a
				if av_data[0] == "{":
Chris PeBenito c04f2a
					error("Extra '{' in file " +\
Chris PeBenito c04f2a
						 file_name + ".")
Chris PeBenito c04f2a
Chris PeBenito c04f2a
				# Add the permission name.
Chris PeBenito c04f2a
				perms.append(av_data[0])
Chris PeBenito c04f2a
Chris PeBenito c04f2a
				# Dequeue the permission name.
Chris PeBenito c04f2a
				av_data = av_data[1:]
Chris PeBenito c04f2a
Chris PeBenito c04f2a
				if len(av_data) == 0:
Chris PeBenito c04f2a
					error("Missing token '}' in file "\
Chris PeBenito c04f2a
						+ file_name + ".")
Chris PeBenito c04f2a
Chris PeBenito c04f2a
			# Dequeue the "}"
Chris PeBenito c04f2a
			av_data = av_data[1:]
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Add the new access vector class to the database.
Chris PeBenito c04f2a
		database.append(Class(name, perms, common))
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	return database
Chris PeBenito c04f2a
Chris PeBenito c04f2a
def get_sc_db(file_name):
Chris PeBenito c04f2a
	"""
Chris PeBenito c04f2a
	Returns a security class database generated from the file file_name.
Chris PeBenito c04f2a
	"""
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	# Read the file then close it.
Chris PeBenito c04f2a
	sc_file = open(file_name)
Chris PeBenito c04f2a
	sc_data = sc_file.readlines()
Chris PeBenito c04f2a
	sc_file.close()
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	# For each line in the security classes file, add the name of the class
Chris PeBenito c04f2a
	#  and whether it is a userspace class or not to the security class
Chris PeBenito c04f2a
	#  database.
Chris PeBenito c04f2a
	database = []
Chris PeBenito c04f2a
	for line in sc_data:
Chris PeBenito c04f2a
		line = line.lstrip()
Chris PeBenito c04f2a
		# If the line is empty or the entire line is a comment, skip.
Chris PeBenito c04f2a
		if line == "" or line[0] == "#":
Chris PeBenito c04f2a
			continue
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Check if the comment to the right of the permission matches
Chris PeBenito c04f2a
		#  USERSPACE_CLASS.
Chris PeBenito c04f2a
		comment_index = line.find("#")
Chris PeBenito c04f2a
		if comment_index != -1 and line[comment_index+1:].strip() == USERSPACE_CLASS:
Chris PeBenito c04f2a
			userspace = True
Chris PeBenito c04f2a
		else:
Chris PeBenito c04f2a
			userspace = False
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# All lines should be in the format "class NAME", meaning
Chris PeBenito c04f2a
		#  it should have two tokens and the first token should be
Chris PeBenito c04f2a
		#  "class".
Chris PeBenito c04f2a
		split_line = line.split()
Chris PeBenito c04f2a
		if len(split_line) < 2 or split_line[0] != "class":
Chris PeBenito c04f2a
			error("Wrong syntax: " + line)
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Add the class's name (split_line[1]) and whether it is a
Chris PeBenito c04f2a
		#  userspace class or not to the database.
Chris PeBenito c04f2a
		# This is appending a tuple of (NAME,USERSPACE), where NAME is
Chris PeBenito c04f2a
		#  the name of the security class and USERSPACE is True if
Chris PeBenito c04f2a
		#  if it has "# USERSPACE_CLASS" on the end of the line, False
Chris PeBenito c04f2a
		#  if not.
Chris PeBenito c04f2a
		database.append((split_line[1], userspace))
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	return database
Chris PeBenito c04f2a
Chris PeBenito c04f2a
def gen_class_perms(av_db, sc_db):
Chris PeBenito c04f2a
	"""
Chris PeBenito c04f2a
	Generates a class permissions document and returns it.
Chris PeBenito c04f2a
	"""
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	# Define class template:
Chris PeBenito c04f2a
	class_perms_line = "define(`all_%s_perms',`{ %s}')\n"
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	# Generate the defines for the individual class permissions.
Chris PeBenito c04f2a
	class_perms = ""
Chris PeBenito c04f2a
	for obj in av_db:
Chris PeBenito c04f2a
		# Don't output commons
Chris PeBenito c04f2a
		if obj.common == True:
Chris PeBenito c04f2a
			continue
Chris PeBenito c04f2a
Chris PeBenito 6e0542
		# Get the list of permissions from the specified class.
Chris PeBenito 6e0542
		perms = get_perms(obj.name, av_db, False)
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Merge all the permissions into one string with one space
Chris PeBenito c04f2a
		#  padding.
Chris PeBenito c04f2a
		perm_str = ""
Chris PeBenito c04f2a
		for perm in perms:
Chris PeBenito c04f2a
			perm_str += perm + " "
Chris PeBenito c04f2a
Chris PeBenito c04f2a
		# Add the line to the class_perms
Chris PeBenito c04f2a
		class_perms += class_perms_line % (obj.name, perm_str)
Chris PeBenito c04f2a
	class_perms += "\n"
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	# Generate the kernel_class_perms and userspace_class_perms sets.
Chris PeBenito c04f2a
	class_line = "\tclass %s all_%s_perms;\n"
Chris PeBenito c04f2a
	kernel_class_perms = "define(`all_kernel_class_perms',`\n"
Chris PeBenito c04f2a
	userspace_class_perms = "define(`all_userspace_class_perms',`\n"
Chris PeBenito c04f2a
	# For each (NAME,USERSPACE) tuple, add the class to the appropriate
Chris PeBenito c04f2a
	# class permission set.
Chris PeBenito c04f2a
	for name, userspace in sc_db:
Chris PeBenito c04f2a
		if userspace:
Chris PeBenito c04f2a
			userspace_class_perms += class_line % (name, name)
Chris PeBenito c04f2a
		else:
Chris PeBenito c04f2a
			kernel_class_perms += class_line % (name, name)
Chris PeBenito c04f2a
	kernel_class_perms += "')\n\n"
Chris PeBenito c04f2a
	userspace_class_perms += "')\n"
Chris PeBenito c04f2a
Chris PeBenito c04f2a
	# Throw all the strings together and return the string.
Chris PeBenito c04f2a
	return class_perms + kernel_class_perms + userspace_class_perms
Chris PeBenito c04f2a
Chris PeBenito c04f2a
def error(error):
Chris PeBenito c04f2a
	"""
Chris PeBenito c04f2a
	Print an error message and exit.
Chris PeBenito c04f2a
	"""
Chris PeBenito c04f2a
Chris PeBenito c04f2a
        sys.stderr.write("%s exiting for: " % sys.argv[0])
Chris PeBenito c04f2a
        sys.stderr.write("%s\n" % error)
Chris PeBenito c04f2a
        sys.stderr.flush()
Chris PeBenito c04f2a
        sys.exit(1)
Chris PeBenito c04f2a
Chris PeBenito c04f2a
# MAIN PROGRAM
Chris PeBenito c04f2a
app_name = sys.argv[0]
Chris PeBenito c04f2a
Chris PeBenito c04f2a
if len(sys.argv) != 3:
Chris PeBenito c04f2a
	error("Incorrect input.\nUsage: " + sys.argv[0] + " access_vectors security_classes" )
Chris PeBenito c04f2a
Chris PeBenito c04f2a
# argv[1] is the access vector file.
Chris PeBenito c04f2a
av_file = sys.argv[1]
Chris PeBenito c04f2a
Chris PeBenito c04f2a
# argv[2] is the security class file.
Chris PeBenito c04f2a
sc_file = sys.argv[2]
Chris PeBenito c04f2a
Chris PeBenito c04f2a
# Output the class permissions document.
Chris PeBenito c04f2a
sys.stdout.write(gen_class_perms(get_av_db(av_file), get_sc_db(sc_file)))