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