@@ -111,6 +111,7 @@ struct bitlk_superblock {
111111struct bitlk_fve_metadata {
112112 /* FVE metadata block header */
113113 uint8_t signature [8 ];
114+ /* size of this block (in 16-byte units) */
114115 uint16_t fve_size ;
115116 uint16_t fve_version ;
116117 uint16_t curr_state ;
@@ -132,6 +133,32 @@ struct bitlk_fve_metadata {
132133 uint64_t creation_time ;
133134} __attribute__ ((packed ));
134135
136+ struct bitlk_validation_hash {
137+ uint16_t size ;
138+ uint16_t role ;
139+ uint16_t type ;
140+ uint16_t flags ;
141+ /* likely a hash type code, anything other than 0x2005 isn't supported */
142+ uint16_t hash_type ;
143+ uint16_t unknown1 ;
144+ /* SHA-256 */
145+ uint8_t hash [32 ];
146+ } __attribute__ ((packed ));
147+
148+ struct bitlk_fve_metadata_validation {
149+ /* FVE metadata validation block header */
150+ uint16_t validation_size ;
151+ uint16_t validation_version ;
152+ uint32_t fve_crc32 ;
153+ /* this is a single nested structure's header defined here for simplicity */
154+ uint16_t nested_struct_size ;
155+ uint16_t nested_struct_role ;
156+ uint16_t nested_struct_type ;
157+ uint16_t nested_struct_flags ;
158+ /* datum containing a similar nested structure (encrypted using VMK) with hash (SHA256) */
159+ uint8_t nested_struct_data [BITLK_VALIDATION_VMK_DATA_SIZE ];
160+ } __attribute__ ((packed ));
161+
135162struct bitlk_entry_header_block {
136163 uint64_t offset ;
137164 uint64_t size ;
@@ -361,6 +388,54 @@ static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, in
361388 return 0 ;
362389}
363390
391+ static bool check_fve_metadata (struct bitlk_fve_metadata * fve )
392+ {
393+ if (memcmp (fve -> signature , BITLK_SIGNATURE , sizeof (fve -> signature )) || le16_to_cpu (fve -> fve_version ) != 2 ||
394+ (fve -> fve_size << 4 ) > BITLK_FVE_METADATA_SIZE )
395+ return false;
396+
397+ return true;
398+ }
399+
400+ static bool check_fve_metadata_validation (struct bitlk_fve_metadata_validation * validation )
401+ {
402+ /* only check if there is room for CRC-32, the actual size must be larger */
403+ if (le16_to_cpu (validation -> validation_size ) < 8 || le16_to_cpu (validation -> validation_version > 2 ))
404+ return false;
405+
406+ return true;
407+ }
408+
409+ static bool parse_fve_metadata_validation (struct bitlk_metadata * params , struct bitlk_fve_metadata_validation * validation )
410+ {
411+ /* extra checks for a nested structure (MAC) and BITLK FVE metadata */
412+
413+ if (le16_to_cpu (validation -> validation_size ) < sizeof (struct bitlk_fve_metadata_validation ))
414+ return false;
415+
416+ if (le16_to_cpu (validation -> nested_struct_size != BITLK_VALIDATION_VMK_HEADER_SIZE + BITLK_VALIDATION_VMK_DATA_SIZE ) ||
417+ le16_to_cpu (validation -> nested_struct_role ) != 0 ||
418+ le16_to_cpu (validation -> nested_struct_type ) != 5 )
419+ return false;
420+
421+ /* nonce */
422+ memcpy (params -> validation -> nonce ,
423+ validation -> nested_struct_data ,
424+ BITLK_NONCE_SIZE );
425+
426+ /* MAC tag */
427+ memcpy (params -> validation -> mac_tag ,
428+ validation -> nested_struct_data + BITLK_NONCE_SIZE ,
429+ BITLK_VMK_MAC_TAG_SIZE );
430+
431+ /* AES-CCM encrypted datum with SHA256 hash */
432+ memcpy (params -> validation -> enc_datum ,
433+ validation -> nested_struct_data + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE ,
434+ BITLK_VALIDATION_VMK_DATA_SIZE - BITLK_NONCE_SIZE - BITLK_VMK_MAC_TAG_SIZE );
435+
436+ return true;
437+ }
438+
364439void BITLK_bitlk_fvek_free (struct bitlk_fvek * fvek )
365440{
366441 if (!fvek )
@@ -391,6 +466,7 @@ void BITLK_bitlk_metadata_free(struct bitlk_metadata *metadata)
391466
392467 free (metadata -> guid );
393468 free (metadata -> description );
469+ free (metadata -> validation );
394470 BITLK_bitlk_vmk_free (metadata -> vmks );
395471 BITLK_bitlk_fvek_free (metadata -> fvek );
396472}
@@ -402,20 +478,25 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
402478 struct bitlk_signature sig = {};
403479 struct bitlk_superblock sb = {};
404480 struct bitlk_fve_metadata fve = {};
481+ struct bitlk_fve_metadata_validation validation = {};
405482 struct bitlk_entry_vmk entry_vmk = {};
406483 uint8_t * fve_entries = NULL ;
484+ uint8_t * fve_validated_block = NULL ;
407485 size_t fve_entries_size = 0 ;
408486 uint32_t fve_metadata_size = 0 ;
487+ uint32_t fve_size_real = 0 ;
409488 int fve_offset = 0 ;
410489 char guid_buf [UUID_STR_LEN ] = {0 };
411490 uint16_t entry_size = 0 ;
412491 uint16_t entry_type = 0 ;
413492 int i = 0 ;
414493 int r = 0 ;
494+ int valid_fve_metadata_idx = -1 ;
415495 int start = 0 ;
416496 size_t key_size = 0 ;
417497 const char * key = NULL ;
418498 char * description = NULL ;
499+ struct crypt_hash * hash ;
419500
420501 struct bitlk_vmk * vmk = NULL ;
421502 struct bitlk_vmk * vmk_p = params -> vmks ;
@@ -490,15 +571,80 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
490571 for (i = 0 ; i < 3 ; i ++ )
491572 params -> metadata_offset [i ] = le64_to_cpu (sb .fve_offset [i ]);
492573
493- log_dbg (cd , "Reading BITLK FVE metadata of size %zu on device %s, offset %" PRIu64 "." ,
494- sizeof (fve ), device_path (device ), params -> metadata_offset [0 ]);
574+ fve_validated_block = malloc (BITLK_FVE_METADATA_SIZE );
575+ if (fve_validated_block == NULL ) {
576+ r = - ENOMEM ;
577+ goto out ;
578+ }
495579
496- /* read FVE metadata from the first metadata area */
497- if (read_lseek_blockwise (devfd , device_block_size (cd , device ),
498- device_alignment (device ), & fve , sizeof (fve ), params -> metadata_offset [0 ]) != sizeof (fve ) ||
499- memcmp (fve .signature , BITLK_SIGNATURE , sizeof (fve .signature )) ||
500- le16_to_cpu (fve .fve_version ) != 2 ) {
501- log_err (cd , _ ("Failed to read BITLK FVE metadata from %s." ), device_path (device ));
580+ for (i = 0 ; i < 3 ; i ++ ) {
581+ /* iterate over FVE metadata copies and pick the valid one */
582+ log_dbg (cd , "Reading BITLK FVE metadata copy #%d of size %zu on device %s, offset %" PRIu64 "." ,
583+ i , sizeof (fve ), device_path (device ), params -> metadata_offset [i ]);
584+
585+ if (read_lseek_blockwise (devfd , device_block_size (cd , device ),
586+ device_alignment (device ), & fve , sizeof (fve ), params -> metadata_offset [i ]) != sizeof (fve ) ||
587+ !check_fve_metadata (& fve ) ||
588+ (fve_size_real = le16_to_cpu (fve .fve_size ) << 4 , read_lseek_blockwise (devfd , device_block_size (cd , device ),
589+ device_alignment (device ), & validation , sizeof (validation ), params -> metadata_offset [i ] + fve_size_real ) != sizeof (validation )) ||
590+ !check_fve_metadata_validation (& validation ) ||
591+ /* double-fetch is here, but we aren't validating MAC */
592+ read_lseek_blockwise (devfd , device_block_size (cd , device ), device_alignment (device ), fve_validated_block , fve_size_real ,
593+ params -> metadata_offset [i ]) != fve_size_real ||
594+ (crypt_crc32 (~0 , fve_validated_block , fve_size_real ) ^ ~0 ) != le32_to_cpu (validation .fve_crc32 )) {
595+ /* found an invalid FVE metadata copy, log and skip */
596+ log_dbg (cd , _ ("Failed to read or validate BITLK FVE metadata copy #%d from %s." ), i , device_path (device ));
597+ } else {
598+ /* found a valid FVE metadata copy, use it */
599+ valid_fve_metadata_idx = i ;
600+ break ;
601+ }
602+ }
603+
604+ if (valid_fve_metadata_idx < 0 ) {
605+ /* all FVE metadata copies are invalid, fail */
606+ log_err (cd , _ ("Failed to read and validate BITLK FVE metadata from %s." ), device_path (device ));
607+ r = - EINVAL ;
608+ goto out ;
609+ }
610+
611+ /* check that a valid FVE metadata block is in its expected location */
612+ if (params -> metadata_offset [valid_fve_metadata_idx ] != le64_to_cpu (fve .fve_offset [valid_fve_metadata_idx ])) {
613+ log_err (cd , _ ("Failed to validate the location of BITLK FVE metadata from %s." ), device_path (device ));
614+ r = - EINVAL ;
615+ goto out ;
616+ }
617+
618+ /* update offsets from a valid FVE metadata copy */
619+ for (i = 0 ; i < 3 ; i ++ )
620+ params -> metadata_offset [i ] = le64_to_cpu (fve .fve_offset [i ]);
621+
622+ /* check that the FVE metadata hasn't changed between reads, because we are preparing for the MAC check */
623+ if (memcmp (& fve , fve_validated_block , sizeof (fve )) != 0 ) {
624+ log_err (cd , _ ("BITLK FVE metadata changed between reads from %s." ), device_path (device ));
625+ r = - EINVAL ;
626+ goto out ;
627+ }
628+
629+ crypt_backend_memzero (& params -> sha256_fve , 32 );
630+ if (crypt_hash_init (& hash , "sha256" )) {
631+ log_err (cd , _ ("Failed to hash BITLK FVE metadata read from %s." ), device_path (device ));
632+ r = - EINVAL ;
633+ goto out ;
634+ }
635+ crypt_hash_write (hash , (const char * )fve_validated_block , fve_size_real );
636+ crypt_hash_final (hash , (char * )& params -> sha256_fve , 32 );
637+ crypt_hash_destroy (hash );
638+
639+ /* do some extended checks against FVE metadata, but not including MAC verification */
640+ params -> validation = malloc (sizeof (struct bitlk_validation ));
641+ if (!params -> validation ) {
642+ r = - ENOMEM ;
643+ goto out ;
644+ }
645+
646+ if (!parse_fve_metadata_validation (params , & validation )) {
647+ log_err (cd , _ ("Failed to parse BITLK FVE validation metadata from %s." ), device_path (device ));
502648 r = - EINVAL ;
503649 goto out ;
504650 }
@@ -583,17 +729,18 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
583729 }
584730 memset (fve_entries , 0 , fve_entries_size );
585731
586- log_dbg (cd , "Reading BITLK FVE metadata entries of size %zu on device %s, offset %" PRIu64 "." ,
587- fve_entries_size , device_path (device ), params -> metadata_offset [0 ] + BITLK_FVE_METADATA_HEADERS_LEN );
732+ log_dbg (cd , "Getting BITLK FVE metadata entries of size %zu on device %s, offset %" PRIu64 "." ,
733+ fve_entries_size , device_path (device ), params -> metadata_offset [valid_fve_metadata_idx ] + BITLK_FVE_METADATA_HEADERS_LEN );
588734
589- if (read_lseek_blockwise (devfd , device_block_size (cd , device ),
590- device_alignment (device ), fve_entries , fve_entries_size ,
591- params -> metadata_offset [0 ] + BITLK_FVE_METADATA_HEADERS_LEN ) != (ssize_t )fve_entries_size ) {
592- log_err (cd , _ ("Failed to read BITLK metadata entries from %s." ), device_path (device ));
735+ if (BITLK_FVE_METADATA_HEADERS_LEN + fve_entries_size > fve_size_real ) {
736+ log_err (cd , _ ("Failed to check BITLK metadata entries previously read from %s." ), device_path (device ));
593737 r = - EINVAL ;
594738 goto out ;
595739 }
596740
741+ /* fetch these entries from validated buffer to avoid double-fetch */
742+ memcpy (fve_entries , fve_validated_block + BITLK_FVE_METADATA_HEADERS_LEN , fve_entries_size );
743+
597744 while ((fve_entries_size - start ) >= (sizeof (entry_size ) + sizeof (entry_type ))) {
598745
599746 /* size of this entry */
@@ -716,6 +863,8 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
716863 }
717864out :
718865 free (fve_entries );
866+ free (fve_validated_block );
867+
719868 return r ;
720869}
721870
@@ -1110,6 +1259,7 @@ int BITLK_get_volume_key(struct crypt_device *cd,
11101259 struct volume_key * open_vmk_key = NULL ;
11111260 struct volume_key * vmk_dec_key = NULL ;
11121261 struct volume_key * recovery_key = NULL ;
1262+ struct bitlk_validation_hash dec_hash = {};
11131263 const struct bitlk_vmk * next_vmk = NULL ;
11141264
11151265 next_vmk = params -> vmks ;
@@ -1172,6 +1322,36 @@ int BITLK_get_volume_key(struct crypt_device *cd,
11721322 }
11731323 crypt_free_volume_key (vmk_dec_key );
11741324
1325+ log_dbg (cd , "Trying to decrypt validation metadata using VMK." );
1326+ r = crypt_bitlk_decrypt_key (crypt_volume_key_get_key (open_vmk_key ),
1327+ crypt_volume_key_length (open_vmk_key ),
1328+ (const char * )params -> validation -> enc_datum ,
1329+ (char * )& dec_hash ,
1330+ BITLK_VALIDATION_VMK_DATA_SIZE - BITLK_NONCE_SIZE - BITLK_VMK_MAC_TAG_SIZE ,
1331+ (const char * )params -> validation -> nonce , BITLK_NONCE_SIZE ,
1332+ (const char * )params -> validation -> mac_tag , BITLK_VMK_MAC_TAG_SIZE );
1333+ if (r < 0 ) {
1334+ log_dbg (cd , "Failed to decrypt validation metadata using VMK." );
1335+ crypt_free_volume_key (open_vmk_key );
1336+ if (r == - ENOTSUP )
1337+ return r ;
1338+ break ;
1339+ }
1340+
1341+ /* now, do the MAC validation */
1342+ if (le16_to_cpu (dec_hash .role ) != 0 || le16_to_cpu (dec_hash .type ) != 1 ||
1343+ (le16_to_cpu (dec_hash .hash_type ) != 0x2005 )) {
1344+ log_dbg (cd , "Failed to parse decrypted validation metadata." );
1345+ crypt_free_volume_key (open_vmk_key );
1346+ return - ENOTSUP ;
1347+ }
1348+
1349+ if (memcmp (dec_hash .hash , params -> sha256_fve , sizeof (dec_hash .hash )) != 0 ) {
1350+ log_dbg (cd , "Failed MAC validation of BITLK FVE metadata." );
1351+ crypt_free_volume_key (open_vmk_key );
1352+ return - EINVAL ;
1353+ }
1354+
11751355 r = decrypt_key (cd , open_fvek_key , params -> fvek -> vk , open_vmk_key ,
11761356 params -> fvek -> mac_tag , BITLK_VMK_MAC_TAG_SIZE ,
11771357 params -> fvek -> nonce , BITLK_NONCE_SIZE , true);
0 commit comments