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