297 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
| #!/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()
 |