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