From 6d4e8591a336bb63ac406c7570d056fc92a5b826 Mon Sep 17 00:00:00 2001 From: Caleb Case Date: Wed, 23 Dec 2009 18:26:00 -0500 Subject: [PATCH] libsemanage: semanage store migration script We created a migration script to ease the burden of transition from the old libsemanage store layout to the new. The script will detect all the stores in /etc/selinux using the old layout and convert them to the new layout in /var/lib/selinux. It also allows you to specify the default priority to use with -p and store to operate on with -s. After migration the script by default will leave the old store unchanged, but can be told to remove the old modules directory with -c. Reloading policy post migration can be disabled with the -n option. Examples: semanage_migrate_store Migrating from /etc/selinux/targeted/modules/active to /var/lib/selinux/targeted/active Attempting to rebuild policy from /var/lib/selinux semanage_migrate_store -s targeted Migrating from /etc/selinux/targeted/modules/active to /var/lib/selinux/targeted/active Attempting to rebuild policy from /var/lib/selinux semanage_migrate_store -p 150 Migrating from /etc/selinux/targeted/modules/active to /var/lib/selinux/targeted/active Attempting to rebuild policy from /var/lib/selinux Signed-off-by: Chad Sellers Signed-off-by: Steve Lawrence --- libsemanage/Makefile | 1 + libsemanage/utils/Makefile | 19 ++ libsemanage/utils/semanage_migrate_store | 314 +++++++++++++++++++++++ 3 files changed, 334 insertions(+) create mode 100644 libsemanage/utils/Makefile create mode 100755 libsemanage/utils/semanage_migrate_store diff --git a/libsemanage/Makefile b/libsemanage/Makefile index 6c574b0c..390176fe 100644 --- a/libsemanage/Makefile +++ b/libsemanage/Makefile @@ -14,6 +14,7 @@ install: $(MAKE) -C include install $(MAKE) -C src install $(MAKE) -C man install + $(MAKE) -C utils install install-pywrap: $(MAKE) -C src install-pywrap diff --git a/libsemanage/utils/Makefile b/libsemanage/utils/Makefile new file mode 100644 index 00000000..725f0eec --- /dev/null +++ b/libsemanage/utils/Makefile @@ -0,0 +1,19 @@ +# Installation directories. +PREFIX ?= $(DESTDIR)/usr +LIBEXECDIR ?= $(PREFIX)/libexec +SELINUXEXECDIR ?= $(LIBEXECDIR)/selinux/ + +all: + +install: all + -mkdir -p $(SELINUXEXECDIR) + install -m 755 semanage_migrate_store $(SELINUXEXECDIR) + +clean: + +distclean: clean + +indent: + +relabel: + diff --git a/libsemanage/utils/semanage_migrate_store b/libsemanage/utils/semanage_migrate_store new file mode 100755 index 00000000..cbc4f31b --- /dev/null +++ b/libsemanage/utils/semanage_migrate_store @@ -0,0 +1,314 @@ +#!/usr/bin/python -E + + +import os +import errno +import shutil +import sys +from optparse import OptionParser + +import bz2 +import ctypes + +sepol = ctypes.cdll.LoadLibrary('libsepol.so') + +try: + import selinux + import semanage +except: + print >> sys.stderr, "You must install libselinux-python and libsemanage-python before running this tool" + exit(1) + + + + +# For some reason this function doesn't exist in libselinux :\ +def copy_with_context(src, dst): + if DEBUG: + print "copying %s to %s" % (src, dst) + try: + con = selinux.lgetfilecon_raw(src)[1] + except: + print >> sys.stderr, "Could not get file context of %s" % src + exit(1) + + try: + selinux.setfscreatecon_raw(con) + except: + print >> sys.stderr, "Could not set fs create context: %s" %con + exit(1) + + try: + shutil.copy2(src, dst) + except OSError as (err, strerr): + print >> sys.stderr, "Could not copy %s to %s, %s" %(src, dst, strerr) + exit(1) + + try: + selinux.setfscreatecon_raw(None) + except: + print >> sys.stderr, "Could not reset fs create context. May need to relabel system." + +def create_dir_from(src, dst, mode): + if DEBUG: print "Making directory %s" % dst + try: + con = selinux.lgetfilecon_raw(src)[1] + selinux.setfscreatecon_raw(con) + os.makedirs(dst, mode) + except OSError as (err, stderr): + if err == errno.EEXIST: + pass + else: + print >> sys.stderr, "Error creating %s" % dst + exit(1) + + try: + selinux.setfscreatecon_raw(None) + except: + print >> sys.stderr, "Could not reset fs create context. May need to relabel system." + +def create_file_from(src, dst): + if DEBUG: print "Making file %s" % dst + try: + con = selinux.lgetfilecon_raw(src)[1] + selinux.setfscreatecon_raw(con) + open(dst, 'a').close() + except OSError as (err, stderr): + print >> sys.stderr, "Error creating %s" % dst + exit(1) + + try: + selinux.setfscreatecon_raw(None) + except: + print >> sys.stderr, "Could not reset fs create context. May need to relabel system." + +def copy_module(store, name, con, 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 >> sys.stderr, "warning: %s has invalid extension, skipping" % name + return + try: + selinux.setfscreatecon_raw(con) + + if base: + root = oldstore_path(store) + else: + root = oldmodules_path(store) + + bottomdir = bottomdir_path(store) + + os.mkdir("%s/%s" % (bottomdir, file)) + + copy_with_context(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+", 0600) + efile.write("pp") + efile.close() + + except: + print >> sys.stderr, "Error installing module %s" % name + exit(1) + + try: + selinux.setfscreatecon_raw(None) + except: + print >> sys.stderr, "Could not reset fs create context. May need to relabel system." + +def disable_module(file, root, name, disabledmodules): + if DEBUG: print "Disabling %s" % name + (disabledname, disabledext) = os.path.splitext(file) + create_file_from(os.path.join(root, name), "%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_from(selinux.selinux_policy_root(), "%s/%s" % (newroot_path(), store), 0755) + create_dir_from(oldmodules, newstore, 0700) + create_dir_from(oldstore, newmodules, 0700) + create_dir_from(oldstore, bottomdir, 0700) + create_dir_from(oldstore, disabledmodules, 0700) + + # use whatever the file context of bottomdir is for the module directories + con = selinux.lgetfilecon_raw(bottomdir)[1] + + # Special case for base since it was in a different location + copy_module(store, "base.pp", con, 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_with_context(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 >> sys.stderr, "Error installing module %s, name conflicts with base" % name + exit(1) + elif ext == ".disabled": + disable_module(file, root, name, disabledmodules) + else: + copy_module(store, name, con, 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 >> sys.stderr, "Could not create semanage handle" + 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 >> sys.stderr, "SELinux policy is not managed or store cannot be accessed." + exit(1) + + rc = semanage.semanage_access_check(handle) + if rc < semanage.SEMANAGE_CAN_WRITE: + semanage.semanage_handle_destroy(handle) + print >> sys.stderr, "Cannot write to policy store." + exit(1) + + rc = semanage.semanage_connect(handle) + if rc < 0: + semanage.semanage_handle_destroy(handle) + print >> sys.stderr, "Could not establish semanage connection" + exit(1) + + semanage.semanage_set_rebuild(handle, 1) + + rc = semanage.semanage_begin_transaction(handle) + if rc < 0: + semanage.semanage_handle_destroy(handle) + print >> sys.stderr, "Could not begin transaction" + exit(1) + + rc = semanage.semanage_commit(handle) + if rc < 0: + print >> sys.stderr, "Could not commit transaction" + + semanage.semanage_handle_destroy(handle) + + +def oldroot_path(): + return "/etc/selinux" + +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 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)") + + (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" + + # 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.local", + "disable_dontaudit", + "preserve_tunables" ] + + + create_dir_from(oldroot_path(), newroot_path(), 0755) + + 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 >> sys.stderr, "warning: Policy type %s has already been migrated, but modules still exist in the old store. Skipping store." % store + continue + + migrate_store(store) + + if CLEAN is True: + def remove_error(function, path, execinfo): + print >> sys.stderr, "warning: Unable to remove old store modules directory %s. Cleaning failed." % oldmodules_path(store) + shutil.rmtree(oldmodules_path(store), onerror=remove_error) + + if NOREBUILD is False: + rebuild_policy() +