Chris PeBenito 95db42
#!/usr/bin/python
Chris PeBenito 95db42
Chris PeBenito 95db42
#  Author: Donald Miner <dminer@tresys.com>
Chris PeBenito 95db42
#
Chris PeBenito 95db42
# Copyright (C) 2003 - 2005 Tresys Technology, LLC
Chris PeBenito 95db42
#      This program is free software; you can redistribute it and/or modify
Chris PeBenito 95db42
#      it under the terms of the GNU General Public License as published by
Chris PeBenito 95db42
#      the Free Software Foundation, version 2.
Chris PeBenito 95db42
Chris PeBenito 95db42
"""
Chris PeBenito 95db42
	This script generates XML documentation information for layers specified
Chris PeBenito 95db42
	by the user.
Chris PeBenito 95db42
"""
Chris PeBenito 95db42
Chris PeBenito 95db42
import sys
Chris PeBenito 95db42
import os
Chris PeBenito 95db42
import glob
Chris PeBenito 4f9f30
import re
Chris PeBenito 95db42
Chris PeBenito 95db42
# GLOBALS
Chris PeBenito 95db42
Chris PeBenito 4f9f30
# Default values of command line arguments:
Chris PeBenito 95db42
directory = "./"
Chris PeBenito 95db42
warn = False
Chris PeBenito 95db42
meta = "metadata"
Chris PeBenito 95db42
layers = []
Chris PeBenito 95db42
tunable_files = []
Chris PeBenito 4f9f30
bool_files = []
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
# Pre compiled regular expressions:
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
# Matches either an interface or a template declaration. Will give the tuple:
Chris PeBenito 4f9f30
#	("interface" or "template", name)
Chris PeBenito 4f9f30
# Some examples:
Chris PeBenito 4f9f30
#	"interface(`kernel_read_system_state',`"
Chris PeBenito 4f9f30
#	 -> ("interface", "kernel_read_system_state")
Chris PeBenito 4f9f30
#	"template(`base_user_template',`"
Chris PeBenito 4f9f30
#	 -> ("template", "base_user_template")
Chris PeBenito 4f9f30
INTERFACE = re.compile("^\s*(interface|template)\(`([A-Za-z0-9_]*)'")
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
# Matches either a gen_bool or a gen_tunable statement. Will give the tuple:
Chris PeBenito 4f9f30
#	("tunable" or "bool", name, "true" or "false")
Chris PeBenito 4f9f30
# Some examples:
Chris PeBenito 4f9f30
#	"gen_bool(secure_mode, false)"
Chris PeBenito 4f9f30
#	 -> ("bool", "secure_mode", "false")
Chris PeBenito 4f9f30
#	"gen_tunable(allow_kerberos, false)"
Chris PeBenito 4f9f30
#	 -> ("tunable", "allow_kerberos", "false")
Chris PeBenito 4f9f30
BOOLEAN = re.compile("^\s*gen_(tunable|bool)\(\s*([A-Za-z0-9_]*)\s*,\s*(true|false)\s*\)")
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
# Matches a XML comment in the policy, which is defined as any line starting
Chris PeBenito 4f9f30
#  with two # and at least one character of white space. Will give the single
Chris PeBenito 4f9f30
#  valued tuple:
Chris PeBenito 4f9f30
#	("comment")
Chris PeBenito 4f9f30
# Some Examples:
Chris PeBenito 4f9f30
#	"## <summary>"
Chris PeBenito 4f9f30
#	 -> ("<summary>")
Chris PeBenito 4f9f30
#	"##		The domain allowed access.	"
Chris PeBenito 4f9f30
#	 -> ("The domain allowed access.")
Chris PeBenito 4f9f30
XML_COMMENT = re.compile("^##\s+(.*?)\s*$")
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
# FUNCTIONS
Chris PeBenito 95db42
def getModuleXML(file_name):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Returns the XML data for a module in a list, one line per list item.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Try to open the file, if it cant, just ignore it.
Chris PeBenito 95db42
	try:
Chris PeBenito 95db42
		module_file = open(file_name, "r")
Chris PeBenito 95db42
		module_code = module_file.readlines()
Chris PeBenito 95db42
		module_file.close()
Chris PeBenito 95db42
	except:
Chris PeBenito 95db42
		warning("cannot open file %s for read, skipping" % file_name)
Chris PeBenito 95db42
		return []
Chris PeBenito 95db42
Chris PeBenito 95db42
	module_buf = []
Chris PeBenito 95db42
Chris PeBenito e81f02
	# Infer the module name, which is the base of the file name.
Chris PeBenito e81f02
	module_buf.append("<module name=\"%s\">\n" 
Chris PeBenito e81f02
		% os.path.splitext(os.path.split(file_name)[-1])[0])
Chris PeBenito 95db42
Chris PeBenito 95db42
	temp_buf = []
Chris PeBenito 4f9f30
	interface = None
Chris PeBenito 95db42
Chris PeBenito 4f9f30
	# finding_header is a flag to denote whether we are still looking
Chris PeBenito 4f9f30
	#  for the XML documentation at the head of the file.
Chris PeBenito 4f9f30
	finding_header = True
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
	# Get rid of whitespace at top of file
Chris PeBenito 4f9f30
	while(module_code and module_code[0].isspace()):
Chris PeBenito 4f9f30
		module_code = module_code[1:]
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Go line by line and figure out what to do with it.
Chris PeBenito 95db42
	for line in module_code:
Chris PeBenito 4f9f30
		if finding_header:
Chris PeBenito 4f9f30
			# If there is a XML comment, add it to the temp buffer.
Chris PeBenito 4f9f30
			comment = XML_COMMENT.match(line)
Chris PeBenito 4f9f30
			if comment:
Chris PeBenito 4f9f30
				temp_buf.append(comment.group(1) + "\n")
Chris PeBenito 95db42
				continue
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
			# Once a line that is not an XML comment is reached,
Chris PeBenito 4f9f30
			#  either put the XML out to module buffer as the
Chris PeBenito 4f9f30
			#  module's documentation, or attribute it to an
Chris PeBenito 4f9f30
			#  interface/template.
Chris PeBenito 4f9f30
			elif temp_buf:
Chris PeBenito 4f9f30
				finding_header = False
Chris PeBenito 4f9f30
				interface = INTERFACE.match(line)
Chris PeBenito 4f9f30
				if not interface:
Chris PeBenito f8838e
					module_buf += temp_buf
Chris PeBenito 4f9f30
					temp_buf = []
Chris PeBenito 4f9f30
					continue
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
		# Skip over empty lines
Chris PeBenito 4f9f30
		if line.isspace():
Chris PeBenito 4f9f30
			continue
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
		# Grab a comment and add it to the temprorary buffer, if it
Chris PeBenito 4f9f30
		#  is there.
Chris PeBenito 4f9f30
		comment = XML_COMMENT.match(line)
Chris PeBenito 4f9f30
		if comment:
Chris PeBenito 4f9f30
			temp_buf.append(comment.group(1) + "\n")
Chris PeBenito 4f9f30
			continue
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
		# Grab the interface information. This is only not true when
Chris PeBenito 4f9f30
		#  the interface is at the top of the file and there is no
Chris PeBenito 4f9f30
		#  documentation for the module.
Chris PeBenito 4f9f30
		if not interface:
Chris PeBenito 4f9f30
			interface = INTERFACE.match(line)
Chris PeBenito 4f9f30
		if interface:
Chris PeBenito 4f9f30
			# Add the opening tag for the interface/template
Chris PeBenito 4f9f30
			module_buf.append("<%s name=\"%s\">\n" % interface.groups())
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
			# Add all the comments attributed to this interface to
Chris PeBenito 4f9f30
			#  the module buffer.
Chris PeBenito 4f9f30
			if temp_buf:
Chris PeBenito 4f9f30
				module_buf += temp_buf
Chris PeBenito e81f02
				temp_buf = []
Chris PeBenito 95db42
Chris PeBenito 4f9f30
			# Add default summaries and parameters so that the
Chris PeBenito 4f9f30
			#  DTD is happy.
Chris PeBenito 4f9f30
			else:
Chris PeBenito 4f9f30
				module_buf.append("<summary>\n")
Chris PeBenito 4f9f30
				module_buf.append("Summary is missing!\n")
Chris PeBenito 4f9f30
				module_buf.append("</summary>\n")
Chris PeBenito 4f9f30
				module_buf.append("<param name=\"?\">\n")
Chris PeBenito 4f9f30
				module_buf.append("Parameter descriptions are missing!\n")
Chris PeBenito 4f9f30
				module_buf.append("</param>\n")
Chris PeBenito e694b5
Chris PeBenito 4f9f30
			# Close the interface/template tag.
Chris PeBenito 4f9f30
			module_buf.append("</%s>\n" % interface.group(1))
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
			interface = None
Chris PeBenito 4f9f30
			continue
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
	# If the file just had a header, add the comments to the module buffer.
Chris PeBenito 4f9f30
	if finding_header:
Chris PeBenito 4f9f30
		module_buf += temp_buf
Chris PeBenito 4f9f30
	# Otherwise there are some lingering XML comments at the bottom, warn
Chris PeBenito 4f9f30
	#  the user.
Chris PeBenito 4f9f30
	elif temp_buf:
Chris PeBenito 95db42
		warning("orphan XML comments at bottom of file %s" % file_name)
Chris PeBenito 4f9f30
Chris PeBenito 95db42
	module_buf.append("</module>\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	return module_buf
Chris PeBenito 95db42
Chris PeBenito 95db42
def getLayerXML(directory):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Returns the XML documentation for a layer.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	layer_buf = []
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Infer the layer name from the directory name.
Chris PeBenito 95db42
	layer_buf.append("<layer name=\"%s\">\n" % os.path.basename(directory))
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Try to open the metadata file for this directory and if it exists,
Chris PeBenito 95db42
	# append the contents to the buffer.
Chris PeBenito 95db42
	try:
Chris PeBenito 95db42
		layer_meta = open(directory+"/"+meta, "r")
Chris PeBenito 95db42
		layer_buf += layer_meta.readlines()
Chris PeBenito 95db42
		layer_meta.close()
Chris PeBenito 95db42
	except:
Chris PeBenito 95db42
		warning("cannot open file %s for read, assuming no data"\
Chris PeBenito 95db42
			% meta)
Chris PeBenito 95db42
Chris PeBenito 95db42
	# For each module file in the layer, add its XML.
Chris PeBenito 95db42
	for module in glob.glob("%s/*.if" % directory):
Chris PeBenito 95db42
		layer_buf += getModuleXML(module)
Chris PeBenito 95db42
Chris PeBenito 95db42
	layer_buf.append("</layer>\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	return layer_buf
Chris PeBenito 95db42
Chris PeBenito 4f9f30
def getTunableXML(file_name, kind):
Chris PeBenito 95db42
	'''
Chris PeBenito 4f9f30
	Return all the XML for the tunables/bools in the file specified.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Try to open the file, if it cant, just ignore it.
Chris PeBenito 95db42
	try:
Chris PeBenito 95db42
		tunable_file = open(file_name, "r")
Chris PeBenito 95db42
		tunable_code = tunable_file.readlines()
Chris PeBenito 95db42
		tunable_file.close()
Chris PeBenito 95db42
	except:
Chris PeBenito 95db42
		warning("cannot open file %s for read, skipping" % file_name)
Chris PeBenito 95db42
		return []
Chris PeBenito 95db42
Chris PeBenito 95db42
	tunable_buf = []
Chris PeBenito 95db42
	temp_buf = []
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Find tunables and booleans line by line and use the comments above
Chris PeBenito 95db42
	# them.
Chris PeBenito 95db42
	for line in tunable_code:
Chris PeBenito 95db42
		# If it is an XML comment, add it to the buffer and go on.
Chris PeBenito 4f9f30
		comment = XML_COMMENT.match(line)
Chris PeBenito 4f9f30
		if comment:
Chris PeBenito 4f9f30
			temp_buf.append(comment.group(1))
Chris PeBenito 95db42
			continue
Chris PeBenito 95db42
Chris PeBenito 4f9f30
		# Get the boolean/tunable data.
Chris PeBenito 4f9f30
		boolean = BOOLEAN.match(line)
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
		# If we reach a boolean/tunable declaration, attribute all XML
Chris PeBenito 4f9f30
		#  in the temp buffer to it and add XML to the tunable buffer.
Chris PeBenito 4f9f30
		if boolean:
Chris PeBenito 4f9f30
			# If there is a gen_bool in a tunable file or a
Chris PeBenito 4f9f30
			# gen_tunable in a boolean file, error and exit.
Chris PeBenito 4f9f30
			if boolean.group(1) != kind:
Chris PeBenito 4f9f30
				error("%s in a %s file." % (boolean.group(1), kind))
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
			tunable_buf.append("<%s name=\"%s\" dftval=\"%s\">\n" % boolean.groups())
Chris PeBenito 95db42
			tunable_buf += temp_buf
Chris PeBenito 95db42
			temp_buf = []
Chris PeBenito 4f9f30
			tunable_buf.append("</%s>\n" % boolean.group(1))
Chris PeBenito 95db42
Chris PeBenito 95db42
	# If there are XML comments at the end of the file, they arn't
Chris PeBenito 95db42
	# attributed to anything. These are ignored.
Chris PeBenito 95db42
	if len(temp_buf):
Chris PeBenito 95db42
		warning("orphan XML comments at bottom of file %s" % file_name)
Chris PeBenito 95db42
Chris PeBenito 95db42
	return tunable_buf
Chris PeBenito 95db42
Chris PeBenito 95db42
def getPolicyXML(directory):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Return the compelete reference policy XML documentation through a list,
Chris PeBenito 95db42
	one line per item.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Keep track of original path so that it will change back at the end.
Chris PeBenito 95db42
	old_dir = os.path.abspath(os.path.curdir)
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Attempt to change directory into the policy directory. If it doesn't
Chris PeBenito 95db42
	# exist just return an empty documentation.
Chris PeBenito 95db42
	try:
Chris PeBenito 95db42
		os.chdir(directory)
Chris PeBenito 95db42
	except:
Chris PeBenito 95db42
		warning("cannot change directory to %s, ignoring"\
Chris PeBenito 95db42
			% directory)
Chris PeBenito 95db42
		return []
Chris PeBenito 95db42
Chris PeBenito 95db42
	policy_buf = []
Chris PeBenito 95db42
	policy_buf.append("<policy>\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Add to the XML each layer specified by the user.
Chris PeBenito 95db42
	for layer in layers:
Chris PeBenito 95db42
		policy_buf += getLayerXML(layer)
Chris PeBenito 95db42
Chris PeBenito 4f9f30
	# Add to the XML each tunable file specified by the user.
Chris PeBenito 95db42
	for tunable_file in tunable_files:
Chris PeBenito 4f9f30
		policy_buf += getTunableXML(tunable_file, "tunable")
Chris PeBenito 4f9f30
Chris PeBenito 4f9f30
	# Add to the XML each bool file specified by the user.
Chris PeBenito 4f9f30
	for bool_file in bool_files:
Chris PeBenito 4f9f30
		policy_buf += getTunableXML(bool_file, "bool")
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
	policy_buf.append("</policy>\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	# Return to old directory.
Chris PeBenito 95db42
	try:
Chris PeBenito 95db42
		os.chdir(old_dir)
Chris PeBenito 95db42
	except:
Chris PeBenito 95db42
		error("cannot change directory to %s" % old_dir)
Chris PeBenito 95db42
Chris PeBenito 95db42
	return policy_buf
Chris PeBenito 95db42
Chris PeBenito 95db42
def usage():
Chris PeBenito 95db42
	"""
Chris PeBenito 95db42
	Displays a message describing the proper usage of this script.
Chris PeBenito 95db42
	"""
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("usage: %s [-w] [-d directory] [-m file] "\
Chris PeBenito 95db42
		% sys.argv[0])
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("layerdirectory [layerdirectory...]\n\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("Options:\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("-w --warn		--	"+\
Chris PeBenito 95db42
				"show warnings\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("-m --meta <file>	--	"+\
Chris PeBenito 95db42
				"the filename of the metadata in each layer\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("-d --directory <dir>	--	"+\
Chris PeBenito 95db42
				"directory where the layers are\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stdout.write("-t --tunable <file>	--	"+\
Chris PeBenito 95db42
				"A file containing tunable declarations\n")
Chris PeBenito 95db42
Chris PeBenito 4f9f30
	sys.stdout.write("-b --bool <file>      --      "+\
Chris PeBenito 4f9f30
				"A file containing bool declarations\n")
Chris PeBenito 4f9f30
Chris PeBenito 95db42
def warning(description):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Warns the user of a non-critical error.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	if warn:
Chris PeBenito 95db42
		sys.stderr.write("%s: " % sys.argv[0] )
Chris PeBenito 95db42
		sys.stderr.write("warning: " + description + "\n")
Chris PeBenito 95db42
Chris PeBenito 95db42
def error(description):
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
	Describes an error and exists the program.
Chris PeBenito 95db42
	'''
Chris PeBenito 95db42
Chris PeBenito 95db42
	sys.stderr.write("%s: " % sys.argv[0] )
Chris PeBenito 95db42
        sys.stderr.write("error: " + description + "\n")
Chris PeBenito 95db42
        sys.stderr.flush()
Chris PeBenito 95db42
        sys.exit(1)
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
# MAIN PROGRAM
Chris PeBenito 95db42
# Check that there are command line arguments.
Chris PeBenito 95db42
if len(sys.argv) <= 1:
Chris PeBenito 95db42
	usage()
Chris PeBenito 95db42
	sys.exit(1)
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
# Parse the command line arguments
Chris PeBenito 95db42
for i in range(1, len(sys.argv)):
Chris PeBenito 95db42
	if sys.argv[i-1] in ("-d", "--directory", "-m", "--meta",\
Chris PeBenito 4f9f30
					"-t", "--tunable", "-b", "--bool"):
Chris PeBenito 95db42
		continue
Chris PeBenito 95db42
	elif sys.argv[i] in ("-w", "--warn"):
Chris PeBenito 95db42
		warn = True
Chris PeBenito 95db42
	elif sys.argv[i] in ("-d", "--directory"):
Chris PeBenito 95db42
		if i < len(sys.argv)-1:
Chris PeBenito 95db42
			directory = sys.argv[i+1]
Chris PeBenito 95db42
		else:
Chris PeBenito 95db42
			usage()
Chris PeBenito 95db42
	elif sys.argv[i] in ("-m", "--meta"):
Chris PeBenito 95db42
		if i < len(sys.argv)-1:
Chris PeBenito 95db42
			meta = sys.argv[i+1]
Chris PeBenito 95db42
		else:
Chris PeBenito 95db42
			usage()
Chris PeBenito 95db42
	elif sys.argv[i] in ("-t", "--tunable"):
Chris PeBenito 95db42
		if i < len(sys.argv)-1:
Chris PeBenito 95db42
			tunable_files.append(sys.argv[i+1])
Chris PeBenito 95db42
		else:
Chris PeBenito 95db42
			usage()
Chris PeBenito 4f9f30
	elif sys.argv[i] in ("-b", "--bool"):
Chris PeBenito 4f9f30
		if i < len(sys.argv)-1:
Chris PeBenito 4f9f30
			bool_files.append(sys.argv[i+1])
Chris PeBenito 4f9f30
		else:
Chris PeBenito 4f9f30
			usage()
Chris PeBenito 4f9f30
Chris PeBenito 95db42
	else:
Chris PeBenito 95db42
		layers.append(sys.argv[i])
Chris PeBenito 95db42
Chris PeBenito 95db42
Chris PeBenito 95db42
# Generate the XML and output it to a file
Chris PeBenito 95db42
lines = getPolicyXML(directory)
Chris PeBenito 95db42
for s in lines:
Chris PeBenito 95db42
	sys.stdout.write(s)