#!/usr/bin/python3 -E from __future__ import print_function import os import errno import shutil import sys from optparse import OptionParser try: import selinux import semanage except ImportError: print("You must install libselinux-python and libsemanage-python before running this tool", file=sys.stderr) exit(1) def copy_file(src, dst): if DEBUG: print("copying %s to %s" % (src, dst)) try: shutil.copy(src, dst) except OSError as the_err: (err, strerr) = the_err.args print("Could not copy %s to %s, %s" % (src, dst, strerr), file=sys.stderr) exit(1) def create_dir(dst, mode): if DEBUG: print("Making directory %s" % dst) try: os.makedirs(dst, mode) except OSError as the_err: (err, stderr) = the_err.args if err == errno.EEXIST: pass else: print("Error creating %s" % dst, file=sys.stderr) exit(1) def create_file(dst): if DEBUG: print("Making file %s" % dst) try: open(dst, 'a').close() except OSError as the_err: (err, stderr) = the_err.args print("Error creating %s" % dst, file=sys.stderr) exit(1) def copy_module(store, name, base): if DEBUG: print("Install module %s" % name) (file, ext) = os.path.splitext(name) if ext != ".pp": # Stray non-pp file in modules directory, skip print("warning: %s has invalid extension, skipping" % name, file=sys.stderr) return try: if base: root = oldstore_path(store) else: root = oldmodules_path(store) bottomdir = bottomdir_path(store) os.mkdir("%s/%s" % (bottomdir, file)) copy_file(os.path.join(root, name), "%s/%s/hll" % (bottomdir, file)) # This is the ext file that will eventually be used to choose a compiler efile = open("%s/%s/lang_ext" % (bottomdir, file), "w+", 0o600) efile.write("pp") efile.close() except (IOError, OSError): print("Error installing module %s" % name, file=sys.stderr) exit(1) def disable_module(file, name, disabledmodules): if DEBUG: print("Disabling %s" % name) (disabledname, disabledext) = os.path.splitext(file) create_file("%s/%s" % (disabledmodules, disabledname)) def migrate_store(store): oldstore = oldstore_path(store) oldmodules = oldmodules_path(store) disabledmodules = disabledmodules_path(store) newstore = newstore_path(store) newmodules = newmodules_path(store) bottomdir = bottomdir_path(store) print("Migrating from %s to %s" % (oldstore, newstore)) # Build up new directory structure create_dir("%s/%s" % (newroot_path(), store), 0o755) create_dir(newstore, 0o700) create_dir(newmodules, 0o700) create_dir(bottomdir, 0o700) create_dir(disabledmodules, 0o700) # Special case for base since it was in a different location copy_module(store, "base.pp", 1) # Dir structure built, start copying files for root, dirs, files in os.walk(oldstore): if root == oldstore: # This is the top level directory, need to move for name in files: # Check to see if it is in TOPPATHS and copy if so if name in TOPPATHS: if name == "seusers": newname = "seusers.local" else: newname = name copy_file(os.path.join(root, name), os.path.join(newstore, newname)) elif root == oldmodules: # This should be the modules directory for name in files: (file, ext) = os.path.splitext(name) if name == "base.pp": print("Error installing module %s, name conflicts with base" % name, file=sys.stderr) exit(1) elif ext == ".disabled": disable_module(file, name, disabledmodules) else: copy_module(store, name, 0) def rebuild_policy(): # Ok, the modules are loaded, lets try to rebuild the policy print("Attempting to rebuild policy from %s" % newroot_path()) curstore = selinux.selinux_getpolicytype()[1] handle = semanage.semanage_handle_create() if not handle: print("Could not create semanage handle", file=sys.stderr) exit(1) semanage.semanage_select_store(handle, curstore, semanage.SEMANAGE_CON_DIRECT) if not semanage.semanage_is_managed(handle): semanage.semanage_handle_destroy(handle) print("SELinux policy is not managed or store cannot be accessed.", file=sys.stderr) exit(1) rc = semanage.semanage_access_check(handle) if rc < semanage.SEMANAGE_CAN_WRITE: semanage.semanage_handle_destroy(handle) print("Cannot write to policy store.", file=sys.stderr) exit(1) rc = semanage.semanage_connect(handle) if rc < 0: semanage.semanage_handle_destroy(handle) print("Could not establish semanage connection", file=sys.stderr) exit(1) semanage.semanage_set_rebuild(handle, 1) rc = semanage.semanage_begin_transaction(handle) if rc < 0: semanage.semanage_handle_destroy(handle) print("Could not begin transaction", file=sys.stderr) exit(1) rc = semanage.semanage_commit(handle) if rc < 0: print("Could not commit transaction", file=sys.stderr) semanage.semanage_handle_destroy(handle) def oldroot_path(): return "%s/etc/selinux" % ROOT def oldstore_path(store): return "%s/%s/modules/active" % (oldroot_path(), store) def oldmodules_path(store): return "%s/modules" % oldstore_path(store) def disabledmodules_path(store): return "%s/disabled" % newmodules_path(store) def newroot_path(): return "%s%s" % (ROOT, PATH) def newstore_path(store): return "%s/%s/active" % (newroot_path(), store) def newmodules_path(store): return "%s/modules" % newstore_path(store) def bottomdir_path(store): return "%s/%s" % (newmodules_path(store), PRIORITY) if __name__ == "__main__": parser = OptionParser() parser.add_option("-p", "--priority", dest="priority", default="100", help="Set priority of modules in new store (default: 100)") parser.add_option("-s", "--store", dest="store", default=None, help="Store to read from and write to") parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False, help="Output debug information") parser.add_option("-c", "--clean", dest="clean", action="store_true", default=False, help="Clean old modules directory after migrate (default: no)") parser.add_option("-n", "--norebuild", dest="norebuild", action="store_true", default=False, help="Disable rebuilding policy after migration (default: no)") parser.add_option("-P", "--path", dest="path", help="Set path for the policy store (default: /var/lib/selinux)") parser.add_option("-r", "--root", dest="root", help="Set an alternative root for the migration (default: /)") (options, args) = parser.parse_args() DEBUG = options.debug PRIORITY = options.priority TYPE = options.store CLEAN = options.clean NOREBUILD = options.norebuild PATH = options.path if PATH is None: PATH = "/var/lib/selinux" ROOT = options.root if ROOT is None: ROOT = "" # List of paths that go in the active 'root' TOPPATHS = [ "commit_num", "ports.local", "interfaces.local", "nodes.local", "booleans.local", "file_contexts.local", "seusers", "users.local", "users_extra", "users_extra.local", "disable_dontaudit", "preserve_tunables", "policy.kern", "file_contexts", "homedir_template", "pkeys.local", "ibendports.local"] create_dir(newroot_path(), 0o755) stores = None if TYPE is not None: stores = [TYPE] else: stores = os.listdir(oldroot_path()) # find stores in oldroot and migrate them to newroot if necessary for store in stores: if not os.path.isdir(oldmodules_path(store)): # already migrated or not an selinux store continue if os.path.isdir(newstore_path(store)): # store has already been migrated, but old modules dir still exits print("warning: Policy type %s has already been migrated, but modules still exist in the old store. Skipping store." % store, file=sys.stderr) continue migrate_store(store) if CLEAN is True: def remove_error(function, path, execinfo): print("warning: Unable to remove old store modules directory %s. Cleaning failed." % oldmodules_path(store), file=sys.stderr) shutil.rmtree(oldmodules_path(store), onerror=remove_error) if NOREBUILD is False: rebuild_policy()