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