Home | History | Annotate | Download | only in utils
      1 #!/usr/bin/python3 -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 
     12 try:
     13     import selinux
     14     import semanage
     15 except ImportError:
     16     print("You must install libselinux-python and libsemanage-python before running this tool", file=sys.stderr)
     17     exit(1)
     18 
     19 
     20 def 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 
     31 def 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 
     45 def 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 
     56 def 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 
     86 def 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 
     93 def 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 
    139 def 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 
    184 def oldroot_path():
    185     return "%s/etc/selinux" % ROOT
    186 
    187 
    188 def oldstore_path(store):
    189     return "%s/%s/modules/active" % (oldroot_path(), store)
    190 
    191 
    192 def oldmodules_path(store):
    193     return "%s/modules" % oldstore_path(store)
    194 
    195 
    196 def disabledmodules_path(store):
    197     return "%s/disabled" % newmodules_path(store)
    198 
    199 
    200 def newroot_path():
    201     return "%s%s" % (ROOT, PATH)
    202 
    203 
    204 def newstore_path(store):
    205     return "%s/%s/active" % (newroot_path(), store)
    206 
    207 
    208 def newmodules_path(store):
    209     return "%s/modules" % newstore_path(store)
    210 
    211 
    212 def bottomdir_path(store):
    213     return "%s/%s" % (newmodules_path(store), PRIORITY)
    214 
    215 
    216 if __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