Home | History | Annotate | Download | only in audit2allow
      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