1#!/usr/bin/python3 -EsI 2# Authors: Karl MacMillan <[email protected]> 3# Authors: Dan Walsh <[email protected]> 4# 5# Copyright (C) 2006-2013 Red Hat 6# see file 'COPYING' for use and warranty information 7# 8# This program is free software; you can redistribute it and/or 9# modify it under the terms of the GNU General Public License as 10# published by the Free Software Foundation; version 2 only 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program; if not, write to the Free Software 19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20# 21 22import sys 23import os 24 25import sepolgen.audit as audit 26import sepolgen.policygen as policygen 27import sepolgen.interfaces as interfaces 28import sepolgen.output as output 29import sepolgen.objectmodel as objectmodel 30import sepolgen.defaults as defaults 31import sepolgen.module as module 32from sepolgen.sepolgeni18n import _ 33import selinux.audit2why as audit2why 34import locale 35try: 36 locale.setlocale(locale.LC_ALL, '') 37except: 38 pass 39 40 41class AuditToPolicy: 42 VERSION = "%prog .1" 43 SYSLOG = "/var/log/messages" 44 45 def __init__(self): 46 self.__options = None 47 self.__parser = None 48 self.__avs = None 49 50 def __parse_options(self): 51 from optparse import OptionParser 52 53 parser = OptionParser(version=self.VERSION) 54 parser.add_option("-b", "--boot", action="store_true", dest="boot", default=False, 55 help="audit messages since last boot conflicts with -i") 56 parser.add_option("-a", "--all", action="store_true", dest="audit", default=False, 57 help="read input from audit log - conflicts with -i") 58 parser.add_option("-p", "--policy", dest="policy", default=None, help="Policy file to use for analysis") 59 parser.add_option("-d", "--dmesg", action="store_true", dest="dmesg", default=False, 60 help="read input from dmesg - conflicts with --all and --input") 61 parser.add_option("-i", "--input", dest="input", 62 help="read input from <input> - conflicts with -a") 63 parser.add_option("-l", "--lastreload", action="store_true", dest="lastreload", default=False, 64 help="read input only after the last reload") 65 parser.add_option("-r", "--requires", action="store_true", dest="requires", default=False, 66 help="generate require statements for rules") 67 parser.add_option("-m", "--module", dest="module", 68 help="set the module name - implies --requires") 69 parser.add_option("-M", "--module-package", dest="module_package", 70 help="generate a module package - conflicts with -o and -m") 71 parser.add_option("-o", "--output", dest="output", 72 help="append output to <filename>, conflicts with -M") 73 parser.add_option("-D", "--dontaudit", action="store_true", 74 dest="dontaudit", default=False, 75 help="generate policy with dontaudit rules") 76 parser.add_option("-R", "--reference", action="store_true", dest="refpolicy", 77 default=True, help="generate refpolicy style output") 78 parser.add_option("-C", "--cil", action="store_true", dest="cil", help="generate CIL output") 79 80 parser.add_option("-N", "--noreference", action="store_false", dest="refpolicy", 81 default=False, help="do not generate refpolicy style output") 82 parser.add_option("-v", "--verbose", action="store_true", dest="verbose", 83 default=False, help="explain generated output") 84 parser.add_option("-e", "--explain", action="store_true", dest="explain_long", 85 default=False, help="fully explain generated output") 86 parser.add_option("-t", "--type", help="only process messages with a type that matches this regex", 87 dest="type") 88 parser.add_option("--perm-map", dest="perm_map", help="file name of perm map") 89 parser.add_option("--interface-info", dest="interface_info", help="file name of interface information") 90 parser.add_option("-x", "--xperms", action="store_true", dest="xperms", 91 default=False, help="generate extended permission rules") 92 parser.add_option("-w", "--why", dest="audit2why", action="store_true", default=(os.path.basename(sys.argv[0]) == "audit2why"), 93 help="Translates SELinux audit messages into a description of why the access was denied") 94 95 options, args = parser.parse_args() 96 97 # Make -d, -a, and -i conflict 98 if options.audit is True or options.boot: 99 if options.input is not None: 100 sys.stderr.write("error: --all/--boot conflicts with --input\n") 101 if options.dmesg is True: 102 sys.stderr.write("error: --all/--boot conflicts with --dmesg\n") 103 if options.input is not None and options.dmesg is True: 104 sys.stderr.write("error: --input conflicts with --dmesg\n") 105 106 # Turn on requires generation if a module name is given. Also verify 107 # the module name. 108 if options.module: 109 name = options.module 110 else: 111 name = options.module_package 112 if name: 113 options.requires = True 114 if not module.is_valid_name(name): 115 sys.stderr.write('error: module names must begin with a letter, optionally followed by letters, numbers, "-", "_", "."\n') 116 sys.exit(2) 117 118 # Make -M and -o or -C conflict 119 if options.module_package: 120 if options.output: 121 sys.stderr.write("error: --module-package conflicts with --output\n") 122 sys.exit(2) 123 if options.module: 124 sys.stderr.write("error: --module-package conflicts with --module\n") 125 sys.exit(2) 126 if options.cil: 127 sys.stderr.write("error: --module-package conflicts with --cil\n") 128 sys.exit(2) 129 130 self.__options = options 131 132 def __read_input(self): 133 parser = audit.AuditParser(last_load_only=self.__options.lastreload) 134 135 filename = None 136 messages = None 137 f = None 138 139 # Figure out what input we want 140 if self.__options.input is not None: 141 filename = self.__options.input 142 elif self.__options.dmesg: 143 messages = audit.get_dmesg_msgs() 144 elif self.__options.audit: 145 try: 146 messages = audit.get_audit_msgs() 147 except OSError as e: 148 sys.stderr.write('could not run ausearch - "%s"\n' % str(e)) 149 sys.exit(1) 150 elif self.__options.boot: 151 try: 152 messages = audit.get_audit_boot_msgs() 153 except OSError as e: 154 sys.stderr.write('could not run ausearch - "%s"\n' % str(e)) 155 sys.exit(1) 156 else: 157 # This is the default if no input is specified 158 f = sys.stdin 159 160 # Get the input 161 if filename is not None: 162 try: 163 f = open(filename) 164 except IOError as e: 165 sys.stderr.write('could not open file %s - "%s"\n' % (filename, str(e))) 166 sys.exit(1) 167 168 if f is not None: 169 parser.parse_file(f) 170 f.close() 171 172 if messages is not None: 173 parser.parse_string(messages) 174 175 self.__parser = parser 176 177 def __process_input(self): 178 if self.__options.type: 179 avcfilter = audit.AVCTypeFilter(self.__options.type) 180 self.__avs = self.__parser.to_access(avcfilter) 181 csfilter = audit.ComputeSidTypeFilter(self.__options.type) 182 self.__role_types = self.__parser.to_role(csfilter) 183 else: 184 self.__avs = self.__parser.to_access() 185 self.__role_types = self.__parser.to_role() 186 187 def __load_interface_info(self): 188 # Load interface info file 189 if self.__options.interface_info: 190 fn = self.__options.interface_info 191 else: 192 fn = defaults.interface_info() 193 try: 194 fd = open(fn) 195 except: 196 sys.stderr.write("could not open interface info [%s]\n" % fn) 197 sys.exit(1) 198 199 ifs = interfaces.InterfaceSet() 200 ifs.from_file(fd) 201 fd.close() 202 203 # Also load perm maps 204 if self.__options.perm_map: 205 fn = self.__options.perm_map 206 else: 207 fn = defaults.perm_map() 208 try: 209 fd = open(fn) 210 except: 211 sys.stderr.write("could not open perm map [%s]\n" % fn) 212 sys.exit(1) 213 214 perm_maps = objectmodel.PermMappings() 215 perm_maps.from_file(fd) 216 217 return (ifs, perm_maps) 218 219 def __output_modulepackage(self, writer, generator): 220 generator.set_module_name(self.__options.module_package) 221 filename = self.__options.module_package + ".te" 222 packagename = self.__options.module_package + ".pp" 223 224 try: 225 fd = open(filename, "w") 226 except IOError as e: 227 sys.stderr.write("could not write output file: %s\n" % str(e)) 228 sys.exit(1) 229 230 writer.write(generator.get_module(), fd) 231 fd.close() 232 233 mc = module.ModuleCompiler() 234 235 try: 236 mc.create_module_package(filename, self.__options.refpolicy) 237 except RuntimeError as e: 238 print(e) 239 sys.exit(1) 240 241 sys.stdout.write( 242"""******************** {important} *********************** 243{text} 244 245semodule -i {packagename} 246 247""".format( 248 important=_("IMPORTANT"), 249 text=_("To make this policy package active, execute:"), 250 packagename=packagename 251)) 252 253 def __output_audit2why(self): 254 import selinux 255 try: 256 import sepolicy 257 except (ImportError, ValueError): 258 sepolicy = None 259 for i in self.__parser.avc_msgs: 260 rc = i.type 261 data = i.data 262 if rc >= 0: 263 print("%s\n\tWas caused by:" % i.message) 264 if rc == audit2why.ALLOW: 265 print("\t\tUnknown - would be allowed by active policy") 266 print("\t\tPossible mismatch between this policy and the one under which the audit message was generated.\n") 267 print("\t\tPossible mismatch between current in-memory boolean settings vs. permanent ones.\n") 268 continue 269 if rc == audit2why.DONTAUDIT: 270 print("\t\tUnknown - should be dontaudit'd by active policy") 271 print("\t\tPossible mismatch between this policy and the one under which the audit message was generated.\n") 272 print("\t\tPossible mismatch between current in-memory boolean settings vs. permanent ones.\n") 273 continue 274 if rc == audit2why.BOOLEAN: 275 if len(data) > 1: 276 print("\tOne of the following booleans was set incorrectly.") 277 for b in data: 278 if sepolicy is not None: 279 print("\tDescription:\n\t%s\n" % sepolicy.boolean_desc(b[0])) 280 print("\tAllow access by executing:\n\t# setsebool -P %s %d" % (b[0], b[1])) 281 else: 282 print("\tThe boolean %s was set incorrectly. " % (data[0][0])) 283 if sepolicy is not None: 284 print("\tDescription:\n\t%s\n" % sepolicy.boolean_desc(data[0][0])) 285 print("\tAllow access by executing:\n\t# setsebool -P %s %d" % (data[0][0], data[0][1])) 286 continue 287 288 if rc == audit2why.TERULE: 289 print("\t\tMissing type enforcement (TE) allow rule.\n") 290 print("\t\tYou can use audit2allow to generate a loadable module to allow this access.\n") 291 continue 292 293 if rc == audit2why.CONSTRAINT: 294 print() # !!!! This avc is a constraint violation. You would need to modify the attributes of either the source or target types to allow this access.\n" 295 print("#Constraint rule:") 296 print("\n#\t" + data[0]) 297 for reason in data[1:]: 298 print("#\tPossible cause is the source %s and target %s are different.\n" % reason) 299 300 if rc == audit2why.RBAC: 301 print("\t\tMissing role allow rule.\n") 302 print("\t\tAdd an allow rule for the role pair.\n") 303 continue 304 305 if rc == audit2why.BOUNDS: 306 print("\t\tTypebounds violation.\n") 307 print("\t\tAdd an allow rule for the parent type.\n") 308 continue 309 310 audit2why.finish() 311 return 312 313 def __output(self): 314 315 if self.__options.audit2why: 316 try: 317 return self.__output_audit2why() 318 except RuntimeError as e: 319 print(e) 320 sys.exit(1) 321 322 g = policygen.PolicyGenerator() 323 324 g.set_gen_dontaudit(self.__options.dontaudit) 325 326 if self.__options.module: 327 g.set_module_name(self.__options.module) 328 329 # Interface generation 330 if self.__options.refpolicy: 331 ifs, perm_maps = self.__load_interface_info() 332 g.set_gen_refpol(ifs, perm_maps) 333 334 # Extended permissions 335 if self.__options.xperms: 336 g.set_gen_xperms(True) 337 338 # Explanation 339 if self.__options.verbose: 340 g.set_gen_explain(policygen.SHORT_EXPLANATION) 341 if self.__options.explain_long: 342 g.set_gen_explain(policygen.LONG_EXPLANATION) 343 344 # Requires 345 if self.__options.requires: 346 g.set_gen_requires(True) 347 348 # CIL output 349 if self.__options.cil: 350 g.set_gen_cil(True) 351 352 # Generate the policy 353 g.add_access(self.__avs) 354 g.add_role_types(self.__role_types) 355 356 # Output 357 writer = output.ModuleWriter() 358 359 # CIL output 360 if self.__options.cil: 361 writer.set_gen_cil(True) 362 363 # Module package 364 if self.__options.module_package: 365 self.__output_modulepackage(writer, g) 366 else: 367 # File or stdout 368 if self.__options.module: 369 g.set_module_name(self.__options.module) 370 371 if self.__options.output: 372 fd = open(self.__options.output, "a") 373 else: 374 fd = sys.stdout 375 writer.write(g.get_module(), fd) 376 377 def main(self): 378 try: 379 self.__parse_options() 380 if self.__options.policy: 381 audit2why.init(self.__options.policy) 382 else: 383 audit2why.init() 384 385 self.__read_input() 386 self.__process_input() 387 self.__output() 388 except KeyboardInterrupt: 389 sys.exit(0) 390 except ValueError as e: 391 print(e) 392 sys.exit(1) 393 except IOError as e: 394 print(e) 395 sys.exit(1) 396 397if __name__ == "__main__": 398 app = AuditToPolicy() 399 app.main() 400