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, os 23 24import sepolgen.audit as audit 25import sepolgen.policygen as policygen 26import sepolgen.interfaces as interfaces 27import sepolgen.output as output 28import sepolgen.objectmodel as objectmodel 29import sepolgen.defaults as defaults 30import sepolgen.module as module 31from sepolgen.sepolgeni18n import _ 32import selinux.audit2why as audit2why 33import locale 34locale.setlocale(locale.LC_ALL, '') 35 36class 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:" 274 print "\n\t" + data[0] 275 for reason in data[1:]: 276 print "#\tPossible cause is the source %s and target %s are different.\n" % reason 277 278 if rc == audit2why.RBAC: 279 print "\t\tMissing role allow rule.\n" 280 print "\t\tAdd an allow rule for the role pair.\n" 281 continue 282 283 audit2why.finish() 284 return 285 286 def __output(self): 287 288 if self.__options.audit2why: 289 try: 290 return self.__output_audit2why() 291 except RuntimeError, e: 292 print e 293 sys.exit(1) 294 295 g = policygen.PolicyGenerator() 296 297 g.set_gen_dontaudit(self.__options.dontaudit) 298 299 if self.__options.module: 300 g.set_module_name(self.__options.module) 301 302 # Interface generation 303 if self.__options.refpolicy: 304 ifs, perm_maps = self.__load_interface_info() 305 g.set_gen_refpol(ifs, perm_maps) 306 307 # Explanation 308 if self.__options.verbose: 309 g.set_gen_explain(policygen.SHORT_EXPLANATION) 310 if self.__options.explain_long: 311 g.set_gen_explain(policygen.LONG_EXPLANATION) 312 313 # Requires 314 if self.__options.requires: 315 g.set_gen_requires(True) 316 317 # Generate the policy 318 g.add_access(self.__avs) 319 g.add_role_types(self.__role_types) 320 321 # Output 322 writer = output.ModuleWriter() 323 324 # Module package 325 if self.__options.module_package: 326 self.__output_modulepackage(writer, g) 327 else: 328 # File or stdout 329 if self.__options.module: 330 g.set_module_name(self.__options.module) 331 332 if self.__options.output: 333 fd = open(self.__options.output, "a") 334 else: 335 fd = sys.stdout 336 writer.write(g.get_module(), fd) 337 338 def main(self): 339 try: 340 self.__parse_options() 341 if self.__options.policy: 342 audit2why.init(self.__options.policy) 343 else: 344 audit2why.init() 345 346 self.__read_input() 347 self.__process_input() 348 self.__output() 349 except KeyboardInterrupt: 350 sys.exit(0) 351 except ValueError, e: 352 print e 353 sys.exit(1) 354 except IOError, e: 355 print e 356 sys.exit(1) 357 358if __name__ == "__main__": 359 app = AuditToPolicy() 360 app.main() 361