@ -16,12 +16,14 @@
# include "KeyUtil.h"
# include <linux/fs.h>
# include <iomanip>
# include <sstream>
# include <string>
# include <fcntl.h>
# include <linux/fs.h>
# include <openssl/sha.h>
# include <sys/ioctl.h>
# include <android-base/file.h>
# include <android-base/logging.h>
@ -29,6 +31,7 @@
# include "KeyStorage.h"
# include "Utils.h"
# include "fscrypt_uapi.h"
namespace android {
namespace vold {
@ -45,6 +48,42 @@ bool randomKey(KeyBuffer* key) {
return true ;
}
// Return true if the kernel supports the ioctls to add/remove fscrypt keys
// directly to/from the filesystem.
bool isFsKeyringSupported ( void ) {
static bool initialized = false ;
static bool supported ;
if ( ! initialized ) {
android : : base : : unique_fd fd ( open ( " /data " , O_RDONLY | O_DIRECTORY | O_CLOEXEC ) ) ;
// FS_IOC_ADD_ENCRYPTION_KEY with a NULL argument will fail with ENOTTY
// if the ioctl isn't supported. Otherwise it will fail with another
// error code such as EFAULT.
errno = 0 ;
( void ) ioctl ( fd , FS_IOC_ADD_ENCRYPTION_KEY , NULL ) ;
if ( errno = = ENOTTY ) {
LOG ( INFO ) < < " Kernel doesn't support FS_IOC_ADD_ENCRYPTION_KEY. Falling back to "
" session keyring " ;
supported = false ;
} else {
if ( errno ! = EFAULT ) {
PLOG ( WARNING ) < < " Unexpected error from FS_IOC_ADD_ENCRYPTION_KEY " ;
}
LOG ( DEBUG ) < < " Detected support for FS_IOC_ADD_ENCRYPTION_KEY " ;
supported = true ;
}
// There's no need to check for FS_IOC_REMOVE_ENCRYPTION_KEY, since it's
// guaranteed to be available if FS_IOC_ADD_ENCRYPTION_KEY is. There's
// also no need to check for support on external volumes separately from
// /data, since either the kernel supports the ioctls on all
// fscrypt-capable filesystems or it doesn't.
initialized = true ;
}
return supported ;
}
// Get raw keyref - used to make keyname and to pass to ioctl
static std : : string generateKeyRef ( const uint8_t * key , int length ) {
SHA512_CTX c ;
@ -78,16 +117,20 @@ static bool fillKey(const KeyBuffer& key, fscrypt_key* fs_key) {
static char const * const NAME_PREFIXES [ ] = { " ext4 " , " f2fs " , " fscrypt " , nullptr } ;
static std : : string key name( const std : : string & prefix , const std : : string & raw_ref ) {
static std : : string key refstring( const std : : string & raw_ref ) {
std : : ostringstream o ;
o < < prefix < < " : " ;
for ( unsigned char i : raw_ref ) {
o < < std : : hex < < std : : setw ( 2 ) < < std : : setfill ( ' 0 ' ) < < ( int ) i ;
}
return o . str ( ) ;
}
// Get the keyring we store all keys in
static std : : string buildLegacyKeyName ( const std : : string & prefix , const std : : string & raw_ref ) {
return prefix + " : " + keyrefstring ( raw_ref ) ;
}
// Get the ID of the keyring we store all fscrypt keys in when the kernel is too
// old to support FS_IOC_ADD_ENCRYPTION_KEY and FS_IOC_REMOVE_ENCRYPTION_KEY.
static bool fscryptKeyring ( key_serial_t * device_keyring ) {
* device_keyring = keyctl_search ( KEY_SPEC_SESSION_KEYRING , " keyring " , " fscrypt " , 0 ) ;
if ( * device_keyring = = - 1 ) {
@ -97,19 +140,17 @@ static bool fscryptKeyring(key_serial_t* device_keyring) {
return true ;
}
// Install password into global keyring
// Return raw key reference for use in policy
bool installKey ( const KeyBuffer & key , std : : string * raw_ref ) {
// Add an encryption key to the legacy global session keyring.
static bool installKeyLegacy ( const KeyBuffer & key , const std : : string & raw_ref ) {
// Place fscrypt_key into automatically zeroing buffer.
KeyBuffer fsKeyBuffer ( sizeof ( fscrypt_key ) ) ;
fscrypt_key & fs_key = * reinterpret_cast < fscrypt_key * > ( fsKeyBuffer . data ( ) ) ;
if ( ! fillKey ( key , & fs_key ) ) return false ;
* raw_ref = generateKeyRef ( fs_key . raw , fs_key . size ) ;
key_serial_t device_keyring ;
if ( ! fscryptKeyring ( & device_keyring ) ) return false ;
for ( char const * const * name_prefix = NAME_PREFIXES ; * name_prefix ! = nullptr ; name_prefix + + ) {
auto ref = keyn ame( * name_prefix , * raw_ref ) ;
auto ref = buildLegacyKeyN ame( * name_prefix , raw_ref ) ;
key_serial_t key_id =
add_key ( " logon " , ref . c_str ( ) , ( void * ) & fs_key , sizeof ( fs_key ) , device_keyring ) ;
if ( key_id = = - 1 ) {
@ -122,12 +163,55 @@ bool installKey(const KeyBuffer& key, std::string* raw_ref) {
return true ;
}
bool evictKey ( const std : : string & raw_ref ) {
// Install a file-based encryption key to the kernel, for use by encrypted files
// on the specified filesystem.
//
// We use FS_IOC_ADD_ENCRYPTION_KEY if the kernel supports it. Otherwise we add
// the key to the legacy global session keyring.
//
// Returns %true on success, %false on failure. On success also sets *raw_ref
// to the raw key reference for use in the encryption policy.
bool installKey ( const KeyBuffer & key , const std : : string & mountpoint , std : : string * raw_ref ) {
* raw_ref = generateKeyRef ( ( const uint8_t * ) key . data ( ) , key . size ( ) ) ;
if ( ! isFsKeyringSupported ( ) ) {
return installKeyLegacy ( key , * raw_ref ) ;
}
// Put the fscrypt_add_key_arg in an automatically-zeroing buffer, since we
// have to copy the raw key into it.
KeyBuffer arg_buf ( sizeof ( struct fscrypt_add_key_arg ) + key . size ( ) , 0 ) ;
struct fscrypt_add_key_arg * arg = ( struct fscrypt_add_key_arg * ) arg_buf . data ( ) ;
arg - > key_spec . type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR ;
memcpy ( arg - > key_spec . u . descriptor , raw_ref . c_str ( ) , FSCRYPT_KEY_DESCRIPTOR_SIZE ) ;
arg - > raw_size = key . size ( ) ;
memcpy ( arg - > raw , key . data ( ) , key . size ( ) ) ;
android : : base : : unique_fd fd ( open ( mountpoint . c_str ( ) , O_RDONLY | O_DIRECTORY | O_CLOEXEC ) ) ;
if ( fd = = - 1 ) {
PLOG ( ERROR ) < < " Failed to open " < < mountpoint < < " to install key " ;
return false ;
}
if ( ioctl ( fd , FS_IOC_ADD_ENCRYPTION_KEY , arg ) ! = 0 ) {
PLOG ( ERROR ) < < " Failed to install fscrypt key with ref " < < keyrefstring ( * raw_ref ) < < " to "
< < mountpoint ;
return false ;
}
LOG ( DEBUG ) < < " Installed fscrypt key with ref " < < keyrefstring ( * raw_ref ) < < " to "
< < mountpoint ;
return true ;
}
// Remove an encryption key from the legacy global session keyring.
static bool evictKeyLegacy ( const std : : string & raw_ref ) {
key_serial_t device_keyring ;
if ( ! fscryptKeyring ( & device_keyring ) ) return false ;
bool success = true ;
for ( char const * const * name_prefix = NAME_PREFIXES ; * name_prefix ! = nullptr ; name_prefix + + ) {
auto ref = keyname ( * name_prefix , raw_ref ) ;
auto ref = buildLegacyKeyN ame( * name_prefix , raw_ref ) ;
auto key_serial = keyctl_search ( device_keyring , " logon " , ref . c_str ( ) , 0 ) ;
// Unlink the key from the keyring. Prefer unlinking to revoking or
@ -144,9 +228,50 @@ bool evictKey(const std::string& raw_ref) {
return success ;
}
// Evict a file-based encryption key from the kernel.
//
// We use FS_IOC_REMOVE_ENCRYPTION_KEY if the kernel supports it. Otherwise we
// remove the key from the legacy global session keyring.
//
// In the latter case, the caller is responsible for dropping caches.
bool evictKey ( const std : : string & mountpoint , const std : : string & raw_ref ) {
if ( ! isFsKeyringSupported ( ) ) {
return evictKeyLegacy ( raw_ref ) ;
}
android : : base : : unique_fd fd ( open ( mountpoint . c_str ( ) , O_RDONLY | O_DIRECTORY | O_CLOEXEC ) ) ;
if ( fd = = - 1 ) {
PLOG ( ERROR ) < < " Failed to open " < < mountpoint < < " to evict key " ;
return false ;
}
struct fscrypt_remove_key_arg arg ;
memset ( & arg , 0 , sizeof ( arg ) ) ;
arg . key_spec . type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR ;
memcpy ( arg . key_spec . u . descriptor , raw_ref . c_str ( ) , FSCRYPT_KEY_DESCRIPTOR_SIZE ) ;
std : : string ref = keyrefstring ( raw_ref ) ;
if ( ioctl ( fd , FS_IOC_REMOVE_ENCRYPTION_KEY , & arg ) ! = 0 ) {
PLOG ( ERROR ) < < " Failed to evict fscrypt key with ref " < < ref < < " from " < < mountpoint ;
return false ;
}
LOG ( DEBUG ) < < " Evicted fscrypt key with ref " < < ref < < " from " < < mountpoint ;
if ( arg . removal_status_flags & FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS ) {
// Should never happen because keys are only added/removed as root.
LOG ( ERROR ) < < " Unexpected case: key with ref " < < ref < < " is still added by other users! " ;
} else if ( arg . removal_status_flags & FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY ) {
LOG ( ERROR ) < < " Files still open after removing key with ref " < < ref
< < " . These files were not locked! " ;
}
return true ;
}
bool retrieveAndInstallKey ( bool create_if_absent , const KeyAuthentication & key_authentication ,
const std : : string & key_path , const std : : string & tmp_path ,
std : : string * key_ref ) {
const std : : string & volume_uuid , std : : string * key_ref ) {
KeyBuffer key ;
if ( pathExists ( key_path ) ) {
LOG ( DEBUG ) < < " Key exists, using: " < < key_path ;
@ -161,7 +286,7 @@ bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_a
if ( ! storeKeyAtomically ( key_path , tmp_path , key_authentication , key ) ) return false ;
}
if ( ! installKey ( key , key_ref) ) {
if ( ! installKey ( key , BuildDataPath( volume_uuid ) , key_ref) ) {
LOG ( ERROR ) < < " Failed to install key in " < < key_path ;
return false ;
}