Integrate parts of meta-intel-iot-security
[AGL/meta-agl.git] / meta-security / classes / xattr-images.bbclass
1 # Both Smack and IMA/EVM rely on xattrs. Inheriting this class ensures
2 # that these xattrs get preserved in tar and jffs2 images.
3 #
4 # It also fixes the rootfs so that the content of directories with
5 # SMACK::TRANSMUTE is correctly labelled. This is because pseudo does
6 # not know the special semantic of SMACK::TRANSMUTE and omits the
7 # updating of the Smack label when creating entries inside such a directory,
8 # for example /etc (see base-files_%.bbappend). Without the fixup,
9 # files already installed during the image creation would have different (and
10 # wrong) Smack labels.
11
12 # xattr support is expected to be compiled into mtd-utils. We just need to
13 # use it.
14 EXTRA_IMAGECMD_jffs2_append = " --with-xattr"
15
16 # By default, OE-core uses tar from the host, which may or may not have the
17 # --xattrs parameter which was introduced in 1.27. For image building we
18 # use a recent enough tar instead.
19 #
20 # The GNU documentation does not specify whether --xattrs-include is necessary.
21 # In practice, it turned out to be not needed when creating archives and
22 # required when extracting, but it seems prudent to use it in both cases.
23 IMAGE_DEPENDS_tar_append = " tar-replacement-native"
24 EXTRANATIVEPATH += "tar-native"
25 IMAGE_CMD_TAR = "tar --xattrs --xattrs-include=*"
26
27 xattr_images_fix_transmute[dirs] = "${IMAGE_ROOTFS}"
28 python xattr_images_fix_transmute () {
29     # The recursive updating of the Smack label ensures that each entry
30     # has the label set for its parent directories if one of those was
31     # marked as transmuting.
32     #
33     # In addition, "_" is set explicitly on everything that would not
34     # have a label otherwise. This is a workaround for tools like swupd
35     # which transfers files from a rootfs onto a target device where Smack
36     # is active: on the target, each file gets assigned a label, typically
37     # the one from the process which creates it. swupd (or rather, the tools
38     # it is currently built on) knows how to set security.SMACK64="_" when
39     # it is set on the original files, but it does not know that it needs
40     # to remove that xattr when not set.
41     import os
42     import errno
43
44     if getattr(os, 'getxattr', None):
45         # Python 3: os has xattr support.
46         def lgetxattr(f, attr):
47             try:
48                 value = os.getxattr(f, attr, follow_symlinks=False)
49                 return value.decode('utf8')
50             except OSError as ex:
51                 if ex.errno == errno.ENODATA:
52                     return None
53
54         def lsetxattr(f, attr, value):
55             os.setxattr(f, attr.encode('utf8'), value.encode('utf8'), follow_symlinks=False)
56     else:
57         # Python 2: xattr support only in xattr module.
58         #
59         # Cannot use the 'xattr' module, it is not part of a standard Python
60         # installation. Instead re-implement using ctypes. Only has to be good
61         # enough for xattrs that are strings. Always operates on the symlinks themselves,
62         # not what they point to.
63         import ctypes
64
65         # We cannot look up the xattr functions inside libc. That bypasses
66         # pseudo, which overrides these functions via LD_PRELOAD. Instead we have to
67         # find the function address and then create a ctypes function from it.
68         libdl = ctypes.CDLL("libdl.so.2")
69         _dlsym = libdl.dlsym
70         _dlsym.restype = ctypes.c_void_p
71         RTLD_DEFAULT = ctypes.c_void_p(0)
72         _lgetxattr = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p, ctypes.c_size_t,
73                     use_errno=True)(_dlsym(RTLD_DEFAULT, 'lgetxattr'))
74         _lsetxattr = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_int,
75                     use_errno=True)(_dlsym(RTLD_DEFAULT, 'lsetxattr'))
76
77         def lgetxattr(f, attr):
78             len = 32
79             while True:
80                 buffer = ctypes.create_string_buffer('\000' * len)
81                 res = _lgetxattr(f, attr, buffer, ctypes.c_size_t(len))
82                 if res >= 0:
83                     return buffer.value
84                 else:
85                     error = ctypes.get_errno()
86                     if ctypes.get_errno() == errno.ERANGE:
87                         len *= 2
88                     elif error == errno.ENODATA:
89                         return None
90                     else:
91                         raise IOError(error, 'lgetxattr(%s, %s): %d = %s = %s' %
92                                              (f, attr, error, errno.errorcode[error], os.strerror(error)))
93
94         def lsetxattr(f, attr, value):
95             res = _lsetxattr(f, attr, value, ctypes.c_size_t(len(value)), ctypes.c_int(0))
96             if res != 0:
97                 error = ctypes.get_errno()
98                 raise IOError(error, 'lsetxattr(%s, %s, %s): %d = %s = %s' %
99                                      (f, attr, value, error, errno.errorcode[error], os.strerror(error)))
100
101     def visit(path, deflabel, deftransmute):
102         isrealdir = os.path.isdir(path) and not os.path.islink(path)
103         curlabel = lgetxattr(path, 'security.SMACK64')
104         transmute = lgetxattr(path, 'security.SMACK64TRANSMUTE') == 'TRUE'
105
106         if not curlabel:
107             # Since swupd doesn't remove the label from an updated file assigned by
108             # the target device's kernel upon unpacking the file from an update,
109             # we have to set the floor label explicitly even though it is the default label
110             # and thus adding it would create additional overhead. Otherwise this
111             # would result in hash mismatches reported by `swupd verify`.
112             lsetxattr(path, 'security.SMACK64', deflabel)
113             if not transmute and deftransmute and isrealdir:
114                 lsetxattr(path, 'security.SMACK64TRANSMUTE', 'TRUE')
115
116         # Identify transmuting directories and change the default Smack
117         # label inside them. In addition, directories themselves must become
118         # transmuting.
119         if isrealdir:
120             if transmute:
121                 deflabel = lgetxattr(path, 'security.SMACK64')
122                 deftransmute = True
123                 if deflabel is None:
124                     raise RuntimeError('%s: transmuting directory without Smack label' % path)
125             elif curlabel:
126                 # Directory with explicit label set and not transmuting => do not
127                 # change the content unless we run into another transmuting directory.
128                deflabel = '_'
129                deftransmute = False
130
131             for entry in os.listdir(path):
132                 visit(os.path.join(path, entry), deflabel, deftransmute)
133
134     visit('.', '_', False)
135 }
136 # Same logic as in ima-evm-rootfs.bbclass: try to run as late as possible.
137 IMAGE_PREPROCESS_COMMAND_append_with-lsm-smack = " xattr_images_fix_transmute ; "