• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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