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