Home | History | Annotate | Download | only in utils
      1 #!/usr/bin/python -E
      2 
      3 
      4 from __future__ import print_function
      5 import os
      6 import errno
      7 import shutil
      8 import sys
      9 from optparse import OptionParser
     10 
     11 import ctypes
     12 
     13 sepol = ctypes.cdll.LoadLibrary('libsepol.so.1')
     14 
     15 try:
     16 	import selinux
     17 	import semanage
     18 except:
     19 	print("You must install libselinux-python and libsemanage-python before running this tool", file=sys.stderr)
     20 	exit(1)
     21 
     22 
     23 def copy_file(src, dst):
     24 	if DEBUG:
     25 		print("copying %s to %s" % (src, dst))
     26 	try:
     27 		shutil.copy(src, dst)
     28 	except OSError as the_err:
     29 		(err, strerr) = the_err.args
     30 		print("Could not copy %s to %s, %s" %(src, dst, strerr), file=sys.stderr)
     31 		exit(1)
     32 
     33 
     34 def create_dir(dst, mode):
     35 	if DEBUG: print("Making directory %s" % dst)
     36 	try:
     37 		os.makedirs(dst, mode)
     38 	except OSError as the_err:
     39 		(err, stderr) = the_err.args
     40 		if err == errno.EEXIST:
     41 			pass
     42 		else:
     43 			print("Error creating %s" % dst, file=sys.stderr)
     44 			exit(1)
     45 
     46 
     47 def create_file(dst):
     48 	if DEBUG: print("Making file %s" % dst)
     49 	try:
     50 		open(dst, 'a').close()
     51 	except OSError as the_err:
     52 		(err, stderr) = the_err.args
     53 		print("Error creating %s" % dst, file=sys.stderr)
     54 		exit(1)
     55 
     56 
     57 def copy_module(store, name, base):
     58 	if DEBUG: print("Install module %s" % name)
     59 	(file, ext) = os.path.splitext(name)
     60 	if ext != ".pp":
     61 		# Stray non-pp file in modules directory, skip
     62 		print("warning: %s has invalid extension, skipping" % name, file=sys.stderr)
     63 		return
     64 	try:
     65 		if base:
     66 			root = oldstore_path(store)
     67 		else:
     68 			root = oldmodules_path(store)
     69 
     70 		bottomdir = bottomdir_path(store)
     71 
     72 		os.mkdir("%s/%s" % (bottomdir, file))
     73 
     74 		copy_file(os.path.join(root, name), "%s/%s/hll" % (bottomdir, file))
     75 
     76 		# This is the ext file that will eventually be used to choose a compiler
     77 		efile = open("%s/%s/lang_ext" % (bottomdir, file), "w+", 0o600)
     78 		efile.write("pp")
     79 		efile.close()
     80 
     81 	except:
     82 		print("Error installing module %s" % name, file=sys.stderr)
     83 		exit(1)
     84 
     85 
     86 def disable_module(file, name, disabledmodules):
     87 	if DEBUG: print("Disabling %s" % name)
     88 	(disabledname, disabledext) = os.path.splitext(file)
     89 	create_file("%s/%s" % (disabledmodules, disabledname))
     90 
     91 def migrate_store(store):
     92 
     93 	oldstore = oldstore_path(store);
     94 	oldmodules = oldmodules_path(store);
     95 	disabledmodules = disabledmodules_path(store);
     96 	newstore = newstore_path(store);
     97 	newmodules = newmodules_path(store);
     98 	bottomdir = bottomdir_path(store);
     99 
    100 	print("Migrating from %s to %s" % (oldstore, newstore))
    101 
    102 	# Build up new directory structure
    103 	create_dir("%s/%s" % (newroot_path(), store), 0o755)
    104 	create_dir(newstore, 0o700)
    105 	create_dir(newmodules, 0o700)
    106 	create_dir(bottomdir, 0o700)
    107 	create_dir(disabledmodules, 0o700)
    108 
    109 	# Special case for base since it was in a different location
    110 	copy_module(store, "base.pp", 1)
    111 
    112 	# Dir structure built, start copying files
    113 	for root, dirs, files in os.walk(oldstore):
    114 		if root == oldstore:
    115 			# This is the top level directory, need to move
    116 			for name in files:
    117 				# Check to see if it is in TOPPATHS and copy if so
    118 				if name in TOPPATHS:
    119 					if name == "seusers":
    120 						newname = "seusers.local"
    121 					else:
    122 						newname = name
    123 					copy_file(os.path.join(root, name), os.path.join(newstore, newname))
    124 
    125 		elif root == oldmodules:
    126 			# This should be the modules directory
    127 			for name in files:
    128 				(file, ext) = os.path.splitext(name)
    129 				if name == "base.pp":
    130 					print("Error installing module %s, name conflicts with base" % name, file=sys.stderr)
    131 					exit(1)
    132 				elif ext == ".disabled":
    133 					disable_module(file, name, disabledmodules)
    134 				else:
    135 					copy_module(store, name, 0)
    136 
    137 def rebuild_policy():
    138 	# Ok, the modules are loaded, lets try to rebuild the policy
    139 	print("Attempting to rebuild policy from %s" % newroot_path())
    140 
    141 	curstore = selinux.selinux_getpolicytype()[1]
    142 
    143 	handle = semanage.semanage_handle_create()
    144 	if not handle:
    145 		print("Could not create semanage handle", file=sys.stderr)
    146 		exit(1)
    147 
    148 	semanage.semanage_select_store(handle, curstore, semanage.SEMANAGE_CON_DIRECT)
    149 
    150 	if not semanage.semanage_is_managed(handle):
    151 		semanage.semanage_handle_destroy(handle)
    152 		print("SELinux policy is not managed or store cannot be accessed.", file=sys.stderr)
    153 		exit(1)
    154 
    155 	rc = semanage.semanage_access_check(handle)
    156 	if rc < semanage.SEMANAGE_CAN_WRITE:
    157 		semanage.semanage_handle_destroy(handle)
    158 		print("Cannot write to policy store.", file=sys.stderr)
    159 		exit(1)
    160 
    161 	rc = semanage.semanage_connect(handle)
    162 	if rc < 0:
    163 		semanage.semanage_handle_destroy(handle)
    164 		print("Could not establish semanage connection", file=sys.stderr)
    165 		exit(1)
    166 
    167 	semanage.semanage_set_rebuild(handle, 1)
    168 
    169 	rc = semanage.semanage_begin_transaction(handle)
    170 	if rc < 0:
    171 		semanage.semanage_handle_destroy(handle)
    172 		print("Could not begin transaction", file=sys.stderr)
    173 		exit(1)
    174 
    175 	rc = semanage.semanage_commit(handle)
    176 	if rc < 0:
    177 		print("Could not commit transaction", file=sys.stderr)
    178 
    179 	semanage.semanage_handle_destroy(handle)
    180 
    181 
    182 def oldroot_path():
    183 	return "%s/etc/selinux" % ROOT
    184 
    185 def oldstore_path(store):
    186 	return "%s/%s/modules/active" % (oldroot_path(), store)
    187 
    188 def oldmodules_path(store):
    189 	return "%s/modules" % oldstore_path(store)
    190 
    191 def disabledmodules_path(store):
    192 	return "%s/disabled" % newmodules_path(store)
    193 
    194 def newroot_path():
    195 	return "%s%s" % (ROOT, PATH)
    196 
    197 def newstore_path(store):
    198 	return "%s/%s/active" % (newroot_path(), store)
    199 
    200 def newmodules_path(store):
    201 	return "%s/modules" % newstore_path(store)
    202 
    203 def bottomdir_path(store):
    204 	return "%s/%s" % (newmodules_path(store), PRIORITY)
    205 
    206 
    207 if __name__ == "__main__":
    208 
    209 	parser = OptionParser()
    210 	parser.add_option("-p", "--priority", dest="priority", default="100",
    211 			  help="Set priority of modules in new store (default: 100)")
    212 	parser.add_option("-s", "--store", dest="store", default=None,
    213 			  help="Store to read from and write to")
    214 	parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False,
    215 			  help="Output debug information")
    216 	parser.add_option("-c", "--clean", dest="clean", action="store_true", default=False,
    217 			  help="Clean old modules directory after migrate (default: no)")
    218 	parser.add_option("-n", "--norebuild", dest="norebuild", action="store_true", default=False,
    219 			  help="Disable rebuilding policy after migration (default: no)")
    220 	parser.add_option("-P", "--path", dest="path",
    221 			  help="Set path for the policy store (default: /var/lib/selinux)")
    222 	parser.add_option("-r", "--root", dest="root",
    223 			  help="Set an alternative root for the migration (default: /)")
    224 
    225 	(options, args) = parser.parse_args()
    226 
    227 	DEBUG = options.debug
    228 	PRIORITY = options.priority
    229 	TYPE = options.store
    230 	CLEAN = options.clean
    231 	NOREBUILD = options.norebuild
    232 	PATH = options.path
    233 	if PATH is None:
    234 		PATH = "/var/lib/selinux"
    235 
    236 	ROOT = options.root
    237 	if ROOT is None:
    238 		ROOT = ""
    239 
    240 	# List of paths that go in the active 'root'
    241 	TOPPATHS = [
    242 		"commit_num",
    243 		"ports.local",
    244 		"interfaces.local",
    245 		"nodes.local",
    246 		"booleans.local",
    247 		"file_contexts.local",
    248 		"seusers",
    249 		"users.local",
    250 		"users_extra",
    251 		"users_extra.local",
    252 		"disable_dontaudit",
    253 		"preserve_tunables",
    254 		"policy.kern",
    255 		"file_contexts",
    256 		"homedir_template"]
    257 
    258 
    259 	create_dir(newroot_path(), 0o755)
    260 
    261 	stores = None
    262 	if TYPE is not None:
    263 		stores = [TYPE]
    264 	else:
    265 		stores = os.listdir(oldroot_path())
    266 
    267 	# find stores in oldroot and migrate them to newroot if necessary
    268 	for store in stores:
    269 		if not os.path.isdir(oldmodules_path(store)):
    270 			# already migrated or not an selinux store
    271 			continue
    272 
    273 		if os.path.isdir(newstore_path(store)):
    274 			# store has already been migrated, but old modules dir still exits
    275 			print("warning: Policy type %s has already been migrated, but modules still exist in the old store. Skipping store." % store, file=sys.stderr)
    276 			continue
    277 
    278 		migrate_store(store)
    279 
    280 		if CLEAN is True:
    281 			def remove_error(function, path, execinfo):
    282 				print("warning: Unable to remove old store modules directory %s. Cleaning failed." % oldmodules_path(store), file=sys.stderr)
    283 			shutil.rmtree(oldmodules_path(store), onerror=remove_error)
    284 
    285 	if NOREBUILD is False:
    286 		rebuild_policy()
    287 
    288