1# Authors: Karl MacMillan <kmacmillan@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""" 21Utilities for dealing with the compilation of modules and creation 22of module tress. 23""" 24 25import re 26import tempfile 27try: 28 from subprocess import getstatusoutput 29except ImportError: 30 from commands import getstatusoutput 31import os 32import os.path 33import shutil 34 35import selinux 36 37from . import defaults 38 39 40def 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 49class 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 88def modname_from_sourcename(sourcename): 89 return os.path.splitext(os.path.split(sourcename)[1])[0] 90 91class 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 parameters (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