FS_IOC_ADD_ENCRYPTION_KEY
ioctl 的 Python 脚本。
此 ioctl 需要一个类型(指向)
fscrypt_add_key_arg
的参数,该参数在 Linux 内核头文件中具有以下定义:
struct fscrypt_add_key_arg {
struct fscrypt_key_specifier key_spec;
__u32 raw_size;
__u32 key_id;
__u32 __reserved[8];
__u8 raw[];
};
#define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1
#define FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER 2
#define FSCRYPT_KEY_DESCRIPTOR_SIZE 8
#define FSCRYPT_KEY_IDENTIFIER_SIZE 16
struct fscrypt_key_specifier {
__u32 type; /* one of FSCRYPT_KEY_SPEC_TYPE_* */
__u32 __reserved;
union {
__u8 __reserved[32]; /* reserve some extra space */
__u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
__u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
} u;
};
这是我的Python代码:
import fcntl
import struct
FS_IOC_ADD_ENCRYPTION_KEY = 0xc0506617
policy_data.key_descriptor: str = get_key_descriptor()
policy_key: bytes = get_policy_key(policy_data)
fscrypt_key_specifier = struct.pack(
'II16s',
0x2,
0,
bytes.fromhex(policy_data.key_descriptor)
)
fscrypt_add_key_arg = struct.pack(
f'{len(fscrypt_key_specifier)}sII8I{len(policy_key)}s',
fscrypt_key_specifier,
len(policy_key),
0,
0, 0, 0, 0, 0, 0, 0, 0,
policy_key
)
fd = os.open('/mnt/external', os.O_RDONLY)
res = fcntl.ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, fscrypt_add_key_arg)
print(res)
当我执行此代码时,我得到一个
OSError
:
Traceback (most recent call last):
File "/home/foo/fscryptdump/./main.py", line 101, in <module>
res = fcntl.ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, fscrypt_add_key_arg)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
OSError: [Errno 22] Invalid argument
我已经仔细检查了该 ioctl 的文档,我认为我传递的值是正确的,但它们的打包方式可能存在问题。
我该如何解决这个问题?
将我的评论扩展为答案:
struct
模块和 C union
s据我所知,
struct
模块没有针对C union
的特殊规定。尽管我认为这不会干扰您在这种特殊情况下的工作,但使用 struct
打包或解包包含联合的结构是一个潜在的问题。结构尺寸和布局布局受每个成员(包括 union
)的尺寸和对齐要求的影响,因此要在包含 struct
的结构中使用 union
,您不仅需要手动考虑 union
' 自己的布局,以及它们对包含结构的影响。
union
是固定大小的数据类型,足以容纳其最大的成员。但它们也可以有尾部填充,因此 union
可以严格大于其最大成员。这取决于所使用的 C 语言实现。
C
unions
是固定大小 数据类型,足够大以包含其任何一个成员。因此,...
struct fscrypt_key_specifier {
__u32 type; /* one of FSCRYPT_KEY_SPEC_TYPE_* */
__u32 __reserved;
union {
__u8 __reserved[32]; /* reserve some extra space */
__u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
__u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
} u;
};
... 至少有 32 个字节长,以便能够容纳成员
__reserved
。因此,正如 @TurePålsson 首先观察到的那样,格式 'II16s'
不可能是打包此类结构的正确格式。它将生成至少小 16 个字节的表示,但如果成员 u
实际上大于 32 个字节,则可能会更加不足。
实际上,根据代码注释和一般编程原则,我预计
FSCRYPT_KEY_DESCRIPTOR_SIZE
和 FSCRYPT_KEY_IDENTIFIER_SIZE
都不会超过 32,即 __reserved
成员的大小。此外,我还做出了一个有根据的猜测,包含三个字节数组(最大的包含 32 个字节)的联合不会使用任何尾随填充进行布局,因此其大小恰好是 32 个字节。
内核的 __u32 和 __u8 数据类型是显式大小数据类型。前者不一定与宿主原生的
unsigned int
类型相匹配。这正是 struct
模块的“标准”数据类型大小旨在解决的情况,但您提供的格式指定“本机”数据类型大小(默认情况下)。也许这是一个没有区别的区别,但可以想象,您或继承您代码的人会遇到一个平台,其中相关的本机大小与struct
的标准大小不匹配。为了更正确地匹配 C 结构定义,您应该使用指定“标准”数据大小和本机字节顺序的格式。这是通过在其前面添加
=
来实现的。您还需要选择与结构匹配的标准尺寸的格式代码,但在这种情况下不需要任何更改。工会会员规模
un打包这种类型的结构,并使用相同的联合成员。
整体推荐fscrypt_key_specifier
:
'=II16s16x'
(标准类型大小、本机字节顺序、16 字节联合成员、联合中成员末尾后的 16 字节)。
struct.pack()
将对填充进行零填充。它还会根据需要截断或填充数据,使其恰好为 16 字节。
struct.unpack()
将为成员读取 16 个字节,并提供恰好那么长的
bytes
。
'=II32s'
(标准类型大小、本机字节顺序、32 字节联合成员)。
struct.pack()
将根据需要截断或填充数据,使其恰好为 32 字节。
struct.unpack()
将为成员读取 32 个字节,并提供恰好那么长的
bytes
。
=
。这将为前两个成员使用本机数据类型大小(和字节顺序)。尽管其中任何一个都可能适合您,但严格来说,它们并未与目标结构类型正确匹配。