217 lines
7.1 KiB
Python
217 lines
7.1 KiB
Python
# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
|
|
#
|
|
# Copyright (C) 2006 Red Hat
|
|
# see file 'COPYING' for use and warranty information
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License as
|
|
# published by the Free Software Foundation; version 2 only
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
|
|
"""
|
|
Utilities for dealing with the compilation of modules and creation
|
|
of module tress.
|
|
"""
|
|
|
|
import re
|
|
import tempfile
|
|
try:
|
|
from subprocess import getstatusoutput
|
|
except ImportError:
|
|
from commands import getstatusoutput
|
|
import os
|
|
import os.path
|
|
import shutil
|
|
|
|
import selinux
|
|
|
|
from . import defaults
|
|
|
|
|
|
def is_valid_name(modname):
|
|
"""Check that a module name is valid.
|
|
"""
|
|
m = re.findall("[^a-zA-Z0-9_\-\.]", modname)
|
|
if len(m) == 0 and modname[0].isalpha():
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
class ModuleTree:
|
|
def __init__(self, modname):
|
|
self.modname = modname
|
|
self.dirname = None
|
|
|
|
def dir_name(self):
|
|
return self.dirname
|
|
|
|
def te_name(self):
|
|
return self.dirname + "/" + self.modname + ".te"
|
|
|
|
def fc_name(self):
|
|
return self.dirname + "/" + self.modname + ".fc"
|
|
|
|
def if_name(self):
|
|
return self.dirname + "/" + self.modname + ".if"
|
|
|
|
def package_name(self):
|
|
return self.dirname + "/" + self.modname + ".pp"
|
|
|
|
def makefile_name(self):
|
|
return self.dirname + "/Makefile"
|
|
|
|
def create(self, parent_dirname, makefile_include=None):
|
|
self.dirname = parent_dirname + "/" + self.modname
|
|
os.mkdir(self.dirname)
|
|
fd = open(self.makefile_name(), "w")
|
|
if makefile_include:
|
|
fd.write("include " + makefile_include)
|
|
else:
|
|
fd.write("include " + defaults.refpolicy_makefile())
|
|
fd.close()
|
|
|
|
# Create empty files for the standard refpolicy
|
|
# module files
|
|
open(self.te_name(), "w").close()
|
|
open(self.fc_name(), "w").close()
|
|
open(self.if_name(), "w").close()
|
|
|
|
def modname_from_sourcename(sourcename):
|
|
return os.path.splitext(os.path.split(sourcename)[1])[0]
|
|
|
|
class ModuleCompiler:
|
|
"""ModuleCompiler eases running of the module compiler.
|
|
|
|
The ModuleCompiler class encapsulates running the commandline
|
|
module compiler (checkmodule) and module packager (semodule_package).
|
|
You are likely interested in the create_module_package method.
|
|
|
|
Several options are controlled via paramaters (only effects the
|
|
non-refpol builds):
|
|
|
|
.mls [boolean] Generate an MLS module (by passed -M to
|
|
checkmodule). True to generate an MLS module, false
|
|
otherwise.
|
|
|
|
.module [boolean] Generate a module instead of a base module.
|
|
True to generate a module, false to generate a base.
|
|
|
|
.checkmodule [string] Fully qualified path to the module compiler.
|
|
Default is /usr/bin/checkmodule.
|
|
|
|
.semodule_package [string] Fully qualified path to the module
|
|
packager. Defaults to /usr/bin/semodule_package.
|
|
.output [file object] File object used to write verbose
|
|
output of the compililation and packaging process.
|
|
"""
|
|
def __init__(self, output=None):
|
|
"""Create a ModuleCompiler instance, optionally with an
|
|
output file object for verbose output of the compilation process.
|
|
"""
|
|
self.mls = selinux.is_selinux_mls_enabled()
|
|
self.module = True
|
|
self.checkmodule = "/usr/bin/checkmodule"
|
|
self.semodule_package = "/usr/bin/semodule_package"
|
|
self.output = output
|
|
self.last_output = ""
|
|
self.refpol_makefile = defaults.refpolicy_makefile()
|
|
self.make = "/usr/bin/make"
|
|
|
|
def o(self, str):
|
|
if self.output:
|
|
self.output.write(str + "\n")
|
|
self.last_output = str
|
|
|
|
def run(self, command):
|
|
self.o(command)
|
|
rc, output = getstatusoutput(command)
|
|
self.o(output)
|
|
|
|
return rc
|
|
|
|
def gen_filenames(self, sourcename):
|
|
"""Generate the module and policy package filenames from
|
|
a source file name. The source file must be in the form
|
|
of "foo.te". This will generate "foo.mod" and "foo.pp".
|
|
|
|
Returns a tuple with (modname, policypackage).
|
|
"""
|
|
splitname = sourcename.split(".")
|
|
if len(splitname) < 2:
|
|
raise RuntimeError("invalid sourcefile name %s (must end in .te)", sourcename)
|
|
# Handle other periods in the filename correctly
|
|
basename = ".".join(splitname[0:-1])
|
|
modname = basename + ".mod"
|
|
packagename = basename + ".pp"
|
|
|
|
return (modname, packagename)
|
|
|
|
def create_module_package(self, sourcename, refpolicy=True):
|
|
"""Create a module package saved in a packagename from a
|
|
sourcename.
|
|
|
|
The create_module_package creates a module package saved in a
|
|
file named sourcename (.pp is the standard extension) from a
|
|
source file (.te is the standard extension). The source file
|
|
should contain SELinux policy statements appropriate for a
|
|
base or non-base module (depending on the setting of .module).
|
|
|
|
Only file names are accepted, not open file objects or
|
|
descriptors because the command line SELinux tools are used.
|
|
|
|
On error a RuntimeError will be raised with a descriptive
|
|
error message.
|
|
"""
|
|
if refpolicy:
|
|
self.refpol_build(sourcename)
|
|
else:
|
|
modname, packagename = self.gen_filenames(sourcename)
|
|
self.compile(sourcename, modname)
|
|
self.package(modname, packagename)
|
|
os.unlink(modname)
|
|
|
|
def refpol_build(self, sourcename):
|
|
# Compile
|
|
command = self.make + " -f " + self.refpol_makefile
|
|
rc = self.run(command)
|
|
|
|
# Raise an error if the process failed
|
|
if rc != 0:
|
|
raise RuntimeError("compilation failed:\n%s" % self.last_output)
|
|
|
|
def compile(self, sourcename, modname):
|
|
s = [self.checkmodule]
|
|
if self.mls:
|
|
s.append("-M")
|
|
if self.module:
|
|
s.append("-m")
|
|
s.append("-o")
|
|
s.append(modname)
|
|
s.append(sourcename)
|
|
|
|
rc = self.run(" ".join(s))
|
|
if rc != 0:
|
|
raise RuntimeError("compilation failed:\n%s" % self.last_output)
|
|
|
|
def package(self, modname, packagename):
|
|
s = [self.semodule_package]
|
|
s.append("-o")
|
|
s.append(packagename)
|
|
s.append("-m")
|
|
s.append(modname)
|
|
|
|
rc = self.run(" ".join(s))
|
|
if rc != 0:
|
|
raise RuntimeError("packaging failed [%s]" % self.last_output)
|
|
|
|
|