blob: c09676aa4efa0c5403f5d9b7fd8e76540f5a6add [file] [log] [blame]
Jeff Vander Stoep74e4f932016-02-08 15:27:10 -08001# 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("[^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 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