1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 * This file is part of libmount from util-linux project.
5 * Copyright (C) 2019 Microsoft Corporation
7 * libmount is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
15 #if defined(HAVE_CRYPTSETUP)
17 #include <libcryptsetup.h>
19 /* Taken from https://gitlab.com/cryptsetup/cryptsetup/blob/master/lib/utils_crypt.c#L225 */
20 static size_t crypt_hex_to_bytes(const char *hex
, char **result
)
22 char buf
[3] = "xx\0", *endp
, *bytes
;
34 for (i
= 0; i
< len
; i
++) {
35 memcpy(buf
, &hex
[i
* 2], 2);
36 bytes
[i
] = strtoul(buf
, &endp
, 16);
37 if (endp
!= &buf
[2]) {
47 int mnt_context_setup_veritydev(struct libmnt_context
*cxt
)
49 const char *backing_file
, *optstr
;
50 char *val
= NULL
, *key
= NULL
, *root_hash_binary
= NULL
, *mapper_device
= NULL
,
51 *mapper_device_full
= NULL
, *backing_file_basename
= NULL
, *root_hash
= NULL
,
53 size_t len
, hash_size
, keysize
= 0;
54 struct crypt_params_verity crypt_params
= {};
55 struct crypt_device
*crypt_dev
= NULL
;
61 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
63 /* dm-verity volumes are read-only, and mount will fail if not set */
64 mnt_context_set_mflags(cxt
, (cxt
->mountflags
| MS_RDONLY
));
66 backing_file
= mnt_fs_get_srcpath(cxt
->fs
);
70 /* To avoid clashes, prefix libmnt_ to all mapper devices */
71 backing_file_basename
= basename(backing_file
);
72 mapper_device
= calloc(strlen(backing_file_basename
) + strlen("libmnt_") + 1, sizeof(char));
75 strcat(mapper_device
, "libmnt_");
76 strcat(mapper_device
, backing_file_basename
);
78 DBG(VERITY
, ul_debugobj(cxt
, "trying to setup verity device for %s", backing_file
));
80 optstr
= mnt_fs_get_user_options(cxt
->fs
);
85 if (rc
== 0 && (cxt
->user_mountflags
& MNT_MS_HASH_DEVICE
) &&
86 mnt_optstr_get_option(optstr
, "verity.hashdevice", &val
, &len
) == 0 && val
) {
87 hash_device
= strndup(val
, len
);
88 rc
= hash_device
? 0 : -ENOMEM
;
94 if (rc
== 0 && (cxt
->user_mountflags
& MNT_MS_ROOT_HASH
) &&
95 mnt_optstr_get_option(optstr
, "verity.roothash", &val
, &len
) == 0 && val
) {
96 root_hash
= strndup(val
, len
);
97 rc
= root_hash
? 0 : -ENOMEM
;
103 if (rc
== 0 && (cxt
->user_mountflags
& MNT_MS_HASH_OFFSET
) &&
104 mnt_optstr_get_option(optstr
, "verity.hashoffset", &val
, &len
) == 0) {
105 rc
= mnt_parse_offset(val
, len
, &offset
);
107 DBG(VERITY
, ul_debugobj(cxt
, "failed to parse verity.hashoffset="));
108 rc
= -MNT_ERR_MOUNTOPT
;
115 rc
= crypt_init_data_device(&crypt_dev
, hash_device
, backing_file
);
119 memset(&crypt_params
, 0, sizeof(struct crypt_params_verity
));
120 crypt_params
.hash_area_offset
= offset
;
121 crypt_params
.fec_area_offset
= 0;
122 crypt_params
.fec_roots
= 0;
123 crypt_params
.fec_device
= NULL
;
124 crypt_params
.flags
= 0;
125 rc
= crypt_load(crypt_dev
, CRYPT_VERITY
, &crypt_params
);
129 hash_size
= crypt_get_volume_key_size(crypt_dev
);
130 if (crypt_hex_to_bytes(root_hash
, &root_hash_binary
) != hash_size
) {
131 DBG(VERITY
, ul_debugobj(cxt
, "root hash %s is not of length %zu", root_hash
, hash_size
));
135 rc
= crypt_activate_by_volume_key(crypt_dev
, mapper_device
, root_hash_binary
, hash_size
,
136 CRYPT_ACTIVATE_READONLY
);
138 * If the mapper device already exists, and if libcryptsetup supports it, get the root
139 * hash associated with the existing one and compare it with the parameter passed by
140 * the user. If they match, then we can be sure the user intended to mount the exact
141 * same device, and simply reuse it and return success.
142 * The kernel does the refcounting for us.
143 * If libcryptsetup does not support getting the root hash out of an existing device,
144 * then return an error and tell the user that the device is already in use.
145 * Pass through only OOM errors or mismatching root hash errors.
148 DBG(VERITY
, ul_debugobj(cxt
, "%s already in use as /dev/mapper/%s", backing_file
, mapper_device
));
149 crypt_free(crypt_dev
);
150 rc
= crypt_init_by_name(&crypt_dev
, mapper_device
);
152 rc
= crypt_get_verity_info(crypt_dev
, &crypt_params
);
154 key
= calloc(hash_size
, 1);
162 rc
= crypt_volume_key_get(crypt_dev
, CRYPT_ANY_SLOT
, key
, &keysize
, NULL
, 0);
165 DBG(VERITY
, ul_debugobj(cxt
, "comparing root hash of existing device with %s", root_hash
));
166 if (memcmp(key
, root_hash_binary
, hash_size
)) {
167 DBG(VERITY
, ul_debugobj(cxt
, "existing device's hash does not match with %s", root_hash
));
172 DBG(VERITY
, ul_debugobj(cxt
, "libcryptsetup does not support extracting root hash of existing device"));
178 DBG(VERITY
, ul_debugobj(cxt
, "root hash of %s matches %s, reusing device", mapper_device
, root_hash
));
183 cxt
->flags
|= MNT_FL_VERITYDEV_READY
;
184 mapper_device_full
= calloc(strlen(mapper_device
) + strlen("/dev/mapper/") + 1, sizeof(char));
185 if (!mapper_device_full
)
188 strcat(mapper_device_full
, "/dev/mapper/");
189 strcat(mapper_device_full
, mapper_device
);
190 rc
= mnt_fs_set_source(cxt
->fs
, mapper_device_full
);
195 crypt_free(crypt_dev
);
196 free(root_hash_binary
);
197 free(mapper_device_full
);
205 int mnt_context_deferred_delete_veritydev(struct libmnt_context
*cxt
)
208 struct crypt_device
*crypt_dev
= NULL
;
209 /* If mounting failed delete immediately, otherwise setup auto cleanup for user umount */
210 uint32_t flags
= mnt_context_get_status(cxt
) ? CRYPT_DEACTIVATE_DEFERRED
: 0;
216 if (!(cxt
->flags
& MNT_FL_VERITYDEV_READY
))
219 src
= mnt_fs_get_srcpath(cxt
->fs
);
223 rc
= crypt_init_by_name(&crypt_dev
, src
);
225 rc
= crypt_deactivate_by_name(crypt_dev
, src
, flags
);
227 cxt
->flags
&= ~MNT_FL_VERITYDEV_READY
;
230 crypt_free(crypt_dev
);
232 DBG(VERITY
, ul_debugobj(cxt
, "deleted [rc=%d]", rc
));
239 int mnt_context_setup_veritydev(struct libmnt_context
*cxt
__attribute__ ((__unused__
)))
244 int mnt_context_deferred_delete_veritydev(struct libmnt_context
*cxt
__attribute__ ((__unused__
)))
250 int mnt_context_is_veritydev(struct libmnt_context
*cxt
)
256 /* The mount flags have to be merged, otherwise we have to use
257 * expensive mnt_context_get_user_mflags() instead of cxt->user_mountflags. */
258 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
262 src
= mnt_fs_get_srcpath(cxt
->fs
);
264 return 0; /* backing file not set */
266 if (cxt
->user_mountflags
& (MNT_MS_HASH_DEVICE
|
268 MNT_MS_HASH_OFFSET
)) {
269 #ifndef HAVE_CRYPTSETUP
270 DBG(VERITY
, ul_debugobj(cxt
, "veritydev specific options detected but libmount built without libcryptsetup"));
273 DBG(VERITY
, ul_debugobj(cxt
, "veritydev specific options detected"));
278 if (!strncmp(src
, "/dev/mapper/libmnt_", strlen("/dev/mapper/libmnt_"))) {
279 #ifndef HAVE_CRYPTSETUP
280 DBG(VERITY
, ul_debugobj(cxt
, "veritydev prefix detected in source device but libmount built without libcryptsetup"));
283 DBG(VERITY
, ul_debugobj(cxt
, "veritydev prefix detected in source device"));