Home | History | Annotate | Download | only in vold
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "KeyBuffer.h"
     18 #include "MetadataCrypt.h"
     19 
     20 #include <string>
     21 #include <thread>
     22 #include <vector>
     23 #include <algorithm>
     24 
     25 #include <fcntl.h>
     26 #include <sys/ioctl.h>
     27 #include <sys/param.h>
     28 #include <sys/stat.h>
     29 #include <sys/types.h>
     30 
     31 #include <linux/dm-ioctl.h>
     32 
     33 #include <android-base/logging.h>
     34 #include <android-base/unique_fd.h>
     35 #include <cutils/properties.h>
     36 #include <fs_mgr.h>
     37 
     38 #include "EncryptInplace.h"
     39 #include "KeyStorage.h"
     40 #include "KeyUtil.h"
     41 #include "secontext.h"
     42 #include "Utils.h"
     43 #include "VoldUtil.h"
     44 
     45 extern struct fstab *fstab;
     46 #define DM_CRYPT_BUF_SIZE 4096
     47 #define TABLE_LOAD_RETRIES 10
     48 #define DEFAULT_KEY_TARGET_TYPE "default-key"
     49 
     50 using android::vold::KeyBuffer;
     51 
     52 static const std::string kDmNameUserdata = "userdata";
     53 
     54 static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
     55     // fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
     56     // partitions in the fsck domain.
     57     if (setexeccon(secontextFsck())) {
     58         PLOG(ERROR) << "Failed to setexeccon";
     59         return false;
     60     }
     61     auto mount_rc = fs_mgr_do_mount(fstab, const_cast<char*>(mount_point),
     62                                     const_cast<char*>(blk_device), nullptr);
     63     if (setexeccon(nullptr)) {
     64         PLOG(ERROR) << "Failed to clear setexeccon";
     65         return false;
     66     }
     67     if (mount_rc != 0) {
     68         LOG(ERROR) << "fs_mgr_do_mount failed with rc " << mount_rc;
     69         return false;
     70     }
     71     LOG(DEBUG) << "Mounted " << mount_point;
     72     return true;
     73 }
     74 
     75 static bool read_key(bool create_if_absent, KeyBuffer* key) {
     76     auto data_rec = fs_mgr_get_crypt_entry(fstab);
     77     if (!data_rec) {
     78         LOG(ERROR) << "Failed to get data_rec";
     79         return false;
     80     }
     81     if (!data_rec->key_dir) {
     82         LOG(ERROR) << "Failed to get key_dir";
     83         return false;
     84     }
     85     LOG(DEBUG) << "key_dir: " << data_rec->key_dir;
     86     if (!android::vold::pathExists(data_rec->key_dir)) {
     87         if (mkdir(data_rec->key_dir, 0777) != 0) {
     88             PLOG(ERROR) << "Unable to create: " << data_rec->key_dir;
     89             return false;
     90         }
     91         LOG(DEBUG) << "Created: " << data_rec->key_dir;
     92     }
     93     std::string key_dir = data_rec->key_dir;
     94     auto dir = key_dir + "/key";
     95     auto temp = key_dir + "/tmp";
     96     if (!android::vold::retrieveKey(create_if_absent, dir, temp, key)) return false;
     97     return true;
     98 }
     99 
    100 static KeyBuffer default_key_params(const std::string& real_blkdev, const KeyBuffer& key) {
    101     KeyBuffer hex_key;
    102     if (android::vold::StrToHex(key, hex_key) != android::OK) {
    103         LOG(ERROR) << "Failed to turn key to hex";
    104         return KeyBuffer();
    105     }
    106     auto res = KeyBuffer() + "AES-256-XTS " + hex_key + " " + real_blkdev.c_str() + " 0";
    107     LOG(DEBUG) << "crypt_params: " << std::string(res.data(), res.size());
    108     return res;
    109 }
    110 
    111 static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t *nr_sec) {
    112     android::base::unique_fd dev_fd(TEMP_FAILURE_RETRY(open(
    113         real_blkdev.c_str(), O_RDONLY | O_CLOEXEC, 0)));
    114     if (dev_fd == -1) {
    115         PLOG(ERROR) << "Unable to open " << real_blkdev << " to measure size";
    116         return false;
    117     }
    118     unsigned long res;
    119     // TODO: should use BLKGETSIZE64
    120     get_blkdev_size(dev_fd.get(), &res);
    121     if (res == 0) {
    122         PLOG(ERROR) << "Unable to measure size of " << real_blkdev;
    123         return false;
    124     }
    125     *nr_sec = res;
    126     return true;
    127 }
    128 
    129 static struct dm_ioctl* dm_ioctl_init(char *buffer, size_t buffer_size,
    130                                       const std::string& dm_name) {
    131     if (buffer_size < sizeof(dm_ioctl)) {
    132         LOG(ERROR) << "dm_ioctl buffer too small";
    133         return nullptr;
    134     }
    135 
    136     memset(buffer, 0, buffer_size);
    137     struct dm_ioctl* io = (struct dm_ioctl*) buffer;
    138     io->data_size = buffer_size;
    139     io->data_start = sizeof(struct dm_ioctl);
    140     io->version[0] = 4;
    141     io->version[1] = 0;
    142     io->version[2] = 0;
    143     io->flags = 0;
    144     dm_name.copy(io->name, sizeof(io->name));
    145     return io;
    146 }
    147 
    148 static bool create_crypto_blk_dev(const std::string& dm_name, uint64_t nr_sec,
    149                                   const std::string& target_type, const KeyBuffer& crypt_params,
    150                                   std::string* crypto_blkdev) {
    151     android::base::unique_fd dm_fd(TEMP_FAILURE_RETRY(open(
    152         "/dev/device-mapper", O_RDWR | O_CLOEXEC, 0)));
    153     if (dm_fd == -1) {
    154         PLOG(ERROR) << "Cannot open device-mapper";
    155         return false;
    156     }
    157     alignas(struct dm_ioctl) char buffer[DM_CRYPT_BUF_SIZE];
    158     auto io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
    159     if (!io || ioctl(dm_fd.get(), DM_DEV_CREATE, io) != 0) {
    160         PLOG(ERROR) << "Cannot create dm-crypt device " << dm_name;
    161         return false;
    162     }
    163 
    164     // Get the device status, in particular, the name of its device file
    165     io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
    166     if (ioctl(dm_fd.get(), DM_DEV_STATUS, io) != 0) {
    167         PLOG(ERROR) << "Cannot retrieve dm-crypt device status " << dm_name;
    168         return false;
    169     }
    170     *crypto_blkdev = std::string() + "/dev/block/dm-" + std::to_string(
    171         (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00));
    172 
    173     io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
    174     size_t paramix = io->data_start + sizeof(struct dm_target_spec);
    175     size_t nullix = paramix + crypt_params.size();
    176     size_t endix = (nullix + 1 + 7) & 8; // Add room for \0 and align to 8 byte boundary
    177 
    178     if (endix > sizeof(buffer)) {
    179         LOG(ERROR) << "crypt_params too big for DM_CRYPT_BUF_SIZE";
    180         return false;
    181     }
    182 
    183     io->target_count = 1;
    184     auto tgt = (struct dm_target_spec *) (buffer + io->data_start);
    185     tgt->status = 0;
    186     tgt->sector_start = 0;
    187     tgt->length = nr_sec;
    188     target_type.copy(tgt->target_type, sizeof(tgt->target_type));
    189     memcpy(buffer + paramix, crypt_params.data(),
    190             std::min(crypt_params.size(), sizeof(buffer) - paramix));
    191     buffer[nullix] = '\0';
    192     tgt->next = endix;
    193 
    194     for (int i = 0; ; i++) {
    195         if (ioctl(dm_fd.get(), DM_TABLE_LOAD, io) == 0) {
    196             break;
    197         }
    198         if (i+1 >= TABLE_LOAD_RETRIES) {
    199             PLOG(ERROR) << "DM_TABLE_LOAD ioctl failed";
    200             return false;
    201         }
    202         PLOG(INFO) << "DM_TABLE_LOAD ioctl failed, retrying";
    203         usleep(500000);
    204     }
    205 
    206     // Resume this device to activate it
    207     io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
    208     if (ioctl(dm_fd.get(), DM_DEV_SUSPEND, io)) {
    209         PLOG(ERROR) << "Cannot resume dm-crypt device " << dm_name;
    210         return false;
    211     }
    212     return true;
    213 }
    214 
    215 #define DATA_PREP_TIMEOUT 1000
    216 static bool prep_data_fs(void)
    217 {
    218     // NOTE: post_fs_data results in init calling back around to vold, so all
    219     // callers to this method must be async
    220 
    221     /* Do the prep of the /data filesystem */
    222     property_set("vold.post_fs_data_done", "0");
    223     property_set("vold.decrypt", "trigger_post_fs_data");
    224     LOG(DEBUG) << "Waiting for post_fs_data_done";
    225 
    226     /* Wait a max of 50 seconds, hopefully it takes much less */
    227     for (int i = 0; ; i++) {
    228         char p[PROPERTY_VALUE_MAX];
    229 
    230         property_get("vold.post_fs_data_done", p, "0");
    231         if (*p == '1') {
    232             LOG(INFO) << "Successful data prep";
    233             return true;
    234         }
    235         if (i + 1 == DATA_PREP_TIMEOUT) {
    236             LOG(ERROR) << "post_fs_data timed out";
    237             return false;
    238         }
    239         usleep(50000);
    240     }
    241 }
    242 
    243 static void async_kick_off() {
    244     LOG(DEBUG) << "Asynchronously restarting framework";
    245     sleep(2); // TODO: this mirrors cryptfs, but can it be made shorter?
    246     property_set("vold.decrypt", "trigger_load_persist_props");
    247     if (!prep_data_fs()) return;
    248     /* startup service classes main and late_start */
    249     property_set("vold.decrypt", "trigger_restart_framework");
    250 }
    251 
    252 bool e4crypt_mount_metadata_encrypted() {
    253     LOG(DEBUG) << "e4crypt_mount_default_encrypted";
    254     KeyBuffer key;
    255     if (!read_key(false, &key)) return false;
    256     auto data_rec = fs_mgr_get_crypt_entry(fstab);
    257     if (!data_rec) {
    258         LOG(ERROR) << "Failed to get data_rec";
    259         return false;
    260     }
    261     uint64_t nr_sec;
    262     if (!get_number_of_sectors(data_rec->blk_device, &nr_sec)) return false;
    263     std::string crypto_blkdev;
    264     if (!create_crypto_blk_dev(kDmNameUserdata, nr_sec, DEFAULT_KEY_TARGET_TYPE,
    265         default_key_params(data_rec->blk_device, key), &crypto_blkdev)) return false;
    266     // FIXME handle the corrupt case
    267 
    268     LOG(DEBUG) << "Restarting filesystem for metadata encryption";
    269     mount_via_fs_mgr(data_rec->mount_point, crypto_blkdev.c_str());
    270     std::thread(&async_kick_off).detach();
    271     return true;
    272 }
    273 
    274 bool e4crypt_enable_crypto() {
    275     LOG(DEBUG) << "e4crypt_enable_crypto";
    276     char encrypted_state[PROPERTY_VALUE_MAX];
    277     property_get("ro.crypto.state", encrypted_state, "");
    278     if (strcmp(encrypted_state, "")) {
    279         LOG(DEBUG) << "e4crypt_enable_crypto got unexpected starting state: " << encrypted_state;
    280         return false;
    281     }
    282 
    283     KeyBuffer key_ref;
    284     if (!read_key(true, &key_ref)) return false;
    285 
    286     auto data_rec = fs_mgr_get_crypt_entry(fstab);
    287     if (!data_rec) {
    288         LOG(ERROR) << "Failed to get data_rec";
    289         return false;
    290     }
    291     uint64_t nr_sec;
    292     if (!get_number_of_sectors(data_rec->blk_device, &nr_sec)) return false;
    293 
    294     std::string crypto_blkdev;
    295     if (!create_crypto_blk_dev(kDmNameUserdata, nr_sec, DEFAULT_KEY_TARGET_TYPE,
    296         default_key_params(data_rec->blk_device, key_ref), &crypto_blkdev)) return false;
    297 
    298     LOG(INFO) << "Beginning inplace encryption, nr_sec: " << nr_sec;
    299     off64_t size_already_done = 0;
    300     auto rc = cryptfs_enable_inplace(const_cast<char *>(crypto_blkdev.c_str()),
    301                                      data_rec->blk_device, nr_sec, &size_already_done, nr_sec, 0);
    302     if (rc != 0) {
    303         LOG(ERROR) << "Inplace crypto failed with code: " << rc;
    304         return false;
    305     }
    306     if (static_cast<uint64_t>(size_already_done) != nr_sec) {
    307         LOG(ERROR) << "Inplace crypto only got up to sector: " << size_already_done;
    308         return false;
    309     }
    310     LOG(INFO) << "Inplace encryption complete";
    311 
    312     property_set("ro.crypto.state", "encrypted");
    313     property_set("ro.crypto.type", "file");
    314 
    315     mount_via_fs_mgr(data_rec->mount_point, crypto_blkdev.c_str());
    316     property_set("vold.decrypt", "trigger_reset_main");
    317     std::thread(&async_kick_off).detach();
    318     return true;
    319 }
    320