1#! /usr/bin/python -Es 2# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com> 3# Authors: Dan Walsh <dwalsh@redhat.com> 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 79 parser.add_option("-N", "--noreference", action="store_false", dest="refpolicy", 80 default=False, help="do not generate refpolicy style output") 81 parser.add_option("-v", "--verbose", action="store_true", dest="verbose", 82 default=False, help="explain generated output") 83 parser.add_option("-e", "--explain", action="store_true", dest="explain_long", 84 default=False, help="fully explain generated output") 85 parser.add_option("-t", "--type", help="only process messages with a type that matches this regex", 86 dest="type") 87 parser.add_option("--perm-map", dest="perm_map", help="file name of perm map") 88 parser.add_option("--interface-info", dest="interface_info", help="file name of interface information") 89 parser.add_option("--debug", dest="debug", action="store_true", default=False, 90 help="leave generated modules for -M") 91 parser.add_option("-w", "--why", dest="audit2why", action="store_true", default=(os.path.basename(sys.argv[0]) == "audit2why"), 92 help="Translates SELinux audit messages into a description of why the access was denied") 93 94 options, args = parser.parse_args() 95 96 # Make -d, -a, and -i conflict 97 if options.audit is True or options.boot: 98 if options.input is not None: 99 sys.stderr.write("error: --all/--boot conflicts with --input\n") 100 if options.dmesg is True: 101 sys.stderr.write("error: --all/--boot conflicts with --dmesg\n") 102 if options.input is not None and options.dmesg is True: 103 sys.stderr.write("error: --input conflicts with --dmesg\n") 104 105 # Turn on requires generation if a module name is given. Also verify 106 # the module name. 107 if options.module: 108 name = options.module 109 else: 110 name = options.module_package 111 if name: 112 options.requires = True 113 if not module.is_valid_name(name): 114 sys.stderr.write('error: module names must begin with a letter, optionally followed by letters, numbers, "-", "_", "."\n') 115 sys.exit(2) 116 117 # Make -M and -o conflict 118 if options.module_package: 119 if options.output: 120 sys.stderr.write("error: --module-package conflicts with --output\n") 121 sys.exit(2) 122 if options.module: 123 sys.stderr.write("error: --module-package conflicts with --module\n") 124 sys.exit(2) 125 126 self.__options = options 127 128 def __read_input(self): 129 parser = audit.AuditParser(last_load_only=self.__options.lastreload) 130 131 filename = None 132 messages = None 133 f = None 134 135 # Figure out what input we want 136 if self.__options.input is not None: 137 filename = self.__options.input 138 elif self.__options.dmesg: 139 messages = audit.get_dmesg_msgs() 140 elif self.__options.audit: 141 try: 142 messages = audit.get_audit_msgs() 143 except OSError as e: 144 sys.stderr.write('could not run ausearch - "%s"\n' % str(e)) 145 sys.exit(1) 146 elif self.__options.boot: 147 try: 148 messages = audit.get_audit_boot_msgs() 149 except OSError as e: 150 sys.stderr.write('could not run ausearch - "%s"\n' % str(e)) 151 sys.exit(1) 152 else: 153 # This is the default if no input is specified 154 f = sys.stdin 155 156 # Get the input 157 if filename is not None: 158 try: 159 f = open(filename) 160 except IOError as e: 161 sys.stderr.write('could not open file %s - "%s"\n' % (filename, str(e))) 162 sys.exit(1) 163 164 if f is not None: 165 parser.parse_file(f) 166 f.close() 167 168 if messages is not None: 169 parser.parse_string(messages) 170 171 self.__parser = parser 172 173 def __process_input(self): 174 if self.__options.type: 175 avcfilter = audit.AVCTypeFilter(self.__options.type) 176 self.__avs = self.__parser.to_access(avcfilter) 177 csfilter = audit.ComputeSidTypeFilter(self.__options.type) 178 self.__role_types = self.__parser.to_role(csfilter) 179 else: 180 self.__avs = self.__parser.to_access() 181 self.__role_types = self.__parser.to_role() 182 183 def __load_interface_info(self): 184 # Load interface info file 185 if self.__options.interface_info: 186 fn = self.__options.interface_info 187 else: 188 fn = defaults.interface_info() 189 try: 190 fd = open(fn) 191 except: 192 sys.stderr.write("could not open interface info [%s]\n" % fn) 193 sys.exit(1) 194 195 ifs = interfaces.InterfaceSet() 196 ifs.from_file(fd) 197 fd.close() 198 199 # Also load perm maps 200 if self.__options.perm_map: 201 fn = self.__options.perm_map 202 else: 203 fn = defaults.perm_map() 204 try: 205 fd = open(fn) 206 except: 207 sys.stderr.write("could not open perm map [%s]\n" % fn) 208 sys.exit(1) 209 210 perm_maps = objectmodel.PermMappings() 211 perm_maps.from_file(fd) 212 213 return (ifs, perm_maps) 214 215 def __output_modulepackage(self, writer, generator): 216 generator.set_module_name(self.__options.module_package) 217 filename = self.__options.module_package + ".te" 218 packagename = self.__options.module_package + ".pp" 219 220 try: 221 fd = open(filename, "w") 222 except IOError as e: 223 sys.stderr.write("could not write output file: %s\n" % str(e)) 224 sys.exit(1) 225 226 writer.write(generator.get_module(), fd) 227 fd.close() 228 229 mc = module.ModuleCompiler() 230 231 try: 232 mc.create_module_package(filename, self.__options.refpolicy) 233 except RuntimeError as e: 234 print(e) 235 sys.exit(1) 236 237 sys.stdout.write(_("******************** IMPORTANT ***********************\n")) 238 sys.stdout.write((_("To make this policy package active, execute:" + 239 "\n\nsemodule -i %s\n\n") % packagename)) 240 241 def __output_audit2why(self): 242 import selinux 243 import seobject 244 for i in self.__parser.avc_msgs: 245 rc = i.type 246 data = i.data 247 if rc >= 0: 248 print("%s\n\tWas caused by:" % i.message) 249 if rc == audit2why.ALLOW: 250 print("\t\tUnknown - would be allowed by active policy") 251 print("\t\tPossible mismatch between this policy and the one under which the audit message was generated.\n") 252 print("\t\tPossible mismatch between current in-memory boolean settings vs. permanent ones.\n") 253 continue 254 if rc == audit2why.DONTAUDIT: 255 print("\t\tUnknown - should be dontaudit'd by active policy") 256 print("\t\tPossible mismatch between this policy and the one under which the audit message was generated.\n") 257 print("\t\tPossible mismatch between current in-memory boolean settings vs. permanent ones.\n") 258 continue 259 if rc == audit2why.BOOLEAN: 260 if len(data) > 1: 261 print("\tOne of the following booleans was set incorrectly.") 262 for b in data: 263 print("\tDescription:\n\t%s\n" % seobject.boolean_desc(b[0])) 264 print("\tAllow access by executing:\n\t# setsebool -P %s %d" % (b[0], b[1])) 265 else: 266 print("\tThe boolean %s was set incorrectly. " % (data[0][0])) 267 print("\tDescription:\n\t%s\n" % seobject.boolean_desc(data[0][0])) 268 print("\tAllow access by executing:\n\t# setsebool -P %s %d" % (data[0][0], data[0][1])) 269 continue 270 271 if rc == audit2why.TERULE: 272 print("\t\tMissing type enforcement (TE) allow rule.\n") 273 print("\t\tYou can use audit2allow to generate a loadable module to allow this access.\n") 274 continue 275 276 if rc == audit2why.CONSTRAINT: 277 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" 278 print("#Constraint rule: \n\t" + data[0]) 279 for reason in data[1:]: 280 print("#\tPossible cause is the source %s and target %s are different.\n\b" % reason) 281 282 if rc == audit2why.RBAC: 283 print("\t\tMissing role allow rule.\n") 284 print("\t\tAdd an allow rule for the role pair.\n") 285 continue 286 287 audit2why.finish() 288 return 289 290 def __output(self): 291 292 if self.__options.audit2why: 293 try: 294 return self.__output_audit2why() 295 except RuntimeError as e: 296 print(e) 297 sys.exit(1) 298 299 g = policygen.PolicyGenerator() 300 301 g.set_gen_dontaudit(self.__options.dontaudit) 302 303 if self.__options.module: 304 g.set_module_name(self.__options.module) 305 306 # Interface generation 307 if self.__options.refpolicy: 308 ifs, perm_maps = self.__load_interface_info() 309 g.set_gen_refpol(ifs, perm_maps) 310 311 # Explanation 312 if self.__options.verbose: 313 g.set_gen_explain(policygen.SHORT_EXPLANATION) 314 if self.__options.explain_long: 315 g.set_gen_explain(policygen.LONG_EXPLANATION) 316 317 # Requires 318 if self.__options.requires: 319 g.set_gen_requires(True) 320 321 # Generate the policy 322 g.add_access(self.__avs) 323 g.add_role_types(self.__role_types) 324 325 # Output 326 writer = output.ModuleWriter() 327 328 # Module package 329 if self.__options.module_package: 330 self.__output_modulepackage(writer, g) 331 else: 332 # File or stdout 333 if self.__options.module: 334 g.set_module_name(self.__options.module) 335 336 if self.__options.output: 337 fd = open(self.__options.output, "a") 338 else: 339 fd = sys.stdout 340 writer.write(g.get_module(), fd) 341 342 def main(self): 343 try: 344 self.__parse_options() 345 if self.__options.policy: 346 audit2why.init(self.__options.policy) 347 else: 348 audit2why.init() 349 350 self.__read_input() 351 self.__process_input() 352 self.__output() 353 except KeyboardInterrupt: 354 sys.exit(0) 355 except ValueError as e: 356 print(e) 357 sys.exit(1) 358 except IOError as e: 359 print(e) 360 sys.exit(1) 361 362if __name__ == "__main__": 363 app = AuditToPolicy() 364 app.main() 365