Check device size when loading LUKS header.
[platform/upstream/cryptsetup.git] / lib / libdevmapper.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <stdint.h>
5 #include <stdarg.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/sysmacros.h>
9 #include <unistd.h>
10 #include <dirent.h>
11 #include <errno.h>
12 #include <libdevmapper.h>
13
14 #include "libcryptsetup.h"
15 #include "internal.h"
16
17 #define DEVICE_DIR      "/dev"
18
19 #define CRYPT_TARGET    "crypt"
20 #define RETRY_COUNT     5
21
22 static void set_dm_error(int level, const char *file, int line,
23                          const char *f, ...)
24 {
25         va_list va;
26
27         if (level > 3)
28                 return;
29
30         va_start(va, f);
31         set_error_va(f, va);
32         va_end(va);
33 }
34
35 static int _dm_simple(int task, const char *name);
36
37 static int dm_init(void)
38 {
39         dm_log_init(set_dm_error);
40         if (!_dm_simple(DM_DEVICE_LIST_VERSIONS, "test")) {
41                 set_error("Cannot communicate with device-mapper. Is the dm_mod module loaded?");
42                 return -1;
43         }
44
45         return 1;       /* unsafe memory */
46 }
47
48 static void dm_exit(void)
49 {
50         dm_log_init(NULL);
51         dm_lib_release();
52 }
53
54 static char *__lookup_dev(char *path, dev_t dev)
55 {
56         struct dirent *entry;
57         struct stat st;
58         char *ptr;
59         char *result = NULL;
60         DIR *dir;
61         int space;
62
63         path[PATH_MAX - 1] = '\0';
64         ptr = path + strlen(path);
65         *ptr++ = '/';
66         *ptr = '\0';
67         space = PATH_MAX - (ptr - path);
68
69         dir = opendir(path);
70         if (!dir)
71                 return NULL;
72
73         while((entry = readdir(dir))) {
74                 if (entry->d_name[0] == '.' &&
75                     (entry->d_name[1] == '\0' || (entry->d_name[1] == '.' &&
76                                                   entry->d_name[2] == '\0')))
77                         continue;
78
79                 strncpy(ptr, entry->d_name, space);
80                 if (lstat(path, &st) < 0)
81                         continue;
82
83                 if (S_ISDIR(st.st_mode)) {
84                         result = __lookup_dev(path, dev);
85                         if (result)
86                                 break;
87                 } else if (S_ISBLK(st.st_mode)) {
88                         if (st.st_rdev == dev) {
89                                 result = strdup(path);
90                                 break;
91                         }
92                 }
93         }
94
95         closedir(dir);
96
97         return result;
98 }
99
100 static char *lookup_dev(const char *dev)
101 {
102         uint32_t major, minor;
103         char buf[PATH_MAX + 1];
104
105         if (sscanf(dev, "%" PRIu32 ":%" PRIu32, &major, &minor) != 2)
106                 return NULL;
107
108         strncpy(buf, DEVICE_DIR, PATH_MAX);
109         buf[PATH_MAX] = '\0';
110
111         return __lookup_dev(buf, makedev(major, minor));
112 }
113
114 static char *get_params(struct crypt_options *options, const char *key)
115 {
116         char *params;
117         char *hexkey;
118         int i;
119
120         hexkey = safe_alloc(options->key_size * 2 + 1);
121         if (!hexkey) {
122                 set_error("Memory allocation problem");
123                 return NULL;
124         }
125
126         for(i = 0; i < options->key_size; i++)
127                 sprintf(&hexkey[i * 2], "%02x", (unsigned char)key[i]);
128
129         params = safe_alloc(strlen(hexkey) + strlen(options->cipher) +
130                             strlen(options->device) + 64);
131         if (!params) {
132                 set_error("Memory allocation problem");
133                 goto out;
134         }
135
136         sprintf(params, "%s %s %" PRIu64 " %s %" PRIu64,
137                 options->cipher, hexkey, options->skip,
138                 options->device, options->offset);
139
140 out:
141         safe_free(hexkey);
142
143         return params;
144 }
145
146 /* DM helpers */
147 static int _dm_simple(int task, const char *name)
148 {
149         int r = 0;
150         struct dm_task *dmt;
151
152         if (!(dmt = dm_task_create(task)))
153                 return 0;
154
155         if (!dm_task_set_name(dmt, name))
156                 goto out;
157
158         r = dm_task_run(dmt);
159
160       out:
161         dm_task_destroy(dmt);
162         return r;
163 }
164
165 static int _error_device(struct crypt_options *options)
166 {
167         struct dm_task *dmt;
168         int r = 0;
169
170         if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
171                 return 0;
172
173         if (!dm_task_set_name(dmt, options->name))
174                 goto error;
175
176         if (!dm_task_add_target(dmt, UINT64_C(0), options->size, "error", ""))
177                 goto error;
178
179         if (!dm_task_set_ro(dmt))
180                 goto error;
181
182         if (!dm_task_no_open_count(dmt))
183                 goto error;
184
185         if (!dm_task_run(dmt))
186                 goto error;
187
188         if (!_dm_simple(DM_DEVICE_RESUME, options->name)) {
189                 _dm_simple(DM_DEVICE_CLEAR, options->name);
190                 goto error;
191         }
192
193         r = 1;
194
195 error:
196         dm_task_destroy(dmt);
197         return r;
198 }
199
200 static int _dm_remove(struct crypt_options *options, int force)
201 {
202         int r = -EINVAL;
203         int retries = force ? RETRY_COUNT : 1;
204
205         /* If force flag is set, replace device with error, read-only target.
206          * it should stop processes from reading it and also removed underlying
207          * device from mapping, so it is usable again.
208          * Force flag should be used only for temporary devices, which are
209          * intended to work inside cryptsetup only!
210          * Anyway, if some process try to read temporary cryptsetup device,
211          * it is bug - no other process should try touch it (e.g. udev).
212          */
213         if (force) {
214                  _error_device(options);
215                 retries = RETRY_COUNT;
216         }
217
218         do {
219                 r = _dm_simple(DM_DEVICE_REMOVE, options->name) ? 0 : -EINVAL;
220                 if (--retries)
221                         sleep(1);
222         } while (r == -EINVAL && retries);
223
224         dm_task_update_nodes();
225
226         return r;
227 }
228
229 static int dm_create_device(int reload, struct crypt_options *options,
230                             const char *key)
231 {
232         struct dm_task *dmt = NULL;
233         struct dm_task *dmt_query = NULL;
234         struct dm_info dmi;
235         char *params = NULL;
236         char *error = NULL;
237         int r = -EINVAL;
238
239         params = get_params(options, key);
240         if (!params)
241                 goto out_no_removal;
242         if (!(dmt = dm_task_create(reload ? DM_DEVICE_RELOAD
243                                           : DM_DEVICE_CREATE)))
244                 goto out;
245         if (!dm_task_set_name(dmt, options->name))
246                 goto out;
247         if (options->flags & CRYPT_FLAG_READONLY && !dm_task_set_ro(dmt))
248                 goto out;
249         if (!dm_task_add_target(dmt, 0, options->size, CRYPT_TARGET, params))
250                 goto out;
251         if (!dm_task_run(dmt))
252                 goto out;
253
254         if (reload) {
255                 dm_task_destroy(dmt);
256                 if (!(dmt = dm_task_create(DM_DEVICE_RESUME)))
257                         goto out;
258                 if (!dm_task_set_name(dmt, options->name))
259                         goto out;
260                 if (!dm_task_run(dmt))
261                         goto out;
262         }
263
264         if (!dm_task_get_info(dmt, &dmi))
265                 goto out;
266         if (dmi.read_only)
267                 options->flags |= CRYPT_FLAG_READONLY;
268
269         r = 0;
270 out:
271         if (r < 0 && !reload) {
272                 if (get_error())
273                         error = strdup(get_error());
274
275                 _dm_remove(options, 0);
276
277                 if (error) {
278                         set_error(error);
279                         free(error);
280                 }
281         }
282
283 out_no_removal:
284         if (params)
285                 safe_free(params);
286         if (dmt)
287                 dm_task_destroy(dmt);
288         if(dmt_query)
289                 dm_task_destroy(dmt_query);
290         dm_task_update_nodes();
291         return r;
292 }
293
294 static int dm_query_device(int details, struct crypt_options *options,
295                            char **key)
296 {
297         struct dm_task *dmt;
298         struct dm_info dmi;
299         uint64_t start, length;
300         char *target_type, *params;
301         void *next = NULL;
302         int r = -EINVAL;
303
304         if (!(dmt = dm_task_create(details ? DM_DEVICE_TABLE
305                                            : DM_DEVICE_STATUS)))
306                 goto out;
307         if (!dm_task_set_name(dmt, options->name))
308                 goto out;
309         r = -ENODEV;
310         if (!dm_task_run(dmt))
311                 goto out;
312
313         r = -EINVAL;
314         if (!dm_task_get_info(dmt, &dmi))
315                 goto out;
316
317         if (!dmi.exists) {
318                 r = -ENODEV;
319                 goto out;
320         }
321
322         next = dm_get_next_target(dmt, next, &start, &length,
323                                   &target_type, &params);
324         if (!target_type || strcmp(target_type, CRYPT_TARGET) != 0 ||
325             start != 0 || next)
326                 goto out;
327
328         options->hash = NULL;
329         options->cipher = NULL;
330         options->offset = 0;
331         options->skip = 0;
332         options->size = length;
333         if (details) {
334                 char *cipher, *key_, *device;
335                 uint64_t val64;
336
337                 set_error("Invalid dm table");
338
339                 cipher = strsep(&params, " ");
340                 key_ = strsep(&params, " ");
341                 if (!params)
342                         goto out;
343
344                 val64 = strtoull(params, &params, 10);
345                 if (*params != ' ')
346                         goto out;
347                 params++;
348                 options->skip = val64;
349
350                 device = strsep(&params, " ");
351                 if (!params)
352                         goto out;
353
354                 val64 = strtoull(params, &params, 10);
355                 if (*params)
356                         goto out;
357                 options->offset = val64;
358
359                 options->cipher = strdup(cipher);
360                 options->key_size = strlen(key_) / 2;
361                 if (key) {
362                         char buffer[3];
363                         char *endp;
364                         int i;
365
366                         *key = safe_alloc(options->key_size);
367                         if (!*key) {
368                                 set_error("Out of memory");
369                                 r = -ENOMEM;
370                                 goto out;
371                         }
372
373                         buffer[2] = '\0';
374                         for(i = 0; i < options->key_size; i++) {
375                                 memcpy(buffer, &key_[i * 2], 2);
376                                 (*key)[i] = strtoul(buffer, &endp, 16);
377                                 if (endp != &buffer[2]) {
378                                         safe_free(key);
379                                         *key = NULL;
380                                         goto out;
381                                 }
382                         }
383                 }
384                 memset(key_, 0, strlen(key_));
385                 options->device = lookup_dev(device);
386
387                 set_error(NULL);
388         }
389
390         r = (dmi.open_count > 0);
391
392 out:
393         if (dmt)
394                 dm_task_destroy(dmt);
395         if (r >= 0) {
396                 if (options->device)
397                         options->flags |= CRYPT_FLAG_FREE_DEVICE;
398                 if (options->cipher)
399                         options->flags |= CRYPT_FLAG_FREE_CIPHER;
400                 options->flags &= ~CRYPT_FLAG_READONLY;
401                 if (dmi.read_only)
402                         options->flags |= CRYPT_FLAG_READONLY;
403         } else {
404                 if (options->device) {
405                         free((char *)options->device);
406                         options->device = NULL;
407                         options->flags &= ~CRYPT_FLAG_FREE_DEVICE;
408                 }
409                 if (options->cipher) {
410                         free((char *)options->cipher);
411                         options->cipher = NULL;
412                         options->flags &= ~CRYPT_FLAG_FREE_CIPHER;
413                 }
414         }
415         return r;
416 }
417
418 static int dm_remove_device(int force, struct crypt_options *options)
419 {
420         if (!options || !options->name)
421                 return -EINVAL;
422
423         return _dm_remove(options, force);;
424 }
425
426
427 static const char *dm_get_dir(void)
428 {
429         return dm_dir();
430 }
431
432 struct setup_backend setup_libdevmapper_backend = {
433         .name = "dm-crypt",
434         .init = dm_init,
435         .exit = dm_exit,
436         .create = dm_create_device,
437         .status = dm_query_device,
438         .remove = dm_remove_device,
439         .dir = dm_get_dir
440 };