cryptsetup - Fix uuid bug, cleanup
[dragonfly.git] / contrib / cryptsetup / lib / libdevmapper.c
CommitLineData
bf114f1d
AH
1#include <sys/ioctl.h>
2#include <sys/stat.h>
3#include <stdio.h>
13af8cbe 4#include <string.h>
bf114f1d
AH
5#include <dirent.h>
6#include <errno.h>
7#include <libdevmapper.h>
8#include <fcntl.h>
13af8cbe 9#include <uuid.h>
bf114f1d
AH
10
11#include "internal.h"
12#include "luks.h"
13
14#define DEVICE_DIR "/dev"
15#define DM_UUID_LEN 129
16#define DM_UUID_PREFIX "CRYPT-"
17#define DM_UUID_PREFIX_LEN 6
18#define DM_CRYPT_TARGET "crypt"
19#define RETRY_COUNT 5
20
21/* Set if dm-crypt version was probed */
22static int _dm_crypt_checked = 0;
23static int _dm_crypt_wipe_key_supported = 0;
24
25static int _dm_use_count = 0;
26static struct crypt_device *_context = NULL;
27
28/* Compatibility for old device-mapper without udev support */
29#ifndef HAVE_DM_TASK_SET_COOKIE
30#define CRYPT_TEMP_UDEV_FLAGS 0
bf114f1d
AH
31#else
32#define CRYPT_TEMP_UDEV_FLAGS DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG | \
33 DM_UDEV_DISABLE_DISK_RULES_FLAG | \
34 DM_UDEV_DISABLE_OTHER_RULES_FLAG
35#endif
36
37static int _dm_use_udev()
38{
39#ifdef USE_UDEV /* cannot be enabled if devmapper is too old */
40 return dm_udev_get_sync_support();
41#else
42 return 0;
43#endif
44}
45
46static void set_dm_error(int level, const char *file, int line,
47 const char *f, ...)
48{
49 char *msg = NULL;
50 va_list va;
51
52 va_start(va, f);
53 if (vasprintf(&msg, f, va) > 0) {
54 if (level < 4) {
55 log_err(_context, msg);
56 log_err(_context, "\n");
57 } else
58 log_dbg(msg);
59 }
60 free(msg);
61 va_end(va);
62}
63
64static int _dm_simple(int task, const char *name, int udev_wait);
65
66static void _dm_set_crypt_compat(int maj, int min, int patch)
67{
68 log_dbg("Detected dm-crypt target of version %i.%i.%i.", maj, min, patch);
69
70 if (maj >= 1 && min >=2)
71 _dm_crypt_wipe_key_supported = 1;
72 else
73 log_dbg("Suspend and resume disabled, no wipe key support.");
74
75 _dm_crypt_checked = 1;
76}
77
78static int _dm_check_versions(void)
79{
80 struct dm_task *dmt;
81 struct dm_versions *target, *last_target;
82
83 if (_dm_crypt_checked)
84 return 1;
85
86 if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
87 return 0;
88
89 if (!dm_task_run(dmt)) {
90 dm_task_destroy(dmt);
91 return 0;
92 }
93
94 target = dm_task_get_versions(dmt);
95 do {
96 last_target = target;
97 if (!strcmp(DM_CRYPT_TARGET, target->name)) {
98 _dm_set_crypt_compat((int)target->version[0],
99 (int)target->version[1],
100 (int)target->version[2]);
101 }
102 target = (void *) target + target->next;
103 } while (last_target != target);
104
105 dm_task_destroy(dmt);
106 return 1;
107}
108
109int dm_init(struct crypt_device *context, int check_kernel)
110{
111 if (!_dm_use_count++) {
112 log_dbg("Initialising device-mapper backend%s, UDEV is %sabled.",
113 check_kernel ? "" : " (NO kernel check requested)",
114 _dm_use_udev() ? "en" : "dis");
115 if (check_kernel && !_dm_check_versions()) {
116 log_err(context, _("Cannot initialize device-mapper. Is dm_mod kernel module loaded?\n"));
117 return -1;
118 }
119 if (getuid() || geteuid())
120 log_dbg(("WARNING: Running as a non-root user. Functionality may be unavailable."));
121 dm_log_init(set_dm_error);
122 dm_log_init_verbose(10);
123 }
124
125 // FIXME: global context is not safe
126 if (context)
127 _context = context;
128
129 return 1; /* unsafe memory */
130}
131
132void dm_exit(void)
133{
134 if (_dm_use_count && (!--_dm_use_count)) {
135 log_dbg("Releasing device-mapper backend.");
136 dm_log_init_verbose(0);
137 dm_log_init(NULL);
138 dm_lib_release();
139 _context = NULL;
140 }
141}
142
143static char *__lookup_dev(char *path, dev_t dev, int dir_level, const int max_level)
144{
145 struct dirent *entry;
146 struct stat st;
147 char *ptr;
148 char *result = NULL;
149 DIR *dir;
150 int space;
151
152 /* Ignore strange nested directories */
153 if (dir_level > max_level)
154 return NULL;
155
156 path[PATH_MAX - 1] = '\0';
157 ptr = path + strlen(path);
158 *ptr++ = '/';
159 *ptr = '\0';
160 space = PATH_MAX - (ptr - path);
161
162 dir = opendir(path);
163 if (!dir)
164 return NULL;
165
166 while((entry = readdir(dir))) {
167 if (entry->d_name[0] == '.' ||
168 !strncmp(entry->d_name, "..", 2))
169 continue;
170
171 strncpy(ptr, entry->d_name, space);
172 if (stat(path, &st) < 0)
173 continue;
174
175 if (S_ISDIR(st.st_mode)) {
176 result = __lookup_dev(path, dev, dir_level + 1, max_level);
177 if (result)
178 break;
179 } else if (S_ISBLK(st.st_mode)) {
180 /* workaround: ignore dm-X devices, these are internal kernel names */
181 if (dir_level == 0 && !strncmp(entry->d_name, "dm-", 3))
182 continue;
183 if (st.st_rdev == dev) {
184 result = strdup(path);
185 break;
186 }
187 }
188 }
189
190 closedir(dir);
191 return result;
192}
193
194static char *lookup_dev(const char *dev_id)
195{
196 uint32_t major, minor;
197 dev_t dev;
198 char *result = NULL, buf[PATH_MAX + 1];
199
200 if (sscanf(dev_id, "%" PRIu32 ":%" PRIu32, &major, &minor) != 2)
201 return NULL;
202
203 dev = makedev(major, minor);
204 strncpy(buf, DEVICE_DIR, PATH_MAX);
205 buf[PATH_MAX] = '\0';
206
207 /* First try low level device */
208 if ((result = __lookup_dev(buf, dev, 0, 0)))
209 return result;
210
211 /* If it is dm, try DM dir */
212 if (dm_is_dm_major(major)) {
213 strncpy(buf, dm_dir(), PATH_MAX);
214 if ((result = __lookup_dev(buf, dev, 0, 0)))
215 return result;
216 }
217
218 strncpy(buf, DEVICE_DIR, PATH_MAX);
219 result = __lookup_dev(buf, dev, 0, 4);
220
221 /* If not found, return NULL */
222 return result;
223}
224
225static int _dev_read_ahead(const char *dev, uint32_t *read_ahead)
226{
227 int fd, r = 0;
228 long read_ahead_long;
229
230 if ((fd = open(dev, O_RDONLY)) < 0)
231 return 0;
232
13af8cbe
AH
233 r = 0;
234 //r = ioctl(fd, BLKRAGET, &read_ahead_long) ? 0 : 1;
bf114f1d
AH
235 close(fd);
236
237 if (r)
238 *read_ahead = (uint32_t) read_ahead_long;
239
240 return r;
241}
242
243static void hex_key(char *hexkey, size_t key_size, const char *key)
244{
245 int i;
246
247 for(i = 0; i < key_size; i++)
248 sprintf(&hexkey[i * 2], "%02x", (unsigned char)key[i]);
249}
250
251static char *get_params(const char *device, uint64_t skip, uint64_t offset,
252 const char *cipher, size_t key_size, const char *key)
253{
254 char *params;
255 char *hexkey;
256
257 hexkey = safe_alloc(key_size * 2 + 1);
258 if (!hexkey)
259 return NULL;
260
261 hex_key(hexkey, key_size, key);
262
263 params = safe_alloc(strlen(hexkey) + strlen(cipher) + strlen(device) + 64);
264 if (!params)
265 goto out;
266
267 sprintf(params, "%s %s %" PRIu64 " %s %" PRIu64,
268 cipher, hexkey, skip, device, offset);
269
270out:
271 safe_free(hexkey);
272 return params;
273}
274
275/* DM helpers */
276static int _dm_simple(int task, const char *name, int udev_wait)
277{
278 int r = 0;
279 struct dm_task *dmt;
280 uint32_t cookie = 0;
281
282 if (!_dm_use_udev())
283 udev_wait = 0;
284
285 if (!(dmt = dm_task_create(task)))
286 return 0;
287
288 if (name && !dm_task_set_name(dmt, name))
289 goto out;
290
291 if (udev_wait && !dm_task_set_cookie(dmt, &cookie, 0))
292 goto out;
293
294 r = dm_task_run(dmt);
295
296 if (udev_wait)
297 (void)dm_udev_wait(cookie);
298
299 out:
300 dm_task_destroy(dmt);
301 return r;
302}
303
304static int _error_device(const char *name, size_t size)
305{
306 struct dm_task *dmt;
307 int r = 0;
308
309 if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
310 return 0;
311
312 if (!dm_task_set_name(dmt, name))
313 goto error;
314
315 if (!dm_task_add_target(dmt, UINT64_C(0), size, "error", ""))
316 goto error;
317
318 if (!dm_task_set_ro(dmt))
319 goto error;
320
321 if (!dm_task_no_open_count(dmt))
322 goto error;
323
324 if (!dm_task_run(dmt))
325 goto error;
326
327 if (!_dm_simple(DM_DEVICE_RESUME, name, 1)) {
328 _dm_simple(DM_DEVICE_CLEAR, name, 0);
329 goto error;
330 }
331
332 r = 1;
333
334error:
335 dm_task_destroy(dmt);
336 return r;
337}
338
339int dm_remove_device(const char *name, int force, uint64_t size)
340{
341 int r = -EINVAL;
342 int retries = force ? RETRY_COUNT : 1;
343 int error_target = 0;
344
345 if (!name || (force && !size))
346 return -EINVAL;
347
348 do {
349 r = _dm_simple(DM_DEVICE_REMOVE, name, 1) ? 0 : -EINVAL;
350 if (--retries && r) {
351 log_dbg("WARNING: other process locked internal device %s, %s.",
352 name, retries ? "retrying remove" : "giving up");
353 if (force && (crypt_get_debug_level() == CRYPT_LOG_DEBUG))
354 debug_processes_using_device(name);
355 sleep(1);
356 if (force && !error_target) {
357 /* If force flag is set, replace device with error, read-only target.
358 * it should stop processes from reading it and also removed underlying
359 * device from mapping, so it is usable again.
360 * Force flag should be used only for temporary devices, which are
361 * intended to work inside cryptsetup only!
362 * Anyway, if some process try to read temporary cryptsetup device,
363 * it is bug - no other process should try touch it (e.g. udev).
364 */
365 _error_device(name, size);
366 error_target = 1;
367 }
368 }
369 } while (r == -EINVAL && retries);
370
371 dm_task_update_nodes();
372
373 return r;
374}
375
376#define UUID_LEN 37 /* 36 + \0, libuuid ... */
377/*
378 * UUID has format: CRYPT-<devicetype>-[<uuid>-]<device name>
379 * CRYPT-PLAIN-name
380 * CRYPT-LUKS1-00000000000000000000000000000000-name
381 * CRYPT-TEMP-name
382 */
383static void dm_prepare_uuid(const char *name, const char *type, const char *uuid, char *buf, size_t buflen)
384{
385 char *ptr, uuid2[UUID_LEN] = {0};
386 uuid_t uu;
387 int i = 0;
13af8cbe 388 uint32_t ret;
bf114f1d
AH
389
390 /* Remove '-' chars */
7422a208
AH
391 if (uuid) {
392 uuid_from_string(uuid, &uu, &ret);
393 if (ret != uuid_s_ok) {
0482a033 394 printf("error in uuid_from_string(%s), err = %d\n", uuid, ret);
7422a208
AH
395 for (ptr = uuid2, i = 0; i < UUID_LEN; i++) {
396 if (uuid[i] != '-') {
397 *ptr = uuid[i];
398 ptr++;
399 }
bf114f1d 400 }
7422a208 401 }
bf114f1d
AH
402 }
403
404 i = snprintf(buf, buflen, DM_UUID_PREFIX "%s%s%s%s%s",
405 type ?: "", type ? "-" : "",
406 uuid2[0] ? uuid2 : "", uuid2[0] ? "-" : "",
407 name);
408
409 log_dbg("DM-UUID is %s", buf);
410 if (i >= buflen)
411 log_err(NULL, _("DM-UUID for device %s was truncated.\n"), name);
412}
413
414int dm_create_device(const char *name,
415 const char *device,
416 const char *cipher,
417 const char *type,
418 const char *uuid,
419 uint64_t size,
420 uint64_t skip,
421 uint64_t offset,
422 size_t key_size,
423 const char *key,
424 int read_only,
425 int reload)
426{
427 struct dm_task *dmt = NULL;
428 struct dm_info dmi;
429 char *params = NULL;
430 char *error = NULL;
431 char dev_uuid[DM_UUID_LEN] = {0};
432 int r = -EINVAL;
433 uint32_t read_ahead = 0;
434 uint32_t cookie = 0;
435 uint16_t udev_flags = 0;
436
437 params = get_params(device, skip, offset, cipher, key_size, key);
438 if (!params)
439 goto out_no_removal;
440
441 if (type && !strncmp(type, "TEMP", 4))
442 udev_flags = CRYPT_TEMP_UDEV_FLAGS;
443
444 /* All devices must have DM_UUID, only resize on old device is exception */
445 if (reload) {
446 if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
447 goto out_no_removal;
448
449 if (!dm_task_set_name(dmt, name))
450 goto out_no_removal;
451 } else {
452 dm_prepare_uuid(name, type, uuid, dev_uuid, sizeof(dev_uuid));
453
454 if (!(dmt = dm_task_create(DM_DEVICE_CREATE)))
455 goto out_no_removal;
456
457 if (!dm_task_set_name(dmt, name))
458 goto out_no_removal;
459
460 if (!dm_task_set_uuid(dmt, dev_uuid))
461 goto out_no_removal;
462
463 if (_dm_use_udev() && !dm_task_set_cookie(dmt, &cookie, udev_flags))
464 goto out_no_removal;
465 }
466
467 if (read_only && !dm_task_set_ro(dmt))
468 goto out_no_removal;
469 if (!dm_task_add_target(dmt, 0, size, DM_CRYPT_TARGET, params))
470 goto out_no_removal;
471
472#ifdef DM_READ_AHEAD_MINIMUM_FLAG
473 if (_dev_read_ahead(device, &read_ahead) &&
474 !dm_task_set_read_ahead(dmt, read_ahead, DM_READ_AHEAD_MINIMUM_FLAG))
475 goto out_no_removal;
476#endif
477
478 if (!dm_task_run(dmt))
479 goto out_no_removal;
480
481 if (reload) {
482 dm_task_destroy(dmt);
483 if (!(dmt = dm_task_create(DM_DEVICE_RESUME)))
484 goto out;
485 if (!dm_task_set_name(dmt, name))
486 goto out;
487 if (uuid && !dm_task_set_uuid(dmt, dev_uuid))
488 goto out;
489 if (_dm_use_udev() && !dm_task_set_cookie(dmt, &cookie, udev_flags))
490 goto out;
491 if (!dm_task_run(dmt))
492 goto out;
493 }
494
495 if (!dm_task_get_info(dmt, &dmi))
496 goto out;
497
498 r = 0;
499out:
500 if (_dm_use_udev()) {
501 (void)dm_udev_wait(cookie);
502 cookie = 0;
503 }
504
505 if (r < 0 && !reload) {
506 if (get_error())
507 error = strdup(get_error());
508
509 dm_remove_device(name, 0, 0);
510
511 if (error) {
512 set_error(error);
513 free(error);
514 }
515 }
516
517out_no_removal:
518 if (cookie && _dm_use_udev())
519 (void)dm_udev_wait(cookie);
520
521 if (params)
522 safe_free(params);
523 if (dmt)
524 dm_task_destroy(dmt);
525
526 dm_task_update_nodes();
527 return r;
528}
529
530int dm_status_device(const char *name)
531{
532 struct dm_task *dmt;
533 struct dm_info dmi;
534 uint64_t start, length;
535 char *target_type, *params;
536 void *next = NULL;
537 int r = -EINVAL;
538
539 if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
540 return -EINVAL;
541
542 if (!dm_task_set_name(dmt, name)) {
543 r = -EINVAL;
544 goto out;
545 }
546
547 if (!dm_task_run(dmt)) {
548 r = -EINVAL;
549 goto out;
550 }
551
552 if (!dm_task_get_info(dmt, &dmi)) {
553 r = -EINVAL;
554 goto out;
555 }
556
557 if (!dmi.exists) {
558 r = -ENODEV;
559 goto out;
560 }
561
562 next = dm_get_next_target(dmt, next, &start, &length,
563 &target_type, &params);
564 if (!target_type || strcmp(target_type, DM_CRYPT_TARGET) != 0 ||
565 start != 0 || next)
566 r = -EINVAL;
567 else
568 r = (dmi.open_count > 0);
569out:
570 if (dmt)
571 dm_task_destroy(dmt);
572
573 return r;
574}
575
576int dm_query_device(const char *name,
577 char **device,
578 uint64_t *size,
579 uint64_t *skip,
580 uint64_t *offset,
581 char **cipher,
582 int *key_size,
583 char **key,
584 int *read_only,
585 int *suspended,
586 char **uuid)
587{
588 struct dm_task *dmt;
589 struct dm_info dmi;
590 uint64_t start, length, val64;
591 char *target_type, *params, *rcipher, *key_, *rdevice, *endp, buffer[3], *tmp_uuid;
592 void *next = NULL;
593 int i, r = -EINVAL;
594
595 if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
596 goto out;
597 if (!dm_task_set_name(dmt, name))
598 goto out;
599 r = -ENODEV;
600 if (!dm_task_run(dmt))
601 goto out;
602
603 r = -EINVAL;
604 if (!dm_task_get_info(dmt, &dmi))
605 goto out;
606
607 if (!dmi.exists) {
608 r = -ENODEV;
609 goto out;
610 }
611
612 next = dm_get_next_target(dmt, next, &start, &length,
613 &target_type, &params);
614 if (!target_type || strcmp(target_type, DM_CRYPT_TARGET) != 0 ||
615 start != 0 || next)
616 goto out;
617
618 if (size)
619 *size = length;
620
621 rcipher = strsep(&params, " ");
622 /* cipher */
623 if (cipher)
624 *cipher = strdup(rcipher);
625
626 /* skip */
627 key_ = strsep(&params, " ");
628 if (!params)
629 goto out;
630 val64 = strtoull(params, &params, 10);
631 if (*params != ' ')
632 goto out;
633 params++;
634 if (skip)
635 *skip = val64;
636
637 /* device */
638 rdevice = strsep(&params, " ");
639 if (device)
640 *device = lookup_dev(rdevice);
641
642 /*offset */
643 if (!params)
644 goto out;
645 val64 = strtoull(params, &params, 10);
646 if (*params)
647 goto out;
648 if (offset)
649 *offset = val64;
650
651 /* key_size */
652 if (key_size)
653 *key_size = strlen(key_) / 2;
654
655 /* key */
656 if (key_size && key) {
657 *key = safe_alloc(*key_size);
658 if (!*key) {
659 r = -ENOMEM;
660 goto out;
661 }
662
663 buffer[2] = '\0';
664 for(i = 0; i < *key_size; i++) {
665 memcpy(buffer, &key_[i * 2], 2);
666 (*key)[i] = strtoul(buffer, &endp, 16);
667 if (endp != &buffer[2]) {
668 safe_free(key);
669 *key = NULL;
670 goto out;
671 }
672 }
673 }
674 memset(key_, 0, strlen(key_));
675
676 if (read_only)
677 *read_only = dmi.read_only;
678
679 if (suspended)
680 *suspended = dmi.suspended;
681
682 if (uuid && (tmp_uuid = (char*)dm_task_get_uuid(dmt)) &&
683 !strncmp(tmp_uuid, DM_UUID_PREFIX, DM_UUID_PREFIX_LEN))
684 *uuid = strdup(tmp_uuid + DM_UUID_PREFIX_LEN);
685
686 r = (dmi.open_count > 0);
687out:
688 if (dmt)
689 dm_task_destroy(dmt);
690
691 return r;
692}
693
694static int _dm_message(const char *name, const char *msg)
695{
696 int r = 0;
697 struct dm_task *dmt;
698
699 if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
700 return 0;
701
702 if (name && !dm_task_set_name(dmt, name))
703 goto out;
704
705 if (!dm_task_set_sector(dmt, (uint64_t) 0))
706 goto out;
707
708 if (!dm_task_set_message(dmt, msg))
709 goto out;
710
711 r = dm_task_run(dmt);
712
713 out:
714 dm_task_destroy(dmt);
715 return r;
716}
717
718int dm_suspend_and_wipe_key(const char *name)
719{
720 if (!_dm_check_versions())
721 return -ENOTSUP;
722
723 if (!_dm_crypt_wipe_key_supported)
724 return -ENOTSUP;
725
726 if (!_dm_simple(DM_DEVICE_SUSPEND, name, 0))
727 return -EINVAL;
728
729 if (!_dm_message(name, "key wipe")) {
730 _dm_simple(DM_DEVICE_RESUME, name, 1);
731 return -EINVAL;
732 }
733
734 return 0;
735}
736
737int dm_resume_and_reinstate_key(const char *name,
738 size_t key_size,
739 const char *key)
740{
741 int msg_size = key_size * 2 + 10; // key set <key>
742 char *msg;
743 int r = 0;
744
745 if (!_dm_check_versions())
746 return -ENOTSUP;
747
748 if (!_dm_crypt_wipe_key_supported)
749 return -ENOTSUP;
750
751 msg = safe_alloc(msg_size);
752 if (!msg)
753 return -ENOMEM;
754
755 memset(msg, 0, msg_size);
756 strcpy(msg, "key set ");
757 hex_key(&msg[8], key_size, key);
758
759 if (!_dm_message(name, msg) ||
760 !_dm_simple(DM_DEVICE_RESUME, name, 1))
761 r = -EINVAL;
762
763 safe_free(msg);
764 return r;
765}
766
767const char *dm_get_dir(void)
768{
769 return dm_dir();
770}