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