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