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 > >
On Fri, Mar 15, 2024 at 11:47:27AM -0400, Jeff Layton wrote:
> On Thu, 2024-03-14 at 23:49 +0000, Daniel Walker (danielwa) wrote:
> > Hi,
> >
> > It seems there is/was a problem using NFS security labels where the server and client use
> > different MAC policy or model.
> >
> > I was reading this page,
> >
> > http://www.selinuxproject.org/page/Labeled_NFS/TODO#Label_Translation_Framework
> >
> > It seems like this problem was known in 2009 when this page was written. Is
> > there a way to accomplish having extended attributes shared over NFS to a client
> > with different selinux policies ?
> >
>
> Currently Linux NFS client and server only support limited server mode,
> where the server presents the contexts as they are and the client
> enforces its own policy locally. There's no requirement that the server
> enforce the same policy (or even enforce a security policy at all), all
> it's doing is storing and presenting the security label.
>
> So what you're saying should "work" today.
>
My situation is more constrained than this. The server would also have an selinux
policy which is active and in use. Server selinux usage is out the users
control.
This could plausibly come up where you have an nfsroot or nfs pivot root
environment with selinux is active and the server also has a different or
conflicting selinux policy active.
I was looking for a way to translate between the two selinux policies which is
how I found the link I provided.
Daniel
On Fri, Mar 15, 2024 at 12:37:22PM +0100, Christian Göttsche wrote: > Introduce a new capable flag, CAP_OPT_NOAUDIT_ONDENY, to not generate > an audit event if the requested capability is not granted. This will be > used in a new capable_any() functionality to reduce the number of > necessary capable calls. > > Handle the flag accordingly in AppArmor and SELinux. > > CC: linux-block@vger.kernel.org > Suggested-by: Paul Moore <paul@paul-moore.com> > Signed-off-by: Christian Göttsche <cgzones@googlemail.com> Thanks. Reviewed-by: Serge Hallyn <serge@hallyn.com> > --- > v5: > rename flag to CAP_OPT_NOAUDIT_ONDENY, suggested by Serge: > https://lore.kernel.org/all/20230606190013.GA640488@mail.hallyn.com/ > --- > include/linux/security.h | 2 ++ > security/apparmor/capability.c | 8 +++++--- > security/selinux/hooks.c | 14 ++++++++------ > 3 files changed, 15 insertions(+), 9 deletions(-) > > diff --git a/include/linux/security.h b/include/linux/security.h > index 41a8f667bdfa..c60cae78ff8b 100644 > --- a/include/linux/security.h > +++ b/include/linux/security.h > @@ -70,6 +70,8 @@ struct lsm_ctx; > #define CAP_OPT_NOAUDIT BIT(1) > /* If capable is being called by a setid function */ > #define CAP_OPT_INSETID BIT(2) > +/* If capable should audit the security request for authorized requests only */ > +#define CAP_OPT_NOAUDIT_ONDENY BIT(3) > > /* LSM Agnostic defines for security_sb_set_mnt_opts() flags */ > #define SECURITY_LSM_NATIVE_LABELS 1 > diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c > index 9934df16c843..08c9c9a0fc19 100644 > --- a/security/apparmor/capability.c > +++ b/security/apparmor/capability.c > @@ -108,7 +108,8 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile > * profile_capable - test if profile allows use of capability @cap > * @profile: profile being enforced (NOT NULL, NOT unconfined) > * @cap: capability to test if allowed > - * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated > + * @opts: CAP_OPT_NOAUDIT/CAP_OPT_NOAUDIT_ONDENY bit determines whether audit > + * record is generated > * @ad: audit data (MAY BE NULL indicating no auditing) > * > * Returns: 0 if allowed else -EPERM > @@ -126,7 +127,7 @@ static int profile_capable(struct aa_profile *profile, int cap, > else > error = -EPERM; > > - if (opts & CAP_OPT_NOAUDIT) { > + if ((opts & CAP_OPT_NOAUDIT) || ((opts & CAP_OPT_NOAUDIT_ONDENY) && error)) { > if (!COMPLAIN_MODE(profile)) > return error; > /* audit the cap request in complain mode but note that it > @@ -143,7 +144,8 @@ static int profile_capable(struct aa_profile *profile, int cap, > * @subj_cred: cred we are testing capability against > * @label: label being tested for capability (NOT NULL) > * @cap: capability to be tested > - * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated > + * @opts: CAP_OPT_NOAUDIT/CAP_OPT_NOAUDIT_ONDENY bit determines whether audit > + * record is generated > * > * Look up capability in profile capability set. > * > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index 3448454c82d0..1a2c7c1a89be 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -1624,7 +1624,7 @@ static int cred_has_capability(const struct cred *cred, > u16 sclass; > u32 sid = cred_sid(cred); > u32 av = CAP_TO_MASK(cap); > - int rc; > + int rc, rc2; > > ad.type = LSM_AUDIT_DATA_CAP; > ad.u.cap = cap; > @@ -1643,11 +1643,13 @@ static int cred_has_capability(const struct cred *cred, > } > > rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); > - if (!(opts & CAP_OPT_NOAUDIT)) { > - int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad); > - if (rc2) > - return rc2; > - } > + if ((opts & CAP_OPT_NOAUDIT) || ((opts & CAP_OPT_NOAUDIT_ONDENY) && rc)) > + return rc; > + > + rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad); > + if (rc2) > + return rc2; > + > return rc; > } > > -- > 2.43.0 > >
Dump in the SELinux debug configuration the statistics for the conditional rules avtab, the role transition, and class and common permission hash tables. Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v2: print key in hash_eval() for class and common tables --- security/selinux/ss/conditional.c | 3 +++ security/selinux/ss/policydb.c | 23 ++++++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index f12476855b27..e868fc403d75 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -169,6 +169,9 @@ int cond_init_bool_indexes(struct policydb *p) p->p_bools.nprim, sizeof(*p->bool_val_to_struct), GFP_KERNEL); if (!p->bool_val_to_struct) return -ENOMEM; + + avtab_hash_eval(&p->te_cond_avtab, "conditional_rules"); + return 0; } diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 3d22d5baa829..b0f688fe0737 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -672,14 +672,17 @@ static int (*const index_f[SYM_NUM])(void *key, void *datum, void *datap) = { /* clang-format on */ #ifdef CONFIG_SECURITY_SELINUX_DEBUG -static void hash_eval(struct hashtab *h, const char *hash_name) +static void hash_eval(struct hashtab *h, const char *hash_name, const char *hash_details) { struct hashtab_info info; hashtab_stat(h, &info); pr_debug( - "SELinux: %s: %d entries and %d/%d buckets used, longest chain length %d, sum of chain length^2 %llu\n", - hash_name, h->nel, info.slots_used, h->size, info.max_chain_len, + "SELinux: %s%s%s: %d entries and %d/%d buckets used, longest chain length %d, sum of chain length^2 %llu\n", + hash_name, + hash_details ? "@" : "", + hash_details ?: "", + h->nel, info.slots_used, h->size, info.max_chain_len, info.chain2_len_sum); } @@ -688,11 +691,11 @@ static void symtab_hash_eval(struct symtab *s) int i; for (i = 0; i < SYM_NUM; i++) - hash_eval(&s[i].table, symtab_name[i]); + hash_eval(&s[i].table, symtab_name[i], NULL); } #else -static inline void hash_eval(struct hashtab *h, const char *hash_name) +static inline void hash_eval(struct hashtab *h, const char *hash_name, const char *hash_details) { } static inline void symtab_hash_eval(struct symtab *s) @@ -1178,6 +1181,8 @@ static int common_read(struct policydb *p, struct symtab *s, void *fp) goto bad; } + hash_eval(&comdatum->permissions.table, "common_permissions", key); + rc = symtab_insert(s, key, comdatum); if (rc) goto bad; @@ -1358,6 +1363,8 @@ static int class_read(struct policydb *p, struct symtab *s, void *fp) goto bad; } + hash_eval(&cladatum->permissions.table, "class_permissions", key); + rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp); if (rc) goto bad; @@ -1898,7 +1905,7 @@ static int range_read(struct policydb *p, void *fp) rt = NULL; r = NULL; } - hash_eval(&p->range_tr, "rangetr"); + hash_eval(&p->range_tr, "rangetr", NULL); rc = 0; out: kfree(rt); @@ -2116,7 +2123,7 @@ static int filename_trans_read(struct policydb *p, void *fp) return rc; } } - hash_eval(&p->filename_trans, "filenametr"); + hash_eval(&p->filename_trans, "filenametr", NULL); return 0; } @@ -2649,6 +2656,8 @@ int policydb_read(struct policydb *p, void *fp) rtd = NULL; } + hash_eval(&p->role_tr, "roletr", NULL); + rc = next_entry(buf, fp, sizeof(u32)); if (rc) goto bad; -- 2.43.0
The number of buckets is calculated by performing a binary AND against the mask of the hash table, which is one less than its size (which is a power of two). This leads to all top bits being discarded, requiring for short or similar inputs a hash function with a good avalanche effect. Use djb2a: # current common prefixes: 7 entries and 5/8 buckets used, longest chain length 2, sum of chain length^2 11 classes: 134 entries and 100/256 buckets used, longest chain length 5, sum of chain length^2 234 roles: 15 entries and 6/16 buckets used, longest chain length 5, sum of chain length^2 57 types: 4448 entries and 3016/8192 buckets used, longest chain length 41, sum of chain length^2 14922 users: 7 entries and 3/8 buckets used, longest chain length 3, sum of chain length^2 17 bools: 306 entries and 221/512 buckets used, longest chain length 4, sum of chain length^2 524 levels: 1 entries and 1/1 buckets used, longest chain length 1, sum of chain length^2 1 categories: 1024 entries and 400/1024 buckets used, longest chain length 4, sum of chain length^2 2740 # patch common prefixes: 7 entries and 5/8 buckets used, longest chain length 2, sum of chain length^2 11 classes: 134 entries and 101/256 buckets used, longest chain length 3, sum of chain length^2 210 roles: 15 entries and 9/16 buckets used, longest chain length 3, sum of chain length^2 31 types: 4448 entries and 3459/8192 buckets used, longest chain length 5, sum of chain length^2 6778 users: 7 entries and 5/8 buckets used, longest chain length 3, sum of chain length^2 13 bools: 306 entries and 236/512 buckets used, longest chain length 5, sum of chain length^2 470 levels: 1 entries and 1/1 buckets used, longest chain length 1, sum of chain length^2 1 categories: 1024 entries and 518/1024 buckets used, longest chain length 7, sum of chain length^2 2992 Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v2: add licensing note --- security/selinux/ss/symtab.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c index c04f8d447873..832660fd84a9 100644 --- a/security/selinux/ss/symtab.c +++ b/security/selinux/ss/symtab.c @@ -12,17 +12,17 @@ static unsigned int symhash(const void *key) { - const char *p, *keyp; - unsigned int size; - unsigned int val; - - val = 0; - keyp = key; - size = strlen(keyp); - for (p = keyp; (p - keyp) < size; p++) - val = (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ - (*p); - return val; + /* + * djb2a + * Public domain from cdb v0.75 + */ + unsigned int hash = 5381; + unsigned char c; + + while ((c = *(const unsigned char *)key++)) + hash = ((hash << 5) + hash) ^ c; + + return hash; } static int symcmp(const void *key1, const void *key2) -- 2.43.0
Iterate on nodes instead of single bits to save node resolution for each single bit. Similar to userspace patch efcd00814879 ("libsepol: optimize ebitmap_and"). Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v3: apply format style v2: fix array size computation --- security/selinux/ss/ebitmap.c | 50 +++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 67c1a73cd5ee..47cb90106118 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -78,19 +78,53 @@ int ebitmap_cpy(struct ebitmap *dst, const struct ebitmap *src) int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1, const struct ebitmap *e2) { - struct ebitmap_node *n; - int bit, rc; + const struct ebitmap_node *n1, *n2; + struct ebitmap_node *new = NULL, **prev; ebitmap_init(dst); - ebitmap_for_each_positive_bit(e1, n, bit) - { - if (ebitmap_get_bit(e2, bit)) { - rc = ebitmap_set_bit(dst, bit, 1); - if (rc < 0) - return rc; + prev = &dst->node; + n1 = e1->node; + n2 = e2->node; + while (n1 && n2) { + if (n1->startbit == n2->startbit) { + unsigned long testmap[EBITMAP_UNIT_NUMS]; + unsigned int i; + bool match = false; + + for (i = 0; i < ARRAY_SIZE(testmap); i++) { + testmap[i] = n1->maps[i] & n2->maps[i]; + if (testmap[i] != 0) + match = true; + } + + if (match) { + new = kmem_cache_zalloc(ebitmap_node_cachep, + GFP_ATOMIC); + if (!new) { + ebitmap_destroy(dst); + return -ENOMEM; + } + new->startbit = n1->startbit; + memcpy(new->maps, testmap, EBITMAP_SIZE / 8); + new->next = NULL; + + *prev = new; + prev = &(new->next); + } + + n1 = n1->next; + n2 = n2->next; + } else if (n1->startbit > n2->startbit) { + n2 = n2->next; + } else { + n1 = n1->next; } } + + if (new) + dst->highbit = new->startbit + EBITMAP_SIZE; + return 0; } -- 2.43.0
Perform a process { execstack } check unless virtual memory is marked executable by default. Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- security/selinux/hooks.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index a0fde0641f77..daf901916836 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -113,6 +113,8 @@ struct selinux_state selinux_state; /* SECMARK reference count */ static atomic_t selinux_secmark_refcount = ATOMIC_INIT(0); +static int default_noexec __ro_after_init; + #ifdef CONFIG_SECURITY_SELINUX_DEVELOP static int selinux_enforcing_boot __initdata; @@ -2221,6 +2223,18 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) return cap_sys_admin; } +static int selinux_vm_execstack(void) +{ + u32 sid; + + if (!default_noexec) + return 0; + + sid = current_sid(); + return avc_has_perm(sid, sid, SECCLASS_PROCESS, + PROCESS__EXECSTACK, NULL); +} + /* binprm security operations */ static u32 ptrace_parent_sid(void) @@ -3767,8 +3781,6 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd, return selinux_file_ioctl(file, cmd, arg); } -static int default_noexec __ro_after_init; - static int file_map_prot_check(struct file *file, unsigned long prot, int shared) { const struct cred *cred = current_cred(); @@ -7120,6 +7132,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(quota_on, selinux_quota_on), LSM_HOOK_INIT(syslog, selinux_syslog), LSM_HOOK_INIT(vm_enough_memory, selinux_vm_enough_memory), + LSM_HOOK_INIT(vm_execstack, selinux_vm_execstack), LSM_HOOK_INIT(netlink_send, selinux_netlink_send), -- 2.43.0
The extensible bitmap supports bit positions up to U32_MAX due to the type of the member highbit being u32. Use u32 consistently as the type for bit positions to announce to callers what range of values is supported. Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v4: - apply format style I hope i addressed all comment from [1] in [2]. v3: - revert type change of unrelated iter variable - use U32_MAX instead of (u32)-1 v2: avoid declarations in init-clauses of for loops [1]: https://lore.kernel.org/selinux/67cee6245e2895e81a0177c4c1ed01ba.paul@paul-moore.com/ [2]: https://lore.kernel.org/selinux/CAJ2a_DdLR40CB6Ua5cNjYhtexNmGkzQRsVrJn+dhVaZO-aVKsA@mail.gmail.com/ Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- security/selinux/ss/ebitmap.c | 31 +++++++++++++++------------- security/selinux/ss/ebitmap.h | 38 ++++++++++++++++------------------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 67c1a73cd5ee..8177f1c3c774 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -21,7 +21,7 @@ #include "ebitmap.h" #include "policydb.h" -#define BITS_PER_U64 (sizeof(u64) * 8) +#define BITS_PER_U64 ((u32)(sizeof(u64) * 8)) static struct kmem_cache *ebitmap_node_cachep __ro_after_init; @@ -79,7 +79,8 @@ int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1, const struct ebitmap *e2) { struct ebitmap_node *n; - int bit, rc; + u32 bit; + int rc; ebitmap_init(dst); @@ -256,7 +257,7 @@ int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, return 1; } -int ebitmap_get_bit(const struct ebitmap *e, unsigned long bit) +int ebitmap_get_bit(const struct ebitmap *e, u32 bit) { const struct ebitmap_node *n; @@ -273,7 +274,7 @@ int ebitmap_get_bit(const struct ebitmap *e, unsigned long bit) return 0; } -int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) +int ebitmap_set_bit(struct ebitmap *e, u32 bit, int value) { struct ebitmap_node *n, *prev, *new; @@ -284,7 +285,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) if (value) { ebitmap_node_set_bit(n, bit); } else { - unsigned int s; + u32 s; ebitmap_node_clr_bit(n, bit); @@ -362,12 +363,12 @@ void ebitmap_destroy(struct ebitmap *e) int ebitmap_read(struct ebitmap *e, void *fp) { struct ebitmap_node *n = NULL; - u32 mapunit, count, startbit, index; + u32 mapunit, count, startbit, index, i; __le32 ebitmap_start; u64 map; __le64 mapbits; __le32 buf[3]; - int rc, i; + int rc; ebitmap_init(e); @@ -381,7 +382,7 @@ int ebitmap_read(struct ebitmap *e, void *fp) if (mapunit != BITS_PER_U64) { pr_err("SELinux: ebitmap: map size %u does not " - "match my size %zd (high bit was %d)\n", + "match my size %d (high bit was %d)\n", mapunit, BITS_PER_U64, e->highbit); goto bad; } @@ -469,19 +470,20 @@ int ebitmap_read(struct ebitmap *e, void *fp) int ebitmap_write(const struct ebitmap *e, void *fp) { struct ebitmap_node *n; - u32 count; + u32 bit, count, last_bit, last_startbit; __le32 buf[3]; u64 map; - int bit, last_bit, last_startbit, rc; + int rc; buf[0] = cpu_to_le32(BITS_PER_U64); count = 0; last_bit = 0; - last_startbit = -1; + last_startbit = U32_MAX; ebitmap_for_each_positive_bit(e, n, bit) { - if (rounddown(bit, (int)BITS_PER_U64) > last_startbit) { + if (last_startbit == U32_MAX || + rounddown(bit, BITS_PER_U64) > last_startbit) { count++; last_startbit = rounddown(bit, BITS_PER_U64); } @@ -495,10 +497,11 @@ int ebitmap_write(const struct ebitmap *e, void *fp) return rc; map = 0; - last_startbit = INT_MIN; + last_startbit = U32_MAX; ebitmap_for_each_positive_bit(e, n, bit) { - if (rounddown(bit, (int)BITS_PER_U64) > last_startbit) { + if (last_startbit == U32_MAX || + rounddown(bit, BITS_PER_U64) > last_startbit) { __le64 buf64[1]; /* this is the very first bit */ diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index 02798b35eecc..24d7d8b3cda3 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -46,10 +46,10 @@ struct ebitmap { #define ebitmap_length(e) ((e)->highbit) -static inline unsigned int ebitmap_start_positive(const struct ebitmap *e, - struct ebitmap_node **n) +static inline u32 ebitmap_start_positive(const struct ebitmap *e, + struct ebitmap_node **n) { - unsigned int ofs; + u32 ofs; for (*n = e->node; *n; *n = (*n)->next) { ofs = find_first_bit((*n)->maps, EBITMAP_SIZE); @@ -64,11 +64,10 @@ static inline void ebitmap_init(struct ebitmap *e) memset(e, 0, sizeof(*e)); } -static inline unsigned int ebitmap_next_positive(const struct ebitmap *e, - struct ebitmap_node **n, - unsigned int bit) +static inline u32 ebitmap_next_positive(const struct ebitmap *e, + struct ebitmap_node **n, u32 bit) { - unsigned int ofs; + u32 ofs; ofs = find_next_bit((*n)->maps, EBITMAP_SIZE, bit - (*n)->startbit + 1); if (ofs < EBITMAP_SIZE) @@ -87,11 +86,10 @@ static inline unsigned int ebitmap_next_positive(const struct ebitmap *e, #define EBITMAP_NODE_OFFSET(node, bit) \ (((bit) - (node)->startbit) % EBITMAP_UNIT_SIZE) -static inline int ebitmap_node_get_bit(const struct ebitmap_node *n, - unsigned int bit) +static inline int ebitmap_node_get_bit(const struct ebitmap_node *n, u32 bit) { - unsigned int index = EBITMAP_NODE_INDEX(n, bit); - unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit); + u32 index = EBITMAP_NODE_INDEX(n, bit); + u32 ofs = EBITMAP_NODE_OFFSET(n, bit); BUG_ON(index >= EBITMAP_UNIT_NUMS); if ((n->maps[index] & (EBITMAP_BIT << ofs))) @@ -99,21 +97,19 @@ static inline int ebitmap_node_get_bit(const struct ebitmap_node *n, return 0; } -static inline void ebitmap_node_set_bit(struct ebitmap_node *n, - unsigned int bit) +static inline void ebitmap_node_set_bit(struct ebitmap_node *n, u32 bit) { - unsigned int index = EBITMAP_NODE_INDEX(n, bit); - unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit); + u32 index = EBITMAP_NODE_INDEX(n, bit); + u32 ofs = EBITMAP_NODE_OFFSET(n, bit); BUG_ON(index >= EBITMAP_UNIT_NUMS); n->maps[index] |= (EBITMAP_BIT << ofs); } -static inline void ebitmap_node_clr_bit(struct ebitmap_node *n, - unsigned int bit) +static inline void ebitmap_node_clr_bit(struct ebitmap_node *n, u32 bit) { - unsigned int index = EBITMAP_NODE_INDEX(n, bit); - unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit); + u32 index = EBITMAP_NODE_INDEX(n, bit); + u32 ofs = EBITMAP_NODE_OFFSET(n, bit); BUG_ON(index >= EBITMAP_UNIT_NUMS); n->maps[index] &= ~(EBITMAP_BIT << ofs); @@ -130,8 +126,8 @@ int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1, const struct ebitmap *e2); int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, u32 last_e2bit); -int ebitmap_get_bit(const struct ebitmap *e, unsigned long bit); -int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value); +int ebitmap_get_bit(const struct ebitmap *e, u32 bit); +int ebitmap_set_bit(struct ebitmap *e, u32 bit, int value); void ebitmap_destroy(struct ebitmap *e); int ebitmap_read(struct ebitmap *e, void *fp); int ebitmap_write(const struct ebitmap *e, void *fp); -- 2.43.0
Use the internal helper current_sid() where applicable. Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- security/selinux/hooks.c | 22 ++++++---------------- security/selinux/xfrm.c | 7 ++----- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f9a61ff64b83..9c41dc9eb0a0 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2961,7 +2961,7 @@ static int selinux_inode_init_security_anon(struct inode *inode, const struct qstr *name, const struct inode *context_inode) { - const struct task_security_struct *tsec = selinux_cred(current_cred()); + u32 sid = current_sid(); struct common_audit_data ad; struct inode_security_struct *isec; int rc; @@ -2990,7 +2990,7 @@ static int selinux_inode_init_security_anon(struct inode *inode, } else { isec->sclass = SECCLASS_ANON_INODE; rc = security_transition_sid( - tsec->sid, tsec->sid, + sid, sid, isec->sclass, name, &isec->sid); if (rc) return rc; @@ -3005,7 +3005,7 @@ static int selinux_inode_init_security_anon(struct inode *inode, ad.type = LSM_AUDIT_DATA_ANONINODE; ad.u.anonclass = name ? (const char *)name->name : "?"; - return avc_has_perm(tsec->sid, + return avc_has_perm(sid, isec->sid, isec->sclass, FILE__CREATE, @@ -3063,14 +3063,12 @@ static int selinux_inode_readlink(struct dentry *dentry) static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, bool rcu) { - const struct cred *cred = current_cred(); struct common_audit_data ad; struct inode_security_struct *isec; - u32 sid; + u32 sid = current_sid(); ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; - sid = cred_sid(cred); isec = inode_security_rcu(inode, rcu); if (IS_ERR(isec)) return PTR_ERR(isec); @@ -3094,12 +3092,11 @@ static noinline int audit_inode_permission(struct inode *inode, static int selinux_inode_permission(struct inode *inode, int mask) { - const struct cred *cred = current_cred(); u32 perms; bool from_access; bool no_block = mask & MAY_NOT_BLOCK; struct inode_security_struct *isec; - u32 sid; + u32 sid = current_sid(); struct av_decision avd; int rc, rc2; u32 audited, denied; @@ -3116,7 +3113,6 @@ static int selinux_inode_permission(struct inode *inode, int mask) perms = file_mask_to_av(inode->i_mode, mask); - sid = cred_sid(cred); isec = inode_security_rcu(inode, no_block); if (IS_ERR(isec)) return PTR_ERR(isec); @@ -5563,13 +5559,7 @@ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb) static int selinux_secmark_relabel_packet(u32 sid) { - const struct task_security_struct *tsec; - u32 tsid; - - tsec = selinux_cred(current_cred()); - tsid = tsec->sid; - - return avc_has_perm(tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO, + return avc_has_perm(current_sid(), sid, SECCLASS_PACKET, PACKET__RELABELTO, NULL); } diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 95fcd2d3433e..90ec4ef1b082 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -76,7 +76,6 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, gfp_t gfp) { int rc; - const struct task_security_struct *tsec = selinux_cred(current_cred()); struct xfrm_sec_ctx *ctx = NULL; u32 str_len; @@ -103,7 +102,7 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, if (rc) goto err; - rc = avc_has_perm(tsec->sid, ctx->ctx_sid, + rc = avc_has_perm(current_sid(), ctx->ctx_sid, SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); if (rc) goto err; @@ -134,12 +133,10 @@ static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx) */ static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx) { - const struct task_security_struct *tsec = selinux_cred(current_cred()); - if (!ctx) return 0; - return avc_has_perm(tsec->sid, ctx->ctx_sid, + return avc_has_perm(current_sid(), ctx->ctx_sid, SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); } -- 2.43.0
Avoid using an identifier starting with double underscores, which signals a reserved identifier. Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- security/selinux/hooks.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index a0fde0641f77..f9a61ff64b83 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6347,16 +6347,16 @@ static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode) static int selinux_lsm_getattr(unsigned int attr, struct task_struct *p, char **value) { - const struct task_security_struct *__tsec; + const struct task_security_struct *tsec; u32 sid; int error; unsigned len; rcu_read_lock(); - __tsec = selinux_cred(__task_cred(p)); + tsec = selinux_cred(__task_cred(p)); if (current != p) { - error = avc_has_perm(current_sid(), __tsec->sid, + error = avc_has_perm(current_sid(), tsec->sid, SECCLASS_PROCESS, PROCESS__GETATTR, NULL); if (error) goto bad; @@ -6364,22 +6364,22 @@ static int selinux_lsm_getattr(unsigned int attr, struct task_struct *p, switch (attr) { case LSM_ATTR_CURRENT: - sid = __tsec->sid; + sid = tsec->sid; break; case LSM_ATTR_PREV: - sid = __tsec->osid; + sid = tsec->osid; break; case LSM_ATTR_EXEC: - sid = __tsec->exec_sid; + sid = tsec->exec_sid; break; case LSM_ATTR_FSCREATE: - sid = __tsec->create_sid; + sid = tsec->create_sid; break; case LSM_ATTR_KEYCREATE: - sid = __tsec->keycreate_sid; + sid = tsec->keycreate_sid; break; case LSM_ATTR_SOCKCREATE: - sid = __tsec->sockcreate_sid; + sid = tsec->sockcreate_sid; break; default: error = -EOPNOTSUPP; -- 2.43.0
Reject ebitmaps with a node containing an empty map or with an incorrect highbit. Both checks are already performed by userspace, the former since 2008 (patch 13cd4c896068 ("initial import from svn trunk revision 2950")), the latter since v2.7 in 2017 (patch 75b14a5de10a ("libsepol: ebitmap: reject loading bitmaps with incorrect high bit")). Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v2: update wording as suggested in [1] [1]: https://lore.kernel.org/selinux/d476b21729cafb28c1b881113a563b1f.paul@paul-moore.com/ --- security/selinux/ss/ebitmap.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 67c1a73cd5ee..f1ba333f127d 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -448,6 +448,10 @@ int ebitmap_read(struct ebitmap *e, void *fp) goto bad; } map = le64_to_cpu(mapbits); + if (!map) { + pr_err("SELinux: ebitmap: empty map\n"); + goto bad; + } index = (startbit - n->startbit) / EBITMAP_UNIT_SIZE; while (map) { @@ -455,6 +459,13 @@ int ebitmap_read(struct ebitmap *e, void *fp) map = EBITMAP_SHIFT_UNIT_SIZE(map); } } + + if (n && n->startbit + EBITMAP_SIZE != e->highbit) { + pr_err("SELinux: ebitmap: high bit %d is not equal to the expected value %ld\n", + e->highbit, n->startbit + EBITMAP_SIZE); + goto bad; + } + ok: rc = 0; out: -- 2.43.0
If no policy has been loaded yet and thus the current context is still "kernel" avoid logging failures in get_ordered_context_list(), like: get_ordered_context_list: error in processing configuration file /etc/selinux/debian/contexts/users/root get_ordered_context_list: error in processing configuration file /etc/selinux/debian/contexts/default_contexts Since get_context_user() needs a valid context, because it tries to split it into its parts, jump right to the failsafe. --- libselinux/src/get_context_list.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libselinux/src/get_context_list.c b/libselinux/src/get_context_list.c index 7e23be05..ec3775a8 100644 --- a/libselinux/src/get_context_list.c +++ b/libselinux/src/get_context_list.c @@ -427,6 +427,12 @@ int get_ordered_context_list(const char *user, fromcon = backup_fromcon; } + if (strcmp(fromcon, "kernel") == 0) + /* get_context_user() needs a valid context, avoid + * unnecessary log messages if no policy has been loaded + * yet. */ + goto failsafe; + /* Determine the ordering to apply from the optional per-user config and from the global config. */ fname_len = strlen(user_contexts_path) + strlen(user) + 2; -- 2.43.0
On Thu, 2024-03-14 at 23:49 +0000, Daniel Walker (danielwa) wrote: > Hi, > > It seems there is/was a problem using NFS security labels where the server and client use > different MAC policy or model. > > I was reading this page, > > http://www.selinuxproject.org/page/Labeled_NFS/TODO#Label_Translation_Framework > > It seems like this problem was known in 2009 when this page was written. Is > there a way to accomplish having extended attributes shared over NFS to a client > with different selinux policies ? > Currently Linux NFS client and server only support limited server mode, where the server presents the contexts as they are and the client enforces its own policy locally. There's no requirement that the server enforce the same policy (or even enforce a security policy at all), all it's doing is storing and presenting the security label. So what you're saying should "work" today. > Maybe it's possible to allow the client to write local file context without > writing that down to the remote filesystem. > That could be done. Just prevent the client from sending updates to the security context to the server based on some switch. But...what do you do when you make a local change and then the inode gets cycled out of the cache? Once you bring it back in, it's going to revert to its old setting. That sort of thing sounds like it would be of limited utility. -- Jeff Layton <jlayton@kernel.org>
Introduce a new capable flag, CAP_OPT_NOAUDIT_ONDENY, to not generate an audit event if the requested capability is not granted. This will be used in a new capable_any() functionality to reduce the number of necessary capable calls. Handle the flag accordingly in AppArmor and SELinux. CC: linux-block@vger.kernel.org Suggested-by: Paul Moore <paul@paul-moore.com> Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v5: rename flag to CAP_OPT_NOAUDIT_ONDENY, suggested by Serge: https://lore.kernel.org/all/20230606190013.GA640488@mail.hallyn.com/ --- include/linux/security.h | 2 ++ security/apparmor/capability.c | 8 +++++--- security/selinux/hooks.c | 14 ++++++++------ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/include/linux/security.h b/include/linux/security.h index 41a8f667bdfa..c60cae78ff8b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -70,6 +70,8 @@ struct lsm_ctx; #define CAP_OPT_NOAUDIT BIT(1) /* If capable is being called by a setid function */ #define CAP_OPT_INSETID BIT(2) +/* If capable should audit the security request for authorized requests only */ +#define CAP_OPT_NOAUDIT_ONDENY BIT(3) /* LSM Agnostic defines for security_sb_set_mnt_opts() flags */ #define SECURITY_LSM_NATIVE_LABELS 1 diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 9934df16c843..08c9c9a0fc19 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -108,7 +108,8 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile * profile_capable - test if profile allows use of capability @cap * @profile: profile being enforced (NOT NULL, NOT unconfined) * @cap: capability to test if allowed - * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated + * @opts: CAP_OPT_NOAUDIT/CAP_OPT_NOAUDIT_ONDENY bit determines whether audit + * record is generated * @ad: audit data (MAY BE NULL indicating no auditing) * * Returns: 0 if allowed else -EPERM @@ -126,7 +127,7 @@ static int profile_capable(struct aa_profile *profile, int cap, else error = -EPERM; - if (opts & CAP_OPT_NOAUDIT) { + if ((opts & CAP_OPT_NOAUDIT) || ((opts & CAP_OPT_NOAUDIT_ONDENY) && error)) { if (!COMPLAIN_MODE(profile)) return error; /* audit the cap request in complain mode but note that it @@ -143,7 +144,8 @@ static int profile_capable(struct aa_profile *profile, int cap, * @subj_cred: cred we are testing capability against * @label: label being tested for capability (NOT NULL) * @cap: capability to be tested - * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated + * @opts: CAP_OPT_NOAUDIT/CAP_OPT_NOAUDIT_ONDENY bit determines whether audit + * record is generated * * Look up capability in profile capability set. * diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 3448454c82d0..1a2c7c1a89be 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1624,7 +1624,7 @@ static int cred_has_capability(const struct cred *cred, u16 sclass; u32 sid = cred_sid(cred); u32 av = CAP_TO_MASK(cap); - int rc; + int rc, rc2; ad.type = LSM_AUDIT_DATA_CAP; ad.u.cap = cap; @@ -1643,11 +1643,13 @@ static int cred_has_capability(const struct cred *cred, } rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); - if (!(opts & CAP_OPT_NOAUDIT)) { - int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad); - if (rc2) - return rc2; - } + if ((opts & CAP_OPT_NOAUDIT) || ((opts & CAP_OPT_NOAUDIT_ONDENY) && rc)) + return rc; + + rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad); + if (rc2) + return rc2; + return rc; } -- 2.43.0
Hi, It seems there is/was a problem using NFS security labels where the server and client use different MAC policy or model. I was reading this page, http://www.selinuxproject.org/page/Labeled_NFS/TODO#Label_Translation_Framework It seems like this problem was known in 2009 when this page was written. Is there a way to accomplish having extended attributes shared over NFS to a client with different selinux policies ? Maybe it's possible to allow the client to write local file context without writing that down to the remote filesystem. Thanks, Daniel
On Thu, 14 Mar 2024 09:57:57 -0700 Alison Schofield <alison.schofield@intel.com> wrote: > On Fri, Feb 23, 2024 at 12:56:34PM -0500, Steven Rostedt wrote: > > From: "Steven Rostedt (Google)" <rostedt@goodmis.org> > > > > [ > > This is a treewide change. I will likely re-create this patch again in > > the second week of the merge window of v6.9 and submit it then. Hoping > > to keep the conflicts that it will cause to a minimum. > > ] Note, change of plans. I plan on sending this in the next merge window, as this merge window I have this patch: https://lore.kernel.org/linux-trace-kernel/20240312113002.00031668@gandalf.local.home/ That will warn if the source string of __string() is different than the source string of __assign_str(). I want to make sure they are identical before just dropping one of them. > > > diff --git a/drivers/cxl/core/trace.h b/drivers/cxl/core/trace.h > > index bdf117a33744..07ba4e033347 100644 > > --- a/drivers/cxl/core/trace.h > > +++ b/drivers/cxl/core/trace.h > > snip to poison > > > @@ -668,8 +668,8 @@ TRACE_EVENT(cxl_poison, > > ), > > > > TP_fast_assign( > > - __assign_str(memdev, dev_name(&cxlmd->dev)); > > - __assign_str(host, dev_name(cxlmd->dev.parent)); > > + __assign_str(memdev); > > + __assign_str(host); > > I think I get that the above changes work because the TP_STRUCT__entry for > these did: > __string(memdev, dev_name(&cxlmd->dev)) > __string(host, dev_name(cxlmd->dev.parent)) That's the point. They have to be identical or you will likely bug. The __string(name, src) is used to find the string length of src which allocates the necessary length on the ring buffer. The __assign_str(name, src) will copy src into the ring buffer. Similar to: len = strlen(src); buf = malloc(len); strcpy(buf, str); Where __string() is strlen() and __assign_str() is strcpy(). It doesn't make sense to use two different strings, and if you did, it would likely be a bug. But the magic behind __string() does much more than just get the length of the string, and it could easily save the pointer to the string (along with its length) and have it copy that in the __assign_str() call, making the src parameter of __assign_str() useless. > > > __entry->serial = cxlmd->cxlds->serial; > > __entry->overflow_ts = cxl_poison_overflow(flags, overflow_ts); > > __entry->dpa = cxl_poison_record_dpa(record); > > @@ -678,12 +678,12 @@ TRACE_EVENT(cxl_poison, > > __entry->trace_type = trace_type; > > __entry->flags = flags; > > if (region) { > > - __assign_str(region, dev_name(®ion->dev)); > > + __assign_str(region); > > memcpy(__entry->uuid, ®ion->params.uuid, 16); > > __entry->hpa = cxl_trace_hpa(region, cxlmd, > > __entry->dpa); > > } else { > > - __assign_str(region, ""); > > + __assign_str(region); > > memset(__entry->uuid, 0, 16); > > __entry->hpa = ULLONG_MAX; > > For the above 2, there was no helper in TP_STRUCT__entry. A recently > posted patch is fixing that up to be __string(region, NULL) See [1], > with the actual assignment still happening in TP_fast_assign. __string(region, NULL) doesn't make sense. It's like: len = strlen(NULL); buf = malloc(len); strcpy(buf, NULL); ?? I'll reply to that email. -- Steve > > Does that assign logic need to move to the TP_STRUCT__entry definition > when you merge these changes? I'm not clear how much logic is able to be > included, ie like 'C' style code in the TP_STRUCT__entry. > > [1] > https://lore.kernel.org/linux-cxl/20240314044301.2108650-1-alison.schofield@intel.com/
On Fri, Feb 23, 2024 at 12:56:34PM -0500, Steven Rostedt wrote: > From: "Steven Rostedt (Google)" <rostedt@goodmis.org> > > [ > This is a treewide change. I will likely re-create this patch again in > the second week of the merge window of v6.9 and submit it then. Hoping > to keep the conflicts that it will cause to a minimum. > ] > > With the rework of how the __string() handles dynamic strings where it > saves off the source string in field in the helper structure[1], the > assignment of that value to the trace event field is stored in the helper > value and does not need to be passed in again. > > This means that with: > > __string(field, mystring) > > Which use to be assigned with __assign_str(field, mystring), no longer > needs the second parameter and it is unused. With this, __assign_str() > will now only get a single parameter. > > There's over 700 users of __assign_str() and because coccinelle does not > handle the TRACE_EVENT() macro I ended up using the following sed script: > > git grep -l __assign_str | while read a ; do > sed -e 's/\(__assign_str([^,]*[^ ,]\) *,[^;]*/\1)/' $a > /tmp/test-file; > mv /tmp/test-file $a; > done > > I then searched for __assign_str() that did not end with ';' as those > were multi line assignments that the sed script above would fail to catch. > > Note, the same updates will need to be done for: > > __assign_str_len() > __assign_rel_str() > __assign_rel_str_len() > __assign_bitmask() > __assign_rel_bitmask() > __assign_cpumask() > __assign_rel_cpumask() > > [1] https://lore.kernel.org/linux-trace-kernel/20240222211442.634192653@goodmis.org/ > > Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org> > --- > arch/arm64/kernel/trace-events-emulation.h | 2 +- > arch/powerpc/include/asm/trace.h | 4 +- > arch/x86/kvm/trace.h | 2 +- > drivers/base/regmap/trace.h | 18 +-- > drivers/base/trace.h | 2 +- > drivers/block/rnbd/rnbd-srv-trace.h | 12 +- > drivers/cxl/core/trace.h | 24 ++-- snip to CXL > diff --git a/drivers/cxl/core/trace.h b/drivers/cxl/core/trace.h > index bdf117a33744..07ba4e033347 100644 > --- a/drivers/cxl/core/trace.h > +++ b/drivers/cxl/core/trace.h snip to poison > @@ -668,8 +668,8 @@ TRACE_EVENT(cxl_poison, > ), > > TP_fast_assign( > - __assign_str(memdev, dev_name(&cxlmd->dev)); > - __assign_str(host, dev_name(cxlmd->dev.parent)); > + __assign_str(memdev); > + __assign_str(host); I think I get that the above changes work because the TP_STRUCT__entry for these did: __string(memdev, dev_name(&cxlmd->dev)) __string(host, dev_name(cxlmd->dev.parent)) > __entry->serial = cxlmd->cxlds->serial; > __entry->overflow_ts = cxl_poison_overflow(flags, overflow_ts); > __entry->dpa = cxl_poison_record_dpa(record); > @@ -678,12 +678,12 @@ TRACE_EVENT(cxl_poison, > __entry->trace_type = trace_type; > __entry->flags = flags; > if (region) { > - __assign_str(region, dev_name(®ion->dev)); > + __assign_str(region); > memcpy(__entry->uuid, ®ion->params.uuid, 16); > __entry->hpa = cxl_trace_hpa(region, cxlmd, > __entry->dpa); > } else { > - __assign_str(region, ""); > + __assign_str(region); > memset(__entry->uuid, 0, 16); > __entry->hpa = ULLONG_MAX; For the above 2, there was no helper in TP_STRUCT__entry. A recently posted patch is fixing that up to be __string(region, NULL) See [1], with the actual assignment still happening in TP_fast_assign. Does that assign logic need to move to the TP_STRUCT__entry definition when you merge these changes? I'm not clear how much logic is able to be included, ie like 'C' style code in the TP_STRUCT__entry. [1] https://lore.kernel.org/linux-cxl/20240314044301.2108650-1-alison.schofield@intel.com/ Thanks for helping, Alison > }
On Wed, Mar 13, 2024 at 7:12 AM Christian Göttsche <cgzones@googlemail.com> wrote: > > The command line option -d is not supported, drop from usage message. > > Signed-off-by: Christian Göttsche <cgzones@googlemail.com> For these 5 patches: Acked-by: James Carter <jwcart2@gmail.com> > --- > libselinux/utils/selabel_digest.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/libselinux/utils/selabel_digest.c b/libselinux/utils/selabel_digest.c > index bf22b472..50f55311 100644 > --- a/libselinux/utils/selabel_digest.c > +++ b/libselinux/utils/selabel_digest.c > @@ -11,7 +11,7 @@ static size_t digest_len; > static __attribute__ ((__noreturn__)) void usage(const char *progname) > { > fprintf(stderr, > - "usage: %s -b backend [-d] [-v] [-B] [-i] [-f file]\n\n" > + "usage: %s -b backend [-v] [-B] [-i] [-f file]\n\n" > "Where:\n\t" > "-b The backend - \"file\", \"media\", \"x\", \"db\" or " > "\"prop\"\n\t" > -- > 2.43.0 > >
Fine with me as well.
Thanks,
Winfried
> Op 14-03-2024 14:59 CET schreef Jordan Williams <jordan@jwillikers.com>:
>
>
> On 3/13/24 17:48, James Carter wrote:
> > In libselinux there is an availability check for strlcpy() and
> > in both libselinux and libsepol there are availability checks for
> > reallocarray() in the src Makfiles. CFLAGS and LDFLAGS are needed
> > for cross-compiling, but, unfortunately, the default CFLAGS cause
> > all of these availability checks to fail to compile because of
> > compilationerrors (rather than just the function not being available).
> >
> > Add CFLAGS and LDFLAGS to the availibility checks, update the checks
> > so that a compilation error will only happen if the function being
> > checked for is not available, and make checks for the same function
> > the same in both libselinux and libsepol.
> >
> > Suggested-by: Jordan Williams <jordan@jwillikers.com>
> > Suggested-by: Winfried Dobbe <winfried_mb2@xmsnet.nl>
> > Signed-off-by: James Carter <jwcart2@gmail.com>
> > ---
> > libselinux/src/Makefile | 4 ++--
> > libsepol/src/Makefile | 2 +-
> > 2 files changed, 3 insertions(+), 3 deletions(-)
> >
> > diff --git a/libselinux/src/Makefile b/libselinux/src/Makefile
> > index d3b981fc..41cfbdca 100644
> > --- a/libselinux/src/Makefile
> > +++ b/libselinux/src/Makefile
> > @@ -104,13 +104,13 @@ override CFLAGS += -I../include -D_GNU_SOURCE $(DISABLE_FLAGS) $(PCRE_CFLAGS)
> >
> > # check for strlcpy(3) availability
> > H := \#
> > -ifeq (yes,$(shell printf '${H}include <string.h>\nint main(void){char*d,*s;strlcpy(d, s, 0);return 0;}' | $(CC) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
> > +ifeq (yes,$(shell printf '${H}include <string.h>\nint main(void){char d[2];const char *s="a";return (size_t)strlcpy(d,s,sizeof(d))>=sizeof(d);}' | $(CC) $(CFLAGS) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
> > override CFLAGS += -DHAVE_STRLCPY
> > endif
> >
> > # check for reallocarray(3) availability
> > H := \#
> > -ifeq (yes,$(shell printf '${H}include <stdlib.h>\nint main(void){reallocarray(NULL, 0, 0);return 0;}' | $(CC) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
> > +ifeq (yes,$(shell printf '${H}include <stdlib.h>\nint main(void){return reallocarray(NULL,0,0)==NULL;}' | $(CC) $(CFLAGS) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
> > override CFLAGS += -DHAVE_REALLOCARRAY
> > endif
> >
> > diff --git a/libsepol/src/Makefile b/libsepol/src/Makefile
> > index 16b9bd5e..7b0e8446 100644
> > --- a/libsepol/src/Makefile
> > +++ b/libsepol/src/Makefile
> > @@ -31,7 +31,7 @@ endif
> >
> > # check for reallocarray(3) availability
> > H := \#
> > -ifeq (yes,$(shell printf '${H}define _GNU_SOURCE\n${H}include <stdlib.h>\nint main(void){void*p=reallocarray(NULL, 1, sizeof(char));return 0;}' | $(CC) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
> > +ifeq (yes,$(shell printf '${H}include <stdlib.h>\nint main(void){return reallocarray(NULL,0,0)==NULL;}' | $(CC) $(CFLAGS) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
> > override CFLAGS += -DHAVE_REALLOCARRAY
> > endif
> >
> This works great, thanks!
On 3/13/24 17:48, James Carter wrote:
> In libselinux there is an availability check for strlcpy() and
> in both libselinux and libsepol there are availability checks for
> reallocarray() in the src Makfiles. CFLAGS and LDFLAGS are needed
> for cross-compiling, but, unfortunately, the default CFLAGS cause
> all of these availability checks to fail to compile because of
> compilationerrors (rather than just the function not being available).
>
> Add CFLAGS and LDFLAGS to the availibility checks, update the checks
> so that a compilation error will only happen if the function being
> checked for is not available, and make checks for the same function
> the same in both libselinux and libsepol.
>
> Suggested-by: Jordan Williams <jordan@jwillikers.com>
> Suggested-by: Winfried Dobbe <winfried_mb2@xmsnet.nl>
> Signed-off-by: James Carter <jwcart2@gmail.com>
> ---
> libselinux/src/Makefile | 4 ++--
> libsepol/src/Makefile | 2 +-
> 2 files changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/libselinux/src/Makefile b/libselinux/src/Makefile
> index d3b981fc..41cfbdca 100644
> --- a/libselinux/src/Makefile
> +++ b/libselinux/src/Makefile
> @@ -104,13 +104,13 @@ override CFLAGS += -I../include -D_GNU_SOURCE $(DISABLE_FLAGS) $(PCRE_CFLAGS)
>
> # check for strlcpy(3) availability
> H := \#
> -ifeq (yes,$(shell printf '${H}include <string.h>\nint main(void){char*d,*s;strlcpy(d, s, 0);return 0;}' | $(CC) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
> +ifeq (yes,$(shell printf '${H}include <string.h>\nint main(void){char d[2];const char *s="a";return (size_t)strlcpy(d,s,sizeof(d))>=sizeof(d);}' | $(CC) $(CFLAGS) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
> override CFLAGS += -DHAVE_STRLCPY
> endif
>
> # check for reallocarray(3) availability
> H := \#
> -ifeq (yes,$(shell printf '${H}include <stdlib.h>\nint main(void){reallocarray(NULL, 0, 0);return 0;}' | $(CC) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
> +ifeq (yes,$(shell printf '${H}include <stdlib.h>\nint main(void){return reallocarray(NULL,0,0)==NULL;}' | $(CC) $(CFLAGS) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
> override CFLAGS += -DHAVE_REALLOCARRAY
> endif
>
> diff --git a/libsepol/src/Makefile b/libsepol/src/Makefile
> index 16b9bd5e..7b0e8446 100644
> --- a/libsepol/src/Makefile
> +++ b/libsepol/src/Makefile
> @@ -31,7 +31,7 @@ endif
>
> # check for reallocarray(3) availability
> H := \#
> -ifeq (yes,$(shell printf '${H}define _GNU_SOURCE\n${H}include <stdlib.h>\nint main(void){void*p=reallocarray(NULL, 1, sizeof(char));return 0;}' | $(CC) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
> +ifeq (yes,$(shell printf '${H}include <stdlib.h>\nint main(void){return reallocarray(NULL,0,0)==NULL;}' | $(CC) $(CFLAGS) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
> override CFLAGS += -DHAVE_REALLOCARRAY
> endif
>
This works great, thanks!
On Mon, Mar 11, 2024 at 10:59 AM Christian Göttsche <cgzones@googlemail.com> wrote: > > The level_datum_t member notdefined is checked to be 1 during validation > and the fuzzer calls policydb_validate(). > Drop the redundant check (as announced in the TODO). > > Signed-off-by: Christian Göttsche <cgzones@googlemail.com> For these 5 patches: Acked-by: James Carter <jwcart2@gmail.com> > --- > checkpolicy/fuzz/checkpolicy-fuzzer.c | 17 ----------------- > 1 file changed, 17 deletions(-) > > diff --git a/checkpolicy/fuzz/checkpolicy-fuzzer.c b/checkpolicy/fuzz/checkpolicy-fuzzer.c > index a3da0b57..f3a17cce 100644 > --- a/checkpolicy/fuzz/checkpolicy-fuzzer.c > +++ b/checkpolicy/fuzz/checkpolicy-fuzzer.c > @@ -130,21 +130,6 @@ static int read_source_policy(policydb_t *p, const uint8_t *data, size_t size) > return 0; > } > > -static int check_level(hashtab_key_t key, hashtab_datum_t datum, void *arg __attribute__ ((unused))) > -{ > - const level_datum_t *levdatum = (level_datum_t *) datum; > - > - // TODO: drop member defined if proven to be always set > - if (!levdatum->isalias && levdatum->notdefined) { > - fprintf(stderr, > - "Error: sensitivity %s was not used in a level definition!\n", > - key); > - abort(); > - } > - > - return 0; > -} > - > static int write_binary_policy(FILE *outfp, policydb_t *p) > { > struct policy_file pf; > @@ -198,8 +183,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) > if (read_source_policy(&parsepolicydb, data, size)) > goto exit; > > - (void) hashtab_map(parsepolicydb.p_levels.table, check_level, NULL); > - > if (parsepolicydb.policy_type == POLICY_BASE) { > if (link_modules(NULL, &parsepolicydb, NULL, 0, VERBOSE)) > goto exit; > -- > 2.43.0 > >
On Fri, Mar 1, 2024 at 12:03 PM Jordan Williams <jordan@jwillikers.com> wrote: > > Pass CFLAGS and LDFLAGS when checking for realocarray and strlcpy. > This brings things inline with the fixes for libsepol/src/Makefile. > This better supports cross-compiling scenarios. > There, flags like -sysroot need to included when running these checks. > > Signed-off-by: Jordan Williams <jordan@jwillikers.com> > > diff --git a/libselinux/src/Makefile b/libselinux/src/Makefile > index d3b981fc..3a9b5300 100644 > --- a/libselinux/src/Makefile > +++ b/libselinux/src/Makefile > @@ -104,13 +104,13 @@ override CFLAGS += -I../include -D_GNU_SOURCE > $(DISABLE_FLAGS) $(PCRE_CFLAGS) > > # check for strlcpy(3) availability > H := \# > -ifeq (yes,$(shell printf '${H}include <string.h>\nint > main(void){char*d,*s;strlcpy(d, s, 0);return 0;}' | $(CC) -x c -o > /dev/null - >/dev/null 2>&1 && echo yes)) > +ifeq (yes,$(shell printf '${H}include <string.h>\nint > main(void){char*d,*s;strlcpy(d, s, 0);return 0;}' | $(CC) $(CFLAGS) > $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes)) > override CFLAGS += -DHAVE_STRLCPY > endif > > # check for reallocarray(3) availability > H := \# > -ifeq (yes,$(shell printf '${H}include <stdlib.h>\nint > main(void){reallocarray(NULL, 0, 0);return 0;}' | $(CC) -x c -o > /dev/null - >/dev/null 2>&1 && echo yes)) > +ifeq (yes,$(shell printf '${H}include <stdlib.h>\nint > main(void){reallocarray(NULL, 0, 0);return 0;}' | $(CC) $(CFLAGS) > $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes)) > override CFLAGS += -DHAVE_REALLOCARRAY > endif > The default CFLAGS causes the compilation to fail even if strlcpy() or reallocarray() exist. See if the patch I sent to the list works for you. Thanks, Jim > -- > 2.44.0 > >
On Thu, Feb 29, 2024 at 11:45 AM Winfried Dobbe <winfried_mb2@xmsnet.nl> wrote: > > In addition to commit 3e3661f602fe7d7dc972bf695fd178370bbd7e54, CFLAGS > are also needed for the reallocarray detection when cross-compiling > libsepoll. > > For example when cross-compiling for Arm Cortex-A9 the compiler finds > stdlib.h (after the addition of LDFLAGS in above mentioned 3e3661f). > But then tries to include soft-float stubs because gcc options > -mfpu=neon -mfloat-abi=hard are missing. See output of detection: > > In file included from /home/wdobbe/.conan2/p/b/swpt_fb08c05e04578/p/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include/gnu/stubs.h:40, > from /home/wdobbe/.conan2/p/b/swpt_fb08c05e04578/p/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include/features.h:474, > from /home/wdobbe/.conan2/p/b/swpt_fb08c05e04578/p/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include/bits/libc-header-start.h:33, > from /home/wdobbe/.conan2/p/b/swpt_fb08c05e04578/p/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include/stdlib.h:25, > from <stdin>:2: > /home/wdobbe/.conan2/p/b/swpt_fb08c05e04578/p/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include/gnu/stubs-32.h:7:11: fatal error: gnu/stubs-soft.h: No such file or directory > 7 | # include <gnu/stubs-soft.h> > | ^~~~~~~~~~~~~~~~~~ > compilation terminated. > > Signed-off-by: Winfried Dobbe <winfried_mb2@xmsnet.nl> > --- > libsepol/src/Makefile | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/libsepol/src/Makefile b/libsepol/src/Makefile > index 16b9bd5e..fd6329d4 100644 > --- a/libsepol/src/Makefile > +++ b/libsepol/src/Makefile > @@ -31,7 +31,7 @@ endif > > # check for reallocarray(3) availability > H := \# > -ifeq (yes,$(shell printf '${H}define _GNU_SOURCE\n${H}include <stdlib.h>\nint main(void){void*p=reallocarray(NULL, 1, sizeof(char));return 0;}' | $(CC) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes)) > +ifeq (yes,$(shell printf '${H}define _GNU_SOURCE\n${H}include <stdlib.h>\nint main(void){void*p=reallocarray(NULL, 1, sizeof(char));return 0;}' | $(CC) $(CFLAGS) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes)) The default CFLAGS causes the compilation to fail even if reallocarray exists on the system. See if the patch I sent to the list works for you. Thanks, Jim > override CFLAGS += -DHAVE_REALLOCARRAY > endif > > -- > 2.35.3 > >
In libselinux there is an availability check for strlcpy() and in both libselinux and libsepol there are availability checks for reallocarray() in the src Makfiles. CFLAGS and LDFLAGS are needed for cross-compiling, but, unfortunately, the default CFLAGS cause all of these availability checks to fail to compile because of compilationerrors (rather than just the function not being available). Add CFLAGS and LDFLAGS to the availibility checks, update the checks so that a compilation error will only happen if the function being checked for is not available, and make checks for the same function the same in both libselinux and libsepol. Suggested-by: Jordan Williams <jordan@jwillikers.com> Suggested-by: Winfried Dobbe <winfried_mb2@xmsnet.nl> Signed-off-by: James Carter <jwcart2@gmail.com> --- libselinux/src/Makefile | 4 ++-- libsepol/src/Makefile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libselinux/src/Makefile b/libselinux/src/Makefile index d3b981fc..41cfbdca 100644 --- a/libselinux/src/Makefile +++ b/libselinux/src/Makefile @@ -104,13 +104,13 @@ override CFLAGS += -I../include -D_GNU_SOURCE $(DISABLE_FLAGS) $(PCRE_CFLAGS) # check for strlcpy(3) availability H := \# -ifeq (yes,$(shell printf '${H}include <string.h>\nint main(void){char*d,*s;strlcpy(d, s, 0);return 0;}' | $(CC) -x c -o /dev/null - >/dev/null 2>&1 && echo yes)) +ifeq (yes,$(shell printf '${H}include <string.h>\nint main(void){char d[2];const char *s="a";return (size_t)strlcpy(d,s,sizeof(d))>=sizeof(d);}' | $(CC) $(CFLAGS) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes)) override CFLAGS += -DHAVE_STRLCPY endif # check for reallocarray(3) availability H := \# -ifeq (yes,$(shell printf '${H}include <stdlib.h>\nint main(void){reallocarray(NULL, 0, 0);return 0;}' | $(CC) -x c -o /dev/null - >/dev/null 2>&1 && echo yes)) +ifeq (yes,$(shell printf '${H}include <stdlib.h>\nint main(void){return reallocarray(NULL,0,0)==NULL;}' | $(CC) $(CFLAGS) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes)) override CFLAGS += -DHAVE_REALLOCARRAY endif diff --git a/libsepol/src/Makefile b/libsepol/src/Makefile index 16b9bd5e..7b0e8446 100644 --- a/libsepol/src/Makefile +++ b/libsepol/src/Makefile @@ -31,7 +31,7 @@ endif # check for reallocarray(3) availability H := \# -ifeq (yes,$(shell printf '${H}define _GNU_SOURCE\n${H}include <stdlib.h>\nint main(void){void*p=reallocarray(NULL, 1, sizeof(char));return 0;}' | $(CC) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes)) +ifeq (yes,$(shell printf '${H}include <stdlib.h>\nint main(void){return reallocarray(NULL,0,0)==NULL;}' | $(CC) $(CFLAGS) $(LDFLAGS) -x c -o /dev/null - >/dev/null 2>&1 && echo yes)) override CFLAGS += -DHAVE_REALLOCARRAY endif -- 2.44.0
Since commit 65c8fd45 ("libselinux: fail selabel_open(3) on invalid option") selabel_open(3) rejects options not supported for the respective backend. Pass SELABEL_OPT_BASEONLY only if the file backend is selected. Reported-by: zgzxx (https://github.com/SELinuxProject/selinux/issues/427) Fixes: 65c8fd45 ("libselinux: fail selabel_open(3) on invalid option") Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- libselinux/utils/selabel_digest.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libselinux/utils/selabel_digest.c b/libselinux/utils/selabel_digest.c index 64051070..47aad21f 100644 --- a/libselinux/utils/selabel_digest.c +++ b/libselinux/utils/selabel_digest.c @@ -71,8 +71,8 @@ int main(int argc, char **argv) struct selabel_handle *hnd; struct selinux_opt selabel_option[] = { { SELABEL_OPT_PATH, file }, - { SELABEL_OPT_BASEONLY, baseonly }, - { SELABEL_OPT_DIGEST, digest } + { SELABEL_OPT_DIGEST, digest }, + { SELABEL_OPT_BASEONLY, baseonly } }; if (argc < 3) @@ -119,10 +119,10 @@ int main(int argc, char **argv) memset(cmd_buf, 0, sizeof(cmd_buf)); selabel_option[0].value = file; - selabel_option[1].value = baseonly; - selabel_option[2].value = digest; + selabel_option[1].value = digest; + selabel_option[2].value = baseonly; - hnd = selabel_open(backend, selabel_option, 3); + hnd = selabel_open(backend, selabel_option, backend == SELABEL_CTX_FILE ? 3 : 2); if (!hnd) { switch (errno) { case EOVERFLOW: -- 2.43.0