Remove dependency of ext2fs, com_err from fota
[platform/core/security/ode.git] / fota / fota.cpp
1 /*
2  *  Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
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 <linux/dm-ioctl.h>
18 #include <sys/mount.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <strings.h>
25 #include <blkid.h>
26 #include <string.h>
27 #include <iomanip>
28 #include <string>
29 #include <sstream>
30 #include <iostream>
31 #include <memory>
32
33 #include <klay/error.h>
34 #include <klay/exception.h>
35 #include <klay/audit/dlog-sink.h>
36
37 #include <upgrade-support.h>
38 #include "progress-bar.h"
39
40 #define DM_MAX_BUFFER_SIZE      4096
41 #define DM_KEY_MIN_LEN_BYTE     32
42 #define DM_DEFAULT_LABEL_NAME   "userdata"
43 #define DM_DEFAULT_CRYPTO_NAME  "aes-cbc-essiv:sha256"
44
45 audit::LogSink *SINK = nullptr;
46
47 namespace ode {
48
49 const char *INTERNAL_PATH = "/opt/usr";
50
51 struct AbstractDevice {
52         AbstractDevice() {}
53         virtual ~AbstractDevice() {}
54         virtual std::string findNode(const std::string &name) = 0;
55 };
56
57 struct MmcDevice : public AbstractDevice {
58 public:
59         MmcDevice(int id) : device("/dev/mmcblk" + std::to_string(id)), deviceHandle(-1)
60         {
61                 deviceHandle = ::open(device.c_str(), O_RDONLY);
62                 if (deviceHandle == -1)
63                         throw std::runtime_error("Invalid device: " + device);
64         }
65
66         virtual ~MmcDevice()
67         {
68                 if (deviceHandle != -1)
69                         ::close(deviceHandle);
70         }
71
72         virtual std::string findNode(const std::string &name)
73         {
74                 blkid_partlist partlist;
75                 blkid_probe probe;
76                 int partno = 0;
77
78                 probe = ::blkid_new_probe();
79                 if (!probe) {
80                         throw std::runtime_error("Failed to call blkid_new_probe");
81                 }
82
83                 if (::blkid_probe_set_device(probe, deviceHandle, 0, 0) < 0) {
84                         ::blkid_free_probe(probe);
85                         throw std::runtime_error("Failed to set prove device: " + device);
86                 }
87
88                 partlist = ::blkid_probe_get_partitions(probe);
89                 if (!partlist) {
90                         ::blkid_free_probe(probe);
91                         throw std::runtime_error("Failed to get partition list in device: " + device);
92                 }
93
94                 int num = ::blkid_partlist_numof_partitions(partlist);
95                 for (int i = 1; i <= num; i++) {
96                         blkid_partition par = ::blkid_partlist_get_partition(partlist, i);
97
98                         const char *n = ::blkid_partition_get_name(par);
99                         if (!n)
100                                 break;
101
102                         if (::strcasecmp(n, name.c_str()) == 0) {
103                                 partno = ::blkid_partition_get_partno(par);
104                                 break;
105                         }
106                 }
107
108                 ::blkid_free_probe(probe);
109
110                 if (partno <= 0) {
111                         throw std::runtime_error("Failed to get partition number with " +  name);
112                 }
113
114                 return device + "p" + std::to_string(partno);
115         }
116
117 private:
118         std::string device;
119         int deviceHandle;
120 };
121
122 // dummy implementation
123 ProgressBar::ProgressBar(UpdateFunc const&) : updateValue(0) {}
124 ProgressBar::~ProgressBar() {}
125
126 void ProgressBar::update(unsigned) {}
127 void ProgressBar::done(void) {}
128
129 //Hot fix for urgent issue - start, TODO : It should be removed.
130 unsigned long getBlockCount(const std::string &src)
131 {
132     int fd = open(src.c_str(), O_RDONLY);
133     if (fd < 0)
134         return 0;
135
136     unsigned long size = 0;
137     if ((ioctl(fd, BLKGETSIZE, &size)) == -1)
138         size = 0;
139     close(fd);
140
141     return size;
142 }
143
144 const std::string convertToHex(const BinaryData &binary)
145 {
146     std::stringstream hex;
147
148     hex << std::hex << std::setfill('0');
149     for (unsigned int byte : binary) {
150         hex << std::setw(2) << byte;
151     }
152     return hex.str();
153 }
154
155 void initDMIoctl(char *buf, size_t size, const std::string &name, unsigned flags)
156 {
157     struct dm_ioctl *io = (struct dm_ioctl *)buf;
158
159     ::memset(io, 0, size);
160     io->data_size = size;
161     io->data_start = sizeof(struct dm_ioctl);
162     io->version[0] = 4;
163     io->version[1] = 0;
164     io->version[2] = 0;
165     io->flags = flags;
166     ::memset(io->name, 0, sizeof(io->name));
167     ::strncpy(io->name, name.c_str(), sizeof(io->name) - 1);
168 }
169
170 BinaryData sanitizeKey(const BinaryData &key)
171 {
172     if (key.size() < DM_KEY_MIN_LEN_BYTE)
173         throw runtime::Exception("Size of key smaller than minimum 32B");
174     BinaryData sanitized(key);
175     sanitized.resize(DM_KEY_MIN_LEN_BYTE);
176     return sanitized;
177 }
178
179 const std::string createCryptoBlkDev(const std::string &realBlkDev,
180                                      const std::string &mountName,
181                                      const BinaryData &key,
182                                      std::string cryptoTypeName)
183 {
184     auto blockCount = getBlockCount(realBlkDev);
185     std::string cryptoBlkDev;
186     int fd = -1;
187
188     /*
189      * dmBuf         |-------------------------------------------------| DM_MAX_BUFFER_SIZE(4096)
190      */
191     char dmBuf[DM_MAX_BUFFER_SIZE]; // first: for dm_io, dm_ts
192
193     // Open dm control IOCTL
194     if ((fd = open("/dev/mapper/control", O_RDWR)) < 0) {
195         throw runtime::Exception("Cannot open device-mapper");
196     }
197
198     std::unique_ptr<int, void(*)(int*)> fdPtr(&fd, [](int* fd){ if (fd) ::close(*fd); });
199
200     /*
201      * dmBuf         |-------------------------------------------------| DM_MAX_BUFFER_SIZE(4096)
202      * dmIo          |----------| size of dm_ioctl
203      */
204     auto dmIo = (struct dm_ioctl *)dmBuf;
205
206     // Create Device (mount_name)
207     initDMIoctl(dmBuf, DM_MAX_BUFFER_SIZE, mountName, 0);
208     if (ioctl(fd, DM_DEV_CREATE, dmBuf) && errno != EBUSY) {
209         throw runtime::Exception("Cannot create dm-crypt device");
210     }
211
212     // Get the device status, in particular, the mount_name of it's device file
213     initDMIoctl(dmBuf, DM_MAX_BUFFER_SIZE, mountName, 0);
214     if (ioctl(fd, DM_DEV_STATUS, dmBuf)) {
215         throw runtime::Exception("Cannot retrieve dm-crypt device status");
216     }
217
218     // Store created device into crypto_blkdev
219     unsigned int dmMinor = (dmIo->dev & 0xff) | ((dmIo->dev >> 12) & 0xfff00);
220     cryptoBlkDev = "/dev/dm-" + std::to_string(dmMinor);
221
222     /*
223      * dmBuf         |-------------------------------------------------| DM_MAX_BUFFER_SIZE(4096)
224      * dmIo          |----------| size of dm_ioctl
225      * dmTs                      |--------------| size of dm_target_spec
226      */
227         auto dmTs = (struct dm_target_spec *)(dmBuf + sizeof(struct dm_ioctl));
228
229     // Load the mapping table for this device
230
231     // Force clean-up whole dm_buffer
232     initDMIoctl(dmBuf, DM_MAX_BUFFER_SIZE, mountName, 0);
233     dmIo->target_count = 1;
234     dmTs->status = 0;
235     dmTs->sector_start = 0;
236     dmTs->length = blockCount;
237     ::memset(dmTs->target_type, 0, sizeof(dmTs->target_type));
238     ::strncpy(dmTs->target_type, "crypt", sizeof(dmTs->target_type) - 1);
239
240     /*
241      * dmBuf         |-------------------------------------------------| DM_MAX_BUFFER_SIZE(4096)
242      * dmIo          |----------| size of dm_ioctl
243      * dmTs                      |--------------| size of dm_target_spec
244      * cryptParams                               |---------------------|
245      */
246     char *cryptParams = dmBuf + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
247
248     // Store cryptParams
249     size_t cryptParamsSize = DM_MAX_BUFFER_SIZE - (cryptParams - dmBuf);
250     std::string keyHex = convertToHex(key);
251     int ret = snprintf(cryptParams,
252                        cryptParamsSize,
253                        "%s %s 0 %s 0",
254                        cryptoTypeName.c_str(),
255                        keyHex.c_str(),
256                        realBlkDev.c_str());
257     if (ret < 0) {
258         throw runtime::Exception("snprintf() failed");
259     }
260     if (static_cast<size_t>(ret) >= cryptParamsSize) {
261         throw runtime::Exception("Crypto params didn't fit the device mapper buffer");
262     }
263
264         cryptParams += strlen(cryptParams) + 1;
265     // Align to an 8 byte boundary
266     cryptParams = (char *)(((unsigned long)cryptParams + 7) & ~8);
267     dmTs->next = cryptParams - dmBuf;
268
269     // Table load
270     if (ioctl(fd, DM_TABLE_LOAD, dmBuf) < 0) {
271         throw runtime::Exception("Cannot load dm-crypt mapping table.");
272     }
273
274     // Resume this device to activate it
275     initDMIoctl(dmBuf, DM_MAX_BUFFER_SIZE, mountName, 0);
276     if (ioctl(fd, DM_DEV_SUSPEND, dmBuf)) {
277         throw runtime::Exception("Cannot resume the dm-crypt device");
278     }
279
280     return cryptoBlkDev;
281 }
282
283 void destroyCryptoBlkDev(const std::string &cryptoBlkDev)
284 {
285     char buf[DM_MAX_BUFFER_SIZE];
286     int fd, ret;
287
288     if ((fd = open("/dev/mapper/control", O_RDWR)) < 0)
289         throw runtime::Exception("Cannot open device-mapper");
290
291     initDMIoctl(buf, sizeof(buf), cryptoBlkDev, 0);
292     ret = ioctl(fd, DM_DEV_REMOVE, buf);
293     int err = errno;
294     close(fd);
295
296     if (ret != 0 && err != ENXIO) {
297         throw runtime::Exception("Cannot remove dm-crypt device");
298     }
299 }
300
301 void DMCryptMount(const std::string &source, const std::string &destination, const BinaryData &key, unsigned int options)
302 {
303     // create crypto type device mapping layer to mount the encrypted partition.
304     auto cryptoBlkDev = createCryptoBlkDev(source, DM_DEFAULT_LABEL_NAME, sanitizeKey(key), DM_DEFAULT_CRYPTO_NAME);
305
306     if (::mount(cryptoBlkDev.c_str(), destination.c_str(), "ext4", 0, 0) < 0)
307         throw runtime::Exception(runtime::GetSystemErrorMessage());
308 }
309
310 void DMCryptUmount(const std::string &destination)
311 {
312     if (::umount(destination.c_str()) && errno != EINVAL && errno != ENOENT)
313         throw runtime::Exception(runtime::GetSystemErrorMessage());
314
315     destroyCryptoBlkDev(DM_DEFAULT_LABEL_NAME);
316 }
317 //Hot fix - end
318
319 } // namespace ode
320
321 namespace {
322
323 const std::string MOUNT = "mount";
324 const std::string UMOUNT = "umount";
325 const std::string REMOVE = "remove";
326
327 void usage()
328 {
329         std::cout <<
330                 "Usage: ode-fota [Operation]" << std::endl <<
331                 std::endl <<
332                 "Operations :" << std::endl <<
333                 "  mount [path]   Mount internal memory using stored master key" << std::endl <<
334                 "  umount [path]  Unmount internal memory" << std::endl <<
335                 "  remove         Remove stored internal memory master key" << std::endl;
336 }
337
338 } // anonymous namespace
339
340 int main(int argc, char* argv[])
341 {
342         try {
343                 using namespace ode;
344
345                 if (argc < 2 || argc > 3) {
346                         usage();
347                         return EXIT_FAILURE;
348                 }
349
350                 MmcDevice dev(0);
351                 std::string devpath = dev.findNode("user");
352
353                 if (MOUNT == argv[1]) {
354                         auto masterKey = UpgradeSupport::loadMasterKey(devpath);
355                         std::string path = INTERNAL_PATH;
356                         if (argc == 3)
357                                 path = argv[2];
358
359                         // mount options are ignored by mount()
360                         DMCryptMount(devpath, path, masterKey, 0);
361                         UpgradeSupport::createUpgradeFlag();
362                 } else if (UMOUNT == argv[1]) {
363                         std::string path = INTERNAL_PATH;
364                         if (argc == 3)
365                                 path = argv[2];
366
367                         DMCryptUmount(path);
368                 } else if (REMOVE == argv[1]) {
369                         UpgradeSupport::removeMasterKey(devpath);
370                 } else {
371                         usage();
372                         return EXIT_FAILURE;
373                 }
374                 return EXIT_SUCCESS;
375         } catch (const std::exception& e) {
376                 std::cerr << e.what() << std::endl;
377         } catch (...) {
378                 std::cerr << "Unknown exception occured" << std::endl;
379         }
380
381         return EXIT_FAILURE;
382 }