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