1 /* $NetBSD: cgdconfig.c,v 1.27 2008/07/24 19:07:36 christos Exp $ */
4 * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Roland C. Dowdeswell.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
34 __COPYRIGHT("@(#) Copyright (c) 2002, 2003\
35 The NetBSD Foundation, Inc. All rights reserved.");
36 __RCSID("$NetBSD: cgdconfig.c,v 1.27 2008/07/24 19:07:36 christos Exp $");
49 #include <sys/ioctl.h>
50 #include <sys/disklabel.h>
52 #include <sys/param.h>
53 #include <sys/resource.h>
55 #include <dev/cgdvar.h>
57 #include <ufs/ffs/fs.h>
60 #include "pkcs5_pbkdf2.h"
62 #include "cgd_kernelops.h"
63 #include "cgdconfig.h"
65 #define CGDCONFIG_DIR "/etc/cgd"
66 #define CGDCONFIG_CFILE CGDCONFIG_DIR "/cgd.conf"
69 ACTION_DEFAULT
, /* default -> configure */
70 ACTION_CONFIGURE
, /* configure, with paramsfile */
71 ACTION_UNCONFIGURE
, /* unconfigure */
72 ACTION_GENERATE
, /* generate a paramsfile */
73 ACTION_GENERATE_CONVERT
, /* generate a ``dup'' paramsfile */
74 ACTION_CONFIGALL
, /* configure all from config file */
75 ACTION_UNCONFIGALL
, /* unconfigure all from config file */
76 ACTION_CONFIGSTDIN
/* configure, key from stdin */
79 /* if nflag is set, do not configure/unconfigure the cgd's */
83 /* if pflag is set to PFLAG_STDIN read from stdin rather than getpass(3) */
85 #define PFLAG_GETPASS 0x01
86 #define PFLAG_STDIN 0x02
87 int pflag
= PFLAG_GETPASS
;
89 static int configure(int, char **, struct params
*, int);
90 static int configure_stdin(struct params
*, int argc
, char **);
91 static int generate(struct params
*, int, char **, const char *);
92 static int generate_convert(struct params
*, int, char **, const char *);
93 static int unconfigure(int, char **, struct params
*, int);
94 static int do_all(const char *, int, char **,
95 int (*)(int, char **, struct params
*, int));
97 #define CONFIG_FLAGS_FROMALL 1 /* called from configure_all() */
98 #define CONFIG_FLAGS_FROMMAIN 2 /* called from main() */
100 static int configure_params(int, const char *, const char *,
102 static void eliminate_cores(void);
103 static bits_t
*getkey(const char *, struct keygen
*, size_t);
104 static bits_t
*getkey_storedkey(const char *, struct keygen
*, size_t);
105 static bits_t
*getkey_randomkey(const char *, struct keygen
*, size_t, int);
106 static bits_t
*getkey_pkcs5_pbkdf2(const char *, struct keygen
*, size_t,
108 static bits_t
*getkey_shell_cmd(const char *, struct keygen
*, size_t);
109 static char *maybe_getpass(char *);
110 static int opendisk_werror(const char *, char *, size_t);
111 static int unconfigure_fd(int);
112 static int verify(struct params
*, int);
113 static int verify_disklabel(int);
114 static int verify_ffs(int);
115 static int verify_reenter(struct params
*);
117 static void usage(void);
119 /* Verbose Framework */
120 unsigned verbose
= 0;
122 #define VERBOSE(x,y) if (verbose >= x) y
123 #define VPRINTF(x,y) if (verbose >= x) (void)printf y
129 (void)fprintf(stderr
, "usage: %s [-nv] [-V vmeth] cgd dev [paramsfile]\n",
131 (void)fprintf(stderr
, " %s -C [-nv] [-f configfile]\n", getprogname());
132 (void)fprintf(stderr
, " %s -U [-nv] [-f configfile]\n", getprogname());
133 (void)fprintf(stderr
, " %s -G [-nv] [-i ivmeth] [-k kgmeth] "
134 "[-o outfile] paramsfile\n", getprogname());
135 (void)fprintf(stderr
, " %s -g [-nv] [-i ivmeth] [-k kgmeth] "
136 "[-o outfile] alg [keylen]\n", getprogname());
137 (void)fprintf(stderr
, " %s -s [-nv] [-i ivmeth] cgd dev alg "
138 "[keylen]\n", getprogname());
139 (void)fprintf(stderr
, " %s -u [-nv] cgd\n", getprogname());
144 parse_size_t(const char *s
, size_t *l
)
150 v
= strtol(s
, &endptr
, 10);
151 if ((v
== LONG_MIN
|| v
== LONG_MAX
) && errno
)
153 if (v
< INT_MIN
|| v
> INT_MAX
) {
166 set_action(enum action
*action
, enum action value
)
168 if (*action
!= ACTION_DEFAULT
)
173 #ifndef CGDCONFIG_AS_LIB
175 main(int argc
, char **argv
)
178 return cgdconfig(argc
, argv
);
183 cgdconfig(int argc
, char *argv
[])
188 enum action action
= ACTION_DEFAULT
;
190 const char *cfile
= NULL
;
191 const char *outfile
= NULL
;
195 if (mlockall(MCL_FUTURE
))
196 err(EXIT_FAILURE
, "Can't lock memory");
200 while ((ch
= getopt(argc
, argv
, "CGUV:b:f:gi:k:no:spuv")) != -1)
203 set_action(&action
, ACTION_CONFIGALL
);
206 set_action(&action
, ACTION_GENERATE_CONVERT
);
209 set_action(&action
, ACTION_UNCONFIGALL
);
212 tp
= params_verify_method(string_fromcharstar(optarg
));
215 p
= params_combine(p
, tp
);
221 if (parse_size_t(optarg
, &size
) == -1)
223 tp
= params_bsize(size
);
226 p
= params_combine(p
, tp
);
232 cfile
= estrdup(optarg
);
235 set_action(&action
, ACTION_GENERATE
);
238 tp
= params_ivmeth(string_fromcharstar(optarg
));
239 p
= params_combine(p
, tp
);
242 kg
= keygen_method(string_fromcharstar(optarg
));
245 keygen_addlist(&p
->keygen
, kg
);
253 outfile
= estrdup(optarg
);
259 set_action(&action
, ACTION_CONFIGSTDIN
);
263 set_action(&action
, ACTION_UNCONFIGURE
);
281 /* validate the consistency of the arguments */
284 case ACTION_DEFAULT
: /* ACTION_CONFIGURE is the default */
285 case ACTION_CONFIGURE
:
286 return configure(argc
, argv
, p
, CONFIG_FLAGS_FROMMAIN
);
287 case ACTION_UNCONFIGURE
:
288 return unconfigure(argc
, argv
, NULL
, CONFIG_FLAGS_FROMMAIN
);
289 case ACTION_GENERATE
:
290 return generate(p
, argc
, argv
, outfile
);
291 case ACTION_GENERATE_CONVERT
:
292 return generate_convert(p
, argc
, argv
, outfile
);
293 case ACTION_CONFIGALL
:
294 return do_all(cfile
, argc
, argv
, configure
);
295 case ACTION_UNCONFIGALL
:
296 return do_all(cfile
, argc
, argv
, unconfigure
);
297 case ACTION_CONFIGSTDIN
:
298 return configure_stdin(p
, argc
, argv
);
300 errx(EXIT_FAILURE
, "undefined action");
306 getkey(const char *dev
, struct keygen
*kg
, size_t len
)
311 VPRINTF(3, ("getkey(\"%s\", %p, %zu) called\n", dev
, kg
, len
));
312 for (; kg
; kg
=kg
->next
) {
313 switch (kg
->kg_method
) {
314 case KEYGEN_STOREDKEY
:
315 tmp
= getkey_storedkey(dev
, kg
, len
);
317 case KEYGEN_RANDOMKEY
:
318 tmp
= getkey_randomkey(dev
, kg
, len
, 1);
320 case KEYGEN_URANDOMKEY
:
321 tmp
= getkey_randomkey(dev
, kg
, len
, 0);
323 case KEYGEN_PKCS5_PBKDF2_SHA1
:
324 tmp
= getkey_pkcs5_pbkdf2(dev
, kg
, len
, 0);
326 /* provide backwards compatibility for old config files */
327 case KEYGEN_PKCS5_PBKDF2_OLD
:
328 tmp
= getkey_pkcs5_pbkdf2(dev
, kg
, len
, 1);
330 case KEYGEN_SHELL_CMD
:
331 tmp
= getkey_shell_cmd(dev
, kg
, len
);
334 warnx("unrecognised keygen method %d in getkey()",
342 ret
= bits_xor_d(tmp
, ret
);
352 getkey_storedkey(const char *target
, struct keygen
*kg
, size_t keylen
)
354 return bits_dup(kg
->kg_key
);
359 getkey_randomkey(const char *target
, struct keygen
*kg
, size_t keylen
, int hard
)
361 return bits_getrandombits(keylen
, hard
);
365 maybe_getpass(char *prompt
)
377 p
= fgets(buf
, sizeof(buf
), stdin
);
379 tmp
= strchr(p
, '\n');
386 errx(EXIT_FAILURE
, "pflag set inappropriately?");
390 err(EXIT_FAILURE
, "failed to read passphrase");
397 * XXX take, and pass through, a compat flag that indicates whether we
398 * provide backwards compatibility with a previous bug. The previous
399 * behaviour is indicated by the keygen method pkcs5_pbkdf2, and a
400 * non-zero compat flag. The new default, and correct keygen method is
401 * called pcks5_pbkdf2/sha1. When the old method is removed, so will
402 * be the compat argument.
405 getkey_pkcs5_pbkdf2(const char *target
, struct keygen
*kg
, size_t keylen
,
413 snprintf(buf
, sizeof(buf
), "%s's passphrase:", target
);
414 passp
= maybe_getpass(buf
);
415 if (pkcs5_pbkdf2(&tmp
, BITS2BYTES(keylen
), (uint8_t *)passp
,
417 bits_getbuf(kg
->kg_salt
), BITS2BYTES(bits_len(kg
->kg_salt
)),
418 kg
->kg_iterations
, compat
)) {
419 warnx("failed to generate PKCS#5 PBKDF2 key");
423 ret
= bits_new(tmp
, keylen
);
424 kg
->kg_key
= bits_dup(ret
);
425 memset(passp
, 0, strlen(passp
));
433 getkey_shell_cmd(const char *target
, struct keygen
*kg
, size_t keylen
)
438 f
= popen(string_tocharstar(kg
->kg_cmd
), "r");
439 ret
= bits_fget(f
, keylen
);
447 unconfigure(int argc
, char **argv
, struct params
*inparams
, int flags
)
451 char buf
[MAXPATHLEN
] = "";
453 /* only complain about additional arguments, if called from main() */
454 if (flags
== CONFIG_FLAGS_FROMMAIN
&& argc
!= 1)
457 /* if called from do_all(), then ensure that 2 or 3 args exist */
458 if (flags
== CONFIG_FLAGS_FROMALL
&& (argc
< 2 || argc
> 3))
461 fd
= opendisk1(*argv
, O_RDWR
, buf
, sizeof(buf
), 1, cgd_kops
.ko_open
);
463 int saved_errno
= errno
;
465 warn("can't open cgd \"%s\", \"%s\"", *argv
, buf
);
467 /* this isn't fatal with nflag != 0 */
472 VPRINTF(1, ("%s (%s): clearing\n", *argv
, buf
));
477 ret
= unconfigure_fd(fd
);
478 (void)cgd_kops
.ko_close(fd
);
483 unconfigure_fd(int fd
)
487 if (cgd_kops
.ko_ioctl(fd
, CGDIOCCLR
, &ci
) == -1) {
497 configure(int argc
, char **argv
, struct params
*inparams
, int flags
)
504 char cgdname
[PATH_MAX
];
509 if (asprintf(&pfile
, "%s/%s",
510 CGDCONFIG_DIR
, basename(argv
[1])) == -1)
513 p
= params_cget(pfile
);
515 } else if (argc
== 3) {
516 p
= params_cget(argv
[2]);
518 /* print usage and exit, only if called from main() */
519 if (flags
== CONFIG_FLAGS_FROMMAIN
) {
520 warnx("wrong number of args");
530 * over-ride with command line specifications and fill in default
534 p
= params_combine(p
, inparams
);
535 ret
= params_filldefaults(p
);
541 if (!params_verify(p
)) {
542 warnx("params invalid");
547 * loop over configuring the disk and checking to see if it
548 * verifies properly. We open and close the disk device each
549 * time, because if the user passes us the block device we
550 * need to flush the buffer cache.
552 * We only loop if one of the verification methods prompts for
556 for (kg
= p
->keygen
; pflag
== PFLAG_GETPASS
&& kg
; kg
= kg
->next
)
557 if ((kg
->kg_method
== KEYGEN_PKCS5_PBKDF2_SHA1
) ||
558 (kg
->kg_method
== KEYGEN_PKCS5_PBKDF2_OLD
)) {
564 fd
= opendisk_werror(argv
[0], cgdname
, sizeof(cgdname
));
571 p
->key
= getkey(argv
[1], p
->keygen
, p
->keylen
);
575 ret
= configure_params(fd
, cgdname
, argv
[1], p
);
585 (void)unconfigure_fd(fd
);
586 (void)cgd_kops
.ko_close(fd
);
589 warnx("verification failed permanently");
593 warnx("verification failed, please reenter passphrase");
597 (void)cgd_kops
.ko_close(fd
);
601 (void)cgd_kops
.ko_close(fd
);
606 configure_stdin(struct params
*p
, int argc
, char **argv
)
610 char cgdname
[PATH_MAX
];
612 if (argc
< 3 || argc
> 4)
615 p
->algorithm
= string_fromcharstar(argv
[2]);
619 if (parse_size_t(argv
[3], &keylen
) == -1) {
620 warn("failed to parse key length");
626 ret
= params_filldefaults(p
);
630 fd
= opendisk_werror(argv
[0], cgdname
, sizeof(cgdname
));
634 p
->key
= bits_fget(stdin
, p
->keylen
);
636 warnx("failed to read key from stdin");
640 return configure_params(fd
, cgdname
, argv
[1], p
);
644 opendisk_werror(const char *cgd
, char *buf
, size_t buflen
)
648 VPRINTF(3, ("opendisk_werror(%s, %s, %zu) called.\n", cgd
, buf
, buflen
));
655 if (strlcpy(buf
, cgd
, buflen
) >= buflen
)
660 fd
= opendisk1(cgd
, O_RDWR
, buf
, buflen
, 0, cgd_kops
.ko_open
);
662 warnx("can't open cgd \"%s\", \"%s\"", cgd
, buf
);
668 configure_params(int fd
, const char *cgd
, const char *dev
, struct params
*p
)
676 (void)memset(&ci
, 0x0, sizeof(ci
));
678 ci
.ci_alg
= string_tocharstar(p
->algorithm
);
679 ci
.ci_ivmethod
= string_tocharstar(p
->ivmeth
);
680 ci
.ci_key
= bits_getbuf(p
->key
);
681 ci
.ci_keylen
= p
->keylen
;
682 ci
.ci_blocksize
= p
->bsize
;
684 VPRINTF(1, (" with alg %s keylen %zu blocksize %zu ivmethod %s\n",
685 string_tocharstar(p
->algorithm
), p
->keylen
, p
->bsize
,
686 string_tocharstar(p
->ivmeth
)));
687 VPRINTF(2, ("key: "));
688 VERBOSE(2, bits_fprint(stdout
, p
->key
));
694 if (cgd_kops
.ko_ioctl(fd
, CGDIOCSET
, &ci
) == -1) {
695 int saved_errno
= errno
;
704 * verify returns 0 for success, -1 for unrecoverable error, or 1 for retry.
707 #define SCANSIZE 8192
710 verify(struct params
*p
, int fd
)
713 switch (p
->verify_method
) {
716 case VERIFY_DISKLABEL
:
717 return verify_disklabel(fd
);
719 return verify_ffs(fd
);
721 return verify_reenter(p
);
723 warnx("unimplemented verification method");
729 verify_disklabel(int fd
)
736 * we simply scan the first few blocks for a disklabel, ignoring
737 * any MBR/filecore sorts of logic. MSDOS and RiscOS can't read
738 * a cgd, anyway, so it is unlikely that there will be non-native
739 * partition information.
742 ret
= cgd_kops
.ko_pread(fd
, buf
, 8192, 0);
744 warn("can't read disklabel area");
748 /* now scan for the disklabel */
750 return disklabel_scan(&l
, buf
, (size_t)ret
);
753 static off_t sblock_try
[] = SBLOCKSEARCH
;
760 for (i
= 0; sblock_try
[i
] != -1; i
++) {
762 char buf
[SBLOCKSIZE
];
767 ret
= cgd_kops
.ko_pread(fd
, &u
, sizeof(u
), sblock_try
[i
]);
771 } else if ((size_t)ret
< sizeof(u
)) {
772 warnx("pread: incomplete block");
775 switch (u
.fs
.fs_magic
) {
778 case FS_UFS1_MAGIC_SWAPPED
:
779 case FS_UFS2_MAGIC_SWAPPED
:
786 return 1; /* failure */
790 verify_reenter(struct params
*p
)
793 bits_t
*orig_key
, *key
;
797 for (kg
= p
->keygen
; kg
&& !ret
; kg
= kg
->next
) {
798 if ((kg
->kg_method
!= KEYGEN_PKCS5_PBKDF2_SHA1
) &&
799 (kg
->kg_method
!= KEYGEN_PKCS5_PBKDF2_OLD
))
802 orig_key
= kg
->kg_key
;
805 /* add a compat flag till the _OLD method goes away */
806 key
= getkey_pkcs5_pbkdf2("re-enter device", kg
,
807 bits_len(orig_key
), kg
->kg_method
== KEYGEN_PKCS5_PBKDF2_OLD
);
808 ret
= !bits_match(key
, orig_key
);
811 bits_free(kg
->kg_key
);
812 kg
->kg_key
= orig_key
;
819 generate(struct params
*p
, int argc
, char **argv
, const char *outfile
)
823 if (argc
< 1 || argc
> 2)
826 p
->algorithm
= string_fromcharstar(argv
[0]);
830 if (parse_size_t(argv
[1], &keylen
) == -1) {
831 warn("Failed to parse key length");
837 ret
= params_filldefaults(p
);
842 p
->keygen
= keygen_generate(KEYGEN_PKCS5_PBKDF2_SHA1
);
847 if (keygen_filldefaults(p
->keygen
, p
->keylen
)) {
848 warnx("Failed to generate defaults for keygen");
852 if (!params_verify(p
)) {
853 warnx("invalid parameters generated");
857 return params_cput(p
, outfile
);
861 generate_convert(struct params
*p
, int argc
, char **argv
, const char *outfile
)
869 oldp
= params_cget(*argv
);
873 /* for sanity, we ensure that none of the keygens are randomkey */
874 for (kg
=p
->keygen
; kg
; kg
=kg
->next
)
875 if (kg
->kg_method
== KEYGEN_RANDOMKEY
)
877 for (kg
=oldp
->keygen
; kg
; kg
=kg
->next
)
878 if (kg
->kg_method
== KEYGEN_RANDOMKEY
)
881 if (!params_verify(oldp
)) {
882 warnx("invalid old parameters file \"%s\"", *argv
);
886 oldp
->key
= getkey("old file", oldp
->keygen
, oldp
->keylen
);
888 /* we copy across the non-keygen info, here. */
890 string_free(p
->algorithm
);
891 string_free(p
->ivmeth
);
893 p
->algorithm
= string_dup(oldp
->algorithm
);
894 p
->ivmeth
= string_dup(oldp
->ivmeth
);
895 p
->keylen
= oldp
->keylen
;
896 p
->bsize
= oldp
->bsize
;
897 if (p
->verify_method
== VERIFY_UNKNOWN
)
898 p
->verify_method
= oldp
->verify_method
;
903 p
->keygen
= keygen_generate(KEYGEN_PKCS5_PBKDF2_SHA1
);
907 (void)params_filldefaults(p
);
908 (void)keygen_filldefaults(p
->keygen
, p
->keylen
);
909 p
->key
= getkey("new file", p
->keygen
, p
->keylen
);
911 kg
= keygen_generate(KEYGEN_STOREDKEY
);
912 kg
->kg_key
= bits_xor(p
->key
, oldp
->key
);
913 keygen_addlist(&p
->keygen
, kg
);
915 if (!params_verify(p
)) {
916 warnx("can't generate new parameters file");
920 return params_cput(p
, outfile
);
928 do_all(const char *cfile
, int argc
, char **argv
,
929 int (*conf
)(int, char **, struct params
*, int))
944 fn
= CGDCONFIG_CFILE
;
950 warn("could not open config file \"%s\"", fn
);
954 ret
= chdir(CGDCONFIG_DIR
);
956 warn("could not chdir to %s", CGDCONFIG_DIR
);
961 line
= fparseln(f
, &len
, &lineno
, "\\\\#", FPARSELN_UNESCALL
);
967 my_argv
= words(line
, &my_argc
);
968 ret
= conf(my_argc
, my_argv
, NULL
, CONFIG_FLAGS_FROMALL
);
970 warnx("action failed on \"%s\" line %lu", fn
,
974 words_free(my_argv
, my_argc
);
980 eliminate_cores(void)
986 if (setrlimit(RLIMIT_CORE
, &rlp
) == -1)
987 err(EXIT_FAILURE
, "Can't disable cores");