diff mbox series

[PR#420,v3] audit2allow: CIL output mode

Message ID 20240302205546.5706-2-toiwoton@gmail.com (mailing list archive)
State Superseded
Delegated to: Petr Lautrbach
Headers show
Series [PR#420,v3] audit2allow: CIL output mode | expand

Commit Message

Topi Miettinen March 2, 2024, 8:55 p.m. UTC
New flag -C for audit2allow sets output format to CIL instead of
Policy Language.

Example:
;============= mozilla_t ==============

;!!!! This avc is allowed in the current policy
(allow mozilla_t user_sudo_t (fd (use)))

;============= user_t ==============

;!!!! This avc can be allowed using the boolean 'allow_execmem'
(allow user_t self (process (execmem)))
(allow user_t chromium_t (process (noatsecure rlimitinh siginh)))

;!!!! This avc is a constraint violation.  You would need to modify the attributes of either the source or target types to allow this access.
;Constraint rule:
;       constrain dir { ioctl read write create getattr setattr lock relabelfrom relabelto append map unlink link rename execute quotaon mounton audit_access open execmod watch watch_mount watch_sb watch_with_perm watch_reads add_name remove_name reparent search rmdir } ((u1 == u2 -Fail-)  or (u1 == system_u -Fail-)  or (u1 == unconfined_u -Fail-)  or (u1 == sysadm_u -Fail-)  or (u2 == system_u -Fail-)  or (t1 != ubac_constrained_type -Fail-)  or (t2 != ubac_constrained_type -Fail-)  or (t1 == ubacfile -Fail-) ); Constraint DENIED

;       Possible cause is the source user (user_u) and target user (sysadm_u) are different.
(allow user_t user_home_dir_t (dir (getattr relabelto)))

Signed-off-by: Topi Miettinen <toiwoton@gmail.com>

---
v3: fixed extended permissions syntax
v2: fix uninitialized variable detected by CI
---
 python/audit2allow/audit2allow            |  14 +-
 python/audit2allow/audit2allow.1          |   3 +
 python/sepolgen/src/sepolgen/output.py    |   5 +
 python/sepolgen/src/sepolgen/policygen.py |  29 +-
 python/sepolgen/src/sepolgen/refpolicy.py | 314 +++++++++++++++++-----
 5 files changed, 279 insertions(+), 86 deletions(-)


base-commit: 82195e77e317d322dd9b5fc31d402462d6845357

Comments

James Carter March 18, 2024, 1:24 p.m. UTC | #1
On Sat, Mar 2, 2024 at 3:56 PM Topi Miettinen <toiwoton@gmail.com> wrote:
>
> New flag -C for audit2allow sets output format to CIL instead of
> Policy Language.
>
> Example:
> ;============= mozilla_t ==============
>
> ;!!!! This avc is allowed in the current policy
> (allow mozilla_t user_sudo_t (fd (use)))
>
> ;============= user_t ==============
>
> ;!!!! This avc can be allowed using the boolean 'allow_execmem'
> (allow user_t self (process (execmem)))
> (allow user_t chromium_t (process (noatsecure rlimitinh siginh)))
>
> ;!!!! This avc is a constraint violation.  You would need to modify the attributes of either the source or target types to allow this access.
> ;Constraint rule:
> ;       constrain dir { ioctl read write create getattr setattr lock relabelfrom relabelto append map unlink link rename execute quotaon mounton audit_access open execmod watch watch_mount watch_sb watch_with_perm watch_reads add_name remove_name reparent search rmdir } ((u1 == u2 -Fail-)  or (u1 == system_u -Fail-)  or (u1 == unconfined_u -Fail-)  or (u1 == sysadm_u -Fail-)  or (u2 == system_u -Fail-)  or (t1 != ubac_constrained_type -Fail-)  or (t2 != ubac_constrained_type -Fail-)  or (t1 == ubacfile -Fail-) ); Constraint DENIED
>
> ;       Possible cause is the source user (user_u) and target user (sysadm_u) are different.
> (allow user_t user_home_dir_t (dir (getattr relabelto)))
>
> Signed-off-by: Topi Miettinen <toiwoton@gmail.com>
>
> ---
> v3: fixed extended permissions syntax
> v2: fix uninitialized variable detected by CI
> ---
>  python/audit2allow/audit2allow            |  14 +-
>  python/audit2allow/audit2allow.1          |   3 +
>  python/sepolgen/src/sepolgen/output.py    |   5 +
>  python/sepolgen/src/sepolgen/policygen.py |  29 +-
>  python/sepolgen/src/sepolgen/refpolicy.py | 314 +++++++++++++++++-----
>  5 files changed, 279 insertions(+), 86 deletions(-)
>
> diff --git a/python/audit2allow/audit2allow b/python/audit2allow/audit2allow
> index 35b0b151..b5927ec1 100644
> --- a/python/audit2allow/audit2allow
> +++ b/python/audit2allow/audit2allow
> @@ -75,6 +75,7 @@ class AuditToPolicy:
>                            help="generate policy with dontaudit rules")
>          parser.add_option("-R", "--reference", action="store_true", dest="refpolicy",
>                            default=True, help="generate refpolicy style output")
> +        parser.add_option("-C", "--cil", action="store_true", dest="cil", help="generate CIL output")
>
>          parser.add_option("-N", "--noreference", action="store_false", dest="refpolicy",
>                            default=False, help="do not generate refpolicy style output")
> @@ -114,7 +115,7 @@ class AuditToPolicy:
>                  sys.stderr.write('error: module names must begin with a letter, optionally followed by letters, numbers, "-", "_", "."\n')
>                  sys.exit(2)
>
> -        # Make -M and -o conflict
> +        # Make -M and -o or -C conflict
>          if options.module_package:
>              if options.output:
>                  sys.stderr.write("error: --module-package conflicts with --output\n")
> @@ -122,6 +123,9 @@ class AuditToPolicy:
>              if options.module:
>                  sys.stderr.write("error: --module-package conflicts with --module\n")
>                  sys.exit(2)
> +            if options.cil:
> +                sys.stderr.write("error: --module-package conflicts with --cil\n")
> +                sys.exit(2)
>
>          self.__options = options
>
> @@ -341,6 +345,10 @@ semodule -i {packagename}
>          if self.__options.requires:
>              g.set_gen_requires(True)
>
> +        # CIL output
> +        if self.__options.cil:
> +            g.set_gen_cil(True)
> +
>          # Generate the policy
>          g.add_access(self.__avs)
>          g.add_role_types(self.__role_types)
> @@ -348,6 +356,10 @@ semodule -i {packagename}
>          # Output
>          writer = output.ModuleWriter()
>
> +        # CIL output
> +        if self.__options.cil:
> +            writer.set_gen_cil(True)
> +
>          # Module package
>          if self.__options.module_package:
>              self.__output_modulepackage(writer, g)
> diff --git a/python/audit2allow/audit2allow.1 b/python/audit2allow/audit2allow.1
> index c208b3b2..2834234d 100644
> --- a/python/audit2allow/audit2allow.1
> +++ b/python/audit2allow/audit2allow.1
> @@ -74,6 +74,9 @@ Generate module/require output <modulename>
>  .B "\-M <modulename>"
>  Generate loadable module package, conflicts with \-o
>  .TP
> +.B "\-C"
> +Generate CIL output, conflicts with \-M
> +.TP
>  .B "\-p <policyfile>"  | "\-\-policy <policyfile>"
>  Policy file to use for analysis
>  .TP
> diff --git a/python/sepolgen/src/sepolgen/output.py b/python/sepolgen/src/sepolgen/output.py
> index aeeaafc8..57380cad 100644
> --- a/python/sepolgen/src/sepolgen/output.py
> +++ b/python/sepolgen/src/sepolgen/output.py
> @@ -40,6 +40,7 @@ class ModuleWriter:
>          self.module = None
>          self.sort = True
>          self.requires = True
> +        self.gen_cil = False
>
>      def write(self, module, fd):
>          self.module = module
> @@ -49,8 +50,12 @@ class ModuleWriter:
>
>          # FIXME - make this handle nesting
>          for node, depth in refpolicy.walktree(self.module, showdepth=True):
> +            node.set_gen_cil(self.gen_cil)
>              fd.write("%s\n" % str(node))
>
> +    def set_gen_cil(self, gen_cil):
> +        self.gen_cil = gen_cil
> +
>  # Helper functions for sort_filter - this is all done old school
>  # C style rather than with polymorphic methods because this sorting
>  # is specific to output. It is not necessarily the comparison you
> diff --git a/python/sepolgen/src/sepolgen/policygen.py b/python/sepolgen/src/sepolgen/policygen.py
> index 183b41a9..93d02cf0 100644
> --- a/python/sepolgen/src/sepolgen/policygen.py
> +++ b/python/sepolgen/src/sepolgen/policygen.py
> @@ -86,6 +86,8 @@ class PolicyGenerator:
>          self.xperms = False
>
>          self.domains = None
> +        self.gen_cil = False
> +        self.comment_start = '#'
>      def set_gen_refpol(self, if_set=None, perm_maps=None):
>          """Set whether reference policy interfaces are generated.
>
> @@ -128,6 +130,10 @@ class PolicyGenerator:
>          """
>          self.xperms = xperms
>
> +    def set_gen_cil(self, gen_cil):
> +        self.gen_cil = gen_cil
> +        self.comment_start = ';'
> +

If gen_cil is false, then I think you want to set
self.comment_start="#". You seem to be assuming that gen_cil is always
true.

>      def __set_module_style(self):
>          if self.ifgen:
>              refpolicy = True
> @@ -173,26 +179,27 @@ class PolicyGenerator:
>              rule.comment = str(refpolicy.Comment(explain_access(av, verbosity=self.explain)))
>
>          if av.type == audit2why.ALLOW:
> -            rule.comment += "\n#!!!! This avc is allowed in the current policy"
> +            rule.comment += "\n%s!!!! This avc is allowed in the current policy" % self.comment_start
>
>              if av.xperms:
> -                rule.comment += "\n#!!!! This av rule may have been overridden by an extended permission av rule"
> +                rule.comment += "\n%s!!!! This av rule may have been overridden by an extended permission av rule" % self.comment_start
>
>          if av.type == audit2why.DONTAUDIT:
> -            rule.comment += "\n#!!!! This avc has a dontaudit rule in the current policy"
> +            rule.comment += "\n%s!!!! This avc has a dontaudit rule in the current policy" % self.comment_start
>
>          if av.type == audit2why.BOOLEAN:
>              if len(av.data) > 1:
> -                rule.comment += "\n#!!!! This avc can be allowed using one of the these booleans:\n#     %s" % ", ".join([x[0] for x in av.data])
> +                rule.comment += "\n%s!!!! This avc can be allowed using one of the these booleans:\n%s     %s" % (self.comment_start, self.comment_start, ", ".join([x[0] for x in av.data]))
>              else:
> -                rule.comment += "\n#!!!! This avc can be allowed using the boolean '%s'" % av.data[0][0]
> +                rule.comment += "\n%s!!!! This avc can be allowed using the boolean '%s'" % (self.comment_start, av.data[0][0])
>
>          if av.type == audit2why.CONSTRAINT:
> -            rule.comment += "\n#!!!! This avc is a constraint violation.  You would need to modify the attributes of either the source or target types to allow this access."
> -            rule.comment += "\n#Constraint rule: "
> -            rule.comment += "\n#\t" + av.data[0]
> +            rule.comment += "\n%s!!!! This avc is a constraint violation.  You would need to modify the attributes of either the source or target types to allow this access." % self.comment_start
> +            rule.comment += "\n%sConstraint rule: " % self.comment_start
> +            rule.comment += "\n%s\t" % self.comment_start + av.data[0]
>              for reason in av.data[1:]:
> -                rule.comment += "\n#\tPossible cause is the source %s and target %s are different." % reason
> +                rule.comment += "\n%s" % self.comment_start
> +                rule.comment += "\tPossible cause is the source %s and target %s are different." % reason
>
>          try:
>              if ( av.type == audit2why.TERULE and
> @@ -206,9 +213,9 @@ class PolicyGenerator:
>                      if i not in self.domains:
>                          types.append(i)
>                  if len(types) == 1:
> -                    rule.comment += "\n#!!!! The source type '%s' can write to a '%s' of the following type:\n# %s\n" % ( av.src_type, av.obj_class, ", ".join(types))
> +                    rule.comment += "\n%s!!!! The source type '%s' can write to a '%s' of the following type:\n# %s\n" % (self.comment_start, av.src_type, av.obj_class, ", ".join(types))
>                  elif len(types) >= 1:
> -                    rule.comment += "\n#!!!! The source type '%s' can write to a '%s' of the following types:\n# %s\n" % ( av.src_type, av.obj_class, ", ".join(types))
> +                    rule.comment += "\n%s!!!! The source type '%s' can write to a '%s' of the following types:\n# %s\n" % (self.comment_start, av.src_type, av.obj_class, ", ".join(types))

The '#' near the end of both of these lines are comments and need to
be replaced like in the previous chunk.

>          except:
>              pass
>
> diff --git a/python/sepolgen/src/sepolgen/refpolicy.py b/python/sepolgen/src/sepolgen/refpolicy.py
> index 9cac1b95..e7fa5645 100644
> --- a/python/sepolgen/src/sepolgen/refpolicy.py
> +++ b/python/sepolgen/src/sepolgen/refpolicy.py
> @@ -53,6 +53,7 @@ class PolicyBase:
>      def __init__(self, parent=None):
>          self.parent = None
>          self.comment = None
> +        self.gen_cil = False
>
>  class Node(PolicyBase):
>      """Base class objects produced from parsing the reference policy.
> @@ -150,6 +151,8 @@ class Node(PolicyBase):
>      def to_string(self):
>          return ""
>
> +    def set_gen_cil(self, gen_cil):
> +        self.gen_cil = gen_cil
>
>  class Leaf(PolicyBase):
>      def __init__(self, parent=None):
> @@ -167,6 +170,8 @@ class Leaf(PolicyBase):
>      def to_string(self):
>          return ""
>
> +    def set_gen_cil(self, gen_cil):
> +        self.gen_cil = gen_cil
>
>
>  # Utility functions
> @@ -413,6 +418,16 @@ class XpermSet():
>
>          return "%s{ %s }" % (compl, " ".join(vals))
>
> +    def to_string_cil(self):
> +        if not self.ranges:
> +            return ""
> +
> +        compl = ("not (", ")") if self.complement else ("", "")
> +
> +        vals = map(lambda x: hex(x[0]) if x[0] == x[1] else "(range %s %s)" % (hex(x[0]), hex(x[1]), ), self.ranges)
> +
> +        return "(%s%s%s)" % (compl[0], " ".join(vals), compl[1])
> +
>  # Basic statements
>
>  class TypeAttribute(Leaf):
> @@ -426,7 +441,13 @@ class TypeAttribute(Leaf):
>          self.attributes = IdSet()
>
>      def to_string(self):
> -        return "typeattribute %s %s;" % (self.type, self.attributes.to_comma_str())
> +        if self.gen_cil:
> +            s = ""
> +            for a in self.attributes:
> +                s += "(typeattribute %s %s)" % (self.type, a)

The typeattribute statement just declares the attribute, so you need
two statements (also, don't you want a '\n' at the end):
s += "(typeattribute %s)\n" % a
s += "typeattributeset %s %s)\n" % (a, self.type)

> +            return s
> +        else:
> +            return "typeattribute %s %s;" % (self.type, self.attributes.to_comma_str())
>
>  class RoleAttribute(Leaf):
>      """SElinux roleattribute statement.

Role attributes in CIL are just like type attributes, just replace
"type" with "role"

> @@ -451,7 +472,10 @@ class Role(Leaf):
>      def to_string(self):
>          s = ""
>          for t in self.types:
> -            s += "role %s types %s;\n" % (self.role, t)
> +            if self.gen_cil:
> +                s += "(roletype %s %s)\n" % (self.role, t)

This works for associating types to a role, but the role needs to be
declared as well. It should be similar to what is done for types
below.

if self.gen_cil:
  s = "(role %s)" % self.role
  for t in self.types:
    s += "(roletype %s %s)\n" % (self.role, t)

Unless it is assumed the role is declared elsewhere.

> +            else:
> +                s += "role %s types %s;\n" % (self.role, t)
>          return s
>
>  class Type(Leaf):
> @@ -462,12 +486,20 @@ class Type(Leaf):
>          self.aliases = IdSet()
>
>      def to_string(self):
> -        s = "type %s" % self.name
> -        if len(self.aliases) > 0:
> -            s = s + "alias %s" % self.aliases.to_space_str()
> -        if len(self.attributes) > 0:
> -            s = s + ", %s" % self.attributes.to_comma_str()
> -        return s + ";"
> +        if self.gen_cil:
> +            s = "(type %s)" % self.name
> +            for a in self.aliases:
> +                s += "(typealiasactual %s %s)\n" % (self.name, a)

The type alias comes first, so "(a, self.name)"

> +            for a in self.attributes:
> +                s += "(typeattributeset %s %s)\n" % (self.name, a)

Like above, the type attribute comes first.

> +            return s
> +        else:
> +            s = "type %s" % self.name
> +            if len(self.aliases) > 0:
> +                s = s + "alias %s" % self.aliases.to_space_str()
> +            if len(self.attributes) > 0:
> +                s = s + ", %s" % self.attributes.to_comma_str()
> +            return s + ";"
>
>  class TypeAlias(Leaf):
>      def __init__(self, parent=None):
> @@ -476,7 +508,13 @@ class TypeAlias(Leaf):
>          self.aliases = IdSet()
>
>      def to_string(self):
> -        return "typealias %s alias %s;" % (self.type, self.aliases.to_space_str())
> +        if self.gen_cil:
> +            s = ""
> +            for a in self.aliases:
> +                s += "(typealias %s %s)\n" % (self.type, a)

The typealias statement declares the alias.

You need:
s += "(typealias %s)\n" % a
s += "(typealiasactual %s %s)\n" % (a, self.type)

Thank you for doing this. Sorry I didn't catch some of these earlier.
Jim

> +            return s
> +        else:
> +            return "typealias %s alias %s;" % (self.type, self.aliases.to_space_str())
>
>  class Attribute(Leaf):
>      def __init__(self, name="", parent=None):
> @@ -484,7 +522,10 @@ class Attribute(Leaf):
>          self.name = name
>
>      def to_string(self):
> -        return "attribute %s;" % self.name
> +        if self.gen_cil:
> +            return "attribute %s;" % self.name
> +        else:
> +            return "(typeattribute %s)" % self.name
>
>  class Attribute_Role(Leaf):
>      def __init__(self, name="", parent=None):
> @@ -492,7 +533,10 @@ class Attribute_Role(Leaf):
>          self.name = name
>
>      def to_string(self):
> -        return "attribute_role %s;" % self.name
> +        if self.gen_cil:
> +            return "(roleattribute %s)" % self.name
> +        else:
> +            return "attribute_role %s;" % self.name
>
>
>  # Classes representing rules
> @@ -555,11 +599,21 @@ class AVRule(Leaf):
>          that is a valid policy language representation (assuming
>          that the types, object class, etc. are valie).
>          """
> -        return "%s %s %s:%s %s;" % (self.__rule_type_str(),
> -                                     self.src_types.to_space_str(),
> -                                     self.tgt_types.to_space_str(),
> -                                     self.obj_classes.to_space_str(),
> -                                     self.perms.to_space_str())
> +        if self.gen_cil:
> +            s = ""
> +            for src in self.src_types:
> +                for tgt in self.tgt_types:
> +                    for obj in self.obj_classes:
> +                        s += "(%s %s %s (%s (%s)))" % (self.__rule_type_str(),
> +                                                       src, tgt, obj,
> +                                                       " ".join(self.perms))
> +            return s
> +        else:
> +            return "%s %s %s:%s %s;" % (self.__rule_type_str(),
> +                                        self.src_types.to_space_str(),
> +                                        self.tgt_types.to_space_str(),
> +                                        self.obj_classes.to_space_str(),
> +                                        self.perms.to_space_str())
>
>  class AVExtRule(Leaf):
>      """Extended permission access vector rule.
> @@ -597,6 +651,16 @@ class AVExtRule(Leaf):
>          elif self.rule_type == self.NEVERALLOWXPERM:
>              return "neverallowxperm"
>
> +    def __rule_type_str_cil(self):
> +        if self.rule_type == self.ALLOWXPERM:
> +            return "allowx"
> +        elif self.rule_type == self.DONTAUDITXPERM:
> +            return "dontauditx"
> +        elif self.rule_type == self.AUDITALLOWXPERM:
> +            return "auditallowx"
> +        elif self.rule_type == self.NEVERALLOWXPERM:
> +            return "neverallowx"
> +
>      def from_av(self, av, op):
>          self.src_types.add(av.src_type)
>          if av.src_type == av.tgt_type:
> @@ -612,12 +676,25 @@ class AVExtRule(Leaf):
>          a valid policy language representation (assuming that
>          the types, object class, etc. are valid).
>          """
> -        return "%s %s %s:%s %s %s;" % (self.__rule_type_str(),
> -                                     self.src_types.to_space_str(),
> -                                     self.tgt_types.to_space_str(),
> -                                     self.obj_classes.to_space_str(),
> -                                     self.operation,
> -                                     self.xperms.to_string())
> +        if self.gen_cil:
> +            s = ""
> +            for src in self.src_types:
> +                for tgt in self.tgt_types:
> +                    for obj in self.obj_classes:
> +                        s += "(%s %s %s (%s %s %s))" % (self.__rule_type_str_cil(),
> +                                                        src, tgt,
> +                                                        self.operation,
> +                                                        obj,
> +                                                        self.xperms.to_string_cil())
> +            return s
> +        else:
> +            return "%s %s %s:%s %s %s;" % (self.__rule_type_str(),
> +                                           self.src_types.to_space_str(),
> +                                           self.tgt_types.to_space_str(),
> +                                           self.obj_classes.to_space_str(),
> +                                           self.operation,
> +                                           self.xperms.to_string())
> +
>
>  class TypeRule(Leaf):
>      """SELinux type rules.
> @@ -630,6 +707,7 @@ class TypeRule(Leaf):
>      TYPE_CHANGE = 1
>      TYPE_MEMBER = 2
>
> +    # NB. Filename type transitions are not generated by audit2allow.
>      def __init__(self, parent=None):
>          Leaf.__init__(self, parent)
>          self.src_types = IdSet()
> @@ -646,12 +724,28 @@ class TypeRule(Leaf):
>          else:
>              return "type_member"
>
> +    def __rule_type_str_cil(self):
> +        if self.rule_type == self.TYPE_TRANSITION:
> +            return "typetransition"
> +        elif self.rule_type == self.TYPE_CHANGE:
> +            return "typechange"
> +        else:
> +            return "typemember"
> +
>      def to_string(self):
> -        return "%s %s %s:%s %s;" % (self.__rule_type_str(),
> -                                     self.src_types.to_space_str(),
> -                                     self.tgt_types.to_space_str(),
> -                                     self.obj_classes.to_space_str(),
> -                                     self.dest_type)
> +        if self.gen_cil:
> +            return "(%s %s %s %s %s)" % (self.__rule_type_str_cil(),
> +                                         self.src_types.to_space_str(),
> +                                         self.tgt_types.to_space_str(),
> +                                         self.obj_classes.to_space_str(),
> +                                         self.dest_type)
> +        else:
> +            return "%s %s %s:%s %s;" % (self.__rule_type_str(),
> +                                        self.src_types.to_space_str(),
> +                                        self.tgt_types.to_space_str(),
> +                                        self.obj_classes.to_space_str(),
> +                                        self.dest_type)
> +
>  class TypeBound(Leaf):
>      """SElinux typebound statement.
>
> @@ -663,8 +757,13 @@ class TypeBound(Leaf):
>          self.tgt_types = IdSet()
>
>      def to_string(self):
> -        return "typebounds %s %s;" % (self.type, self.tgt_types.to_comma_str())
> -
> +        if self.gen_cil:
> +            s = ""
> +            for t in self.tgt_types:
> +                s += "(typebounds %s %s)" % (self.type, t)
> +            return s
> +        else:
> +            return "typebounds %s %s;" % (self.type, self.tgt_types.to_comma_str())
>
>  class RoleAllow(Leaf):
>      def __init__(self, parent=None):
> @@ -673,8 +772,15 @@ class RoleAllow(Leaf):
>          self.tgt_roles = IdSet()
>
>      def to_string(self):
> -        return "allow %s %s;" % (self.src_roles.to_comma_str(),
> -                                 self.tgt_roles.to_comma_str())
> +        if self.gen_cil:
> +            s = ""
> +            for src in self.src_roles:
> +                for tgt in self.tgt_roles:
> +                    s += "(roleallow %s %s)" % (src, tgt)
> +            return s
> +        else:
> +            return "allow %s %s;" % (self.src_roles.to_comma_str(),
> +                                     self.tgt_roles.to_comma_str())
>
>  class RoleType(Leaf):
>      def __init__(self, parent=None):
> @@ -685,7 +791,10 @@ class RoleType(Leaf):
>      def to_string(self):
>          s = ""
>          for t in self.types:
> -            s += "role %s types %s;\n" % (self.role, t)
> +            if self.gen_cil:
> +                s += "(roletype %s %s)\n" % (self.role, t)
> +            else:
> +                s += "role %s types %s;\n" % (self.role, t)
>          return s
>
>  class ModuleDeclaration(Leaf):
> @@ -696,10 +805,13 @@ class ModuleDeclaration(Leaf):
>          self.refpolicy = False
>
>      def to_string(self):
> -        if self.refpolicy:
> -            return "policy_module(%s, %s)" % (self.name, self.version)
> +        if self.gen_cil:
> +            return ""
>          else:
> -            return "module %s %s;" % (self.name, self.version)
> +            if self.refpolicy:
> +                return "policy_module(%s, %s)" % (self.name, self.version)
> +            else:
> +                return "module %s %s;" % (self.name, self.version)
>
>  class Conditional(Node):
>      def __init__(self, parent=None):
> @@ -729,7 +841,10 @@ class InitialSid(Leaf):
>          self.context = None
>
>      def to_string(self):
> -        return "sid %s %s" % (self.name, str(self.context))
> +        if self.gen_cil:
> +            return "(sid %s %s)" % (self.name, str(self.context))
> +        else:
> +            return "sid %s %s" % (self.name, str(self.context))
>
>  class GenfsCon(Leaf):
>      def __init__(self, parent=None):
> @@ -739,7 +854,10 @@ class GenfsCon(Leaf):
>          self.context = None
>
>      def to_string(self):
> -        return "genfscon %s %s %s" % (self.filesystem, self.path, str(self.context))
> +        if self.gen_cil:
> +            return "(genfscon %s %s %s)" % (self.filesystem, self.path, str(self.context))
> +        else:
> +            return "genfscon %s %s %s" % (self.filesystem, self.path, str(self.context))
>
>  class FilesystemUse(Leaf):
>      XATTR = 1
> @@ -754,14 +872,24 @@ class FilesystemUse(Leaf):
>
>      def to_string(self):
>          s = ""
> -        if self.type == self.XATTR:
> -            s = "fs_use_xattr "
> -        elif self.type == self.TRANS:
> -            s = "fs_use_trans "
> -        elif self.type == self.TASK:
> -            s = "fs_use_task "
> +        if self.gen_cil:
> +            if self.type == self.XATTR:
> +                s = "fsuse xattr "
> +            elif self.type == self.TRANS:
> +                s = "fsuse trans "
> +            elif self.type == self.TASK:
> +                s = "fsuse task "
> +
> +            return "(%s %s %s)" % (s, self.filesystem, str(self.context))
> +        else:
> +            if self.type == self.XATTR:
> +                s = "fs_use_xattr "
> +            elif self.type == self.TRANS:
> +                s = "fs_use_trans "
> +            elif self.type == self.TASK:
> +                s = "fs_use_task "
>
> -        return "%s %s %s;" % (s, self.filesystem, str(self.context))
> +            return "%s %s %s;" % (s, self.filesystem, str(self.context))
>
>  class PortCon(Leaf):
>      def __init__(self, parent=None):
> @@ -771,7 +899,10 @@ class PortCon(Leaf):
>          self.context = None
>
>      def to_string(self):
> -        return "portcon %s %s %s" % (self.port_type, self.port_number, str(self.context))
> +        if self.gen_cil:
> +            return "(portcon %s %s %s)" % (self.port_type, self.port_number, str(self.context))
> +        else:
> +            return "portcon %s %s %s" % (self.port_type, self.port_number, str(self.context))
>
>  class NodeCon(Leaf):
>      def __init__(self, parent=None):
> @@ -781,7 +912,10 @@ class NodeCon(Leaf):
>          self.context = None
>
>      def to_string(self):
> -        return "nodecon %s %s %s" % (self.start, self.end, str(self.context))
> +        if self.gen_cil:
> +            return "(nodecon %s %s %s)" % (self.start, self.end, str(self.context))
> +        else:
> +            return "nodecon %s %s %s" % (self.start, self.end, str(self.context))
>
>  class NetifCon(Leaf):
>      def __init__(self, parent=None):
> @@ -791,8 +925,13 @@ class NetifCon(Leaf):
>          self.packet_context = None
>
>      def to_string(self):
> -        return "netifcon %s %s %s" % (self.interface, str(self.interface_context),
> -                                   str(self.packet_context))
> +        if self.gen_cil:
> +            return "(netifcon %s %s %s)" % (self.interface, str(self.interface_context),
> +                                            str(self.packet_context))
> +        else:
> +            return "netifcon %s %s %s" % (self.interface, str(self.interface_context),
> +                                          str(self.packet_context))
> +
>  class PirqCon(Leaf):
>      def __init__(self, parent=None):
>          Leaf.__init__(self, parent)
> @@ -800,7 +939,10 @@ class PirqCon(Leaf):
>          self.context = None
>
>      def to_string(self):
> -        return "pirqcon %s %s" % (self.pirq_number, str(self.context))
> +        if self.gen_cil:
> +            return "(pirqcon %s %s)" % (self.pirq_number, str(self.context))
> +        else:
> +            return "pirqcon %s %s" % (self.pirq_number, str(self.context))
>
>  class IomemCon(Leaf):
>      def __init__(self, parent=None):
> @@ -809,7 +951,10 @@ class IomemCon(Leaf):
>          self.context = None
>
>      def to_string(self):
> -        return "iomemcon %s %s" % (self.device_mem, str(self.context))
> +        if self.gen_cil:
> +            return "(iomemcon %s %s)" % (self.device_mem, str(self.context))
> +        else:
> +            return "iomemcon %s %s" % (self.device_mem, str(self.context))
>
>  class IoportCon(Leaf):
>      def __init__(self, parent=None):
> @@ -818,7 +963,10 @@ class IoportCon(Leaf):
>          self.context = None
>
>      def to_string(self):
> -        return "ioportcon %s %s" % (self.ioport, str(self.context))
> +        if self.gen_cil:
> +            return "(ioportcon %s %s)" % (self.ioport, str(self.context))
> +        else:
> +            return "ioportcon %s %s" % (self.ioport, str(self.context))
>
>  class PciDeviceCon(Leaf):
>      def __init__(self, parent=None):
> @@ -827,7 +975,10 @@ class PciDeviceCon(Leaf):
>          self.context = None
>
>      def to_string(self):
> -        return "pcidevicecon %s %s" % (self.device, str(self.context))
> +        if self.gen_cil:
> +            return "(pcidevicecon %s %s)" % (self.device, str(self.context))
> +        else:
> +            return "pcidevicecon %s %s" % (self.device, str(self.context))
>
>  class DeviceTreeCon(Leaf):
>      def __init__(self, parent=None):
> @@ -836,7 +987,10 @@ class DeviceTreeCon(Leaf):
>          self.context = None
>
>      def to_string(self):
> -        return "devicetreecon %s %s" % (self.path, str(self.context))
> +        if self.gen_cil:
> +            return "(devicetreecon %s %s)" % (self.path, str(self.context))
> +        else:
> +            return "devicetreecon %s %s" % (self.path, str(self.context))
>
>  # Reference policy specific types
>
> @@ -993,25 +1147,33 @@ class Require(Leaf):
>
>      def to_string(self):
>          s = []
> -        s.append("require {")
> -        for type in self.types:
> -            s.append("\ttype %s;" % type)
> -        for obj_class, perms in self.obj_classes.items():
> -            s.append("\tclass %s %s;" % (obj_class, perms.to_space_str()))
> -        for role in self.roles:
> -            s.append("\trole %s;" % role)
> -        for bool in self.data:
> -            s.append("\tbool %s;" % bool)
> -        for user in self.users:
> -            s.append("\tuser %s;" % user)
> -        s.append("}")
> -
> -        # Handle empty requires
> -        if len(s) == 2:
> -            return ""
> -
> -        return "\n".join(s)
> -
> +        if self.gen_cil:
> +            # Can't require classes, perms, booleans, users
> +            for type in self.types:
> +                s.append("(typeattributeset cil_gen_require %s)" % type)
> +            for role in self.roles:
> +                s.append("(roleattributeset cil_gen_require %s)" % role)
> +
> +            return "\n".join(s)
> +        else:
> +            s.append("require {")
> +            for type in self.types:
> +                s.append("\ttype %s;" % type)
> +            for obj_class, perms in self.obj_classes.items():
> +                s.append("\tclass %s %s;" % (obj_class, perms.to_space_str()))
> +            for role in self.roles:
> +                s.append("\trole %s;" % role)
> +            for bool in self.data:
> +                s.append("\tbool %s;" % bool)
> +            for user in self.users:
> +                s.append("\tuser %s;" % user)
> +            s.append("}")
> +
> +            # Handle empty requires
> +            if len(s) == 2:
> +                return ""
> +
> +            return "\n".join(s)
>
>  class ObjPermSet:
>      def __init__(self, name):
> @@ -1044,7 +1206,10 @@ class Comment:
>          else:
>              out = []
>              for line in self.lines:
> -                out.append("#" + line)
> +                if self.gen_cil:
> +                    out.append(";" + line)
> +                else:
> +                    out.append("#" + line)
>              return "\n".join(out)
>
>      def merge(self, other):
> @@ -1056,4 +1221,5 @@ class Comment:
>      def __str__(self):
>          return self.to_string()
>
> -
> +    def set_gen_cil(self, gen_cil):
> +        self.gen_cil = gen_cil
>
> base-commit: 82195e77e317d322dd9b5fc31d402462d6845357
> --
> 2.43.0
>
>
diff mbox series

Patch

diff --git a/python/audit2allow/audit2allow b/python/audit2allow/audit2allow
index 35b0b151..b5927ec1 100644
--- a/python/audit2allow/audit2allow
+++ b/python/audit2allow/audit2allow
@@ -75,6 +75,7 @@  class AuditToPolicy:
                           help="generate policy with dontaudit rules")
         parser.add_option("-R", "--reference", action="store_true", dest="refpolicy",
                           default=True, help="generate refpolicy style output")
+        parser.add_option("-C", "--cil", action="store_true", dest="cil", help="generate CIL output")
 
         parser.add_option("-N", "--noreference", action="store_false", dest="refpolicy",
                           default=False, help="do not generate refpolicy style output")
@@ -114,7 +115,7 @@  class AuditToPolicy:
                 sys.stderr.write('error: module names must begin with a letter, optionally followed by letters, numbers, "-", "_", "."\n')
                 sys.exit(2)
 
-        # Make -M and -o conflict
+        # Make -M and -o or -C conflict
         if options.module_package:
             if options.output:
                 sys.stderr.write("error: --module-package conflicts with --output\n")
@@ -122,6 +123,9 @@  class AuditToPolicy:
             if options.module:
                 sys.stderr.write("error: --module-package conflicts with --module\n")
                 sys.exit(2)
+            if options.cil:
+                sys.stderr.write("error: --module-package conflicts with --cil\n")
+                sys.exit(2)
 
         self.__options = options
 
@@ -341,6 +345,10 @@  semodule -i {packagename}
         if self.__options.requires:
             g.set_gen_requires(True)
 
+        # CIL output
+        if self.__options.cil:
+            g.set_gen_cil(True)
+
         # Generate the policy
         g.add_access(self.__avs)
         g.add_role_types(self.__role_types)
@@ -348,6 +356,10 @@  semodule -i {packagename}
         # Output
         writer = output.ModuleWriter()
 
+        # CIL output
+        if self.__options.cil:
+            writer.set_gen_cil(True)
+
         # Module package
         if self.__options.module_package:
             self.__output_modulepackage(writer, g)
diff --git a/python/audit2allow/audit2allow.1 b/python/audit2allow/audit2allow.1
index c208b3b2..2834234d 100644
--- a/python/audit2allow/audit2allow.1
+++ b/python/audit2allow/audit2allow.1
@@ -74,6 +74,9 @@  Generate module/require output <modulename>
 .B "\-M <modulename>" 
 Generate loadable module package, conflicts with \-o
 .TP
+.B "\-C"
+Generate CIL output, conflicts with \-M
+.TP
 .B "\-p <policyfile>"  | "\-\-policy <policyfile>"
 Policy file to use for analysis
 .TP
diff --git a/python/sepolgen/src/sepolgen/output.py b/python/sepolgen/src/sepolgen/output.py
index aeeaafc8..57380cad 100644
--- a/python/sepolgen/src/sepolgen/output.py
+++ b/python/sepolgen/src/sepolgen/output.py
@@ -40,6 +40,7 @@  class ModuleWriter:
         self.module = None
         self.sort = True
         self.requires = True
+        self.gen_cil = False
 
     def write(self, module, fd):
         self.module = module
@@ -49,8 +50,12 @@  class ModuleWriter:
 
         # FIXME - make this handle nesting
         for node, depth in refpolicy.walktree(self.module, showdepth=True):
+            node.set_gen_cil(self.gen_cil)
             fd.write("%s\n" % str(node))
 
+    def set_gen_cil(self, gen_cil):
+        self.gen_cil = gen_cil
+
 # Helper functions for sort_filter - this is all done old school
 # C style rather than with polymorphic methods because this sorting
 # is specific to output. It is not necessarily the comparison you
diff --git a/python/sepolgen/src/sepolgen/policygen.py b/python/sepolgen/src/sepolgen/policygen.py
index 183b41a9..93d02cf0 100644
--- a/python/sepolgen/src/sepolgen/policygen.py
+++ b/python/sepolgen/src/sepolgen/policygen.py
@@ -86,6 +86,8 @@  class PolicyGenerator:
         self.xperms = False
 
         self.domains = None
+        self.gen_cil = False
+        self.comment_start = '#'
     def set_gen_refpol(self, if_set=None, perm_maps=None):
         """Set whether reference policy interfaces are generated.
 
@@ -128,6 +130,10 @@  class PolicyGenerator:
         """
         self.xperms = xperms
 
+    def set_gen_cil(self, gen_cil):
+        self.gen_cil = gen_cil
+        self.comment_start = ';'
+
     def __set_module_style(self):
         if self.ifgen:
             refpolicy = True
@@ -173,26 +179,27 @@  class PolicyGenerator:
             rule.comment = str(refpolicy.Comment(explain_access(av, verbosity=self.explain)))
 
         if av.type == audit2why.ALLOW:
-            rule.comment += "\n#!!!! This avc is allowed in the current policy"
+            rule.comment += "\n%s!!!! This avc is allowed in the current policy" % self.comment_start
 
             if av.xperms:
-                rule.comment += "\n#!!!! This av rule may have been overridden by an extended permission av rule"
+                rule.comment += "\n%s!!!! This av rule may have been overridden by an extended permission av rule" % self.comment_start
 
         if av.type == audit2why.DONTAUDIT:
-            rule.comment += "\n#!!!! This avc has a dontaudit rule in the current policy"
+            rule.comment += "\n%s!!!! This avc has a dontaudit rule in the current policy" % self.comment_start
 
         if av.type == audit2why.BOOLEAN:
             if len(av.data) > 1:
-                rule.comment += "\n#!!!! This avc can be allowed using one of the these booleans:\n#     %s" % ", ".join([x[0] for x in av.data])
+                rule.comment += "\n%s!!!! This avc can be allowed using one of the these booleans:\n%s     %s" % (self.comment_start, self.comment_start, ", ".join([x[0] for x in av.data]))
             else:
-                rule.comment += "\n#!!!! This avc can be allowed using the boolean '%s'" % av.data[0][0]
+                rule.comment += "\n%s!!!! This avc can be allowed using the boolean '%s'" % (self.comment_start, av.data[0][0])
 
         if av.type == audit2why.CONSTRAINT:
-            rule.comment += "\n#!!!! This avc is a constraint violation.  You would need to modify the attributes of either the source or target types to allow this access."
-            rule.comment += "\n#Constraint rule: "
-            rule.comment += "\n#\t" + av.data[0]
+            rule.comment += "\n%s!!!! This avc is a constraint violation.  You would need to modify the attributes of either the source or target types to allow this access." % self.comment_start
+            rule.comment += "\n%sConstraint rule: " % self.comment_start
+            rule.comment += "\n%s\t" % self.comment_start + av.data[0]
             for reason in av.data[1:]:
-                rule.comment += "\n#\tPossible cause is the source %s and target %s are different." % reason
+                rule.comment += "\n%s" % self.comment_start
+                rule.comment += "\tPossible cause is the source %s and target %s are different." % reason
 
         try:
             if ( av.type == audit2why.TERULE and
@@ -206,9 +213,9 @@  class PolicyGenerator:
                     if i not in self.domains:
                         types.append(i)
                 if len(types) == 1:
-                    rule.comment += "\n#!!!! The source type '%s' can write to a '%s' of the following type:\n# %s\n" % ( av.src_type, av.obj_class, ", ".join(types))
+                    rule.comment += "\n%s!!!! The source type '%s' can write to a '%s' of the following type:\n# %s\n" % (self.comment_start, av.src_type, av.obj_class, ", ".join(types))
                 elif len(types) >= 1:
-                    rule.comment += "\n#!!!! The source type '%s' can write to a '%s' of the following types:\n# %s\n" % ( av.src_type, av.obj_class, ", ".join(types))
+                    rule.comment += "\n%s!!!! The source type '%s' can write to a '%s' of the following types:\n# %s\n" % (self.comment_start, av.src_type, av.obj_class, ", ".join(types))
         except:
             pass
 
diff --git a/python/sepolgen/src/sepolgen/refpolicy.py b/python/sepolgen/src/sepolgen/refpolicy.py
index 9cac1b95..e7fa5645 100644
--- a/python/sepolgen/src/sepolgen/refpolicy.py
+++ b/python/sepolgen/src/sepolgen/refpolicy.py
@@ -53,6 +53,7 @@  class PolicyBase:
     def __init__(self, parent=None):
         self.parent = None
         self.comment = None
+        self.gen_cil = False
 
 class Node(PolicyBase):
     """Base class objects produced from parsing the reference policy.
@@ -150,6 +151,8 @@  class Node(PolicyBase):
     def to_string(self):
         return ""
 
+    def set_gen_cil(self, gen_cil):
+        self.gen_cil = gen_cil
 
 class Leaf(PolicyBase):
     def __init__(self, parent=None):
@@ -167,6 +170,8 @@  class Leaf(PolicyBase):
     def to_string(self):
         return ""
 
+    def set_gen_cil(self, gen_cil):
+        self.gen_cil = gen_cil
 
 
 # Utility functions
@@ -413,6 +418,16 @@  class XpermSet():
 
         return "%s{ %s }" % (compl, " ".join(vals))
 
+    def to_string_cil(self):
+        if not self.ranges:
+            return ""
+
+        compl = ("not (", ")") if self.complement else ("", "")
+
+        vals = map(lambda x: hex(x[0]) if x[0] == x[1] else "(range %s %s)" % (hex(x[0]), hex(x[1]), ), self.ranges)
+
+        return "(%s%s%s)" % (compl[0], " ".join(vals), compl[1])
+
 # Basic statements
 
 class TypeAttribute(Leaf):
@@ -426,7 +441,13 @@  class TypeAttribute(Leaf):
         self.attributes = IdSet()
 
     def to_string(self):
-        return "typeattribute %s %s;" % (self.type, self.attributes.to_comma_str())
+        if self.gen_cil:
+            s = ""
+            for a in self.attributes:
+                s += "(typeattribute %s %s)" % (self.type, a)
+            return s
+        else:
+            return "typeattribute %s %s;" % (self.type, self.attributes.to_comma_str())
 
 class RoleAttribute(Leaf):
     """SElinux roleattribute statement.
@@ -451,7 +472,10 @@  class Role(Leaf):
     def to_string(self):
         s = ""
         for t in self.types:
-            s += "role %s types %s;\n" % (self.role, t)
+            if self.gen_cil:
+                s += "(roletype %s %s)\n" % (self.role, t)
+            else:
+                s += "role %s types %s;\n" % (self.role, t)
         return s
 
 class Type(Leaf):
@@ -462,12 +486,20 @@  class Type(Leaf):
         self.aliases = IdSet()
 
     def to_string(self):
-        s = "type %s" % self.name
-        if len(self.aliases) > 0:
-            s = s + "alias %s" % self.aliases.to_space_str()
-        if len(self.attributes) > 0:
-            s = s + ", %s" % self.attributes.to_comma_str()
-        return s + ";"
+        if self.gen_cil:
+            s = "(type %s)" % self.name
+            for a in self.aliases:
+                s += "(typealiasactual %s %s)\n" % (self.name, a)
+            for a in self.attributes:
+                s += "(typeattributeset %s %s)\n" % (self.name, a)
+            return s
+        else:
+            s = "type %s" % self.name
+            if len(self.aliases) > 0:
+                s = s + "alias %s" % self.aliases.to_space_str()
+            if len(self.attributes) > 0:
+                s = s + ", %s" % self.attributes.to_comma_str()
+            return s + ";"
 
 class TypeAlias(Leaf):
     def __init__(self, parent=None):
@@ -476,7 +508,13 @@  class TypeAlias(Leaf):
         self.aliases = IdSet()
 
     def to_string(self):
-        return "typealias %s alias %s;" % (self.type, self.aliases.to_space_str())
+        if self.gen_cil:
+            s = ""
+            for a in self.aliases:
+                s += "(typealias %s %s)\n" % (self.type, a)
+            return s
+        else:
+            return "typealias %s alias %s;" % (self.type, self.aliases.to_space_str())
 
 class Attribute(Leaf):
     def __init__(self, name="", parent=None):
@@ -484,7 +522,10 @@  class Attribute(Leaf):
         self.name = name
 
     def to_string(self):
-        return "attribute %s;" % self.name
+        if self.gen_cil:
+            return "attribute %s;" % self.name
+        else:
+            return "(typeattribute %s)" % self.name
 
 class Attribute_Role(Leaf):
     def __init__(self, name="", parent=None):
@@ -492,7 +533,10 @@  class Attribute_Role(Leaf):
         self.name = name
 
     def to_string(self):
-        return "attribute_role %s;" % self.name
+        if self.gen_cil:
+            return "(roleattribute %s)" % self.name
+        else:
+            return "attribute_role %s;" % self.name
 
 
 # Classes representing rules
@@ -555,11 +599,21 @@  class AVRule(Leaf):
         that is a valid policy language representation (assuming
         that the types, object class, etc. are valie).
         """
-        return "%s %s %s:%s %s;" % (self.__rule_type_str(),
-                                     self.src_types.to_space_str(),
-                                     self.tgt_types.to_space_str(),
-                                     self.obj_classes.to_space_str(),
-                                     self.perms.to_space_str())
+        if self.gen_cil:
+            s = ""
+            for src in self.src_types:
+                for tgt in self.tgt_types:
+                    for obj in self.obj_classes:
+                        s += "(%s %s %s (%s (%s)))" % (self.__rule_type_str(),
+                                                       src, tgt, obj,
+                                                       " ".join(self.perms))
+            return s
+        else:
+            return "%s %s %s:%s %s;" % (self.__rule_type_str(),
+                                        self.src_types.to_space_str(),
+                                        self.tgt_types.to_space_str(),
+                                        self.obj_classes.to_space_str(),
+                                        self.perms.to_space_str())
 
 class AVExtRule(Leaf):
     """Extended permission access vector rule.
@@ -597,6 +651,16 @@  class AVExtRule(Leaf):
         elif self.rule_type == self.NEVERALLOWXPERM:
             return "neverallowxperm"
 
+    def __rule_type_str_cil(self):
+        if self.rule_type == self.ALLOWXPERM:
+            return "allowx"
+        elif self.rule_type == self.DONTAUDITXPERM:
+            return "dontauditx"
+        elif self.rule_type == self.AUDITALLOWXPERM:
+            return "auditallowx"
+        elif self.rule_type == self.NEVERALLOWXPERM:
+            return "neverallowx"
+
     def from_av(self, av, op):
         self.src_types.add(av.src_type)
         if av.src_type == av.tgt_type:
@@ -612,12 +676,25 @@  class AVExtRule(Leaf):
         a valid policy language representation (assuming that
         the types, object class, etc. are valid).
         """
-        return "%s %s %s:%s %s %s;" % (self.__rule_type_str(),
-                                     self.src_types.to_space_str(),
-                                     self.tgt_types.to_space_str(),
-                                     self.obj_classes.to_space_str(),
-                                     self.operation,
-                                     self.xperms.to_string())
+        if self.gen_cil:
+            s = ""
+            for src in self.src_types:
+                for tgt in self.tgt_types:
+                    for obj in self.obj_classes:
+                        s += "(%s %s %s (%s %s %s))" % (self.__rule_type_str_cil(),
+                                                        src, tgt,
+                                                        self.operation,
+                                                        obj,
+                                                        self.xperms.to_string_cil())
+            return s
+        else:
+            return "%s %s %s:%s %s %s;" % (self.__rule_type_str(),
+                                           self.src_types.to_space_str(),
+                                           self.tgt_types.to_space_str(),
+                                           self.obj_classes.to_space_str(),
+                                           self.operation,
+                                           self.xperms.to_string())
+
 
 class TypeRule(Leaf):
     """SELinux type rules.
@@ -630,6 +707,7 @@  class TypeRule(Leaf):
     TYPE_CHANGE = 1
     TYPE_MEMBER = 2
 
+    # NB. Filename type transitions are not generated by audit2allow.
     def __init__(self, parent=None):
         Leaf.__init__(self, parent)
         self.src_types = IdSet()
@@ -646,12 +724,28 @@  class TypeRule(Leaf):
         else:
             return "type_member"
 
+    def __rule_type_str_cil(self):
+        if self.rule_type == self.TYPE_TRANSITION:
+            return "typetransition"
+        elif self.rule_type == self.TYPE_CHANGE:
+            return "typechange"
+        else:
+            return "typemember"
+
     def to_string(self):
-        return "%s %s %s:%s %s;" % (self.__rule_type_str(),
-                                     self.src_types.to_space_str(),
-                                     self.tgt_types.to_space_str(),
-                                     self.obj_classes.to_space_str(),
-                                     self.dest_type)
+        if self.gen_cil:
+            return "(%s %s %s %s %s)" % (self.__rule_type_str_cil(),
+                                         self.src_types.to_space_str(),
+                                         self.tgt_types.to_space_str(),
+                                         self.obj_classes.to_space_str(),
+                                         self.dest_type)
+        else:
+            return "%s %s %s:%s %s;" % (self.__rule_type_str(),
+                                        self.src_types.to_space_str(),
+                                        self.tgt_types.to_space_str(),
+                                        self.obj_classes.to_space_str(),
+                                        self.dest_type)
+
 class TypeBound(Leaf):
     """SElinux typebound statement.
 
@@ -663,8 +757,13 @@  class TypeBound(Leaf):
         self.tgt_types = IdSet()
 
     def to_string(self):
-        return "typebounds %s %s;" % (self.type, self.tgt_types.to_comma_str())
-
+        if self.gen_cil:
+            s = ""
+            for t in self.tgt_types:
+                s += "(typebounds %s %s)" % (self.type, t)
+            return s
+        else:
+            return "typebounds %s %s;" % (self.type, self.tgt_types.to_comma_str())
 
 class RoleAllow(Leaf):
     def __init__(self, parent=None):
@@ -673,8 +772,15 @@  class RoleAllow(Leaf):
         self.tgt_roles = IdSet()
 
     def to_string(self):
-        return "allow %s %s;" % (self.src_roles.to_comma_str(),
-                                 self.tgt_roles.to_comma_str())
+        if self.gen_cil:
+            s = ""
+            for src in self.src_roles:
+                for tgt in self.tgt_roles:
+                    s += "(roleallow %s %s)" % (src, tgt)
+            return s
+        else:
+            return "allow %s %s;" % (self.src_roles.to_comma_str(),
+                                     self.tgt_roles.to_comma_str())
 
 class RoleType(Leaf):
     def __init__(self, parent=None):
@@ -685,7 +791,10 @@  class RoleType(Leaf):
     def to_string(self):
         s = ""
         for t in self.types:
-            s += "role %s types %s;\n" % (self.role, t)
+            if self.gen_cil:
+                s += "(roletype %s %s)\n" % (self.role, t)
+            else:
+                s += "role %s types %s;\n" % (self.role, t)
         return s
 
 class ModuleDeclaration(Leaf):
@@ -696,10 +805,13 @@  class ModuleDeclaration(Leaf):
         self.refpolicy = False
 
     def to_string(self):
-        if self.refpolicy:
-            return "policy_module(%s, %s)" % (self.name, self.version)
+        if self.gen_cil:
+            return ""
         else:
-            return "module %s %s;" % (self.name, self.version)
+            if self.refpolicy:
+                return "policy_module(%s, %s)" % (self.name, self.version)
+            else:
+                return "module %s %s;" % (self.name, self.version)
 
 class Conditional(Node):
     def __init__(self, parent=None):
@@ -729,7 +841,10 @@  class InitialSid(Leaf):
         self.context = None
 
     def to_string(self):
-        return "sid %s %s" % (self.name, str(self.context))
+        if self.gen_cil:
+            return "(sid %s %s)" % (self.name, str(self.context))
+        else:
+            return "sid %s %s" % (self.name, str(self.context))
 
 class GenfsCon(Leaf):
     def __init__(self, parent=None):
@@ -739,7 +854,10 @@  class GenfsCon(Leaf):
         self.context = None
 
     def to_string(self):
-        return "genfscon %s %s %s" % (self.filesystem, self.path, str(self.context))
+        if self.gen_cil:
+            return "(genfscon %s %s %s)" % (self.filesystem, self.path, str(self.context))
+        else:
+            return "genfscon %s %s %s" % (self.filesystem, self.path, str(self.context))
 
 class FilesystemUse(Leaf):
     XATTR = 1
@@ -754,14 +872,24 @@  class FilesystemUse(Leaf):
 
     def to_string(self):
         s = ""
-        if self.type == self.XATTR:
-            s = "fs_use_xattr "
-        elif self.type == self.TRANS:
-            s = "fs_use_trans "
-        elif self.type == self.TASK:
-            s = "fs_use_task "
+        if self.gen_cil:
+            if self.type == self.XATTR:
+                s = "fsuse xattr "
+            elif self.type == self.TRANS:
+                s = "fsuse trans "
+            elif self.type == self.TASK:
+                s = "fsuse task "
+
+            return "(%s %s %s)" % (s, self.filesystem, str(self.context))
+        else:
+            if self.type == self.XATTR:
+                s = "fs_use_xattr "
+            elif self.type == self.TRANS:
+                s = "fs_use_trans "
+            elif self.type == self.TASK:
+                s = "fs_use_task "
 
-        return "%s %s %s;" % (s, self.filesystem, str(self.context))
+            return "%s %s %s;" % (s, self.filesystem, str(self.context))
 
 class PortCon(Leaf):
     def __init__(self, parent=None):
@@ -771,7 +899,10 @@  class PortCon(Leaf):
         self.context = None
 
     def to_string(self):
-        return "portcon %s %s %s" % (self.port_type, self.port_number, str(self.context))
+        if self.gen_cil:
+            return "(portcon %s %s %s)" % (self.port_type, self.port_number, str(self.context))
+        else:
+            return "portcon %s %s %s" % (self.port_type, self.port_number, str(self.context))
 
 class NodeCon(Leaf):
     def __init__(self, parent=None):
@@ -781,7 +912,10 @@  class NodeCon(Leaf):
         self.context = None
 
     def to_string(self):
-        return "nodecon %s %s %s" % (self.start, self.end, str(self.context))
+        if self.gen_cil:
+            return "(nodecon %s %s %s)" % (self.start, self.end, str(self.context))
+        else:
+            return "nodecon %s %s %s" % (self.start, self.end, str(self.context))
 
 class NetifCon(Leaf):
     def __init__(self, parent=None):
@@ -791,8 +925,13 @@  class NetifCon(Leaf):
         self.packet_context = None
 
     def to_string(self):
-        return "netifcon %s %s %s" % (self.interface, str(self.interface_context),
-                                   str(self.packet_context))
+        if self.gen_cil:
+            return "(netifcon %s %s %s)" % (self.interface, str(self.interface_context),
+                                            str(self.packet_context))
+        else:
+            return "netifcon %s %s %s" % (self.interface, str(self.interface_context),
+                                          str(self.packet_context))
+
 class PirqCon(Leaf):
     def __init__(self, parent=None):
         Leaf.__init__(self, parent)
@@ -800,7 +939,10 @@  class PirqCon(Leaf):
         self.context = None
 
     def to_string(self):
-        return "pirqcon %s %s" % (self.pirq_number, str(self.context))
+        if self.gen_cil:
+            return "(pirqcon %s %s)" % (self.pirq_number, str(self.context))
+        else:
+            return "pirqcon %s %s" % (self.pirq_number, str(self.context))
 
 class IomemCon(Leaf):
     def __init__(self, parent=None):
@@ -809,7 +951,10 @@  class IomemCon(Leaf):
         self.context = None
 
     def to_string(self):
-        return "iomemcon %s %s" % (self.device_mem, str(self.context))
+        if self.gen_cil:
+            return "(iomemcon %s %s)" % (self.device_mem, str(self.context))
+        else:
+            return "iomemcon %s %s" % (self.device_mem, str(self.context))
 
 class IoportCon(Leaf):
     def __init__(self, parent=None):
@@ -818,7 +963,10 @@  class IoportCon(Leaf):
         self.context = None
 
     def to_string(self):
-        return "ioportcon %s %s" % (self.ioport, str(self.context))
+        if self.gen_cil:
+            return "(ioportcon %s %s)" % (self.ioport, str(self.context))
+        else:
+            return "ioportcon %s %s" % (self.ioport, str(self.context))
 
 class PciDeviceCon(Leaf):
     def __init__(self, parent=None):
@@ -827,7 +975,10 @@  class PciDeviceCon(Leaf):
         self.context = None
 
     def to_string(self):
-        return "pcidevicecon %s %s" % (self.device, str(self.context))
+        if self.gen_cil:
+            return "(pcidevicecon %s %s)" % (self.device, str(self.context))
+        else:
+            return "pcidevicecon %s %s" % (self.device, str(self.context))
 
 class DeviceTreeCon(Leaf):
     def __init__(self, parent=None):
@@ -836,7 +987,10 @@  class DeviceTreeCon(Leaf):
         self.context = None
 
     def to_string(self):
-        return "devicetreecon %s %s" % (self.path, str(self.context))
+        if self.gen_cil:
+            return "(devicetreecon %s %s)" % (self.path, str(self.context))
+        else:
+            return "devicetreecon %s %s" % (self.path, str(self.context))
 
 # Reference policy specific types
 
@@ -993,25 +1147,33 @@  class Require(Leaf):
 
     def to_string(self):
         s = []
-        s.append("require {")
-        for type in self.types:
-            s.append("\ttype %s;" % type)
-        for obj_class, perms in self.obj_classes.items():
-            s.append("\tclass %s %s;" % (obj_class, perms.to_space_str()))
-        for role in self.roles:
-            s.append("\trole %s;" % role)
-        for bool in self.data:
-            s.append("\tbool %s;" % bool)
-        for user in self.users:
-            s.append("\tuser %s;" % user)
-        s.append("}")
-
-        # Handle empty requires
-        if len(s) == 2:
-            return ""
-
-        return "\n".join(s)
-
+        if self.gen_cil:
+            # Can't require classes, perms, booleans, users
+            for type in self.types:
+                s.append("(typeattributeset cil_gen_require %s)" % type)
+            for role in self.roles:
+                s.append("(roleattributeset cil_gen_require %s)" % role)
+
+            return "\n".join(s)
+        else:
+            s.append("require {")
+            for type in self.types:
+                s.append("\ttype %s;" % type)
+            for obj_class, perms in self.obj_classes.items():
+                s.append("\tclass %s %s;" % (obj_class, perms.to_space_str()))
+            for role in self.roles:
+                s.append("\trole %s;" % role)
+            for bool in self.data:
+                s.append("\tbool %s;" % bool)
+            for user in self.users:
+                s.append("\tuser %s;" % user)
+            s.append("}")
+
+            # Handle empty requires
+            if len(s) == 2:
+                return ""
+
+            return "\n".join(s)
 
 class ObjPermSet:
     def __init__(self, name):
@@ -1044,7 +1206,10 @@  class Comment:
         else:
             out = []
             for line in self.lines:
-                out.append("#" + line)
+                if self.gen_cil:
+                    out.append(";" + line)
+                else:
+                    out.append("#" + line)
             return "\n".join(out)
 
     def merge(self, other):
@@ -1056,4 +1221,5 @@  class Comment:
     def __str__(self):
         return self.to_string()
 
-
+    def set_gen_cil(self, gen_cil):
+        self.gen_cil = gen_cil