|
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)))
|