aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'support/segenxml.py')
-rw-r--r--support/segenxml.py79
1 files changed, 69 insertions, 10 deletions
diff --git a/support/segenxml.py b/support/segenxml.py
index e37ea041..115f2870 100644
--- a/support/segenxml.py
+++ b/support/segenxml.py
@@ -43,7 +43,8 @@ INTERFACE = re.compile(r"^\s*(interface|template)\(`(\w*)'")
# -> ("bool", "secure_mode", "false")
# "gen_tunable(allow_kerberos, false)"
# -> ("tunable", "allow_kerberos", "false")
-BOOLEAN = re.compile(r"^\s*gen_(tunable|bool)\(\s*(\w*)\s*,\s*(true|false)\s*\)")
+BOOLEAN = re.compile(r"^\s*gen_(tunable|bool)\(\s*\`?\s*(\w*)\s*\'?\s*,\s*(true|false)\s*\)")
+TEMPLATE_BOOLEAN = re.compile(r"^\s*gen_(tunable|bool)\(\s*\`?\s*([\w\$]*)\s*\'?\s*,\s*(true|false)\s*\)")
# Matches a XML comment in the policy, which is defined as any line starting
# with two # and at least one character of white space. Will give the single
@@ -54,8 +55,16 @@ BOOLEAN = re.compile(r"^\s*gen_(tunable|bool)\(\s*(\w*)\s*,\s*(true|false)\s*\)"
# -> ("<summary>")
# "## The domain allowed access. "
# -> ("The domain allowed access.")
-XML_COMMENT = re.compile(r"^##\s+(.*?)\s*$")
+XML_COMMENT = re.compile(r"^\s*##\s+(.*?)\s*$")
+# Matches a template call in the policy, which is defined as any line having
+# a function call like structure, being a string, followed by a set of
+# arguments between an opening and closing bracket. Regexp cannot deal with
+# unknown number of arguments, so we will split arguments in the code later on.
+# Some examples:
+# "userdom_user_access_template(gpg, gpg_t)"
+# "zarafa_domain_template(gateway)"
+TEMPLATE_CALL = re.compile(r"^\s*(\w*_template)\(\s*(\w*)\s*(?:,\s*(?:[^,)]*)\s*)*\)")
# FUNCTIONS
def getModuleXML(file_name):
@@ -164,7 +173,13 @@ def getModuleXML(file_name):
interface = None
continue
-
+ # If the line is a boolean/tunable definition, ignore it for now (these
+ # lines are processed later on) and dismiss the XML comment received
+ # thus far as it is otherwise attributed to an interface.
+ tunable = TEMPLATE_BOOLEAN.match(line)
+ if tunable:
+ temp_buf = []
+ continue
# If the file just had a header, add the comments to the module buffer.
if finding_header:
@@ -197,6 +212,49 @@ def getTunableXML(file_name, kind):
tunable_buf = []
temp_buf = []
+ tunable_processed_code = []
+
+ # We first go through the code and substitute template calls with the
+ # complete template content. This needs to happen iteratively, because
+ # a template can call another template. In order to ensure no cyclic
+ # template calls keep us busy, we max out at 9999 substitutions
+ has_changed = True
+ subst_threshold = 9999
+ while (has_changed and (subst_threshold > 0)):
+ has_changed = False
+ for line in tunable_code:
+ # Get the template call match
+ template_call = TEMPLATE_CALL.match(line)
+ # If we reach a template call, read in the template data
+ # from the template directory, but substitute all $1 with
+ # the second match, $2 with the third match, etc.
+ if template_call:
+ # Read template file based on template_call.group(1)
+ try:
+ template_file = open(templatedir + "/" + template_call.group(1) + ".iftemplate", "r")
+ template_code = template_file.readlines()
+ template_file.close()
+ except OSError:
+ warning("cannot open file %s for read, bailing out" % templatedir + "/" + template_call.group(1) + ".iftemplate")
+ return []
+ # Substitute content (i.e. $1 for argument 1, $2 for argument 2, etc.)
+ template_split = re.findall(r"[\w\" {}]+", line.strip())
+ for index, item in enumerate(template_code):
+ for group in range(1, len(template_split)):
+ template_code[index] = template_code[index].replace("$" + str(group), template_split[group].strip())
+ # Now 'inject' the code in the tunable_code variable
+ tunable_processed_code.extend(template_code)
+ has_changed = True
+ subst_threshold -= 1
+ else:
+ tunable_processed_code.append(line)
+ # It is a bad practice to try and update lists while in a loop, so we
+ # created an intermediate one and are now assigning it back
+ tunable_code = tunable_processed_code
+ tunable_processed_code = []
+ # If subst_threshold is 0 or less we want to know
+ if (subst_threshold <= 0):
+ warning("Detected a possible loop in policy code and template usage")
# Find tunables and booleans line by line and use the comments above
# them.
@@ -251,14 +309,15 @@ def usage():
Displays a message describing the proper usage of this script.
"""
- sys.stdout.write("usage: %s [-w] [-mtb] <file>\n\n" % sys.argv[0])
+ sys.stdout.write("usage: %s [-w] [-T <templatedir>] [-mtb] <file>\n\n" % sys.argv[0])
sys.stdout.write("-w --warn\t\t\tshow warnings\n"+\
"-m --module <file>\t\tname of module to process\n"+\
"-t --tunable <file>\t\tname of global tunable file to process\n"+\
- "-b --boolean <file>\t\tname of global boolean file to process\n\n")
+ "-b --boolean <file>\t\tname of global boolean file to process\n"+\
+ "-T --templates <dir>\t\tname of template directory to use\n\n")
sys.stdout.write("examples:\n")
- sys.stdout.write("> %s -w -m policy/modules/apache\n" % sys.argv[0])
+ sys.stdout.write("> %s -w -T tmp/templates -m policy/modules/apache\n" % sys.argv[0])
sys.stdout.write("> %s -t policy/global_tunables\n" % sys.argv[0])
def warning(description):
@@ -289,6 +348,7 @@ warn = False
module = False
tunable = False
boolean = False
+templatedir = ''
# Check that there are command line arguments.
if len(sys.argv) <= 1:
@@ -297,7 +357,7 @@ if len(sys.argv) <= 1:
# Parse command line args
try:
- opts, args = getopt.getopt(sys.argv[1:], 'whm:t:b:', ['warn', 'help', 'module=', 'tunable=', 'boolean='])
+ opts, args = getopt.getopt(sys.argv[1:], 'whm:t:b:T:', ['warn', 'help', 'module=', 'tunable=', 'boolean=', 'templates='])
except getopt.GetoptError:
usage()
sys.exit(2)
@@ -309,13 +369,12 @@ for o, a in opts:
sys.exit(0)
elif o in ('-m', '--module'):
module = a
- break
elif o in ('-t', '--tunable'):
tunable = a
- break
elif o in ('-b', '--boolean'):
boolean = a
- break
+ elif o in ('-T', '--templates'):
+ templatedir = a
else:
usage()
sys.exit(2)