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:"
    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 
    358 if __name__ == "__main__":
    359     app = AuditToPolicy()
    360     app.main()
    361