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/properties.h>
     35 #include <android-base/unique_fd.h>
     36 #include <cutils/fs.h>
     37 #include <fs_mgr.h>
     38 
     39 #include "EncryptInplace.h"
     40 #include "KeyStorage.h"
     41 #include "KeyUtil.h"
     42 #include "secontext.h"
     43 #include "Utils.h"
     44 #include "VoldUtil.h"
     45 
     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_default, 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(struct fstab_rec const* data_rec, bool create_if_absent, KeyBuffer* key) {
     76     if (!data_rec->key_dir) {
     77         LOG(ERROR) << "Failed to get key_dir";
     78         return false;
     79     }
     80     std::string key_dir = data_rec->key_dir;
     81     auto dir = key_dir + "/key";
     82     LOG(DEBUG) << "key_dir/key: " << dir;
     83     if (fs_mkdirs(dir.c_str(), 0700)) {
     84         PLOG(ERROR) << "Creating directories: " << dir;
     85         return false;
     86     }
     87     auto temp = key_dir + "/tmp";
     88     if (!android::vold::retrieveKey(create_if_absent, dir, temp, key)) return false;
     89     return true;
     90 }
     91 
     92 static KeyBuffer default_key_params(const std::string& real_blkdev, const KeyBuffer& key) {
     93     KeyBuffer hex_key;
     94     if (android::vold::StrToHex(key, hex_key) != android::OK) {
     95         LOG(ERROR) << "Failed to turn key to hex";
     96         return KeyBuffer();
     97     }
     98     auto res = KeyBuffer() + "AES-256-XTS " + hex_key + " " + real_blkdev.c_str() + " 0";
     99     return res;
    100 }
    101 
    102 static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t *nr_sec) {
    103     android::base::unique_fd dev_fd(TEMP_FAILURE_RETRY(open(
    104         real_blkdev.c_str(), O_RDONLY | O_CLOEXEC, 0)));
    105     if (dev_fd == -1) {
    106         PLOG(ERROR) << "Unable to open " << real_blkdev << " to measure size";
    107         return false;
    108     }
    109     unsigned long res;
    110     // TODO: should use BLKGETSIZE64
    111     get_blkdev_size(dev_fd.get(), &res);
    112     if (res == 0) {
    113         PLOG(ERROR) << "Unable to measure size of " << real_blkdev;
    114         return false;
    115     }
    116     *nr_sec = res;
    117     return true;
    118 }
    119 
    120 static struct dm_ioctl* dm_ioctl_init(char *buffer, size_t buffer_size,
    121                                       const std::string& dm_name) {
    122     if (buffer_size < sizeof(dm_ioctl)) {
    123         LOG(ERROR) << "dm_ioctl buffer too small";
    124         return nullptr;
    125     }
    126 
    127     memset(buffer, 0, buffer_size);
    128     struct dm_ioctl* io = (struct dm_ioctl*) buffer;
    129     io->data_size = buffer_size;
    130     io->data_start = sizeof(struct dm_ioctl);
    131     io->version[0] = 4;
    132     io->version[1] = 0;
    133     io->version[2] = 0;
    134     io->flags = 0;
    135     dm_name.copy(io->name, sizeof(io->name));
    136     return io;
    137 }
    138 
    139 static bool create_crypto_blk_dev(const std::string& dm_name, uint64_t nr_sec,
    140                                   const std::string& target_type, const KeyBuffer& crypt_params,
    141                                   std::string* crypto_blkdev) {
    142     android::base::unique_fd dm_fd(TEMP_FAILURE_RETRY(open(
    143         "/dev/device-mapper", O_RDWR | O_CLOEXEC, 0)));
    144     if (dm_fd == -1) {
    145         PLOG(ERROR) << "Cannot open device-mapper";
    146         return false;
    147     }
    148     alignas(struct dm_ioctl) char buffer[DM_CRYPT_BUF_SIZE];
    149     auto io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
    150     if (!io || ioctl(dm_fd.get(), DM_DEV_CREATE, io) != 0) {
    151         PLOG(ERROR) << "Cannot create dm-crypt device " << dm_name;
    152         return false;
    153     }
    154 
    155     // Get the device status, in particular, the name of its device file
    156     io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
    157     if (ioctl(dm_fd.get(), DM_DEV_STATUS, io) != 0) {
    158         PLOG(ERROR) << "Cannot retrieve dm-crypt device status " << dm_name;
    159         return false;
    160     }
    161     *crypto_blkdev = std::string() + "/dev/block/dm-" + std::to_string(
    162         (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00));
    163 
    164     io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
    165     size_t paramix = io->data_start + sizeof(struct dm_target_spec);
    166     size_t nullix = paramix + crypt_params.size();
    167     size_t endix = (nullix + 1 + 7) & 8; // Add room for \0 and align to 8 byte boundary
    168 
    169     if (endix > sizeof(buffer)) {
    170         LOG(ERROR) << "crypt_params too big for DM_CRYPT_BUF_SIZE";
    171         return false;
    172     }
    173 
    174     io->target_count = 1;
    175     auto tgt = (struct dm_target_spec *) (buffer + io->data_start);
    176     tgt->status = 0;
    177     tgt->sector_start = 0;
    178     tgt->length = nr_sec;
    179     target_type.copy(tgt->target_type, sizeof(tgt->target_type));
    180     memcpy(buffer + paramix, crypt_params.data(),
    181             std::min(crypt_params.size(), sizeof(buffer) - paramix));
    182     buffer[nullix] = '\0';
    183     tgt->next = endix;
    184 
    185     for (int i = 0; ; i++) {
    186         if (ioctl(dm_fd.get(), DM_TABLE_LOAD, io) == 0) {
    187             break;
    188         }
    189         if (i+1 >= TABLE_LOAD_RETRIES) {
    190             PLOG(ERROR) << "DM_TABLE_LOAD ioctl failed";
    191             return false;
    192         }
    193         PLOG(INFO) << "DM_TABLE_LOAD ioctl failed, retrying";
    194         usleep(500000);
    195     }
    196 
    197     // Resume this device to activate it
    198     io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
    199     if (ioctl(dm_fd.get(), DM_DEV_SUSPEND, io)) {
    200         PLOG(ERROR) << "Cannot resume dm-crypt device " << dm_name;
    201         return false;
    202     }
    203     return true;
    204 }
    205 
    206 bool e4crypt_mount_metadata_encrypted(const std::string& mount_point, bool needs_encrypt) {
    207     LOG(DEBUG) << "e4crypt_mount_metadata_encrypted: " << mount_point << " " << needs_encrypt;
    208     auto encrypted_state = android::base::GetProperty("ro.crypto.state", "");
    209     if (encrypted_state != "") {
    210         LOG(DEBUG) << "e4crypt_enable_crypto got unexpected starting state: " << encrypted_state;
    211         return false;
    212     }
    213     auto data_rec = fs_mgr_get_entry_for_mount_point(fstab_default, mount_point);
    214     if (!data_rec) {
    215         LOG(ERROR) << "Failed to get data_rec";
    216         return false;
    217     }
    218     KeyBuffer key;
    219     if (!read_key(data_rec, needs_encrypt, &key)) return false;
    220     uint64_t nr_sec;
    221     if (!get_number_of_sectors(data_rec->blk_device, &nr_sec)) return false;
    222     std::string crypto_blkdev;
    223     if (!create_crypto_blk_dev(kDmNameUserdata, nr_sec, DEFAULT_KEY_TARGET_TYPE,
    224                                default_key_params(data_rec->blk_device, key), &crypto_blkdev))
    225         return false;
    226     // FIXME handle the corrupt case
    227     if (needs_encrypt) {
    228         LOG(INFO) << "Beginning inplace encryption, nr_sec: " << nr_sec;
    229         off64_t size_already_done = 0;
    230         auto rc =
    231             cryptfs_enable_inplace(const_cast<char*>(crypto_blkdev.c_str()), data_rec->blk_device,
    232                                    nr_sec, &size_already_done, nr_sec, 0, false);
    233         if (rc != 0) {
    234             LOG(ERROR) << "Inplace crypto failed with code: " << rc;
    235             return false;
    236         }
    237         if (static_cast<uint64_t>(size_already_done) != nr_sec) {
    238             LOG(ERROR) << "Inplace crypto only got up to sector: " << size_already_done;
    239             return false;
    240         }
    241         LOG(INFO) << "Inplace encryption complete";
    242     }
    243 
    244     LOG(DEBUG) << "Mounting metadata-encrypted filesystem:" << mount_point;
    245     mount_via_fs_mgr(data_rec->mount_point, crypto_blkdev.c_str());
    246     return true;
    247 }
    248