Android FBE - novelinux/android GitHub Wiki
- 名称: FBE, File-Based Encryption,基于文件的加密
- Android官方文档: https://source.android.com/security/encryption/file-based.html
- 支持版本: Android 7.0支持
- 主要作用: Android 7.0及更高版本支持基于文件的加密(FBE)。基于文件的加密允许不同文件被不同keys加密并且解锁也是独立的.
- 题外话: 工作于 Google 的开发者Ted(Theodore Ts’o / Ted Ts’o)2015年提供了一套用于 EXT4 文件系统加密的方案,这套方案 在Android N被引入。此外,这套方案仍在持续开发,以便成为上游内核的默认方案。这些补丁由Ted本人和 Michael Halcrow 共同完成。 对开发来说,在内核中引入加密支持,仅需要大概三千多行代码。类似的加密机制之后还可能在 F2FS 文件系统中实现——Flash-Friendly File-System,这是一种面向闪存介质的文件系统。
提到Android FBE就不得不提到Android Direct Boot,下面我们就从Direct Boot开始介绍FBE.
当设备通电但用户尚未解锁设备时,Android 7.0以安全的直接引导模式(Direct boot)运行。为了支持这一点,系统提供了两个数据存储位置:
- 设备加密存储: 这是在直接引导模式期间和用户解锁设备后可用的存储位置。(/data/user_de/)
- 凭证加密存储: 它是默认存储位置,仅在用户解锁设备后可用。(/data/data --> /data/user/)
默认情况下,应用程序不会在直接引导模式下运行。如果您的应用程序需要在直接引导模式下采取行动,您可以注册应在此模式下运行的应用程序组件。在直接引导模式下需要运行的应用程序的一些常见用例包括:
- 1.已安排通知的应用程式,例如闹钟应用程式;
- 2.提供重要用户通知的应用,例如短信应用;
- 3.提供无障碍服务的应用,如Talkback.
如果您的应用程序需要在直接引导模式下运行时访问数据,请使用设备加密存储。设备加密存储包含使用仅在设备执行成功验证的引导后可用的密钥加密的数据。 对于应使用与用户凭据关联的密钥(如PIN或密码)加密的数据,请使用凭据加密存储。凭证加密存储仅在用户成功解锁设备后才可用,直到用户再次重新启动设备为止。 如果用户在解锁设备后启用锁定屏幕,则不会锁定凭证加密存储。
首先init进程启动过程通过fstab将数据分区挂载到/data
path: system/core/init/builtins.cpp
/* mount_all <fstab> [ <path> ]*
*
* This function might request a reboot, in which case it will
* not return.
*/
static int do_mount_all(const std::vector<std::string>& args) {
...
} else if (ret == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
if (e4crypt_install_keyring()) {
return -1;
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
} else if (ret > 0) {
ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);
}
/* else ... < 0: error */
return ret;
}
/data/目录下的目录一共有三套密钥来设置对应的加密策略,分别是:
- unencrypted key: 用来设置/data下除了如下目录的所有目录的policy:
"lost+found",
"system_ce", "system_de",
"misc_ce", "misc_de",
"media",
"data", "user", "user_de",
-
de key: 通常用来设置/data下后缀含有_de的目录的policy.
-
ce key: 通常用来设置/data下后缀含有_ce的目录的policy.
- device_key_path - "/data/unencrypted/key"
sagit:/ # ls -l /data/unencrypted/key/
total 64
-rw------- 1 root root 92 1970-02-03 08:46 encrypted_key
-rw------- 1 root root 427 1970-02-03 08:46 keymaster_key_blob
-rw------- 1 root root 16384 1970-02-03 08:46 secdiscardable
-rw------- 1 root root 10 1970-02-03 08:46 stretching
-rw------- 1 root root 1 1970-02-03 08:46 version
- device_key_ref - "/data/unencrypted/ref"
sagit:/ # ls -l /data/unencrypted/ref
-rw------- 1 root root 8 1970-02-03 08:46 /data/unencrypted/ref
- LOGS
01-15 06:48:24.514 592 598 E keymaster1_device: HwKmClose
01-15 06:48:24.514 592 598 D vold : Created key /data/unencrypted/key
01-15 06:48:24.514 592 598 D vold : Added key 123488388 (ext4:0e30728e813bbb1b) to keyring 1063057322 in process 592
01-15 06:48:24.515 1 1 I vdc : 200 593 1
path: system/core/init/builtins.cpp
static int do_installkey(const std::vector<std::string>& args) {
if (!is_file_crypto()) {
return 0;
}
return e4crypt_create_device_key(args[1].c_str(),
do_installkeys_ensure_dir_exists);
}
path: system/extras/ext4_utils/ext4_crypt.h
static const char* e4crypt_unencrypted_folder = "/unencrypted";
static const char* e4crypt_key_ref = "/unencrypted/ref";
path: system/extras/ext4_utils/ext4_crypt_init_extensions.cpp
int e4crypt_create_device_key(const char* dir,
int ensure_dir_exists(const char*))
{
init_logging();
// Make sure folder exists. Use make_dir to set selinux permissions.
std::string unencrypted_dir = std::string(dir) + e4crypt_unencrypted_folder;
if (ensure_dir_exists(unencrypted_dir.c_str())) {
KLOG_ERROR(TAG, "Failed to create %s (%s)\n",
unencrypted_dir.c_str(),
strerror(errno));
return -1;
}
const char* argv[] = { "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto" };
int rc = android_fork_execvp(4, (char**) argv, NULL, false, true);
LOG(INFO) << "enablefilecrypto result: " << rc;
return rc;
}
path: system/vold/cryptfs.c
int cryptfs_enable_file()
{
return e4crypt_initialize_global_de();
}
path: system/vold/Ext4Crypt.cpp
const std::string device_key_dir = std::string() + DATA_MNT_POINT + e4crypt_unencrypted_folder;
const std::string device_key_path = device_key_dir + "/key";
const std::string device_key_temp = device_key_dir + "/temp";
...
bool e4crypt_initialize_global_de() {
LOG(INFO) << "e4crypt_initialize_global_de";
if (s_global_de_initialized) {
LOG(INFO) << "Already initialized";
return true;
}
std::string mode_filename = std::string("/data") + e4crypt_key_mode;
std::string mode = cryptfs_get_file_encryption_mode();
if (!android::base::WriteStringToFile(mode, mode_filename)) {
PLOG(ERROR) << "Cannot save type";
return false;
}
std::string device_key;
if (path_exists(device_key_path)) {
if (!android::vold::retrieveKey(device_key_path,
kEmptyAuthentication, &device_key)) return false;
} else {
LOG(INFO) << "Creating new key";
if (!random_key(&device_key)) return false;
if (!store_key(device_key_path, device_key_temp,
kEmptyAuthentication, device_key)) return false;
}
std::string device_key_ref;
if (!install_key(device_key, &device_key_ref)) {
LOG(ERROR) << "Failed to install device key";
return false;
}
std::string ref_filename = std::string("/data") + e4crypt_key_ref;
if (!android::base::WriteStringToFile(device_key_ref, ref_filename)) {
PLOG(ERROR) << "Cannot save key reference";
return false;
}
s_global_de_initialized = true;
return true;
}
- /data/misc/vold/user_keys:
sagit:/data/misc/vold/user_keys # ls -l de/0/
total 104
-rw------- 1 root root 92 1970-02-02 03:34 encrypted_key
-rw------- 1 root root 427 1970-02-02 03:34 keymaster_key_blob
-rw------- 1 root root 16384 1970-02-02 03:34 secdiscardable
-rw------- 1 root root 10 1970-02-02 03:34 stretching
-rw------- 1 root root 1 1970-02-02 03:34 version
sagit:/data/misc/vold/user_keys # ls -l ce/0/current/
total 104
-rw------- 1 root root 92 1970-02-02 03:34 encrypted_key
-rw------- 1 root root 427 1970-02-02 03:34 keymaster_key_blob
-rw------- 1 root root 16384 1970-02-02 03:34 secdiscardable
-rw------- 1 root root 10 1970-02-02 03:34 stretching
-rw------- 1 root root 1 1970-02-02 03:34 version
- LOGS
01-15 06:48:24.543 592 598 D vold : e4crypt_init_user0
01-15 06:48:24.543 592 598 D vold : Preparing: /data/misc/vold/user_keys
01-15 06:48:24.543 592 598 D vold : Preparing: /data/misc/vold/user_keys/ce
01-15 06:48:24.544 592 598 D vold : Preparing: /data/misc/vold/user_keys/de
01-15 06:48:24.544 592 598 D vold : Preparing: /data/misc/vold/user_keys/ce/0
01-15 06:48:24.544 592 598 D vold : Skipping non-key .
01-15 06:48:24.544 592 598 D vold : Skipping non-key ..
01-15 06:48:24.553 592 598 E keymaster1_device: Keymaster Initialized
01-15 06:48:24.553 592 598 E keymaster1_device: TA API Major Verion: 2
01-15 06:48:24.553 592 598 E keymaster1_device: TA API Minor Verion: 0
01-15 06:48:24.553 592 598 E keymaster1_device: TA Major Verion: 2
01-15 06:48:24.553 592 598 E keymaster1_device: TA Minor Verion: 25
01-15 06:48:24.553 592 598 E keymaster1_device: set_version_req->flags: 1
01-15 06:48:24.554 592 598 D vold : Creating key that doesn't need auth token
01-15 06:48:24.588 592 598 D vold : Created key /data/misc/vold/user_keys/de/0
01-15 06:48:24.588 592 598 D vold : Added key 1043496796 (ext4:5b317534021c2524) to keyring 1063057322 in process 592
01-15 06:48:24.588 592 598 D vold : Added key 884894402 (ext4:5ebd155fc20b3ffa) to keyring 1063057322 in process 592
01-15 06:48:24.588 592 598 D vold : Created keys for user 0
01-15 06:48:24.588 592 598 D vold : Skipping non-de-key .
01-15 06:48:24.588 592 598 D vold : Skipping non-de-key ..
path: system/core/init/builtins.cpp
static int do_init_user0(const std::vector<std::string>& args) {
return e4crypt_do_init_user0();
}
path: system/extras/ext4_utils/ext4_crypt_init_extensions.cpp
int e4crypt_do_init_user0()
{
init_logging();
const char* argv[] = { "/system/bin/vdc", "--wait", "cryptfs", "init_user0" };
int rc = android_fork_execvp(4, (char**) argv, NULL, false, true);
LOG(INFO) << "init_user0 result: " << rc;
return rc;
}
path: system/vold/Ext4Crypt.cpp
bool e4crypt_init_user0() {
LOG(DEBUG) << "e4crypt_init_user0";
if (e4crypt_is_native()) {
if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
if (!path_exists(get_de_key_path(0))) {
if (!create_and_install_user_keys(0, false)) return false;
}
// TODO: switch to loading only DE_0 here once framework makes
// explicit calls to install DE keys for secondary users
if (!load_all_de_keys()) return false;
}
// We can only safely prepare DE storage here, since CE keys are probably
// entangled with user credentials. The framework will always prepare CE
// storage once CE keys are installed.
if (!e4crypt_prepare_user_storage(nullptr, 0, 0, FLAG_STORAGE_DE)) {
LOG(ERROR) << "Failed to prepare user 0 storage";
return false;
}
// If this is a non-FBE device that recently left an emulated mode,
// restore user data directories to known-good state.
if (!e4crypt_is_native() && !e4crypt_is_emulated()) {
e4crypt_unlock_user_key(0, 0, "!", "!");
}
return true;
}
static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) {
std::string de_key, ce_key;
if (!random_key(&de_key)) return false;
if (!random_key(&ce_key)) return false;
if (create_ephemeral) {
// If the key should be created as ephemeral, don't store it.
s_ephemeral_users.insert(user_id);
} else {
auto const directory_path = get_ce_key_directory_path(user_id);
if (!prepare_dir(directory_path, 0700, AID_ROOT, AID_ROOT)) return false;
auto const paths = get_ce_key_paths(directory_path);
std::string ce_key_path;
if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
if (!store_key(ce_key_path, user_key_temp,
kEmptyAuthentication, ce_key)) return false;
fixate_user_ce_key(directory_path, ce_key_path, paths);
// Write DE key second; once this is written, all is good.
if (!store_key(get_de_key_path(user_id), user_key_temp,
kEmptyAuthentication, de_key)) return false;
}
std::string de_raw_ref;
if (!install_key(de_key, &de_raw_ref)) return false;
s_de_key_raw_refs[user_id] = de_raw_ref;
std::string ce_raw_ref;
if (!install_key(ce_key, &ce_raw_ref)) return false;
s_ce_keys[user_id] = ce_key;
s_ce_key_raw_refs[user_id] = ce_raw_ref;
LOG(DEBUG) << "Created keys for user " << user_id;
return true;
}
static bool load_all_de_keys() {
auto de_dir = user_key_dir + "/de";
auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(de_dir.c_str()), closedir);
if (!dirp) {
PLOG(ERROR) << "Unable to read de key directory";
return false;
}的
for (;;) {
errno = 0;
auto entry = readdir(dirp.get());
if (!entry) {
if (errno) {
PLOG(ERROR) << "Unable to read de key directory";
return false;
}
break;
}
if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) {
LOG(DEBUG) << "Skipping non-de-key " << entry->d_name;
continue;
}
userid_t user_id = atoi(entry->d_name);
if (s_de_key_raw_refs.count(user_id) == 0) {
auto key_path = de_dir + "/" + entry->d_name;
std::string key;
if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
std::string raw_ref;
if (!install_key(key, &raw_ref)) return false;
s_de_key_raw_refs[user_id] = raw_ref;
LOG(DEBUG) << "Installed de key for user " << user_id;
}
}
// ext4enc:TODO: go through all DE directories, ensure that all user dirs have the
// correct policy set on them, and that no rogue ones exist.
return true;
}
random_key是随机生成加密密钥的函数.
path: system/vold/Ext4Crypt.cpp
static bool random_key(std::string* key) {
if (android::vold::ReadRandomBytes(EXT4_AES_256_XTS_KEY_SIZE, *key) != 0) {
// TODO status_t plays badly with PLOG, fix it.
LOG(ERROR) << "Random read failed";
return false;
}
return true;
}
path: system/vold/Utils.cpp
status_t ReadRandomBytes(size_t bytes, std::string& out) {
out.clear();
int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
if (fd == -1) {
return -errno;
}
char buf[BUFSIZ];
size_t n;
while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], std::min(sizeof(buf), bytes)))) > 0) {
out.append(buf, n);
bytes -= n;
}
close(fd);
if (bytes == 0) {
return OK;
} else {
return -EIO;
}
}
install_key函数主要功能是将生成的key通知到内核并将密钥hash返回给调用函数.
path: system/vold/Ext4Crypt.cpp
// Install password into global keyring
// Return raw key reference for use in policy
static bool install_key(const std::string& key, std::string* raw_ref) {
ext4_encryption_key ext4_key;
if (!fill_key(key, &ext4_key)) return false;
*raw_ref = generate_key_ref(ext4_key.raw, ext4_key.size);
auto ref = keyname(*raw_ref);
key_serial_t device_keyring;
if (!e4crypt_keyring(&device_keyring)) return false;
key_serial_t key_id =
add_key("logon", ref.c_str(), (void*)&ext4_key, sizeof(ext4_key), device_keyring);
if (key_id == -1) {
PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring;
return false;
}
LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
<< " in process " << getpid();
return true;
}
path: system/vold/Ext4Crypt.cpp
static bool fill_key(const std::string& key, ext4_encryption_key* ext4_key) {
if (key.size() != EXT4_AES_256_XTS_KEY_SIZE) {
LOG(ERROR) << "Wrong size key " << key.size();
return false;
}
static_assert(EXT4_AES_256_XTS_KEY_SIZE <= sizeof(ext4_key->raw), "Key too long!");
ext4_key->mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
ext4_key->size = key.size();
memset(ext4_key->raw, 0, sizeof(ext4_key->raw));
memcpy(ext4_key->raw, key.data(), key.size());
return true;
}
path: system/vold/Ext4Crypt.cpp
// Get raw keyref - used to make keyname and to pass to ioctl
static std::string generate_key_ref(const char* key, int length) {
SHA512_CTX c;
SHA512_Init(&c);
SHA512_Update(&c, key, length);
unsigned char key_ref1[SHA512_DIGEST_LENGTH];
SHA512_Final(key_ref1, &c);
SHA512_Init(&c);
SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
unsigned char key_ref2[SHA512_DIGEST_LENGTH];
SHA512_Final(key_ref2, &c);
static_assert(EXT4_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH,
"Hash too short for descriptor");
return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE);
}
path: system/vold/Ext4Crypt.cpp
static std::string keyname(const std::string& raw_ref) {
std::ostringstream o;
o << "ext4:";
for (auto i : raw_ref) {
o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
}
return o.str();
}
path: system/vold/Ext4Crypt.cpp
// Get the keyring we store all keys in
static bool e4crypt_keyring(key_serial_t* device_keyring) {
*device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "e4crypt", 0);
if (*device_keyring == -1) {
PLOG(ERROR) << "Unable to find device keyring";
return false;
}
return true;
}
- add_key
path: system/extras/ext4_utils/key_control.cpp
key_serial_t add_key(const char *type,
const char *description,
const void *payload,
size_t plen,
key_serial_t ringid)
{
return syscall(__NR_add_key, type, description, payload, plen, ringid);
}
- keyctl_search
path: system/extras/ext4_utils/key_control.cpp
long keyctl_search(key_serial_t ringid, const char *type,
const char *description, key_serial_t destringid)
{
return keyctl(KEYCTL_SEARCH, ringid, type, description, destringid);
}
- Documentation
path: kernel/Documentation/security/keys.txt
store_key函数主要目的是将密钥加密保存到制定位置.
path: system/vold/Ext4Crypt.cpp
// NB this assumes that there is only one thread listening for crypt commands, because
// it creates keys in a fixed location.
static bool store_key(const std::string& key_path, const std::string& tmp_path,
const android::vold::KeyAuthentication& auth, const std::string& key) {
if (path_exists(key_path)) {
LOG(ERROR) << "Already exists, cannot create key at: " << key_path;
return false;
}
if (path_exists(tmp_path)) {
android::vold::destroyKey(tmp_path); // May be partially created so ignore errors
}
if (!android::vold::storeKey(tmp_path, auth, key)) return false;
if (rename(tmp_path.c_str(), key_path.c_str()) != 0) {
PLOG(ERROR) << "Unable to move new key to location: " << key_path;
return false;
}
LOG(DEBUG) << "Created key " << key_path;
return true;
}
path: system/vold/KeyStorage.cpp
bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key) {
if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
PLOG(ERROR) << "key mkdir " << dir;
return false;
}
if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
std::string secdiscardable;
if (ReadRandomBytes(SECDISCARDABLE_BYTES, secdiscardable) != OK) {
// TODO status_t plays badly with PLOG, fix it.
LOG(ERROR) << "Random read failed";
return false;
}
if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
std::string stretching = auth.secret.empty() ? kStretch_nopassword : getStretching();
if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
std::string salt;
if (stretchingNeedsSalt(stretching)) {
if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
LOG(ERROR) << "Random read failed";
return false;
}
if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
}
std::string appId;
if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
Keymaster keymaster;
if (!keymaster) return false;
std::string kmKey;
if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
std::string encryptedKey;
if (!encryptWithKeymasterKey(keymaster, kmKey, auth, appId, key, &encryptedKey)) return false;
if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
return true;
}
retrieveKey函数的主要目的是解密storeKey函数加密保存的key.
path: system/vold/KeyStorage.cpp
static const char* kFn_encrypted_key = "encrypted_key";
static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
static const char* kFn_salt = "salt";
static const char* kFn_secdiscardable = "secdiscardable";
static const char* kFn_stretching = "stretching";
static const char* kFn_version = "version";
...
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key) {
std::string version;
if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
if (version != kCurrentVersion) {
LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
return false;
}
std::string secdiscardable;
if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false;
std::string stretching;
if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
std::string salt;
if (stretchingNeedsSalt(stretching)) {
if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
}
std::string appId;
if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
std::string kmKey;
if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
std::string encryptedMessage;
if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
Keymaster keymaster;
if (!keymaster) return false;
return decryptWithKeymasterKey(keymaster, kmKey, auth, appId, encryptedMessage, key);
}
-
policy - "/data/unencrypted/ref"
-
LOGS
[ 5.612948] ext4_utils: Setting 3fcbc19c policy on /data/bootchart!
[ 5.616033] ext4_utils: Setting 3fcbc19c policy on /data/misc!
[ 5.623805] ext4_utils: Setting 3fcbc19c policy on /data/local!
[ 5.629432] ext4_utils: Setting 3fcbc19c policy on /data/app-private!
[ 5.630260] ext4_utils: Setting 3fcbc19c policy on /data/app-ephemeral!
[ 5.631094] ext4_utils: Setting 3fcbc19c policy on /data/app-asec!
[ 5.631901] ext4_utils: Setting 3fcbc19c policy on /data/app-lib!
[ 5.633430] ext4_utils: Setting 3fcbc19c policy on /data/app!
- policy - "/data/unencrypted/ref"
path: system/core/init/builtins.cpp
static int do_mkdir(const std::vector<std::string>& args) {
mode_t mode = 0755;
int ret;
/* mkdir <path> [mode] [owner] policy - "/data/unencrypted/ref"[group] */
if (args.size() >= 3) {
mode = std::stoul(args[2], 0, 8);
}
ret = make_dir(args[1].c_str(), mode);
/* chmod in case the directory already exists */
if (ret == -1 && errno == EEXIST) {
ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
}
if (ret == -1) {
return -errno;
}
if (args.size() >= 4) {
uid_t uid = decode_uid(args[3].c_str());
gid_t gid = -1;
if (args.size() == 5) {
gid = decode_uid(args[4].c_str());
}
if (lchown(args[1].c_str(), uid, gid) == -1) {
return -errno;
}
/* chown may have cleared S_ISUID and S_ISGID, chmod again */
if (mode & (S_ISUID | S_ISGID)) {
ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
if (ret == -1) {
return -errno;
}
}
}
if (e4crypt_is_native()) {
if (e4crypt_set_directory_policy(args[1].c_str())) {
wipe_data_via_recovery(std::string() + "set_policy_failed:" + args[1]);
return -1;
}
}
return 0;
}
path: path: system/extras/ext4_utils/ext4_crypt.cpp
bool e4crypt_is_native() {
char value[PROPERTY_VALUE_MAX];
property_get("ro.crypto.type", value, "none");
return !strcmp(value, "file");
}
policy - "/data/unencrypted/ref"
path: system/extras/ext4_utils/ext4_crypt_init_extensions.cpp
int e4crypt_set_directory_policy(const char* dir)
{
init_logging();
// Only set policy on first level /data directories
// To make this less restrictive, consider using a policy file.
// However this is overkill for as long as the policy is simply
// to apply a global policy to all /data folders created via makedir
if (!dir || strncmp(dir, "/data/", 6) || strchr(dir + 6, '/')) {
return 0;
}
// Special case various directories that must not be encrypted,
// often because their subdirectories must be encrypted.
// This isn't a nice way to do this, see b/26641735
std::vector<std::string> directories_to_exclude = {
"lost+found",
"system_ce", "system_de",
"misc_ce", "misc_de",
"media",
"data", "user", "user_de",
};
std::string prefix = "/data/";
for (auto d: directories_to_exclude) {
if ((prefix + d) == dir) {
KLOG_INFO(TAG, "Not setting policy on %s\n", dir);
return 0;
}
}
std::string ref_filename = std::string("/data") + e4crypt_key_ref;
std::string policy;
if (!android::base::ReadFileToString(ref_filename, &policy)) {
KLOG_ERROR(TAG, "Unable to read system policy to set on %s\n", dir);
return -1;
}
KLOG_INFO(TAG, "Setting policy on %s\n", dir);
int result = e4crypt_policy_ensure(dir, policy.c_str(), policy.size());
if (result) {
KLOG_ERROR(TAG, "Setting %02x%02x%02x%02x policy on %s failed!\n",
policy[0], policy[1], policy[2], policy[3], dir);
return -1;
}
return 0;
}
- LOGS
01-15 06:48:24.588 592 598 D vold : e4crypt_prepare_user_storage for volume null, user 0, serial 0, flags 1
01-15 06:48:24.588 592 598 D vold : Preparing: /data/system/users/0
01-15 06:48:24.588 592 598 D vold : Preparing: /data/misc/profiles/cur/0
01-15 06:48:24.588 592 598 D vold : Preparing: /data/misc/profiles/cur/0/foreign-dex
01-15 06:48:24.589 592 598 D vold : Preparing: /data/system_de/0
01-15 06:48:24.589 592 598 D vold : Preparing: /data/misc_de/0
01-15 06:48:24.589 592 598 D vold : Preparing: /data/user_de/0
01-15 06:48:24.589 592 598 I vold : Policy for /data/system_de/0 set to 5b317534021c2524
01-15 06:48:24.589 592 598 I vold : Policy for /data/misc_de/0 set to 5b317534021c2524
01-15 06:48:24.589 592 598 I vold : Policy for /data/user_de/0 set to 5b317534021c2524
- flowchart
e4crypt_init_user0()
|
+-> create_and_install_user_keys (/data/misc/vold/user_keys)
|
+-> load_all_de_keys
|
+-> e4crypt_prepare_user_storage
| |
| +-> ensure_policy
| |
| +-> e4crypt_policy_ensure -> e4crypt_policy_set -> ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep);
|
+- non-FBE -> e4crypt_unlock_user_key(0, 0, "!", "!")
path: system/core/init/builtins.cpp
static int do_init_user0(const std::vector<std::string>& args) {
return e4crypt_do_init_user0();
}
path: system/extras/ext4_utils/ext4_crypt_init_extensions.cpp
int e4crypt_do_init_user0()
{
init_logging();
const char* argv[] = { "/system/bin/vdc", "--wait", "cryptfs", "init_user0" };
int rc = android_fork_execvp(4, (char**) argv, NULL, false, true);
LOG(INFO) << "init_user0 result: " << rc;
return rc;
}
path: system/vold/Ext4Crypt.cpp
bool e4crypt_init_user0() {
LOG(DEBUG) << "e4crypt_init_user0";
if (e4crypt_is_native()) {
if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
if (!path_exists(get_de_key_path(0))) {
if (!create_and_install_user_keys(0, false)) return false;
}
// TODO: switch to loading only DE_0 here once framework makes
// explicit calls to install DE keys for secondary users
if (!load_all_de_keys()) return false;
}
// We can only safely prepare DE storage here, since CE keys are probably
// entangled with user credentials. The framework will always prepare CE
// storage once CE keys are installed.
if (!e4crypt_prepare_user_storage(nullptr, 0, 0, FLAG_STORAGE_DE)) {
LOG(ERROR) << "Failed to prepare user 0 storage";
return false;
}
// If this is a non-FBE device that recently left an emulated mode,
// restore user data directories to known-good state.
if (!e4crypt_is_native() && !e4crypt_is_emulated()) {
e4crypt_unlock_user_key(0, 0, "!", "!");
}
return true;
}
path: system/vold/Ext4Crypt.cpp
bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial,
int flags) {
LOG(DEBUG) << "e4crypt_prepare_user_storage for volume " << escape_null(volume_uuid)
<< ", user " << user_id << ", serial " << serial << ", flags " << flags;
if (flags & FLAG_STORAGE_DE) {
// DE_sys key
auto system_legacy_path = android::vold::BuildDataSystemLegacyPath(user_id);
auto misc_legacy_path = android::vold::BuildDataMiscLegacyPath(user_id);
auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id);
auto foreign_de_path = android::vold::BuildDataProfilesForeignDexDePath(user_id);
// DE_n key
auto system_de_path = android::vold::BuildDataSystemDePath(user_id);
auto misc_de_path = android::vold::BuildDataMiscDePath(user_id);
auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);
if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false;
#if MANAGE_MISC_DIRS
if (!prepare_dir(misc_legacy_path, 0750, multiuser_get_uid(user_id, AID_SYSTEM),
multiuser_get_uid(user_id, AID_EVERYBODY))) return false;
#endif
if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
if (!prepare_dir(foreign_de_path, 0773, AID_SYSTEM, AID_SYSTEM)) return false;
if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false;
if (!prepare_dir(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
// For now, FBE is only supported on internal storage
if (e4crypt_is_native() && volume_uuid == nullptr) {
std::string de_raw_ref;
if (!lookup_key_ref(s_de_key_raw_refs, user_id, &de_raw_ref)) return false;
if (!ensure_policy(de_raw_ref, system_de_path)) return false;
if (!ensure_policy(de_raw_ref, misc_de_path)) return false;
if (!ensure_policy(de_raw_ref, user_de_path)) return false;
}
}
if (flags & FLAG_STORAGE_CE) {
// CE_n key
auto system_ce_path = android::vold::BuildDataSystemCePath(user_id);
auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id);
auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id);
auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);
if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false;
if (!prepare_dir(media_ce_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false;
if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
// For now, FBE is only supported on internal storage
if (e4crypt_is_native() && volume_uuid == nullptr) {
std::string ce_raw_ref;
if (!lookup_key_ref(s_ce_key_raw_refs, user_id, &ce_raw_ref)) return false;
if (!ensure_policy(ce_raw_ref, system_ce_path)) return false;
if (!ensure_policy(ce_raw_ref, misc_ce_path)) return false;
if (!ensure_policy(ce_raw_ref, media_ce_path)) return false;
if (!ensure_policy(ce_raw_ref, user_ce_path)) return false;
// Now that credentials have been installed, we can run restorecon
// over these paths
// NOTE: these paths need to be kept in sync with libselinux
android::vold::RestoreconRecursive(system_ce_path);
android::vold::RestoreconRecursive(misc_ce_path);
}
}
return true;
}
path: system/vold/Ext4Crypt.cpp
static bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
std::string* raw_ref) {
auto refi = key_map.find(user_id);
if (refi == key_map.end()) {
LOG(ERROR) << "Cannot find key for " << user_id;
return false;
}
*raw_ref = refi->second;
return true;
}
path: system/vold/Ext4Crypt.cpp
static bool ensure_policy(const std::string& raw_ref, const std::string& path) {
if (e4crypt_policy_ensure(path.c_str(),
raw_ref.data(), raw_ref.size(),
cryptfs_get_file_encryption_mode()) != 0) {
LOG(ERROR) << "Failed to set policy on: " << path;
return false;
}
return true;
}
- LOG
01-15 06:48:45.367 1639 1639 I SystemServer: StartPackageManagerService
...
01-15 06:48:58.591 1639 2809 I PackageManager: /system/priv-app/CNEService changed; collecting certs
...
01-15 06:49:05.285 1639 2812 I PackageManager: /data/app/partner-BaiduMap changed; collecting certs
01-15 06:49:08.026 1639 1639 V PackageManager: reconcileAppsData for null u0 0x1
01-15 06:49:08.027 746 746 I SELinux : SELinux: Loaded file_contexts contexts from /file_contexts.bin.
01-15 06:49:08.028 746 746 D installd: Detected label change from u:object_r:system_data_file:s0 to u:object_r:app_data_file:s0:c512,c768 at /data/user_de/0/com.android.cts.priv.ctsshim; running recursive restorecon
...
01-15 06:49:08.213 746 746 D installd: Detected label change from u:object_r:system_data_file:s0 to u:object_r:radio_data_file:s0 at /data/user_de/0/com.qti.editnumber; running recursive restorecon
01-15 06:49:08.214 1639 1639 V PackageManager: reconcileAppsData finished 214 packages
...
- Flowchart
PackageManagerService
|
+-> reconcileAppsDataLI +-> prepareAppDataLeafLIF -> mInstaller.createAppData -> create_app_data(installd)
| |
+-> prepareAppDataLIF <-+
| |
+-> maybeMigrateAppDataLIF
- LOG
01-15 06:50:41.581 1639 1755 I ActivityManager: Start proc 5566:com.android.defcontainer/u0a9 for on-hold
01-15 06:50:41.582 1639 1755 D ActivityManager: Finishing user boot 0
...
01-15 06:50:41.585 1639 1755 D CryptdConnector: SND -> {2 cryptfs unlock_user_key 0 0 [scrubbed] [scrubbed]}
01-15 06:50:41.586 592 598 D vold : e4crypt_unlock_user_key 0 serial=0 token_present=0
01-15 06:50:41.586 592 598 W vold : Tried to unlock already-unlocked key for user 0
...
01-15 06:50:41.586 1639 3249 D CryptdConnector: RCV <- {200 2 Command succeeded}
01-15 06:50:41.587 1639 1755 D CryptdConnector: SND -> {3 cryptfs prepare_user_storage ! 0 0 2}
01-15 06:50:41.587 592 598 D vold : e4crypt_prepare_user_storage for volume null, user 0, serial 0, flags 2
01-15 06:50:41.587 592 598 D vold : Preparing: /data/system_ce/0
01-15 06:50:41.587 592 598 D vold : Preparing: /data/misc_ce/0
01-15 06:50:41.587 592 598 D vold : Preparing: /data/media/0
01-15 06:50:41.588 592 598 D vold : Preparing: /data/data
01-15 06:50:41.630 592 598 I vold : Policy for /data/system_ce/0 set to 5ebd155fc20b3ffa
01-15 06:50:41.630 592 598 I vold : Policy for /data/misc_ce/0 set to 5ebd155fc20b3ffa
01-15 06:50:41.631 592 598 I vold : Policy for /data/media/0 set to 5ebd155fc20b3ffa
01-15 06:50:41.631 592 598 I vold : Policy for /data/data set to 5ebd155fc20b3ffa
01-15 06:50:41.631 592 598 V vold : Starting restorecon of /data/system_ce/0
01-15 06:50:41.635 592 598 V vold : Finished restorecon of /data/system_ce/0
01-15 06:50:41.635 592 598 V vold : Starting restorecon of /data/misc_ce/0
01-15 06:50:41.636 592 598 V vold : Finished restorecon of /data/misc_ce/0
01-15 06:50:41.637 1639 3249 D CryptdConnector: RCV <- {200 5 Command succeeded}
- Flowchart
ActivityManagerService.systemReady
|
UserController.startUser
|
UserController.finishUserBoot
|
UserController.maybeUnlockUser
|
UserController.unlockUserCleared
|
+-> MountService.unlockUserKey
| |
| +-> e4crypt_unlock_user_key
|
UserController.finishUserUnlocking
|
UserManagerService.onBeforeUnlockUser
|
PackageManagerService.prepareUserData
|
PackageManagerService.prepareUserDataLI
|
MountService.prepareUserStorage
|
+-> e4crypt_prepare_user_storage
01-15 06:50:41.639 1639 1755 V PackageManager: reconcileAppsData for null u0 0x2
...
01-15 06:50:42.037 746 746 D installd: Detected label change from u:object_r:system_data_file:s0 to u:object_r:radio_data_file:s0 at /data/data/com.qti.editnumber; running recursive restorecon
01-15 06:50:42.038 1639 1755 V PackageManager: reconcileAppsData finished 214 packages
不论使用什么key进行加密最终的policy(密钥hash)设置都是通过函数e4crypt_policy_ensure来实现的,其主要作用是将对应的policy 添加到对应的目录的xattr属性中保存起来, 其具体实现如下所示:
path: system/extras/ext4_utils/ext4_crypt.cpp
int e4crypt_policy_ensure(const char *directory, const char *policy, size_t policy_length) {
bool is_empty;
if (!is_dir_empty(directory, &is_empty)) return -1;
if (is_empty) {
if (!e4crypt_policy_set(directory, policy, policy_length)) return -1;
} else {
if (!e4crypt_policy_check(directory, policy, policy_length)) return -1;
}
return 0;
}
path: system/extras/ext4_utils/ext4_crypt.cpp
struct ext4_encryption_policy {
char version;
char contents_encryption_mode;
char filenames_encryption_mode;
char flags;
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
} __attribute__((__packed__));
path: system/extras/ext4_utils/ext4_crypt.cpp
static bool e4crypt_policy_set(const char *directory, const char *policy, size_t policy_length) {
if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
LOG(ERROR) << "Policy wrong length: " << policy_length;
return false;
}
int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (fd == -1) {
PLOG(ERROR) << "Failed to open directory " << directory;
return false;
}
ext4_encryption_policy eep;
eep.version = 0;
eep.contents_encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE;
eep.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
eep.flags = 0;
memcpy(eep.master_key_descriptor, policy, EXT4_KEY_DESCRIPTOR_SIZE);
if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep)) {
PLOG(ERROR) << "Failed to set encryption policy for " << directory;
close(fd);
return false;
}
close(fd);
char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(policy, policy_hex);
LOG(INFO) << "Policy for " << directory << " set to " << policy_hex;
return true;
}
path: fs/ext4/ioctl.c
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct super_block *sb = inode->i_sb;
struct ext4_inode_info *ei = EXT4_I(inode);
unsigned int flags;
ext4_debug("cmd = %u, arg = %lu\n", cmd, arg);
switch (cmd) {
...
case EXT4_IOC_SET_ENCRYPTION_POLICY: {
#ifdef CONFIG_EXT4_FS_ENCRYPTION
struct ext4_encryption_policy policy;
int err = 0;
if (copy_from_user(&policy,
(struct ext4_encryption_policy __user *)arg,
sizeof(policy))) {
err = -EFAULT;
goto encryption_policy_out;
}
err = ext4_process_policy(&policy, inode);
encryption_policy_out:
return err;
#else
return -EOPNOTSUPP;
#endif
}
...
}
...
}
path: fs/ext4/crypto_policy.c
int ext4_process_policy(const struct ext4_encryption_policy *policy,
struct inode *inode)
{
if (policy->version != 0)
return -EINVAL;
if (!ext4_inode_has_encryption_context(inode)) {
if (!S_ISDIR(inode->i_mode))
return -EINVAL;
if (!ext4_empty_dir(inode))
return -ENOTEMPTY;
return ext4_create_encryption_context_from_policy(inode,
policy);
}
if (ext4_is_encryption_context_consistent_with_policy(inode, policy))
return 0;
printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n",
__func__);
return -EINVAL;
}
- ext4_inode_has_encryption_context
static int ext4_inode_has_encryption_context(struct inode *inode)
{
int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0);
return (res > 0);
}
- ext4_create_encryption_context_from_policy
static int ext4_create_encryption_context_from_policy(
struct inode *inode, const struct ext4_encryption_policy *policy)
{
struct ext4_encryption_context ctx;
handle_t *handle;
int res, res2;
res = ext4_convert_inline_data(inode);
if (res)
return res;
ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V1;
memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
EXT4_KEY_DESCRIPTOR_SIZE);
if (!ext4_valid_contents_enc_mode(policy->contents_encryption_mode)) {
printk(KERN_WARNING
"%s: Invalid contents encryption mode %d\n", __func__,
policy->contents_encryption_mode);
return -EINVAL;
}
if (!ext4_valid_filenames_enc_mode(policy->filenames_encryption_mode)) {
printk(KERN_WARNING
"%s: Invalid filenames encryption mode %d\n", __func__,
policy->filenames_encryption_mode);
return -EINVAL;
}
if (policy->flags & ~EXT4_POLICY_FLAGS_VALID)
return -EINVAL;
ctx.contents_encryption_mode = policy->contents_encryption_mode;
ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
ctx.flags = policy->flags;
BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
handle = ext4_journal_start(inode, EXT4_HT_MISC,
ext4_jbd2_credits_xattr(inode));
if (IS_ERR(handle))
return PTR_ERR(handle);
res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
sizeof(ctx), 0);
if (!res) {
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
res = ext4_mark_inode_dirty(handle, inode);
if (res)
EXT4_ERROR_INODE(inode, "Failed to mark inode dirty");
}
res2 = ext4_journal_stop(handle);
if (!res)
res = res2;
return res;
}