OSDN Git Service

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