• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#! /usr/bin/python -Es
2# Copyright (C) 2012-2013 Red Hat
3# AUTHOR: Miroslav Grepl <mgrepl@redhat.com>
4# AUTHOR: David Quigley <selinux@davequigley.com>
5# see file 'COPYING' for use and warranty information
6#
7# semanage is a tool for managing SELinux configuration files
8#
9#    This program is free software; you can redistribute it and/or
10#    modify it under the terms of the GNU General Public License as
11#    published by the Free Software Foundation; either version 2 of
12#    the License, or (at your option) any later version.
13#
14#    This program is distributed in the hope that it will be useful,
15#    but WITHOUT ANY WARRANTY; without even the implied warranty of
16#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17#    GNU General Public License for more details.
18#
19#    You should have received a copy of the GNU General Public License
20#    along with this program; if not, write to the Free Software
21#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22#                                        02111-1307  USA
23#
24#
25
26import traceback
27import argparse
28import seobject
29import sys
30PROGNAME = "policycoreutils"
31try:
32    import gettext
33    kwargs = {}
34    if sys.version_info < (3,):
35        kwargs['unicode'] = True
36    gettext.install(PROGNAME,
37                    localedir="/usr/share/locale",
38                    codeset='utf-8',
39                    **kwargs)
40except:
41    try:
42        import builtins
43        builtins.__dict__['_'] = str
44    except ImportError:
45        import __builtin__
46        __builtin__.__dict__['_'] = unicode
47
48# define custom usages for selected main actions
49usage_login = "semanage login [-h] [-n] [-N] [-S STORE] ["
50usage_login_dict = {' --add': ('-s SEUSER', '-r RANGE', 'LOGIN',), ' --modify': ('-s SEUSER', '-r RANGE', 'LOGIN',), ' --delete': ('LOGIN',), ' --list': ('-C',), ' --extract': ('',), ' --deleteall': ('',)}
51
52usage_fcontext = "semanage fcontext [-h] [-n] [-N] [-S STORE] ["
53usage_fcontext_dict = {' --add': ('(', '-t TYPE', '-f FTYPE', '-r RANGE', '-s SEUSER', '|', '-e EQUAL', ')', 'FILE_SPEC',), ' --delete': ('(', '-t TYPE', '-f FTYPE', '|', '-e EQUAL', ')', 'FILE_SPEC',), ' --modify': ('(', '-t TYPE', '-f FTYPE', '-r RANGE', '-s SEUSER', '|', '-e EQUAL', ')', 'FILE_SPEC',), ' --list': ('[-C]',), ' --extract': ('',), ' --deleteall': ('',)}
54
55usage_user = "semanage user [-h] [-n] [-N] [-S STORE] ["
56usage_user_dict = {' --add': ('(', '-L LEVEL', '-R ROLES', '-r RANGE', '-s SEUSER', 'selinux_name'')'), ' --delete': ('selinux_name',), ' --modify': ('(', '-L LEVEL', '-R ROLES', '-r RANGE', '-s SEUSER', 'selinux_name', ')'), ' --list': ('-C',), ' --extract': ('',), ' --deleteall': ('',)}
57
58usage_port = "semanage port [-h] [-n] [-N] [-S STORE] ["
59usage_port_dict = {' --add': ('-t TYPE', '-p PROTOCOL', '-r RANGE', '(', 'port_name', '|', 'port_range', ')'), ' --modify': ('-t TYPE', '-p PROTOCOL', '-r RANGE', '(', 'port_name', '|', 'port_range', ')'), ' --delete': ('-p PROTOCOL', '(', 'port_name', '|', 'port_range', ')'), ' --list': ('-C',), ' --extract': ('',), ' --deleteall': ('',)}
60
61usage_node = "semanage node [-h] [-n] [-N] [-S STORE] ["
62usage_node_dict = {' --add': ('-M NETMASK', '-p PROTOCOL', '-t TYPE', '-r RANGE', 'node'), ' --modify': ('-M NETMASK', '-p PROTOCOL', '-t TYPE', '-r RANGE', 'node'), ' --delete': ('-M NETMASK', '-p PROTOCOL', 'node'), ' --list': ('-C',), ' --extract': ('',), ' --deleteall': ('',)}
63
64usage_interface = "semanage interface [-h] [-n] [-N] [-S STORE] ["
65usage_interface_dict = {' --add': ('-t TYPE', '-r RANGE', 'interface'), ' --modify': ('-t TYPE', '-r RANGE', 'interface'), ' --delete': ('interface',), ' --list': ('-C',), ' --extract': ('',), ' --deleteall': ('',)}
66
67usage_boolean = "semanage boolean [-h] [-n] [-N] [-S STORE] ["
68usage_boolean_dict = {' --modify': ('(', '--on', '|', '--off', ')', 'boolean'), ' --list': ('-C',), '  --extract': ('',), ' --deleteall': ('',)}
69
70import sepolicy
71
72
73class CheckRole(argparse.Action):
74
75    def __call__(self, parser, namespace, value, option_string=None):
76        newval = getattr(namespace, self.dest)
77        if not newval:
78            newval = []
79        roles = sepolicy.get_all_roles()
80        for v in value.split():
81            if v not in roles:
82                raise ValueError("%s must be an SELinux role:\nValid roles: %s" % (v, ", ".join(roles)))
83            newval.append(v)
84        setattr(namespace, self.dest, newval)
85
86store = ''
87
88
89class SetStore(argparse.Action):
90
91    def __call__(self, parser, namespace, values, option_string=None):
92        global store
93        store = values
94        setattr(namespace, self.dest, values)
95
96
97class seParser(argparse.ArgumentParser):
98
99    def error(self, message):
100        if len(sys.argv) == 2:
101            self.print_help()
102        else:
103            self.print_usage()
104        self.exit(2, ('%s: error: %s\n') % (self.prog, message))
105
106
107class SetExportFile(argparse.Action):
108
109    def __call__(self, parser, namespace, values, option_string=None):
110        if values:
111            if values is not "-":
112                try:
113                    sys.stdout = open(values, 'w')
114                except:
115                    sys.stderr.write(traceback.format_exc())
116                    sys.exit(1)
117        setattr(namespace, self.dest, values)
118
119
120class SetImportFile(argparse.Action):
121
122    def __call__(self, parser, namespace, values, option_string=None):
123        if values and values is not "-":
124            try:
125                sys.stdin = open(values, 'r')
126            except IOError as e:
127                sys.stderr.write("%s: %s\n" % (e.__class__.__name__, str(e)))
128                sys.exit(1)
129        setattr(namespace, self.dest, values)
130
131# functions for OBJECT initialization
132
133
134def login_ini():
135    OBJECT = seobject.loginRecords(store)
136    return OBJECT
137
138
139def user_ini():
140    OBJECT = seobject.seluserRecords(store)
141    return OBJECT
142
143
144def port_ini():
145    OBJECT = seobject.portRecords(store)
146    return OBJECT
147
148
149def module_ini():
150    OBJECT = seobject.moduleRecords(store)
151    return OBJECT
152
153
154def interface_ini():
155    OBJECT = seobject.interfaceRecords(store)
156    return OBJECT
157
158
159def node_ini():
160    OBJECT = seobject.nodeRecords(store)
161    return OBJECT
162
163
164def fcontext_ini():
165    OBJECT = seobject.fcontextRecords(store)
166    return OBJECT
167
168
169def boolean_ini():
170    OBJECT = seobject.booleanRecords(store)
171    return OBJECT
172
173
174def permissive_ini():
175    OBJECT = seobject.permissiveRecords(store)
176    return OBJECT
177
178
179def dontaudit_ini():
180    OBJECT = seobject.dontauditClass(store)
181    return OBJECT
182
183# define dictonary for seobject OBEJCTS
184object_dict = {'login': login_ini, 'user': user_ini, 'port': port_ini, 'module': module_ini, 'interface': interface_ini, 'node': node_ini, 'fcontext': fcontext_ini, 'boolean': boolean_ini, 'permissive': permissive_ini, 'dontaudit': dontaudit_ini}
185
186
187def generate_custom_usage(usage_text, usage_dict):
188    # generate custom usage from given text and dictonary
189    sorted_keys = []
190    for i in usage_dict.keys():
191        sorted_keys.append(i)
192    sorted_keys.sort()
193    for k in sorted_keys:
194        usage_text += "%s %s |" % (k, (" ".join(usage_dict[k])))
195    usage_text = usage_text[:-1] + "]"
196    usage_text = _(usage_text)
197
198    return usage_text
199
200
201def handle_opts(args, dict, target_key):
202    # handle conflict and required options for given dictonary
203    # {action:[conflict_opts,require_opts]}
204
205    # first we need to catch conflicts
206    for k in args.__dict__.keys():
207        try:
208            if k in dict[target_key][0] and args.__dict__[k]:
209                print("%s option can not be used with --%s" % (target_key, k))
210                sys.exit(2)
211        except KeyError:
212            continue
213
214    for k in args.__dict__.keys():
215        try:
216            if k in dict[target_key][1] and not args.__dict__[k]:
217                print("%s option is needed for %s" % (k, target_key))
218                sys.exit(2)
219        except KeyError:
220            continue
221
222
223def handleLogin(args):
224    # {action:[conflict_opts,require_opts]}
225    login_args = {'list': [('login', 'seuser'), ('')], 'add': [('locallist'), ('seuser', 'login')], 'modify': [('locallist'), ('login')], 'delete': [('locallist'), ('login')], 'extract': [('locallist', 'login', 'seuser'), ('')], 'deleteall': [('locallist'), ('')]}
226
227    handle_opts(args, login_args, args.action)
228
229    OBJECT = object_dict['login']()
230    OBJECT.set_reload(args.noreload)
231
232    if args.action is "add":
233        OBJECT.add(args.login, args.seuser, args.range)
234    if args.action is "modify":
235        OBJECT.modify(args.login, args.seuser, args.range)
236    if args.action is "delete":
237        OBJECT.delete(args.login)
238    if args.action is "list":
239        OBJECT.list(args.noheading, args.locallist)
240    if args.action is "deleteall":
241        OBJECT.deleteall()
242    if args.action is "extract":
243        for i in OBJECT.customized():
244            print("login %s" % (str(i)))
245
246
247def parser_add_store(parser, name):
248    parser.add_argument('-S', '--store', action=SetStore, help=_("Select an alternate SELinux Policy Store to manage"))
249
250
251def parser_add_priority(parser, name):
252    parser.add_argument('-P', '--priority', type=int, default=400, help=_("Select a priority for module operations"))
253
254
255def parser_add_noheading(parser, name):
256    parser.add_argument('-n', '--noheading', action='store_false', default=True, help=_("Do not print heading when listing %s object types") % name)
257
258
259def parser_add_noreload(parser, name):
260    parser.add_argument('-N', '--noreload', action='store_false', default=True, help=_('Do not reload policy after commit'))
261
262
263def parser_add_locallist(parser, name):
264    parser.add_argument('-C', '--locallist', action='store_true', default=False, help=_("List %s local customizations") % name)
265
266
267def parser_add_add(parser, name):
268    parser.add_argument('-a', '--add', dest='action', action='store_const', const='add', help=_("Add a record of the %s object type") % name)
269
270
271def parser_add_type(parser, name):
272    parser.add_argument('-t', '--type', help=_('SELinux Type for the object'))
273
274
275def parser_add_level(parser, name):
276    parser.add_argument('-L', '--level', default='s0', help=_('Default SELinux Level for SELinux user, s0 Default. (MLS/MCS Systems only)'))
277
278
279def parser_add_range(parser, name):
280    parser.add_argument('-r', '--range', default="s0",
281                        help=_('''
282MLS/MCS Security Range (MLS/MCS Systems only)
283SELinux Range  for SELinux login mapping
284defaults to the SELinux user record range.
285SELinux Range for SELinux user defaults to s0.
286'''))
287
288
289def parser_add_proto(parser, name):
290    parser.add_argument('-p', '--proto', help=_('''
291    Protocol  for  the specified port (tcp|udp) or internet protocol
292    version for the specified node (ipv4|ipv6).
293'''))
294
295
296def parser_add_modify(parser, name):
297    parser.add_argument('-m', '--modify', dest='action', action='store_const', const='modify', help=_("Modify a record of the %s object type") % name)
298
299
300def parser_add_list(parser, name):
301    parser.add_argument('-l', '--list', dest='action', action='store_const', const='list', help=_("List records of the %s object type") % name)
302
303
304def parser_add_delete(parser, name):
305    parser.add_argument('-d', '--delete', dest='action', action='store_const', const='delete', help=_("Delete a record of the %s object type") % name)
306
307
308def parser_add_extract(parser, name):
309    parser.add_argument('-E', '--extract', dest='action', action='store_const', const='extract', help=_("Extract customizable commands, for use within a transaction"))
310
311
312def parser_add_deleteall(parser, name):
313    parser.add_argument('-D', '--deleteall', dest='action', action='store_const', const='deleteall', help=_('Remove all %s objects local customizations') % name)
314
315
316def parser_add_seuser(parser, name):
317    parser.add_argument('-s', '--seuser', default="", help=_("SELinux user name"))
318
319
320def setupLoginParser(subparsers):
321    generated_usage = generate_custom_usage(usage_login, usage_login_dict)
322    loginParser = subparsers.add_parser('login', usage=generated_usage, help=_("Manage login mappings between linux users and SELinux confined users"))
323    parser_add_locallist(loginParser, "login")
324    parser_add_noheading(loginParser, "login")
325    parser_add_noreload(loginParser, "login")
326    parser_add_store(loginParser, "login")
327    parser_add_range(loginParser, "login")
328
329    login_action = loginParser.add_mutually_exclusive_group(required=True)
330
331    parser_add_add(login_action, "login")
332    parser_add_delete(login_action, "login")
333    parser_add_modify(login_action, "login")
334    parser_add_list(login_action, "login")
335    parser_add_extract(login_action, "login")
336    parser_add_deleteall(login_action, "login")
337    parser_add_seuser(loginParser, "login")
338
339    loginParser.add_argument('login', nargs='?', default=None, help=_("login_name | %%groupname"))
340
341    loginParser.set_defaults(func=handleLogin)
342
343
344def handleFcontext(args):
345    fcontext_args = {'list': [('equal', 'ftype', 'seuser', 'type'), ('')], 'add': [('locallist'), ('type', 'file_spec')], 'modify': [('locallist'), ('type', 'file_spec')], 'delete': [('locallist'), ('file_spec')], 'extract': [('locallist', 'equal', 'ftype', 'seuser', 'type'), ('')], 'deleteall': [('locallist'), ('')]}
346    # we can not use mutually for equal because we can define some actions together with equal
347    fcontext_equal_args = {'equal': [('list', 'locallist', 'type', 'ftype', 'seuser', 'deleteall', 'extract'), ()]}
348
349    if args.action and args.equal:
350        handle_opts(args, fcontext_equal_args, "equal")
351    else:
352        handle_opts(args, fcontext_args, args.action)
353
354    OBJECT = object_dict['fcontext']()
355    OBJECT.set_reload(args.noreload)
356
357    if args.action is "add":
358        if args.equal:
359            OBJECT.add_equal(args.file_spec, args.equal)
360        else:
361            OBJECT.add(args.file_spec, args.type, args.ftype, args.range, args.seuser)
362    if args.action is "modify":
363        if args.equal:
364            OBJECT.add_equal(args.file_spec, args.equal)
365        else:
366            OBJECT.modify(args.file_spec, args.type, args.ftype, args.range, args.seuser)
367    if args.action is "delete":
368        if args.equal:
369            OBJECT.delete(args.file_spec, args.equal)
370        else:
371            OBJECT.delete(args.file_spec, args.ftype)
372    if args.action is "list":
373        OBJECT.list(args.noheading, args.locallist)
374    if args.action is "deleteall":
375        OBJECT.deleteall()
376    if args.action is "extract":
377        for i in OBJECT.customized():
378            print("fcontext %s" % str(i))
379
380
381def setupFcontextParser(subparsers):
382    ftype_help = '''
383File Type.   This is used with fcontext.  Requires a  file  type
384as  shown  in  the  mode  field by ls, e.g. use d to match only
385directories or f to match only regular files. The following
386file type options can be passed:
387f (regular file),d (directory),c (character device),
388b (block device),s (socket),l (symbolic link),p (named pipe)
389If you do not specify a file type, the file type will default to "all files".
390'''
391    generate_usage = generate_custom_usage(usage_fcontext, usage_fcontext_dict)
392    fcontextParser = subparsers.add_parser('fcontext', usage=generate_usage, help=_("Manage file context mapping definitions"))
393    parser_add_locallist(fcontextParser, "fcontext")
394    parser_add_noheading(fcontextParser, "fcontext")
395    parser_add_noreload(fcontextParser, "fcontext")
396    parser_add_store(fcontextParser, "fcontext")
397
398    fcontext_action = fcontextParser.add_mutually_exclusive_group(required=True)
399    parser_add_add(fcontext_action, "fcontext")
400    parser_add_delete(fcontext_action, "fcontext")
401    parser_add_modify(fcontext_action, "fcontext")
402    parser_add_list(fcontext_action, "fcontext")
403    parser_add_extract(fcontext_action, "fcontext")
404    parser_add_deleteall(fcontext_action, "fcontext")
405
406    fcontextParser.add_argument('-e', '--equal', help=_('''Substitute  target  path with sourcepath when generating default
407                                                                  label.  This is used with fcontext. Requires source  and  target
408                                                                  path  arguments.  The context labeling for the target subtree is
409                                                                  made equivalent to that defined for the source.'''))
410    fcontextParser.add_argument('-f', '--ftype', default="", choices=["a", "f", "d", "c", "b", "s", "l", "p"], help=_(ftype_help))
411    parser_add_seuser(fcontextParser, "fcontext")
412    parser_add_type(fcontextParser, "fcontext")
413    parser_add_range(fcontextParser, "fcontext")
414    fcontextParser.add_argument('file_spec', nargs='?', default=None, help=_('file_spec'))
415    fcontextParser.set_defaults(func=handleFcontext)
416
417
418def handleUser(args):
419    user_args = {'list': [('selinux_name', 'seuser', 'roles'), ('')], 'add': [('locallist'), ('roles', 'selinux_name')], 'modify': [('locallist'), ('selinux_name')], 'delete': [('locallist'), ('selinux_name')], 'extract': [('locallist', 'selinux_name', 'seuser', 'role'), ('')], 'deleteall': [('locallist'), ('')]}
420
421    handle_opts(args, user_args, args.action)
422
423    OBJECT = object_dict['user']()
424    OBJECT.set_reload(args.noreload)
425
426    if args.action is "add":
427        OBJECT.add(args.selinux_name, args.roles, args.level, args.range, args.prefix)
428    if args.action is "modify":
429        OBJECT.modify(args.selinux_name, args.roles, args.level, args.range, args.prefix)
430    if args.action is "delete":
431        OBJECT.delete(args.selinux_name)
432    if args.action is "list":
433        OBJECT.list(args.noheading, args.locallist)
434    if args.action is "deleteall":
435        OBJECT.deleteall()
436    if args.action is "extract":
437        for i in OBJECT.customized():
438            print("user %s" % str(i))
439
440
441def setupUserParser(subparsers):
442    generated_usage = generate_custom_usage(usage_user, usage_user_dict)
443    userParser = subparsers.add_parser('user', usage=generated_usage, help=_('Manage SELinux confined users (Roles and levels for an SELinux user)'))
444    parser_add_locallist(userParser, "user")
445    parser_add_noheading(userParser, "user")
446    parser_add_noreload(userParser, "user")
447    parser_add_store(userParser, "user")
448
449    user_action = userParser.add_mutually_exclusive_group(required=True)
450    parser_add_add(user_action, "user")
451    parser_add_delete(user_action, "user")
452    parser_add_modify(user_action, "user")
453    parser_add_list(user_action, "user")
454    parser_add_extract(user_action, "user")
455    parser_add_deleteall(user_action, "user")
456
457    parser_add_level(userParser, "user")
458    parser_add_range(userParser, "user")
459    userParser.add_argument('-R', '--roles', default=[],
460                            action=CheckRole,
461                            help=_('''
462SELinux Roles.  You must enclose multiple roles within quotes,                  separate by spaces. Or specify -R multiple times.
463'''))
464    userParser.add_argument('-P', '--prefix', default="user", help=argparse.SUPPRESS)
465    userParser.add_argument('selinux_name', nargs='?', default=None, help=_('selinux_name'))
466    userParser.set_defaults(func=handleUser)
467
468
469def handlePort(args):
470    port_args = {'list': [('port', 'type', 'proto'), ('')], 'add': [('locallist'), ('type', 'port', 'proto')], 'modify': [('localist'), ('port', 'proto')], 'delete': [('locallist'), ('port', 'proto')], 'extract': [('locallist', 'port', 'type', 'proto'), ('')], 'deleteall': [('locallist'), ('')]}
471
472    handle_opts(args, port_args, args.action)
473
474    OBJECT = object_dict['port']()
475    OBJECT.set_reload(args.noreload)
476
477    if args.action is "add":
478        OBJECT.add(args.port, args.proto, args.range, args.type)
479    if args.action is "modify":
480        OBJECT.modify(args.port, args.proto, args.range, args.type)
481    if args.action is "delete":
482        OBJECT.delete(args.port, args.proto)
483    if args.action is "list":
484        OBJECT.list(args.noheading, args.locallist)
485    if args.action is "deleteall":
486        OBJECT.deleteall()
487    if args.action is "extract":
488        for i in OBJECT.customized():
489            print("port %s" % str(i))
490
491
492def setupPortParser(subparsers):
493    generated_usage = generate_custom_usage(usage_port, usage_port_dict)
494    portParser = subparsers.add_parser('port', usage=generated_usage, help=_('Manage network port type definitions'))
495    parser_add_locallist(portParser, "port")
496    parser_add_noheading(portParser, "port")
497    parser_add_noreload(portParser, "port")
498    parser_add_store(portParser, "port")
499
500    port_action = portParser.add_mutually_exclusive_group(required=True)
501    parser_add_add(port_action, "port")
502    parser_add_delete(port_action, "port")
503    parser_add_modify(port_action, "port")
504    parser_add_list(port_action, "port")
505    parser_add_extract(port_action, "port")
506    parser_add_deleteall(port_action, "port")
507    parser_add_type(portParser, "port")
508    parser_add_range(portParser, "port")
509    parser_add_proto(portParser, "port")
510    portParser.add_argument('port', nargs='?', default=None, help=_('port | port_range'))
511    portParser.set_defaults(func=handlePort)
512
513
514def handleInterface(args):
515    interface_args = {'list': [('interface'), ('')], 'add': [('locallist'), ('type', 'interface')], 'modify': [('locallist'), ('type', 'interface')], 'delete': [('locallist'), ('interface')], 'extract': [('locallist', 'interface', 'type'), ('')], 'deleteall': [('locallist'), ('')]}
516
517    handle_opts(args, interface_args, args.action)
518
519    OBJECT = object_dict['interface']()
520    OBJECT.set_reload(args.noreload)
521
522    if args.action is "add":
523        OBJECT.add(args.interface, args.range, args.type)
524    if args.action is "modify":
525        OBJECT.modify(args.interface, args.range, args.type)
526    if args.action is "delete":
527        OBJECT.delete(args.interface)
528    if args.action is "list":
529        OBJECT.list(args.noheading, args.locallist)
530    if args.action is "deleteall":
531        OBJECT.deleteall()
532    if args.action is "extract":
533        for i in OBJECT.customized():
534            print("interface %s" % str(i))
535
536
537def setupInterfaceParser(subparsers):
538    generated_usage = generate_custom_usage(usage_interface, usage_interface_dict)
539    interfaceParser = subparsers.add_parser('interface', usage=generated_usage, help=_('Manage network interface type definitions'))
540    parser_add_locallist(interfaceParser, "interface")
541    parser_add_noheading(interfaceParser, "interface")
542    parser_add_noreload(interfaceParser, "interface")
543    parser_add_store(interfaceParser, "interface")
544    parser_add_type(interfaceParser, "interface")
545    parser_add_range(interfaceParser, "interface")
546
547    interface_action = interfaceParser.add_mutually_exclusive_group(required=True)
548    parser_add_add(interface_action, "interface")
549    parser_add_delete(interface_action, "interface")
550    parser_add_modify(interface_action, "interface")
551    parser_add_list(interface_action, "interface")
552    parser_add_extract(interface_action, "interface")
553    parser_add_deleteall(interface_action, "interface")
554    interfaceParser.add_argument('interface', nargs='?', default=None, help=_('interface_spec'))
555    interfaceParser.set_defaults(func=handleInterface)
556
557
558def handleModule(args):
559    OBJECT = seobject.moduleRecords(store)
560    OBJECT.set_reload(args.noreload)
561    if args.action == "add":
562        OBJECT.add(args.module_name, args.priority)
563    if args.action == "enable":
564        OBJECT.set_enabled(args.module_name, True)
565    if args.action == "disable":
566        OBJECT.set_enabled(args.module_name, False)
567    if args.action == "remove":
568        OBJECT.delete(args.module_name, args.priority)
569    if args.action is "deleteall":
570        OBJECT.deleteall()
571    if args.action == "list":
572        OBJECT.list(args.noheading, args.locallist)
573    if args.action is "extract":
574        for i in OBJECT.customized():
575            print("module %s" % str(i))
576
577
578def setupModuleParser(subparsers):
579    moduleParser = subparsers.add_parser('module', help=_('Manage SELinux policy modules'))
580    parser_add_noheading(moduleParser, "module")
581    parser_add_noreload(moduleParser, "module")
582    parser_add_store(moduleParser, "module")
583    parser_add_locallist(moduleParser, "module")
584    parser_add_priority(moduleParser, "module")
585
586    mgroup = moduleParser.add_mutually_exclusive_group(required=True)
587    parser_add_add(mgroup, "module")
588    parser_add_list(mgroup, "module")
589    parser_add_extract(mgroup, "module")
590    parser_add_deleteall(mgroup, "module")
591    mgroup.add_argument('-r', '--remove', dest='action', action='store_const', const='remove', help=_("Remove a module"))
592    mgroup.add_argument('-d', '--disable', dest='action', action='store_const', const='disable', help=_("Disable a module"))
593    mgroup.add_argument('-e', '--enable', dest='action', action='store_const', const='enable', help=_("Enable a module"))
594    moduleParser.add_argument('module_name', nargs='?', default=None, help=_('Name of the module to act on'))
595    moduleParser.set_defaults(func=handleModule)
596
597
598def handleNode(args):
599    node_args = {'list': [('node', 'type', 'proto', 'netmask'), ('')], 'add': [('locallist'), ('type', 'node', 'proto', 'netmask')], 'modify': [('locallist'), ('node', 'netmask', 'proto')], 'delete': [('locallist'), ('node', 'netmask', 'prototype')], 'extract': [('locallist', 'node', 'type', 'proto', 'netmask'), ('')], 'deleteall': [('locallist'), ('')]}
600    handle_opts(args, node_args, args.action)
601
602    OBJECT = object_dict['node']()
603    OBJECT.set_reload(args.noreload)
604
605    if args.action is "add":
606        OBJECT.add(args.node, args.netmask, args.proto, args.range, args.type)
607    if args.action is "modify":
608        OBJECT.modify(args.node, args.netmask, args.proto, args.range, args.type)
609    if args.action is "delete":
610        OBJECT.delete(args.node, args.netmask, args.proto)
611    if args.action is "list":
612        OBJECT.list(args.noheading, args.locallist)
613    if args.action is "deleteall":
614        OBJECT.deleteall()
615    if args.action is "extract":
616        for i in OBJECT.customized():
617            print("node %s" % str(i))
618
619
620def setupNodeParser(subparsers):
621    generated_usage = generate_custom_usage(usage_node, usage_node_dict)
622    nodeParser = subparsers.add_parser('node', usage=generated_usage, help=_('Manage network node type definitions'))
623    parser_add_locallist(nodeParser, "node")
624    parser_add_noheading(nodeParser, "node")
625    parser_add_noreload(nodeParser, "node")
626    parser_add_store(nodeParser, "node")
627
628    node_action = nodeParser.add_mutually_exclusive_group(required=True)
629    parser_add_add(node_action, "node")
630    parser_add_delete(node_action, "node")
631    parser_add_modify(node_action, "node")
632    parser_add_list(node_action, "node")
633    parser_add_extract(node_action, "node")
634    parser_add_deleteall(node_action, "node")
635
636    nodeParser.add_argument('-M', '--netmask', help=_('Network Mask'))
637    parser_add_type(nodeParser, "node")
638    parser_add_range(nodeParser, "node")
639    parser_add_proto(nodeParser, "node")
640    nodeParser.add_argument('node', nargs='?', default=None, help=_('node'))
641    nodeParser.set_defaults(func=handleNode)
642
643
644def handleBoolean(args):
645    boolean_args = {'list': [('state', 'boolean'), ('')], 'modify': [('localist'), ('boolean', 'state')], 'extract': [('locallist', 'state', 'boolean'), ('')], 'deleteall': [('locallist'), ('')], 'state': [('locallist', 'list', 'extract', 'deleteall'), ('modify')]}
646
647    handle_opts(args, boolean_args, args.action)
648
649    OBJECT = object_dict['boolean']()
650    OBJECT.set_reload(args.noreload)
651
652    if args.action is "modify":
653        if args.boolean:
654            OBJECT.modify(args.boolean, args.state, False)
655    if args.action is "list":
656        OBJECT.list(args.noheading, args.locallist)
657    if args.action is "deleteall":
658        OBJECT.deleteall()
659    if args.action is "extract":
660        for i in OBJECT.customized():
661            print("boolean %s" % str(i))
662
663
664def setupBooleanParser(subparsers):
665    generated_usage = generate_custom_usage(usage_boolean, usage_boolean_dict)
666    booleanParser = subparsers.add_parser('boolean', usage=generated_usage, help=_('Manage booleans to selectively enable functionality'))
667    parser_add_locallist(booleanParser, "boolean")
668    parser_add_noheading(booleanParser, "boolean")
669    parser_add_noreload(booleanParser, "boolean")
670    parser_add_store(booleanParser, "boolean")
671    booleanParser.add_argument('boolean', nargs="?", default=None, help=_('boolean'))
672
673    boolean_action = booleanParser.add_mutually_exclusive_group(required=True)
674    #add_add(boolean_action)
675    parser_add_modify(boolean_action, "boolean")
676    parser_add_list(boolean_action, "boolean")
677    parser_add_extract(boolean_action, "boolean")
678    parser_add_deleteall(boolean_action, "boolean")
679
680    booleanGroup = booleanParser.add_mutually_exclusive_group(required=False)
681    booleanGroup.add_argument('-1', '--on', dest='state', action='store_const', const='on', help=_('Enable the boolean'))
682    booleanGroup.add_argument('-0', '--off', dest='state', action='store_const', const='off', help=_('Disable the boolean'))
683
684    booleanParser.set_defaults(func=handleBoolean)
685
686
687def handlePermissive(args):
688    OBJECT = object_dict['permissive']()
689    OBJECT.set_reload(args.noreload)
690
691    if args.action is "list":
692        OBJECT.list(args.noheading)
693    elif args.type is not None:
694        if args.action is "add":
695            OBJECT.add(args.type)
696        if args.action is "delete":
697            OBJECT.delete(args.type)
698    else:
699        args.parser.print_usage(sys.stderr)
700        sys.stderr.write(_('semanage permissive: error: the following argument is required: type\n'))
701        sys.exit(1)
702
703
704def setupPermissiveParser(subparsers):
705    permissiveParser = subparsers.add_parser('permissive', help=_('Manage process type enforcement mode'))
706
707    pgroup = permissiveParser.add_mutually_exclusive_group(required=True)
708    parser_add_add(pgroup, "permissive")
709    parser_add_delete(pgroup, "permissive")
710    parser_add_list(pgroup, "permissive")
711    #TODO: probably should be also added => need to implement own option handling
712    #parser_add_deleteall(pgroup)
713
714    parser_add_noheading(permissiveParser, "permissive")
715    parser_add_noreload(permissiveParser, "permissive")
716    parser_add_store(permissiveParser, "permissive")
717    permissiveParser.add_argument('type', nargs='?', default=None, help=_('type'))
718    permissiveParser.set_defaults(func=handlePermissive)
719    permissiveParser.set_defaults(parser=permissiveParser)
720
721
722def handleDontaudit(args):
723    OBJECT = object_dict['dontaudit']()
724    OBJECT.set_reload(args.noreload)
725    OBJECT.toggle(args.action)
726
727
728def setupDontauditParser(subparsers):
729    dontauditParser = subparsers.add_parser('dontaudit', help=_('Disable/Enable dontaudit rules in policy'))
730    parser_add_noreload(dontauditParser, "dontaudit")
731    parser_add_store(dontauditParser, "dontaudit")
732    dontauditParser.add_argument('action', choices=["on", "off"])
733    dontauditParser.set_defaults(func=handleDontaudit)
734
735
736def handleExport(args):
737    manageditems = ["boolean", "login", "interface", "user", "port", "node", "fcontext", "module"]
738    for i in manageditems:
739        print("%s -D" % i)
740    for i in manageditems:
741        OBJECT = object_dict[i]()
742        for c in OBJECT.customized():
743            print("%s %s" % (i, str(c)))
744
745    sys.exit(0)
746
747
748def setupExportParser(subparsers):
749    exportParser = subparsers.add_parser('export', help=_('Output local customizations'))
750    parser_add_store(exportParser, "export")
751    exportParser.add_argument('-f', '--output_file', dest='output_file', action=SetExportFile, help=_('Output file'))
752    exportParser.set_defaults(func=handleExport)
753
754import re
755
756
757def mkargv(line):
758    dquote = "\""
759    squote = "\'"
760    l = line.split()
761    ret = []
762    i = 0
763    while i < len(l):
764        cnt = len(re.findall(dquote, l[i]))
765        if cnt > 1:
766            ret.append(l[i].strip(dquote))
767            i = i + 1
768            continue
769        if cnt == 1:
770            quote = [l[i].strip(dquote)]
771            i = i + 1
772
773            while i < len(l) and dquote not in l[i]:
774                quote.append(l[i])
775                i = i + 1
776            quote.append(l[i].strip(dquote))
777            ret.append(" ".join(quote))
778            i = i + 1
779            continue
780
781        cnt = len(re.findall(squote, l[i]))
782        if cnt > 1:
783            ret.append(l[i].strip(squote))
784            i = i + 1
785            continue
786        if cnt == 1:
787            quote = [l[i].strip(squote)]
788            i = i + 1
789            while i < len(l) and squote not in l[i]:
790                quote.append(l[i])
791                i = i + 1
792
793            quote.append(l[i].strip(squote))
794            ret.append(" ".join(quote))
795            i = i + 1
796            continue
797
798        ret.append(l[i])
799        i = i + 1
800
801    return ret
802
803
804def handleImport(args):
805    trans = seobject.semanageRecords(store)
806    trans.start()
807
808    for l in sys.stdin.readlines():
809        if len(l.strip()) == 0:
810            continue
811
812        try:
813            commandParser = createCommandParser()
814            args = commandParser.parse_args(mkargv(l))
815            args.func(args)
816        except ValueError as e:
817            sys.stderr.write("%s: %s\n" % (e.__class__.__name__, str(e)))
818            sys.exit(1)
819        except IOError as e:
820            sys.stderr.write("%s: %s\n" % (e.__class__.__name__, str(e)))
821            sys.exit(1)
822        except KeyboardInterrupt:
823            sys.exit(0)
824
825    trans.set_reload(args.noreload)
826    trans.finish()
827
828
829def setupImportParser(subparsers):
830    importParser = subparsers.add_parser('import', help=_('Import local customizations'))
831    parser_add_noreload(importParser, "import")
832    parser_add_store(importParser, "import")
833    importParser.add_argument('-f', '--input_file', dest='input_file', action=SetImportFile, help=_('Input file'))
834    importParser.set_defaults(func=handleImport)
835
836
837def createCommandParser():
838    commandParser = seParser(prog='semanage',
839                             formatter_class=argparse.ArgumentDefaultsHelpFormatter,
840                             description='''semanage is used to configure certain elements
841                                                            of SELinux policy with-out requiring modification
842                                                            to or recompilation from policy source.''')
843
844    #To add a new subcommand define the parser for it in a function above and call it here.
845    subparsers = commandParser.add_subparsers(dest='subcommand')
846    subparsers.required = True
847    setupImportParser(subparsers)
848    setupExportParser(subparsers)
849    setupLoginParser(subparsers)
850    setupUserParser(subparsers)
851    setupPortParser(subparsers)
852    setupInterfaceParser(subparsers)
853    setupModuleParser(subparsers)
854    setupNodeParser(subparsers)
855    setupFcontextParser(subparsers)
856    setupBooleanParser(subparsers)
857    setupPermissiveParser(subparsers)
858    setupDontauditParser(subparsers)
859
860    return commandParser
861
862
863def make_io_args(args):
864    # import/export backward compability
865    args_origin = ["-S", "-o", "-i", "targeted", "minimum", "mls"]
866    args_file = []
867    args_ie = []
868    args_subcommand = []
869
870    for i in args:
871        if i == "-o":
872            args_subcommand = ["export"]
873            continue
874        if i == "-i":
875            args_subcommand = ["import"]
876            continue
877        if i not in args_origin:
878            args_file = ["-f", i]
879            continue
880        args_ie.append(i)
881
882    return args_subcommand + args_ie + args_file
883
884
885def make_args(sys_args):
886    args = []
887    if "-o" in sys_args[1:] or "-i" in sys_args[1:]:
888        args = make_io_args(sys_args[1:])
889    else:
890        args = sys_args[1:]
891
892    return args
893
894
895def do_parser():
896    try:
897        commandParser = createCommandParser()
898        args = commandParser.parse_args(make_args(sys.argv))
899        args.func(args)
900        sys.exit(0)
901    except IOError as e:
902        sys.stderr.write("%s: %s\n" % (e.__class__.__name__, str(e)))
903        sys.exit(1)
904    except KeyboardInterrupt:
905        sys.exit(0)
906    except ValueError as e:
907        sys.stderr.write("%s: %s\n" % (e.__class__.__name__, e.args[0]))
908        sys.exit(1)
909    except KeyError as e:
910        sys.stderr.write("%s: %s\n" % (e.__class__.__name__, e.args[0]))
911        sys.exit(1)
912    except OSError as e:
913        sys.stderr.write("%s: %s\n" % (e.__class__.__name__, e.args[1]))
914        sys.exit(1)
915    except RuntimeError as e:
916        sys.stderr.write("%s: %s\n" % (e.__class__.__name__, e.args[0]))
917        sys.exit(1)
918
919if __name__ == '__main__':
920    do_parser()
921