]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sbin/geom/class/eli/geom_eli.c
Copy stable/8 to releng/8.2 in preparation for FreeBSD-8.2 release.
[FreeBSD/releng/8.2.git] / sbin / geom / class / eli / geom_eli.c
1 /*-
2  * Copyright (c) 2004-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/types.h>
31 #include <sys/sysctl.h>
32
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <stdint.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <readpassphrase.h>
40 #include <string.h>
41 #include <strings.h>
42 #include <libgeom.h>
43 #include <paths.h>
44 #include <errno.h>
45 #include <assert.h>
46
47 #include <sys/param.h>
48 #include <sys/mman.h>
49 #include <sys/resource.h>
50 #include <opencrypto/cryptodev.h>
51 #include <geom/eli/g_eli.h>
52 #include <geom/eli/pkcs5v2.h>
53
54 #include "core/geom.h"
55 #include "misc/subr.h"
56
57
58 uint32_t lib_version = G_LIB_VERSION;
59 uint32_t version = G_ELI_VERSION;
60
61 #define GELI_BACKUP_DIR "/var/backups/"
62 #define GELI_ENC_ALGO   "aes"
63
64 static void eli_main(struct gctl_req *req, unsigned flags);
65 static void eli_init(struct gctl_req *req);
66 static void eli_attach(struct gctl_req *req);
67 static void eli_configure(struct gctl_req *req);
68 static void eli_setkey(struct gctl_req *req);
69 static void eli_delkey(struct gctl_req *req);
70 static void eli_resume(struct gctl_req *req);
71 static void eli_kill(struct gctl_req *req);
72 static void eli_backup(struct gctl_req *req);
73 static void eli_restore(struct gctl_req *req);
74 static void eli_resize(struct gctl_req *req);
75 static void eli_clear(struct gctl_req *req);
76 static void eli_dump(struct gctl_req *req);
77
78 static int eli_backup_create(struct gctl_req *req, const char *prov,
79     const char *file);
80
81 /*
82  * Available commands:
83  *
84  * init [-bhPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] prov
85  * label - alias for 'init'
86  * attach [-dprv] [-j passfile] [-k keyfile] prov
87  * detach [-fl] prov ...
88  * stop - alias for 'detach'
89  * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov
90  * configure [-bB] prov ...
91  * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov
92  * delkey [-afv] [-n keyno] prov
93  * suspend [-v] -a | prov ...
94  * resume [-pv] [-j passfile] [-k keyfile] prov
95  * kill [-av] [prov ...]
96  * backup [-v] prov file
97  * restore [-fv] file prov
98  * resize [-v] -s oldsize prov
99  * clear [-v] prov ...
100  * dump [-v] prov ...
101  */
102 struct g_command class_commands[] = {
103         { "init", G_FLAG_VERBOSE, eli_main,
104             {
105                 { 'a', "aalgo", "none", G_TYPE_STRING },
106                 { 'b', "boot", NULL, G_TYPE_BOOL },
107                 { 'B', "backupfile", "", G_TYPE_STRING },
108                 { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING },
109                 { 'i', "iterations", "-1", G_TYPE_NUMBER },
110                 { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
111                 { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
112                 { 'l', "keylen", "0", G_TYPE_NUMBER },
113                 { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
114                 { 's', "sectorsize", "0", G_TYPE_NUMBER },
115                 G_OPT_SENTINEL
116             },
117             NULL,
118             "[-bPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] prov"
119         },
120         { "label", G_FLAG_VERBOSE, eli_main,
121             {
122                 { 'a', "aalgo", "none", G_TYPE_STRING },
123                 { 'b', "boot", NULL, G_TYPE_BOOL },
124                 { 'B', "backupfile", "", G_TYPE_STRING },
125                 { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING },
126                 { 'i', "iterations", "-1", G_TYPE_NUMBER },
127                 { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
128                 { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
129                 { 'l', "keylen", "0", G_TYPE_NUMBER },
130                 { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
131                 { 's', "sectorsize", "0", G_TYPE_NUMBER },
132                 G_OPT_SENTINEL
133             },
134             NULL,
135             "- an alias for 'init'"
136         },
137         { "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main,
138             {
139                 { 'd', "detach", NULL, G_TYPE_BOOL },
140                 { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
141                 { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
142                 { 'p', "nopassphrase", NULL, G_TYPE_BOOL },
143                 { 'r', "readonly", NULL, G_TYPE_BOOL },
144                 G_OPT_SENTINEL
145             },
146             NULL,
147             "[-dprv] [-j passfile] [-k keyfile] prov"
148         },
149         { "detach", 0, NULL,
150             {
151                 { 'f', "force", NULL, G_TYPE_BOOL },
152                 { 'l', "last", NULL, G_TYPE_BOOL },
153                 G_OPT_SENTINEL
154             },
155             NULL,
156             "[-fl] prov ..."
157         },
158         { "stop", 0, NULL,
159             {
160                 { 'f', "force", NULL, G_TYPE_BOOL },
161                 { 'l', "last", NULL, G_TYPE_BOOL },
162                 G_OPT_SENTINEL
163             },
164             NULL,
165             "- an alias for 'detach'"
166         },
167         { "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
168             {
169                 { 'a', "aalgo", "none", G_TYPE_STRING },
170                 { 'd', "detach", NULL, G_TYPE_BOOL },
171                 { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING },
172                 { 'l', "keylen", "0", G_TYPE_NUMBER },
173                 { 's', "sectorsize", "0", G_TYPE_NUMBER },
174                 G_OPT_SENTINEL
175             },
176             NULL,
177             "[-d] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov"
178         },
179         { "configure", G_FLAG_VERBOSE, eli_main,
180             {
181                 { 'b', "boot", NULL, G_TYPE_BOOL },
182                 { 'B', "noboot", NULL, G_TYPE_BOOL },
183                 G_OPT_SENTINEL
184             },
185             NULL,
186             "[-bB] prov ..."
187         },
188         { "setkey", G_FLAG_VERBOSE, eli_main,
189             {
190                 { 'i', "iterations", "-1", G_TYPE_NUMBER },
191                 { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
192                 { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
193                 { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
194                 { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
195                 { 'n', "keyno", "-1", G_TYPE_NUMBER },
196                 { 'p', "nopassphrase", NULL, G_TYPE_BOOL },
197                 { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
198                 G_OPT_SENTINEL
199             },
200             NULL,
201             "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov"
202         },
203         { "delkey", G_FLAG_VERBOSE, eli_main,
204             {
205                 { 'a', "all", NULL, G_TYPE_BOOL },
206                 { 'f', "force", NULL, G_TYPE_BOOL },
207                 { 'n', "keyno", "-1", G_TYPE_NUMBER },
208                 G_OPT_SENTINEL
209             },
210             NULL,
211             "[-afv] [-n keyno] prov"
212         },
213         { "suspend", G_FLAG_VERBOSE, NULL,
214             {
215                 { 'a', "all", NULL, G_TYPE_BOOL },
216                 G_OPT_SENTINEL
217             },
218             NULL,
219             "[-v] -a | prov ..."
220         },
221         { "resume", G_FLAG_VERBOSE, eli_main,
222             {
223                 { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
224                 { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
225                 { 'p', "nopassphrase", NULL, G_TYPE_BOOL },
226                 G_OPT_SENTINEL
227             },
228             NULL,
229             "[-pv] [-j passfile] [-k keyfile] prov"
230         },
231         { "kill", G_FLAG_VERBOSE, eli_main,
232             {
233                 { 'a', "all", NULL, G_TYPE_BOOL },
234                 G_OPT_SENTINEL
235             },
236             NULL,
237             "[-av] [prov ...]"
238         },
239         { "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, NULL,
240             "[-v] prov file"
241         },
242         { "restore", G_FLAG_VERBOSE, eli_main,
243             {
244                 { 'f', "force", NULL, G_TYPE_BOOL },
245                 G_OPT_SENTINEL
246             },
247             NULL,
248             "[-fv] file prov"
249         },
250         { "resize", G_FLAG_VERBOSE, eli_main,
251             {
252                 { 's', "oldsize", NULL, G_TYPE_NUMBER },
253                 G_OPT_SENTINEL
254             },
255             NULL,
256             "[-v] -s oldsize prov"
257         },
258         { "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, NULL,
259             "[-v] prov ..."
260         },
261         { "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, NULL,
262             "[-v] prov ..."
263         },
264         G_CMD_SENTINEL
265 };
266
267 static int verbose = 0;
268
269 static int
270 eli_protect(struct gctl_req *req)
271 {
272         struct rlimit rl;
273
274         /* Disable core dumps. */
275         rl.rlim_cur = 0;
276         rl.rlim_max = 0;
277         if (setrlimit(RLIMIT_CORE, &rl) == -1) {
278                 gctl_error(req, "Cannot disable core dumps: %s.",
279                     strerror(errno));
280                 return (-1);
281         }
282         /* Disable swapping. */
283         if (mlockall(MCL_FUTURE) == -1) {
284                 gctl_error(req, "Cannot lock memory: %s.", strerror(errno));
285                 return (-1);
286         }
287         return (0);
288 }
289
290 static void
291 eli_main(struct gctl_req *req, unsigned int flags)
292 {
293         const char *name;
294
295         if (eli_protect(req) == -1)
296                 return;
297
298         if ((flags & G_FLAG_VERBOSE) != 0)
299                 verbose = 1;
300
301         name = gctl_get_ascii(req, "verb");
302         if (name == NULL) {
303                 gctl_error(req, "No '%s' argument.", "verb");
304                 return;
305         }
306         if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0)
307                 eli_init(req);
308         else if (strcmp(name, "attach") == 0)
309                 eli_attach(req);
310         else if (strcmp(name, "configure") == 0)
311                 eli_configure(req);
312         else if (strcmp(name, "setkey") == 0)
313                 eli_setkey(req);
314         else if (strcmp(name, "delkey") == 0)
315                 eli_delkey(req);
316         else if (strcmp(name, "resume") == 0)
317                 eli_resume(req);
318         else if (strcmp(name, "kill") == 0)
319                 eli_kill(req);
320         else if (strcmp(name, "backup") == 0)
321                 eli_backup(req);
322         else if (strcmp(name, "restore") == 0)
323                 eli_restore(req);
324         else if (strcmp(name, "resize") == 0)
325                 eli_resize(req);
326         else if (strcmp(name, "dump") == 0)
327                 eli_dump(req);
328         else if (strcmp(name, "clear") == 0)
329                 eli_clear(req);
330         else
331                 gctl_error(req, "Unknown command: %s.", name);
332 }
333
334 static void
335 arc4rand(unsigned char *buf, size_t size)
336 {
337         uint32_t *buf4;
338         size_t size4;
339         unsigned int i;
340
341         buf4 = (uint32_t *)buf;
342         size4 = size / 4;
343
344         for (i = 0; i < size4; i++)
345                 buf4[i] = arc4random();
346         for (i *= 4; i < size; i++)
347                 buf[i] = arc4random() % 0xff;
348 }
349
350 static int
351 eli_is_attached(const char *prov)
352 {
353         char name[MAXPATHLEN];
354         unsigned secsize;
355
356         /*
357          * Not the best way to do it, but the easiest.
358          * We try to open provider and check if it is a GEOM provider
359          * by asking about its sectorsize.
360          */
361         snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX);
362         secsize = g_get_sectorsize(name);
363         if (secsize > 0)
364                 return (1);
365         return (0);
366 }
367
368 static int
369 eli_genkey_files(struct gctl_req *req, bool new, const char *type,
370     struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize)
371 {
372         char *p, buf[MAXPHYS], argname[16];
373         const char *file;
374         int error, fd, i;
375         ssize_t done;
376
377         assert((strcmp(type, "keyfile") == 0 && ctxp != NULL &&
378             passbuf == NULL && passbufsize == 0) ||
379             (strcmp(type, "passfile") == 0 && ctxp == NULL &&
380             passbuf != NULL && passbufsize > 0));
381         assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0');
382
383         for (i = 0; ; i++) {
384                 snprintf(argname, sizeof(argname), "%s%s%d",
385                     new ? "new" : "", type, i);
386
387                 /* No more {key,pass}files? */
388                 if (!gctl_has_param(req, argname))
389                         return (i);
390
391                 file = gctl_get_ascii(req, argname);
392                 assert(file != NULL);
393
394                 if (strcmp(file, "-") == 0)
395                         fd = STDIN_FILENO;
396                 else {
397                         fd = open(file, O_RDONLY);
398                         if (fd == -1) {
399                                 gctl_error(req, "Cannot open %s %s: %s.",
400                                     type, file, strerror(errno));
401                                 return (-1);
402                         }
403                 }
404                 if (strcmp(type, "keyfile") == 0) {
405                         while ((done = read(fd, buf, sizeof(buf))) > 0)
406                                 g_eli_crypto_hmac_update(ctxp, buf, done);
407                 } else /* if (strcmp(type, "passfile") == 0) */ {
408                         while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) {
409                                 buf[done] = '\0';
410                                 p = strchr(buf, '\n');
411                                 if (p != NULL) {
412                                         *p = '\0';
413                                         done = p - buf;
414                                 }
415                                 if (strlcat(passbuf, buf, passbufsize) >=
416                                     passbufsize) {
417                                         gctl_error(req,
418                                             "Passphrase in %s too long.", file);
419                                         bzero(buf, sizeof(buf));
420                                         return (-1);
421                                 }
422                                 if (p != NULL)
423                                         break;
424                         }
425                 }
426                 error = errno;
427                 if (strcmp(file, "-") != 0)
428                         close(fd);
429                 bzero(buf, sizeof(buf));
430                 if (done == -1) {
431                         gctl_error(req, "Cannot read %s %s: %s.",
432                             type, file, strerror(error));
433                         return (-1);
434                 }
435         }
436         /* NOTREACHED */
437 }
438
439 static int
440 eli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf,
441     size_t passbufsize)
442 {
443         char *p;
444
445         for (;;) {
446                 p = readpassphrase(
447                     new ? "Enter new passphrase:" : "Enter passphrase:",
448                     passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY);
449                 if (p == NULL) {
450                         bzero(passbuf, passbufsize);
451                         gctl_error(req, "Cannot read passphrase: %s.",
452                             strerror(errno));
453                         return (-1);
454                 }
455
456                 if (new) {
457                         char tmpbuf[BUFSIZ];
458
459                         p = readpassphrase("Reenter new passphrase: ",
460                             tmpbuf, sizeof(tmpbuf),
461                             RPP_ECHO_OFF | RPP_REQUIRE_TTY);
462                         if (p == NULL) {
463                                 bzero(passbuf, passbufsize);
464                                 gctl_error(req,
465                                     "Cannot read passphrase: %s.",
466                                     strerror(errno));
467                                 return (-1);
468                         }
469
470                         if (strcmp(passbuf, tmpbuf) != 0) {
471                                 bzero(passbuf, passbufsize);
472                                 fprintf(stderr, "They didn't match.\n");
473                                 continue;
474                         }
475                         bzero(tmpbuf, sizeof(tmpbuf));
476                 }
477                 return (0);
478         }
479         /* NOTREACHED */
480 }
481
482 static int
483 eli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new,
484     struct hmac_ctx *ctxp)
485 {
486         char passbuf[MAXPHYS];
487         bool nopassphrase;
488         int nfiles;
489
490         nopassphrase =
491             gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
492         if (nopassphrase) {
493                 if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) {
494                         gctl_error(req,
495                             "Options -%c and -%c are mutually exclusive.",
496                             new ? 'J' : 'j', new ? 'P' : 'p');
497                         return (-1);
498                 }
499                 return (0);
500         }
501
502         if (!new && md->md_iterations == -1) {
503                 gctl_error(req, "Missing -p flag.");
504                 return (-1);
505         }
506         passbuf[0] = '\0';
507         nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf,
508             sizeof(passbuf));
509         if (nfiles == -1)
510                 return (-1);
511         else if (nfiles == 0) {
512                 if (eli_genkey_passphrase_prompt(req, new, passbuf,
513                     sizeof(passbuf)) == -1) {
514                         return (-1);
515                 }
516         }
517         /*
518          * Field md_iterations equal to -1 means "choose some sane
519          * value for me".
520          */
521         if (md->md_iterations == -1) {
522                 assert(new);
523                 if (verbose)
524                         printf("Calculating number of iterations...\n");
525                 md->md_iterations = pkcs5v2_calculate(2000000);
526                 assert(md->md_iterations > 0);
527                 if (verbose) {
528                         printf("Done, using %d iterations.\n",
529                             md->md_iterations);
530                 }
531         }
532         /*
533          * If md_iterations is equal to 0, user doesn't want PKCS#5v2.
534          */
535         if (md->md_iterations == 0) {
536                 g_eli_crypto_hmac_update(ctxp, md->md_salt,
537                     sizeof(md->md_salt));
538                 g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf));
539         } else /* if (md->md_iterations > 0) */ {
540                 unsigned char dkey[G_ELI_USERKEYLEN];
541
542                 pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt,
543                     sizeof(md->md_salt), passbuf, md->md_iterations);
544                 g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey));
545                 bzero(dkey, sizeof(dkey));
546         }
547         bzero(passbuf, sizeof(passbuf));
548
549         return (0);
550 }
551
552 static unsigned char *
553 eli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key,
554     bool new)
555 {
556         struct hmac_ctx ctx;
557         bool nopassphrase;
558         int nfiles;
559
560         nopassphrase =
561             gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
562
563         g_eli_crypto_hmac_init(&ctx, NULL, 0);
564
565         nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0);
566         if (nfiles == -1)
567                 return (NULL);
568         else if (nfiles == 0 && nopassphrase) {
569                 gctl_error(req, "No key components given.");
570                 return (NULL);
571         }
572
573         if (eli_genkey_passphrase(req, md, new, &ctx) == -1)
574                 return (NULL);
575
576         g_eli_crypto_hmac_final(&ctx, key, 0);
577
578         return (key);
579 }
580
581 static int
582 eli_metadata_read(struct gctl_req *req, const char *prov,
583     struct g_eli_metadata *md)
584 {
585         unsigned char sector[sizeof(struct g_eli_metadata)];
586         int error;
587
588         if (g_get_sectorsize(prov) == 0) {
589                 int fd;
590
591                 /* This is a file probably. */
592                 fd = open(prov, O_RDONLY);
593                 if (fd == -1) {
594                         gctl_error(req, "Cannot open %s: %s.", prov,
595                             strerror(errno));
596                         return (-1);
597                 }
598                 if (read(fd, sector, sizeof(sector)) != sizeof(sector)) {
599                         gctl_error(req, "Cannot read metadata from %s: %s.",
600                             prov, strerror(errno));
601                         close(fd);
602                         return (-1);
603                 }
604                 close(fd);
605         } else {
606                 /* This is a GEOM provider. */
607                 error = g_metadata_read(prov, sector, sizeof(sector),
608                     G_ELI_MAGIC);
609                 if (error != 0) {
610                         gctl_error(req, "Cannot read metadata from %s: %s.",
611                             prov, strerror(error));
612                         return (-1);
613                 }
614         }
615         if (eli_metadata_decode(sector, md) != 0) {
616                 gctl_error(req, "MD5 hash mismatch for %s.", prov);
617                 return (-1);
618         }
619         return (0);
620 }
621
622 static int
623 eli_metadata_store(struct gctl_req *req, const char *prov,
624     struct g_eli_metadata *md)
625 {
626         unsigned char sector[sizeof(struct g_eli_metadata)];
627         int error;
628
629         eli_metadata_encode(md, sector);
630         if (g_get_sectorsize(prov) == 0) {
631                 int fd;
632
633                 /* This is a file probably. */
634                 fd = open(prov, O_WRONLY | O_TRUNC);
635                 if (fd == -1) {
636                         gctl_error(req, "Cannot open %s: %s.", prov,
637                             strerror(errno));
638                         bzero(sector, sizeof(sector));
639                         return (-1);
640                 }
641                 if (write(fd, sector, sizeof(sector)) != sizeof(sector)) {
642                         gctl_error(req, "Cannot write metadata to %s: %s.",
643                             prov, strerror(errno));
644                         bzero(sector, sizeof(sector));
645                         close(fd);
646                         return (-1);
647                 }
648                 close(fd);
649         } else {
650                 /* This is a GEOM provider. */
651                 error = g_metadata_store(prov, sector, sizeof(sector));
652                 if (error != 0) {
653                         gctl_error(req, "Cannot write metadata to %s: %s.",
654                             prov, strerror(errno));
655                         bzero(sector, sizeof(sector));
656                         return (-1);
657                 }
658         }
659         bzero(sector, sizeof(sector));
660         return (0);
661 }
662
663 static void
664 eli_init(struct gctl_req *req)
665 {
666         struct g_eli_metadata md;
667         unsigned char sector[sizeof(struct g_eli_metadata)];
668         unsigned char key[G_ELI_USERKEYLEN];
669         char backfile[MAXPATHLEN];
670         const char *str, *prov;
671         unsigned secsize;
672         off_t mediasize;
673         intmax_t val;
674         int error, nargs;
675
676         nargs = gctl_get_int(req, "nargs");
677         if (nargs != 1) {
678                 gctl_error(req, "Invalid number of arguments.");
679                 return;
680         }
681         prov = gctl_get_ascii(req, "arg0");
682         mediasize = g_get_mediasize(prov);
683         secsize = g_get_sectorsize(prov);
684         if (mediasize == 0 || secsize == 0) {
685                 gctl_error(req, "Cannot get informations about %s: %s.", prov,
686                     strerror(errno));
687                 return;
688         }
689
690         bzero(&md, sizeof(md));
691         strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic));
692         md.md_version = G_ELI_VERSION;
693         md.md_flags = 0;
694         if (gctl_get_int(req, "boot"))
695                 md.md_flags |= G_ELI_FLAG_BOOT;
696         md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
697         str = gctl_get_ascii(req, "aalgo");
698         if (strcmp(str, "none") != 0) {
699                 md.md_aalgo = g_eli_str2aalgo(str);
700                 if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN &&
701                     md.md_aalgo <= CRYPTO_ALGORITHM_MAX) {
702                         md.md_flags |= G_ELI_FLAG_AUTH;
703                 } else {
704                         /*
705                          * For backward compatibility, check if the -a option
706                          * was used to provide encryption algorithm.
707                          */
708                         md.md_ealgo = g_eli_str2ealgo(str);
709                         if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
710                             md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
711                                 gctl_error(req,
712                                     "Invalid authentication algorithm.");
713                                 return;
714                         } else {
715                                 fprintf(stderr, "warning: The -e option, not "
716                                     "the -a option is now used to specify "
717                                     "encryption algorithm to use.\n");
718                         }
719                 }
720         }
721         if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
722             md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
723                 str = gctl_get_ascii(req, "ealgo");
724                 md.md_ealgo = g_eli_str2ealgo(str);
725                 if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
726                     md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
727                         gctl_error(req, "Invalid encryption algorithm.");
728                         return;
729                 }
730         }
731         val = gctl_get_intmax(req, "keylen");
732         md.md_keylen = val;
733         md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen);
734         if (md.md_keylen == 0) {
735                 gctl_error(req, "Invalid key length.");
736                 return;
737         }
738         md.md_provsize = mediasize;
739
740         val = gctl_get_intmax(req, "iterations");
741         if (val != -1) {
742                 int nonewpassphrase;
743
744                 /*
745                  * Don't allow to set iterations when there will be no
746                  * passphrase.
747                  */
748                 nonewpassphrase = gctl_get_int(req, "nonewpassphrase");
749                 if (nonewpassphrase) {
750                         gctl_error(req,
751                             "Options -i and -P are mutually exclusive.");
752                         return;
753                 }
754         }
755         md.md_iterations = val;
756
757         val = gctl_get_intmax(req, "sectorsize");
758         if (val == 0)
759                 md.md_sectorsize = secsize;
760         else {
761                 if (val < 0 || (val % secsize) != 0) {
762                         gctl_error(req, "Invalid sector size.");
763                         return;
764                 }
765                 if (val > sysconf(_SC_PAGE_SIZE)) {
766                         fprintf(stderr,
767                             "warning: Using sectorsize bigger than the page size!\n");
768                 }
769                 md.md_sectorsize = val;
770         }
771
772         md.md_keys = 0x01;
773         arc4rand(md.md_salt, sizeof(md.md_salt));
774         arc4rand(md.md_mkeys, sizeof(md.md_mkeys));
775
776         /* Generate user key. */
777         if (eli_genkey(req, &md, key, true) == NULL) {
778                 bzero(key, sizeof(key));
779                 bzero(&md, sizeof(md));
780                 return;
781         }
782
783         /* Encrypt the first and the only Master Key. */
784         error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys);
785         bzero(key, sizeof(key));
786         if (error != 0) {
787                 bzero(&md, sizeof(md));
788                 gctl_error(req, "Cannot encrypt Master Key: %s.",
789                     strerror(error));
790                 return;
791         }
792
793         eli_metadata_encode(&md, sector);
794         bzero(&md, sizeof(md));
795         error = g_metadata_store(prov, sector, sizeof(sector));
796         bzero(sector, sizeof(sector));
797         if (error != 0) {
798                 gctl_error(req, "Cannot store metadata on %s: %s.", prov,
799                     strerror(error));
800                 return;
801         }
802         if (verbose)
803                 printf("Metadata value stored on %s.\n", prov);
804         /* Backup metadata to a file. */
805         str = gctl_get_ascii(req, "backupfile");
806         if (str[0] != '\0') {
807                 /* Backupfile given be the user, just copy it. */
808                 strlcpy(backfile, str, sizeof(backfile));
809         } else {
810                 /* Generate file name automatically. */
811                 const char *p = prov;
812                 unsigned int i;
813
814                 if (strncmp(p, _PATH_DEV, strlen(_PATH_DEV)) == 0)
815                         p += strlen(_PATH_DEV);
816                 snprintf(backfile, sizeof(backfile), "%s%s.eli",
817                     GELI_BACKUP_DIR, p);
818                 /* Replace all / with _. */
819                 for (i = strlen(GELI_BACKUP_DIR); backfile[i] != '\0'; i++) {
820                         if (backfile[i] == '/')
821                                 backfile[i] = '_';
822                 }
823         }
824         if (strcmp(backfile, "none") != 0 &&
825             eli_backup_create(req, prov, backfile) == 0) {
826                 printf("\nMetadata backup can be found in %s and\n", backfile);
827                 printf("can be restored with the following command:\n");
828                 printf("\n\t# geli restore %s %s\n\n", backfile, prov);
829         }
830 }
831
832 static void
833 eli_attach(struct gctl_req *req)
834 {
835         struct g_eli_metadata md;
836         unsigned char key[G_ELI_USERKEYLEN];
837         const char *prov;
838         off_t mediasize;
839         int nargs;
840
841         nargs = gctl_get_int(req, "nargs");
842         if (nargs != 1) {
843                 gctl_error(req, "Invalid number of arguments.");
844                 return;
845         }
846         prov = gctl_get_ascii(req, "arg0");
847
848         if (eli_metadata_read(req, prov, &md) == -1)
849                 return;
850
851         mediasize = g_get_mediasize(prov);
852         if (md.md_provsize != (uint64_t)mediasize) {
853                 gctl_error(req, "Provider size mismatch.");
854                 return;
855         }
856
857         if (eli_genkey(req, &md, key, false) == NULL) {
858                 bzero(key, sizeof(key));
859                 return;
860         }
861
862         gctl_ro_param(req, "key", sizeof(key), key);
863         if (gctl_issue(req) == NULL) {
864                 if (verbose)
865                         printf("Attached to %s.\n", prov);
866         }
867         bzero(key, sizeof(key));
868 }
869
870 static void
871 eli_configure_detached(struct gctl_req *req, const char *prov, bool boot)
872 {
873         struct g_eli_metadata md;
874
875         if (eli_metadata_read(req, prov, &md) == -1)
876                 return;
877
878         if (boot && (md.md_flags & G_ELI_FLAG_BOOT)) {
879                 if (verbose)
880                         printf("BOOT flag already configured for %s.\n", prov);
881         } else if (!boot && !(md.md_flags & G_ELI_FLAG_BOOT)) {
882                 if (verbose)
883                         printf("BOOT flag not configured for %s.\n", prov);
884         } else {
885                 if (boot)
886                         md.md_flags |= G_ELI_FLAG_BOOT;
887                 else
888                         md.md_flags &= ~G_ELI_FLAG_BOOT;
889                 eli_metadata_store(req, prov, &md);
890         }
891         bzero(&md, sizeof(md));
892 }
893
894 static void
895 eli_configure(struct gctl_req *req)
896 {
897         const char *prov;
898         bool boot, noboot;
899         int i, nargs;
900
901         nargs = gctl_get_int(req, "nargs");
902         if (nargs == 0) {
903                 gctl_error(req, "Too few arguments.");
904                 return;
905         }
906
907         boot = gctl_get_int(req, "boot");
908         noboot = gctl_get_int(req, "noboot");
909
910         if (boot && noboot) {
911                 gctl_error(req, "Options -b and -B are mutually exclusive.");
912                 return;
913         }
914         if (!boot && !noboot) {
915                 gctl_error(req, "No option given.");
916                 return;
917         }
918
919         /* First attached providers. */
920         gctl_issue(req);
921         /* Now the rest. */
922         for (i = 0; i < nargs; i++) {
923                 prov = gctl_get_ascii(req, "arg%d", i);
924                 if (!eli_is_attached(prov))
925                         eli_configure_detached(req, prov, boot);
926         }
927 }
928
929 static void
930 eli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md)
931 {
932         unsigned char key[G_ELI_USERKEYLEN];
933         intmax_t val, old = 0;
934         int error;
935
936         val = gctl_get_intmax(req, "iterations");
937         /* Check if iterations number should be changed. */
938         if (val != -1)
939                 md->md_iterations = val;
940         else
941                 old = md->md_iterations;
942
943         /* Generate key for Master Key encryption. */
944         if (eli_genkey(req, md, key, true) == NULL) {
945                 bzero(key, sizeof(key));
946                 return;
947         }
948         /*
949          * If number of iterations has changed, but wasn't given as a
950          * command-line argument, update the request.
951          */
952         if (val == -1 && md->md_iterations != old) {
953                 error = gctl_change_param(req, "iterations", sizeof(intmax_t),
954                     &md->md_iterations);
955                 assert(error == 0);
956         }
957
958         gctl_ro_param(req, "key", sizeof(key), key);
959         gctl_issue(req);
960         bzero(key, sizeof(key));
961 }
962
963 static void
964 eli_setkey_detached(struct gctl_req *req, const char *prov,
965  struct g_eli_metadata *md)
966 {
967         unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
968         unsigned char *mkeydst;
969         unsigned int nkey;
970         intmax_t val;
971         int error;
972
973         if (md->md_keys == 0) {
974                 gctl_error(req, "No valid keys on %s.", prov);
975                 return;
976         }
977
978         /* Generate key for Master Key decryption. */
979         if (eli_genkey(req, md, key, false) == NULL) {
980                 bzero(key, sizeof(key));
981                 return;
982         }
983
984         /* Decrypt Master Key. */
985         error = g_eli_mkey_decrypt(md, key, mkey, &nkey);
986         bzero(key, sizeof(key));
987         if (error != 0) {
988                 bzero(md, sizeof(*md));
989                 if (error == -1)
990                         gctl_error(req, "Wrong key for %s.", prov);
991                 else /* if (error > 0) */ {
992                         gctl_error(req, "Cannot decrypt Master Key: %s.",
993                             strerror(error));
994                 }
995                 return;
996         }
997         if (verbose)
998                 printf("Decrypted Master Key %u.\n", nkey);
999
1000         val = gctl_get_intmax(req, "keyno");
1001         if (val != -1)
1002                 nkey = val;
1003 #if 0
1004         else
1005                 ; /* Use the key number which was found during decryption. */
1006 #endif
1007         if (nkey >= G_ELI_MAXMKEYS) {
1008                 gctl_error(req, "Invalid '%s' argument.", "keyno");
1009                 return;
1010         }
1011
1012         val = gctl_get_intmax(req, "iterations");
1013         /* Check if iterations number should and can be changed. */
1014         if (val != -1) {
1015                 if (bitcount32(md->md_keys) != 1) {
1016                         gctl_error(req, "To be able to use '-i' option, only "
1017                             "one key can be defined.");
1018                         return;
1019                 }
1020                 if (md->md_keys != (1 << nkey)) {
1021                         gctl_error(req, "Only already defined key can be "
1022                             "changed when '-i' option is used.");
1023                         return;
1024                 }
1025                 md->md_iterations = val;
1026         }
1027
1028         mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN;
1029         md->md_keys |= (1 << nkey);
1030
1031         bcopy(mkey, mkeydst, sizeof(mkey));
1032         bzero(mkey, sizeof(mkey));
1033
1034         /* Generate key for Master Key encryption. */
1035         if (eli_genkey(req, md, key, true) == NULL) {
1036                 bzero(key, sizeof(key));
1037                 bzero(md, sizeof(*md));
1038                 return;
1039         }
1040
1041         /* Encrypt the Master-Key with the new key. */
1042         error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst);
1043         bzero(key, sizeof(key));
1044         if (error != 0) {
1045                 bzero(md, sizeof(*md));
1046                 gctl_error(req, "Cannot encrypt Master Key: %s.",
1047                     strerror(error));
1048                 return;
1049         }
1050
1051         /* Store metadata with fresh key. */
1052         eli_metadata_store(req, prov, md);
1053         bzero(md, sizeof(*md));
1054 }
1055
1056 static void
1057 eli_setkey(struct gctl_req *req)
1058 {
1059         struct g_eli_metadata md;
1060         const char *prov;
1061         int nargs;
1062
1063         nargs = gctl_get_int(req, "nargs");
1064         if (nargs != 1) {
1065                 gctl_error(req, "Invalid number of arguments.");
1066                 return;
1067         }
1068         prov = gctl_get_ascii(req, "arg0");
1069
1070         if (eli_metadata_read(req, prov, &md) == -1)
1071                 return;
1072
1073         if (eli_is_attached(prov))
1074                 eli_setkey_attached(req, &md);
1075         else
1076                 eli_setkey_detached(req, prov, &md);
1077
1078         if (req->error == NULL || req->error[0] == '\0') {
1079                 printf("Note, that the master key encrypted with old keys "
1080                     "and/or passphrase may still exists in a metadata backup "
1081                     "file.\n");
1082         }
1083 }
1084
1085 static void
1086 eli_delkey_attached(struct gctl_req *req, const char *prov __unused)
1087 {
1088
1089         gctl_issue(req);
1090 }
1091
1092 static void
1093 eli_delkey_detached(struct gctl_req *req, const char *prov)
1094 {
1095         struct g_eli_metadata md;
1096         unsigned char *mkeydst;
1097         unsigned int nkey;
1098         intmax_t val;
1099         bool all, force;
1100
1101         if (eli_metadata_read(req, prov, &md) == -1)
1102                 return;
1103
1104         all = gctl_get_int(req, "all");
1105         if (all)
1106                 arc4rand(md.md_mkeys, sizeof(md.md_mkeys));
1107         else {
1108                 force = gctl_get_int(req, "force");
1109                 val = gctl_get_intmax(req, "keyno");
1110                 if (val == -1) {
1111                         gctl_error(req, "Key number has to be specified.");
1112                         return;
1113                 }
1114                 nkey = val;
1115                 if (nkey >= G_ELI_MAXMKEYS) {
1116                         gctl_error(req, "Invalid '%s' argument.", "keyno");
1117                         return;
1118                 }
1119                 if (!(md.md_keys & (1 << nkey)) && !force) {
1120                         gctl_error(req, "Master Key %u is not set.", nkey);
1121                         return;
1122                 }
1123                 md.md_keys &= ~(1 << nkey);
1124                 if (md.md_keys == 0 && !force) {
1125                         gctl_error(req, "This is the last Master Key. Use '-f' "
1126                             "option if you really want to remove it.");
1127                         return;
1128                 }
1129                 mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN;
1130                 arc4rand(mkeydst, G_ELI_MKEYLEN);
1131         }
1132
1133         eli_metadata_store(req, prov, &md);
1134         bzero(&md, sizeof(md));
1135 }
1136
1137 static void
1138 eli_delkey(struct gctl_req *req)
1139 {
1140         const char *prov;
1141         int nargs;
1142
1143         nargs = gctl_get_int(req, "nargs");
1144         if (nargs != 1) {
1145                 gctl_error(req, "Invalid number of arguments.");
1146                 return;
1147         }
1148         prov = gctl_get_ascii(req, "arg0");
1149
1150         if (eli_is_attached(prov))
1151                 eli_delkey_attached(req, prov);
1152         else
1153                 eli_delkey_detached(req, prov);
1154 }
1155
1156 static void
1157 eli_resume(struct gctl_req *req)
1158 {
1159         struct g_eli_metadata md;
1160         unsigned char key[G_ELI_USERKEYLEN];
1161         const char *prov;
1162         off_t mediasize;
1163         int nargs;
1164
1165         nargs = gctl_get_int(req, "nargs");
1166         if (nargs != 1) {
1167                 gctl_error(req, "Invalid number of arguments.");
1168                 return;
1169         }
1170         prov = gctl_get_ascii(req, "arg0");
1171
1172         if (eli_metadata_read(req, prov, &md) == -1)
1173                 return;
1174
1175         mediasize = g_get_mediasize(prov);
1176         if (md.md_provsize != (uint64_t)mediasize) {
1177                 gctl_error(req, "Provider size mismatch.");
1178                 return;
1179         }
1180
1181         if (eli_genkey(req, &md, key, false) == NULL) {
1182                 bzero(key, sizeof(key));
1183                 return;
1184         }
1185
1186         gctl_ro_param(req, "key", sizeof(key), key);
1187         if (gctl_issue(req) == NULL) {
1188                 if (verbose)
1189                         printf("Resumed %s.\n", prov);
1190         }
1191         bzero(key, sizeof(key));
1192 }
1193
1194 static int
1195 eli_trash_metadata(struct gctl_req *req, const char *prov, int fd, off_t offset)
1196 {
1197         unsigned int overwrites;
1198         unsigned char *sector;
1199         ssize_t size;
1200         int error;
1201
1202         size = sizeof(overwrites);
1203         if (sysctlbyname("kern.geom.eli.overwrites", &overwrites, &size,
1204             NULL, 0) == -1 || overwrites == 0) {
1205                 overwrites = G_ELI_OVERWRITES;
1206         }
1207
1208         size = g_sectorsize(fd);
1209         if (size <= 0) {
1210                 gctl_error(req, "Cannot obtain provider sector size %s: %s.",
1211                     prov, strerror(errno));
1212                 return (-1);
1213         }
1214         sector = malloc(size);
1215         if (sector == NULL) {
1216                 gctl_error(req, "Cannot allocate %zd bytes of memory.", size);
1217                 return (-1);
1218         }
1219
1220         error = 0;
1221         do {
1222                 arc4rand(sector, size);
1223                 if (pwrite(fd, sector, size, offset) != size) {
1224                         if (error == 0)
1225                                 error = errno;
1226                 }
1227                 (void)g_flush(fd);
1228         } while (--overwrites > 0);
1229         if (error != 0) {
1230                 gctl_error(req, "Cannot trash metadata on provider %s: %s.",
1231                     prov, strerror(error));
1232                 return (-1);
1233         }
1234         return (0);
1235 }
1236
1237 static void
1238 eli_kill_detached(struct gctl_req *req, const char *prov)
1239 {
1240         off_t offset;
1241         int fd;
1242
1243         /*
1244          * NOTE: Maybe we should verify if this is geli provider first,
1245          *       but 'kill' command is quite critical so better don't waste
1246          *       the time.
1247          */
1248 #if 0
1249         error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md),
1250             G_ELI_MAGIC);
1251         if (error != 0) {
1252                 gctl_error(req, "Cannot read metadata from %s: %s.", prov,
1253                     strerror(error));
1254                 return;
1255         }
1256 #endif
1257
1258         fd = g_open(prov, 1);
1259         if (fd == -1) {
1260                 gctl_error(req, "Cannot open provider %s: %s.", prov,
1261                     strerror(errno));
1262                 return;
1263         }
1264         offset = g_mediasize(fd) - g_sectorsize(fd);
1265         if (offset <= 0) {
1266                 gctl_error(req,
1267                     "Cannot obtain media size or sector size for provider %s: %s.",
1268                     prov, strerror(errno));
1269                 (void)g_close(fd);
1270                 return;
1271         }
1272         (void)eli_trash_metadata(req, prov, fd, offset);
1273         (void)g_close(fd);
1274 }
1275
1276 static void
1277 eli_kill(struct gctl_req *req)
1278 {
1279         const char *prov;
1280         int i, nargs, all;
1281
1282         nargs = gctl_get_int(req, "nargs");
1283         all = gctl_get_int(req, "all");
1284         if (!all && nargs == 0) {
1285                 gctl_error(req, "Too few arguments.");
1286                 return;
1287         }
1288         /*
1289          * How '-a' option combine with a list of providers:
1290          * Delete Master Keys from all attached providers:
1291          * geli kill -a
1292          * Delete Master Keys from all attached providers and from
1293          * detached da0 and da1:
1294          * geli kill -a da0 da1
1295          * Delete Master Keys from (attached or detached) da0 and da1:
1296          * geli kill da0 da1
1297          */
1298
1299         /* First detached providers. */
1300         for (i = 0; i < nargs; i++) {
1301                 prov = gctl_get_ascii(req, "arg%d", i);
1302                 if (!eli_is_attached(prov))
1303                         eli_kill_detached(req, prov);
1304         }
1305         /* Now attached providers. */
1306         gctl_issue(req);
1307 }
1308
1309 static int
1310 eli_backup_create(struct gctl_req *req, const char *prov, const char *file)
1311 {
1312         struct g_eli_metadata md;
1313         unsigned char *sector;
1314         ssize_t secsize;
1315         off_t mediasize;
1316         int filefd, provfd, ret;
1317
1318         ret = -1;
1319         provfd = filefd = -1;
1320         sector = NULL;
1321         secsize = 0;
1322
1323         provfd = g_open(prov, 0);
1324         if (provfd == -1) {
1325                 gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
1326                 goto out;
1327         }
1328         filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600);
1329         if (filefd == -1) {
1330                 gctl_error(req, "Cannot open %s: %s.", file, strerror(errno));
1331                 goto out;
1332         }
1333
1334         mediasize = g_mediasize(provfd);
1335         secsize = g_sectorsize(provfd);
1336         if (mediasize == -1 || secsize == -1) {
1337                 gctl_error(req, "Cannot get informations about %s: %s.", prov,
1338                     strerror(errno));
1339                 goto out;
1340         }
1341
1342         sector = malloc(secsize);
1343         if (sector == NULL) {
1344                 gctl_error(req, "Cannot allocate memory.");
1345                 goto out;
1346         }
1347
1348         /* Read metadata from the provider. */
1349         if (pread(provfd, sector, secsize, mediasize - secsize) != secsize) {
1350                 gctl_error(req, "Cannot read metadata: %s.", strerror(errno));
1351                 goto out;
1352         }
1353         /* Check if this is geli provider. */
1354         if (eli_metadata_decode(sector, &md) != 0) {
1355                 gctl_error(req, "MD5 hash mismatch: not a geli provider?");
1356                 goto out;
1357         }
1358         /* Write metadata to the destination file. */
1359         if (write(filefd, sector, secsize) != secsize) {
1360                 gctl_error(req, "Cannot write to %s: %s.", file,
1361                     strerror(errno));
1362                 goto out;
1363         }
1364         (void)fsync(filefd);
1365         /* Success. */
1366         ret = 0;
1367 out:
1368         if (provfd >= 0)
1369                 (void)g_close(provfd);
1370         if (filefd >= 0)
1371                 (void)close(filefd);
1372         if (sector != NULL) {
1373                 bzero(sector, secsize);
1374                 free(sector);
1375         }
1376         return (ret);
1377 }
1378
1379 static void
1380 eli_backup(struct gctl_req *req)
1381 {
1382         const char *file, *prov;
1383         int nargs;
1384
1385         nargs = gctl_get_int(req, "nargs");
1386         if (nargs != 2) {
1387                 gctl_error(req, "Invalid number of arguments.");
1388                 return;
1389         }
1390         prov = gctl_get_ascii(req, "arg0");
1391         file = gctl_get_ascii(req, "arg1");
1392
1393         eli_backup_create(req, prov, file);
1394 }
1395
1396 static void
1397 eli_restore(struct gctl_req *req)
1398 {
1399         struct g_eli_metadata md;
1400         const char *file, *prov;
1401         unsigned char *sector;
1402         ssize_t secsize;
1403         off_t mediasize;
1404         int nargs, filefd, provfd;
1405
1406         nargs = gctl_get_int(req, "nargs");
1407         if (nargs != 2) {
1408                 gctl_error(req, "Invalid number of arguments.");
1409                 return;
1410         }
1411         file = gctl_get_ascii(req, "arg0");
1412         prov = gctl_get_ascii(req, "arg1");
1413
1414         provfd = filefd = -1;
1415         sector = NULL;
1416         secsize = 0;
1417
1418         filefd = open(file, O_RDONLY);
1419         if (filefd == -1) {
1420                 gctl_error(req, "Cannot open %s: %s.", file, strerror(errno));
1421                 goto out;
1422         }
1423         provfd = g_open(prov, 1);
1424         if (provfd == -1) {
1425                 gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
1426                 goto out;
1427         }
1428
1429         mediasize = g_mediasize(provfd);
1430         secsize = g_sectorsize(provfd);
1431         if (mediasize == -1 || secsize == -1) {
1432                 gctl_error(req, "Cannot get informations about %s: %s.", prov,
1433                     strerror(errno));
1434                 goto out;
1435         }
1436
1437         sector = malloc(secsize);
1438         if (sector == NULL) {
1439                 gctl_error(req, "Cannot allocate memory.");
1440                 goto out;
1441         }
1442
1443         /* Read metadata from the backup file. */
1444         if (read(filefd, sector, secsize) != secsize) {
1445                 gctl_error(req, "Cannot read from %s: %s.", file,
1446                     strerror(errno));
1447                 goto out;
1448         }
1449         /* Check if this file contains geli metadata. */
1450         if (eli_metadata_decode(sector, &md) != 0) {
1451                 gctl_error(req, "MD5 hash mismatch: not a geli backup file?");
1452                 goto out;
1453         }
1454         /* Check if the provider size has changed since we did the backup. */
1455         if (md.md_provsize != (uint64_t)mediasize) {
1456                 if (gctl_get_int(req, "force")) {
1457                         md.md_provsize = mediasize;
1458                         eli_metadata_encode(&md, sector);
1459                 } else {
1460                         gctl_error(req, "Provider size mismatch: "
1461                             "wrong backup file?");
1462                         goto out;
1463                 }
1464         }
1465         /* Write metadata from the provider. */
1466         if (pwrite(provfd, sector, secsize, mediasize - secsize) != secsize) {
1467                 gctl_error(req, "Cannot write metadata: %s.", strerror(errno));
1468                 goto out;
1469         }
1470         (void)g_flush(provfd);
1471 out:
1472         if (provfd >= 0)
1473                 (void)g_close(provfd);
1474         if (filefd >= 0)
1475                 (void)close(filefd);
1476         if (sector != NULL) {
1477                 bzero(sector, secsize);
1478                 free(sector);
1479         }
1480 }
1481
1482 static void
1483 eli_resize(struct gctl_req *req)
1484 {
1485         struct g_eli_metadata md;
1486         const char *prov;
1487         unsigned char *sector;
1488         ssize_t secsize;
1489         off_t mediasize, oldsize;
1490         int nargs, provfd;
1491
1492         nargs = gctl_get_int(req, "nargs");
1493         if (nargs != 1) {
1494                 gctl_error(req, "Invalid number of arguments.");
1495                 return;
1496         }
1497         prov = gctl_get_ascii(req, "arg0");
1498
1499         provfd = -1;
1500         sector = NULL;
1501         secsize = 0;
1502
1503         provfd = g_open(prov, 1);
1504         if (provfd == -1) {
1505                 gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
1506                 goto out;
1507         }
1508
1509         mediasize = g_mediasize(provfd);
1510         secsize = g_sectorsize(provfd);
1511         if (mediasize == -1 || secsize == -1) {
1512                 gctl_error(req, "Cannot get information about %s: %s.", prov,
1513                     strerror(errno));
1514                 goto out;
1515         }
1516
1517         sector = malloc(secsize);
1518         if (sector == NULL) {
1519                 gctl_error(req, "Cannot allocate memory.");
1520                 goto out;
1521         }
1522
1523         oldsize = gctl_get_intmax(req, "oldsize");
1524         if (oldsize < 0 || oldsize > mediasize) {
1525                 gctl_error(req, "Invalid oldsize: Out of range.");
1526                 goto out;
1527         }
1528         if (oldsize == mediasize) {
1529                 gctl_error(req, "Size hasn't changed.");
1530                 goto out;
1531         }
1532
1533         /* Read metadata from the 'oldsize' offset. */
1534         if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) {
1535                 gctl_error(req, "Cannot read old metadata: %s.",
1536                     strerror(errno));
1537                 goto out;
1538         }
1539
1540         /* Check if this sector contains geli metadata. */
1541         if (eli_metadata_decode(sector, &md) != 0) {
1542                 gctl_error(req, "MD5 hash mismatch: no metadata for oldsize.");
1543                 goto out;
1544         }
1545
1546         /*
1547          * If the old metadata doesn't have a correct provider size, refuse
1548          * to resize.
1549          */
1550         if (md.md_provsize != (uint64_t)oldsize) {
1551                 gctl_error(req, "Provider size mismatch at oldsize.");
1552                 goto out;
1553         }
1554
1555         /*
1556          * Update the old metadata with the current provider size and write
1557          * it back to the correct place on the provider.
1558          */
1559         md.md_provsize = mediasize;
1560         eli_metadata_encode(&md, sector);
1561         if (pwrite(provfd, sector, secsize, mediasize - secsize) != secsize) {
1562                 gctl_error(req, "Cannot write metadata: %s.", strerror(errno));
1563                 goto out;
1564         }
1565         (void)g_flush(provfd);
1566
1567         /* Now trash the old metadata. */
1568         if (eli_trash_metadata(req, prov, provfd, oldsize - secsize) == -1)
1569                 goto out;
1570 out:
1571         if (provfd >= 0)
1572                 (void)g_close(provfd);
1573         if (sector != NULL) {
1574                 bzero(sector, secsize);
1575                 free(sector);
1576         }
1577 }
1578
1579 static void
1580 eli_clear(struct gctl_req *req)
1581 {
1582         const char *name;
1583         int error, i, nargs;
1584
1585         nargs = gctl_get_int(req, "nargs");
1586         if (nargs < 1) {
1587                 gctl_error(req, "Too few arguments.");
1588                 return;
1589         }
1590
1591         for (i = 0; i < nargs; i++) {
1592                 name = gctl_get_ascii(req, "arg%d", i);
1593                 error = g_metadata_clear(name, G_ELI_MAGIC);
1594                 if (error != 0) {
1595                         fprintf(stderr, "Cannot clear metadata on %s: %s.\n",
1596                             name, strerror(error));
1597                         gctl_error(req, "Not fully done.");
1598                         continue;
1599                 }
1600                 if (verbose)
1601                         printf("Metadata cleared on %s.\n", name);
1602         }
1603 }
1604
1605 static void
1606 eli_dump(struct gctl_req *req)
1607 {
1608         struct g_eli_metadata md, tmpmd;
1609         const char *name;
1610         int error, i, nargs;
1611
1612         nargs = gctl_get_int(req, "nargs");
1613         if (nargs < 1) {
1614                 gctl_error(req, "Too few arguments.");
1615                 return;
1616         }
1617
1618         for (i = 0; i < nargs; i++) {
1619                 name = gctl_get_ascii(req, "arg%d", i);
1620                 error = g_metadata_read(name, (unsigned char *)&tmpmd,
1621                     sizeof(tmpmd), G_ELI_MAGIC);
1622                 if (error != 0) {
1623                         fprintf(stderr, "Cannot read metadata from %s: %s.\n",
1624                             name, strerror(error));
1625                         gctl_error(req, "Not fully done.");
1626                         continue;
1627                 }
1628                 if (eli_metadata_decode((unsigned char *)&tmpmd, &md) != 0) {
1629                         fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n",
1630                             name);
1631                         gctl_error(req, "Not fully done.");
1632                         continue;
1633                 }
1634                 printf("Metadata on %s:\n", name);
1635                 eli_metadata_dump(&md);
1636                 printf("\n");
1637         }
1638 }