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