diff --git a/refpolicy/Changelog b/refpolicy/Changelog index b8eaac6..751bfb8 100644 --- a/refpolicy/Changelog +++ b/refpolicy/Changelog @@ -1,4 +1,6 @@ * Add Makefile support for building loadable modules. + * Add genclassperms.py tool to add require blocks + for loadable modules. * Change sedoctool to make required modules part of base by default, otherwise make as modules, in modules.conf. * Fix segenxml to handle modules with no interfaces. diff --git a/refpolicy/Makefile b/refpolicy/Makefile index 42e3757..7025fce 100644 --- a/refpolicy/Makefile +++ b/refpolicy/Makefile @@ -73,14 +73,18 @@ XMLLINT := $(BINDIR)/xmllint CFLAGS := -Wall # policy source layout -POLDIR = policy -MODDIR = $(POLDIR)/modules -FLASKDIR = $(POLDIR)/flask +POLDIR := policy +MODDIR := $(POLDIR)/modules +FLASKDIR := $(POLDIR)/flask +SECCLASS := $(FLASKDIR)/security_classes +ISIDS := $(FLASKDIR)/initial_sids +AVS := $(FLASKDIR)/access_vectors # policy building support tools SUPPORT := support GENXML := $(SUPPORT)/segenxml.py GENDOC := $(SUPPORT)/sedoctool.py +GENPERM := $(SUPPORT)/genclassperms.py FCSORT := $(SUPPORT)/fc_sort SETTUN := $(SUPPORT)/set_tunables diff --git a/refpolicy/Rules.modular b/refpolicy/Rules.modular index b24297c..a8eef96 100644 --- a/refpolicy/Rules.modular +++ b/refpolicy/Rules.modular @@ -11,13 +11,18 @@ BASE_FC := base.fc BASE_SECTIONS := tmp/pre_te_files.conf tmp/generated_definitions.conf tmp/all_interfaces.conf tmp/all_attrs_types.conf $(GLOBALTUN) tmp/only_te_rules.conf tmp/all_post.conf -BASE_PRE_TE_FILES := $(addprefix $(FLASKDIR)/,security_classes initial_sids access_vectors) $(M4SUPPORT) $(POLDIR)/mls +BASE_PRE_TE_FILES := $(SECCLASS) $(ISIDS) $(AVS) $(M4SUPPORT) $(POLDIR)/mls BASE_TE_FILES := $(BASE_MODS) BASE_POST_TE_FILES := $(POLDIR)/users $(POLDIR)/constraints BASE_FC_FILES := $(BASE_MODS:.te=.fc) MOD_MODULES := $(MOD_MODS:.te=.mod) -MOD_PKGS := $(MOD_MODS:.te=.pp) +MOD_PKGS := $(notdir $(MOD_MODS:.te=.pp)) + +# search layer dirs for source files +vpath %.te $(ALL_LAYERS) +vpath %.if $(ALL_LAYERS) +vpath %.fc $(ALL_LAYERS) ######################################## # @@ -68,14 +73,9 @@ tmp/pre_te_files.conf: $(BASE_PRE_TE_FILES) $(QUIET) cat $^ > $@ tmp/generated_definitions.conf: $(ALL_LAYERS) $(BASE_TE_FILES) -# per-userdomain templates: @test -d tmp || mkdir -p tmp - $(QUIET) echo "define(\`per_userdomain_templates',\`" > $@ - $(QUIET) for i in $(patsubst %.te,%,$(notdir $(BASE_TE_FILES))); do \ - echo "ifdef(\`""$$i""_per_userdomain_template',\`""$$i""_per_userdomain_template("'$$1'")')" \ - >> $@ ;\ - done - $(QUIET) echo "')" >> $@ +# define all available object classes + $(QUIET) $(GENPERM) $(AVS) $(SECCLASS) > $@ # define foo.te $(QUIET) for i in $(notdir $(BASE_TE_FILES)); do \ echo "define(\`$$i')" >> $@ ;\ @@ -131,20 +131,16 @@ endif ######################################## # -# Build modules packages +# Build module packages # -%.pp: %.mod %.fc - @echo "Creating $(NAME) $(@F) package" - $(QUIET) $(SEMOD_PKG) $@ %^ +tmp/%.mod: $(M4SUPPORT) tmp/generated_definitions.conf tmp/all_interfaces.conf %.te + @echo "Compliling $(NAME) $(@F) module" + $(QUIET) m4 $(M4PARAM) -s $^ > $(@:.mod=.tmp) + $(QUIET) $(CHECKMODULE) -m $(@:.mod=.tmp) -o $@ -######################################## -# -# Compile modules -# -%.mod: $(M4SUPPORT) tmp/all_interfaces.conf %.te - @echo "Compiling $(NAME) $(@F) module" - $(QUIET) m4 $(M4PARAM) -s $^ > tmp/$(@F).tmp - $(QUIET) $(CHECKMODULE) -m tmp/$(@F).tmp -o $@ +%.pp: tmp/%.mod %.fc + @echo "Creating $(NAME) $(@F) policy package" + $(QUIET) $(SEMOD_PKG) $@ $^ ######################################## # @@ -153,8 +149,6 @@ endif clean: rm -fR tmp rm -f base.conf - rm -f $(BASE_PKG) - find . -iname "*.mod" | xargs rm -f - find . -iname "*.pp" | xargs rm -f + rm -f *.pp .PHONY: default base modules clean diff --git a/refpolicy/Rules.monolithic b/refpolicy/Rules.monolithic index 404ef7f..d2a2f1e 100644 --- a/refpolicy/Rules.monolithic +++ b/refpolicy/Rules.monolithic @@ -18,7 +18,7 @@ ALL_INTERFACES := $(ALL_MODULES:.te=.if) ALL_TE_FILES := $(ALL_MODULES) ALL_FC_FILES := $(ALL_MODULES:.te=.fc) -PRE_TE_FILES := $(addprefix $(FLASKDIR)/,security_classes initial_sids access_vectors) $(M4SUPPORT) $(POLDIR)/mls +PRE_TE_FILES := $(SECCLASS) $(ISIDS) $(AVS) $(M4SUPPORT) $(POLDIR)/mls POST_TE_FILES := $(POLDIR)/users $(POLDIR)/constraints POLICY_SECTIONS := tmp/pre_te_files.conf tmp/generated_definitions.conf tmp/all_interfaces.conf tmp/all_attrs_types.conf $(GLOBALTUN) tmp/only_te_rules.conf tmp/all_post.conf diff --git a/refpolicy/policy/support/loadable_module.spt b/refpolicy/policy/support/loadable_module.spt index 479ece7..dfa28ce 100644 --- a/refpolicy/policy/support/loadable_module.spt +++ b/refpolicy/policy/support/loadable_module.spt @@ -12,6 +12,8 @@ define(`policy_module',` ifdef(`monolithic_policy',`',` module $1 $2; ') + + require { all_kernel_class_perms } ') ############################## diff --git a/refpolicy/support/genclassperms.py b/refpolicy/support/genclassperms.py new file mode 100755 index 0000000..9ddae68 --- /dev/null +++ b/refpolicy/support/genclassperms.py @@ -0,0 +1,284 @@ +#!/usr/bin/python + +# Author: Donald Miner +# +# Copyright (C) 2003 - 2005 Tresys Technology, LLC +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 2. + + +""" + This script generates an object class perm definition file. +""" + +import sys + +USERSPACE_CLASS = "userspace" + +class Class: + """ + This object stores an access vector class. + """ + + def __init__(self, name, perms, common): + # The name of the class. + self.name = name + + # A list of permissions the class contains. + self.perms = perms + + # True if the class is declared as common, False if not. + self.common = common + +def get_perms(name, av_db): + """ + Returns the list of permissions contained within an access vector + class that is stored in the access vector database av_db. + Returns an empty list if the object name is not found. + """ + + # Traverse through the access vector database and try to find the + # object with the name passed. + for obj in av_db: + if obj.name == name: + return obj.perms + + return [] + +def get_av_db(file_name): + """ + Returns an access vector database generated from the file file_name. + """ + + av_file = open(file_name, "r") + av_data = [] + # Read the file and strip out comments on the way. + # At the end of the loop, av_data will contain a list of individual + # words. i.e. ['common', 'file', '{', ...]. All comments and whitespace + # will be gone. + while True: + av_line = av_file.readline() + + # If EOF has been reached: + if not av_line: + break + + # Check if there is a comment, and if there is, remove it. + comment_index = av_line.find("#") + if comment_index != -1: + av_line = av_line[:comment_index] + + # Pad the braces with whitespace so that they are split into + # their own word. It doesn't matter if there will be extra + # white space, it'll get thrown away when the string is split. + av_line.replace("{"," { ") + av_line.replace("}"," } ") + + # Split up the words on the line and add it to av_data. + av_data += av_line.split() + + av_file.close() + + # Parsing the file: + # The implementation of this parse is a queue. We use the list of words + # from av_data and use the front element, then dequeue it. Each + # loop of this while is a common or class declaration. Several + # expected tokens are parsed and dequeued out of av_data for each loop. + # Dequeue from the beginning of the list until av_data is empty: + database = [] + while len(av_data) != 0: + # At the beginning of every loop, the next word should be + # "common" or "class", meaning that each loop is a common + # or class declaration. + # av_data = av_data[1:] removes the first element in the + # list, this is what is dequeueing data. + + # Figure out whether the next class will be a common or a class. + if av_data[0] == "class": + common = False + elif av_data[0] == "common": + common = True + else: + error("Unexpected token in file " + file_name + ": "\ + + av_data[0] + ".") + + # Dequeue the "class" or "common" key word. + av_data = av_data[1:] + + if len(av_data) == 0: + error("Missing token in file " + file_name + ".") + + # Get and dequeue the name of the class or common. + name = av_data[0] + av_data = av_data[1:] + + # Retrieve the permissions inherited from a common set: + perms = [] + # If the object we are working with is a class, since only + # classes inherit: + if common == False: + if len(av_data) == 0: + error("Missing token in file " + file_name + ".") + + # If the class inherits from something else: + if av_data[0] == "inherits": + # Dequeue the "inherits" key word. + av_data = av_data[1:] + + if len(av_data) == 0: + error("Missing token in file "\ + + file_name + " for " +\ + keyword + " " + name + ".") + + # av_data[0] is the name of the parent. + # Append the permissions of the parent to + # the current class' permissions. + perms += get_perms(av_data[0], database) + # Dequeue the name of the parent. + av_data = av_data[1:] + + # Retrieve the permissions defined with this set. + if len(av_data) > 0 and av_data[0] == "{": + # Dequeue the "{" + av_data = av_data[1:] + + # Keep appending permissions until a close brace is + # found. + while av_data[0] != "}": + if av_data[0] == "{": + error("Extra '{' in file " +\ + file_name + ".") + + # Add the permission name. + perms.append(av_data[0]) + + # Dequeue the permission name. + av_data = av_data[1:] + + if len(av_data) == 0: + error("Missing token '}' in file "\ + + file_name + ".") + + # Dequeue the "}" + av_data = av_data[1:] + + # Add the new access vector class to the database. + database.append(Class(name, perms, common)) + + return database + +def get_sc_db(file_name): + """ + Returns a security class database generated from the file file_name. + """ + + # Read the file then close it. + sc_file = open(file_name) + sc_data = sc_file.readlines() + sc_file.close() + + # For each line in the security classes file, add the name of the class + # and whether it is a userspace class or not to the security class + # database. + database = [] + for line in sc_data: + line = line.lstrip() + # If the line is empty or the entire line is a comment, skip. + if line == "" or line[0] == "#": + continue + + # Check if the comment to the right of the permission matches + # USERSPACE_CLASS. + comment_index = line.find("#") + if comment_index != -1 and line[comment_index+1:].strip() == USERSPACE_CLASS: + userspace = True + else: + userspace = False + + # All lines should be in the format "class NAME", meaning + # it should have two tokens and the first token should be + # "class". + split_line = line.split() + if len(split_line) < 2 or split_line[0] != "class": + error("Wrong syntax: " + line) + + # Add the class's name (split_line[1]) and whether it is a + # userspace class or not to the database. + # This is appending a tuple of (NAME,USERSPACE), where NAME is + # the name of the security class and USERSPACE is True if + # if it has "# USERSPACE_CLASS" on the end of the line, False + # if not. + database.append((split_line[1], userspace)) + + return database + +def gen_class_perms(av_db, sc_db): + """ + Generates a class permissions document and returns it. + """ + + # Define class template: + class_perms_line = "define(`all_%s_perms',`{ %s}')\n" + + # Generate the defines for the individual class permissions. + class_perms = "" + for obj in av_db: + # Don't output commons + if obj.common == True: + continue + + # Get the list of permissions. + perms = get_perms(obj.name, av_db) + + # Merge all the permissions into one string with one space + # padding. + perm_str = "" + for perm in perms: + perm_str += perm + " " + + # Add the line to the class_perms + class_perms += class_perms_line % (obj.name, perm_str) + class_perms += "\n" + + # Generate the kernel_class_perms and userspace_class_perms sets. + class_line = "\tclass %s all_%s_perms;\n" + kernel_class_perms = "define(`all_kernel_class_perms',`\n" + userspace_class_perms = "define(`all_userspace_class_perms',`\n" + # For each (NAME,USERSPACE) tuple, add the class to the appropriate + # class permission set. + for name, userspace in sc_db: + if userspace: + userspace_class_perms += class_line % (name, name) + else: + kernel_class_perms += class_line % (name, name) + kernel_class_perms += "')\n\n" + userspace_class_perms += "')\n" + + # Throw all the strings together and return the string. + return class_perms + kernel_class_perms + userspace_class_perms + +def error(error): + """ + Print an error message and exit. + """ + + sys.stderr.write("%s exiting for: " % sys.argv[0]) + sys.stderr.write("%s\n" % error) + sys.stderr.flush() + sys.exit(1) + +# MAIN PROGRAM +app_name = sys.argv[0] + +if len(sys.argv) != 3: + error("Incorrect input.\nUsage: " + sys.argv[0] + " access_vectors security_classes" ) + +# argv[1] is the access vector file. +av_file = sys.argv[1] + +# argv[2] is the security class file. +sc_file = sys.argv[2] + +# Output the class permissions document. +sys.stdout.write(gen_class_perms(get_av_db(av_file), get_sc_db(sc_file)))