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