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