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