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