Home | History | Annotate | Download | only in semanage
      1 #!/usr/bin/python3 -Es
      2 # Copyright (C) 2012-2013 Red Hat
      3 # AUTHOR: Miroslav Grepl <mgrepl (at] redhat.com>
      4 # AUTHOR: David Quigley <selinux (at] 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 
     26 import traceback
     27 import argparse
     28 import seobject
     29 import sys
     30 PROGNAME = "policycoreutils"
     31 try:
     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)
     40 except:
     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
     49 usage_login = "semanage login [-h] [-n] [-N] [-S STORE] ["
     50 usage_login_dict = {' --add': ('-s SEUSER', '-r RANGE', 'LOGIN',), ' --modify': ('-s SEUSER', '-r RANGE', 'LOGIN',), ' --delete': ('LOGIN',), ' --list': ('-C',), ' --extract': ('',), ' --deleteall': ('',)}
     51 
     52 usage_fcontext = "semanage fcontext [-h] [-n] [-N] [-S STORE] ["
     53 usage_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 
     55 usage_user = "semanage user [-h] [-n] [-N] [-S STORE] ["
     56 usage_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 
     58 usage_port = "semanage port [-h] [-n] [-N] [-S STORE] ["
     59 usage_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 
     61 usage_ibpkey = "semanage ibpkey [-h] [-n] [-N] [-s STORE] ["
     62 usage_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 
     64 usage_ibendport = "semanage ibendport [-h] [-n] [-N] [-s STORE] ["
     65 usage_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 
     67 usage_node = "semanage node [-h] [-n] [-N] [-S STORE] ["
     68 usage_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 
     70 usage_interface = "semanage interface [-h] [-n] [-N] [-S STORE] ["
     71 usage_interface_dict = {' --add': ('-t TYPE', '-r RANGE', 'interface'), ' --modify': ('-t TYPE', '-r RANGE', 'interface'), ' --delete': ('interface',), ' --list': ('-C',), ' --extract': ('',), ' --deleteall': ('',)}
     72 
     73 usage_boolean = "semanage boolean [-h] [-n] [-N] [-S STORE] ["
     74 usage_boolean_dict = {' --modify': ('(', '--on', '|', '--off', ')', 'boolean'), ' --list': ('-C',), '  --extract': ('',), ' --deleteall': ('',)}
     75 
     76 
     77 
     78 
     79 class 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 
     98 class 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 
    108 class 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 
    121 class 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
    133 object_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 
    148 def 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 
    162 def 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 
    184 def 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 
    207 def parser_add_store(parser, name):
    208     parser.add_argument('-S', '--store', default='', help=_("Select an alternate SELinux Policy Store to manage"))
    209 
    210 
    211 def parser_add_priority(parser, name):
    212     parser.add_argument('-P', '--priority', type=int, default=400, help=_("Select a priority for module operations"))
    213 
    214 
    215 def 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 
    219 def 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 
    223 def parser_add_locallist(parser, name):
    224     parser.add_argument('-C', '--locallist', action='store_true', default=False, help=_("List %s local customizations") % name)
    225 
    226 
    227 def 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 
    231 def parser_add_type(parser, name):
    232     parser.add_argument('-t', '--type', help=_('SELinux Type for the object'))
    233 
    234 
    235 def 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 
    239 def parser_add_range(parser, name):
    240     parser.add_argument('-r', '--range', default="s0",
    241                         help=_('''
    242 MLS/MCS Security Range (MLS/MCS Systems only)
    243 SELinux Range  for SELinux login mapping
    244 defaults to the SELinux user record range.
    245 SELinux Range for SELinux user defaults to s0.
    246 '''))
    247 
    248 
    249 def 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 
    255 def parser_add_subnet_prefix(parser, name):
    256     parser.add_argument('-x', '--subnet_prefix', help=_('''
    257     Subnet prefix for  the specified infiniband ibpkey.
    258 '''))
    259 
    260 def parser_add_ibdev_name(parser, name):
    261     parser.add_argument('-z', '--ibdev_name', help=_('''
    262     Name for the specified infiniband end port.
    263 '''))
    264 
    265 def 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 
    269 def 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 
    273 def 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 
    277 def 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 
    281 def 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 
    285 def parser_add_seuser(parser, name):
    286     parser.add_argument('-s', '--seuser', default="", help=_("SELinux user name"))
    287 
    288 
    289 def 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 
    313 def 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 
    349 def setupFcontextParser(subparsers):
    350     ftype_help = '''
    351 File Type.   This is used with fcontext.  Requires a  file  type
    352 as  shown  in  the  mode  field by ls, e.g. use d to match only
    353 directories or f to match only regular files. The following
    354 file type options can be passed:
    355 f (regular file),d (directory),c (character device),
    356 b (block device),s (socket),l (symbolic link),p (named pipe)
    357 If 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 
    386 def 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 
    408 def 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=_('''
    429 SELinux 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 
    436 def 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 
    458 def 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 
    481 def 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 
    503 def 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 
    524 def 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 
    546 def 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 
    567 def 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 
    589 def 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 
    610 def 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 
    629 def 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 
    648 def 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 
    669 def 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 
    693 def 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 
    712 def 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 
    735 def 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 
    749 def 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 
    767 def handleDontaudit(args):
    768     OBJECT = object_dict['dontaudit'](args)
    769     OBJECT.toggle(args.action)
    770 
    771 
    772 def 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 
    780 def 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 
    792 def 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 
    798 import re
    799 
    800 
    801 def 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 
    848 def 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 
    872 def 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 
    880 def 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 
    908 def 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 
    930 def 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 
    940 def 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 
    964 if __name__ == '__main__':
    965     do_parser()
    966