• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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