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