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 defaults 26 27import selinux 28 29import re 30import tempfile 31import commands 32import os 33import os.path 34import subprocess 35import shutil 36 37def is_valid_name(modname): 38 """Check that a module name is valid. 39 """ 40 m = re.findall("[^a-zA-Z0-9_\-\.]", modname) 41 if len(m) == 0 and modname[0].isalpha(): 42 return True 43 else: 44 return False 45 46class ModuleTree: 47 def __init__(self, modname): 48 self.modname = modname 49 self.dirname = None 50 51 def dir_name(self): 52 return self.dirname 53 54 def te_name(self): 55 return self.dirname + "/" + self.modname + ".te" 56 57 def fc_name(self): 58 return self.dirname + "/" + self.modname + ".fc" 59 60 def if_name(self): 61 return self.dirname + "/" + self.modname + ".if" 62 63 def package_name(self): 64 return self.dirname + "/" + self.modname + ".pp" 65 66 def makefile_name(self): 67 return self.dirname + "/Makefile" 68 69 def create(self, parent_dirname, makefile_include=None): 70 self.dirname = parent_dirname + "/" + self.modname 71 os.mkdir(self.dirname) 72 fd = open(self.makefile_name(), "w") 73 if makefile_include: 74 fd.write("include " + makefile_include) 75 else: 76 fd.write("include " + defaults.refpolicy_makefile()) 77 fd.close() 78 79 # Create empty files for the standard refpolicy 80 # module files 81 open(self.te_name(), "w").close() 82 open(self.fc_name(), "w").close() 83 open(self.if_name(), "w").close() 84 85def modname_from_sourcename(sourcename): 86 return os.path.splitext(os.path.split(sourcename)[1])[0] 87 88class ModuleCompiler: 89 """ModuleCompiler eases running of the module compiler. 90 91 The ModuleCompiler class encapsulates running the commandline 92 module compiler (checkmodule) and module packager (semodule_package). 93 You are likely interested in the create_module_package method. 94 95 Several options are controlled via paramaters (only effects the 96 non-refpol builds): 97 98 .mls [boolean] Generate an MLS module (by passed -M to 99 checkmodule). True to generate an MLS module, false 100 otherwise. 101 102 .module [boolean] Generate a module instead of a base module. 103 True to generate a module, false to generate a base. 104 105 .checkmodule [string] Fully qualified path to the module compiler. 106 Default is /usr/bin/checkmodule. 107 108 .semodule_package [string] Fully qualified path to the module 109 packager. Defaults to /usr/bin/semodule_package. 110 .output [file object] File object used to write verbose 111 output of the compililation and packaging process. 112 """ 113 def __init__(self, output=None): 114 """Create a ModuleCompiler instance, optionally with an 115 output file object for verbose output of the compilation process. 116 """ 117 self.mls = selinux.is_selinux_mls_enabled() 118 self.module = True 119 self.checkmodule = "/usr/bin/checkmodule" 120 self.semodule_package = "/usr/bin/semodule_package" 121 self.output = output 122 self.last_output = "" 123 self.refpol_makefile = defaults.refpolicy_makefile() 124 self.make = "/usr/bin/make" 125 126 def o(self, str): 127 if self.output: 128 self.output.write(str + "\n") 129 self.last_output = str 130 131 def run(self, command): 132 self.o(command) 133 rc, output = commands.getstatusoutput(command) 134 self.o(output) 135 136 return rc 137 138 def gen_filenames(self, sourcename): 139 """Generate the module and policy package filenames from 140 a source file name. The source file must be in the form 141 of "foo.te". This will generate "foo.mod" and "foo.pp". 142 143 Returns a tuple with (modname, policypackage). 144 """ 145 splitname = sourcename.split(".") 146 if len(splitname) < 2: 147 raise RuntimeError("invalid sourcefile name %s (must end in .te)", sourcename) 148 # Handle other periods in the filename correctly 149 basename = ".".join(splitname[0:-1]) 150 modname = basename + ".mod" 151 packagename = basename + ".pp" 152 153 return (modname, packagename) 154 155 def create_module_package(self, sourcename, refpolicy=True): 156 """Create a module package saved in a packagename from a 157 sourcename. 158 159 The create_module_package creates a module package saved in a 160 file named sourcename (.pp is the standard extension) from a 161 source file (.te is the standard extension). The source file 162 should contain SELinux policy statements appropriate for a 163 base or non-base module (depending on the setting of .module). 164 165 Only file names are accepted, not open file objects or 166 descriptors because the command line SELinux tools are used. 167 168 On error a RuntimeError will be raised with a descriptive 169 error message. 170 """ 171 if refpolicy: 172 self.refpol_build(sourcename) 173 else: 174 modname, packagename = self.gen_filenames(sourcename) 175 self.compile(sourcename, modname) 176 self.package(modname, packagename) 177 os.unlink(modname) 178 179 def refpol_build(self, sourcename): 180 # Compile 181 command = self.make + " -f " + self.refpol_makefile 182 rc = self.run(command) 183 184 # Raise an error if the process failed 185 if rc != 0: 186 raise RuntimeError("compilation failed:\n%s" % self.last_output) 187 188 def compile(self, sourcename, modname): 189 s = [self.checkmodule] 190 if self.mls: 191 s.append("-M") 192 if self.module: 193 s.append("-m") 194 s.append("-o") 195 s.append(modname) 196 s.append(sourcename) 197 198 rc = self.run(" ".join(s)) 199 if rc != 0: 200 raise RuntimeError("compilation failed:\n%s" % self.last_output) 201 202 def package(self, modname, packagename): 203 s = [self.semodule_package] 204 s.append("-o") 205 s.append(packagename) 206 s.append("-m") 207 s.append(modname) 208 209 rc = self.run(" ".join(s)) 210 if rc != 0: 211 raise RuntimeError("packaging failed [%s]" % self.last_output) 212 213 214