Home | History | Annotate | Download | only in sepolgen
      1 # Authors: Karl MacMillan <kmacmillan (at] mentalrootkit.com>
      2 #
      3 # Copyright (C) 2006 Red Hat 
      4 # see file 'COPYING' for use and warranty information
      5 #
      6 # This program is free software; you can redistribute it and/or
      7 # modify it under the terms of the GNU General Public License as
      8 # published by the Free Software Foundation; version 2 only
      9 #
     10 # This program is distributed in the hope that it will be useful,
     11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 # GNU General Public License for more details.
     14 #
     15 # You should have received a copy of the GNU General Public License
     16 # along with this program; if not, write to the Free Software
     17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
     18 #
     19 
     20 """
     21 Utilities for dealing with the compilation of modules and creation
     22 of module tress.
     23 """
     24 
     25 import re
     26 import tempfile
     27 try:
     28     from subprocess import getstatusoutput
     29 except ImportError:
     30     from commands import getstatusoutput
     31 import os
     32 import os.path
     33 import shutil
     34 
     35 import selinux
     36 
     37 from . import defaults
     38 
     39 
     40 def is_valid_name(modname):
     41     """Check that a module name is valid.
     42     """
     43     m = re.findall(r"[^a-zA-Z0-9_\-\.]", modname)
     44     if len(m) == 0 and modname[0].isalpha():
     45         return True
     46     else:
     47         return False
     48 
     49 class ModuleTree:
     50     def __init__(self, modname):
     51         self.modname = modname
     52         self.dirname = None
     53 
     54     def dir_name(self):
     55         return self.dirname
     56 
     57     def te_name(self):
     58         return self.dirname + "/" + self.modname + ".te"
     59 
     60     def fc_name(self):
     61         return self.dirname + "/" + self.modname + ".fc"
     62 
     63     def if_name(self):
     64         return self.dirname + "/" + self.modname + ".if"
     65 
     66     def package_name(self):
     67         return self.dirname + "/" + self.modname + ".pp"
     68 
     69     def makefile_name(self):
     70         return self.dirname + "/Makefile"
     71 
     72     def create(self, parent_dirname, makefile_include=None):
     73         self.dirname = parent_dirname + "/" + self.modname
     74         os.mkdir(self.dirname)
     75         fd = open(self.makefile_name(), "w")
     76         if makefile_include:
     77             fd.write("include " + makefile_include)
     78         else:
     79             fd.write("include " + defaults.refpolicy_makefile())
     80         fd.close()
     81 
     82         # Create empty files for the standard refpolicy
     83         # module files
     84         open(self.te_name(), "w").close()
     85         open(self.fc_name(), "w").close()
     86         open(self.if_name(), "w").close()
     87 
     88 def modname_from_sourcename(sourcename):
     89     return os.path.splitext(os.path.split(sourcename)[1])[0]
     90 
     91 class ModuleCompiler:
     92     """ModuleCompiler eases running of the module compiler.
     93 
     94     The ModuleCompiler class encapsulates running the commandline
     95     module compiler (checkmodule) and module packager (semodule_package).
     96     You are likely interested in the create_module_package method.
     97     
     98     Several options are controlled via paramaters (only effects the 
     99     non-refpol builds):
    100     
    101      .mls          [boolean] Generate an MLS module (by passed -M to
    102                    checkmodule). True to generate an MLS module, false
    103                    otherwise.
    104                    
    105      .module       [boolean] Generate a module instead of a base module.
    106                    True to generate a module, false to generate a base.
    107                    
    108      .checkmodule  [string] Fully qualified path to the module compiler.
    109                    Default is /usr/bin/checkmodule.
    110                    
    111      .semodule_package [string] Fully qualified path to the module
    112                    packager. Defaults to /usr/bin/semodule_package.
    113      .output       [file object] File object used to write verbose
    114                    output of the compililation and packaging process.
    115     """
    116     def __init__(self, output=None):
    117         """Create a ModuleCompiler instance, optionally with an
    118         output file object for verbose output of the compilation process.
    119         """
    120         self.mls = selinux.is_selinux_mls_enabled()
    121         self.module = True
    122         self.checkmodule = "/usr/bin/checkmodule"
    123         self.semodule_package = "/usr/bin/semodule_package"
    124         self.output = output
    125         self.last_output = ""
    126         self.refpol_makefile = defaults.refpolicy_makefile()
    127         self.make = "/usr/bin/make"
    128 
    129     def o(self, str):
    130         if self.output:
    131             self.output.write(str + "\n")
    132         self.last_output = str
    133 
    134     def run(self, command):
    135         self.o(command)
    136         rc, output = getstatusoutput(command)
    137         self.o(output)
    138         
    139         return rc
    140     
    141     def gen_filenames(self, sourcename):
    142         """Generate the module and policy package filenames from
    143         a source file name. The source file must be in the form
    144         of "foo.te". This will generate "foo.mod" and "foo.pp".
    145         
    146         Returns a tuple with (modname, policypackage).
    147         """
    148         splitname = sourcename.split(".")
    149         if len(splitname) < 2:
    150             raise RuntimeError("invalid sourcefile name %s (must end in .te)", sourcename)
    151         # Handle other periods in the filename correctly
    152         basename = ".".join(splitname[0:-1])
    153         modname = basename + ".mod"
    154         packagename = basename + ".pp"
    155         
    156         return (modname, packagename)
    157 
    158     def create_module_package(self, sourcename, refpolicy=True):
    159         """Create a module package saved in a packagename from a
    160         sourcename.
    161 
    162         The create_module_package creates a module package saved in a
    163         file named sourcename (.pp is the standard extension) from a
    164         source file (.te is the standard extension). The source file
    165         should contain SELinux policy statements appropriate for a
    166         base or non-base module (depending on the setting of .module).
    167 
    168         Only file names are accepted, not open file objects or
    169         descriptors because the command line SELinux tools are used.
    170 
    171         On error a RuntimeError will be raised with a descriptive
    172         error message.
    173         """
    174         if refpolicy:
    175             self.refpol_build(sourcename)
    176         else:
    177             modname, packagename = self.gen_filenames(sourcename)
    178             self.compile(sourcename, modname)
    179             self.package(modname, packagename)
    180             os.unlink(modname)
    181             
    182     def refpol_build(self, sourcename):
    183         # Compile
    184         command = self.make + " -f " + self.refpol_makefile
    185         rc = self.run(command)
    186 
    187         # Raise an error if the process failed
    188         if rc != 0:
    189             raise RuntimeError("compilation failed:\n%s" % self.last_output)
    190         
    191     def compile(self, sourcename, modname):
    192         s = [self.checkmodule]
    193         if self.mls:
    194             s.append("-M")
    195         if self.module:
    196             s.append("-m")
    197         s.append("-o")
    198         s.append(modname)
    199         s.append(sourcename)
    200 
    201         rc = self.run(" ".join(s))
    202         if rc != 0:
    203             raise RuntimeError("compilation failed:\n%s" % self.last_output)
    204 
    205     def package(self, modname, packagename):
    206         s = [self.semodule_package]
    207         s.append("-o")
    208         s.append(packagename)
    209         s.append("-m")
    210         s.append(modname)
    211         
    212         rc = self.run(" ".join(s))
    213         if rc != 0:
    214             raise RuntimeError("packaging failed [%s]" % self.last_output)
    215         
    216     
    217