cgdconfig.c revision 1.54 1 /* $NetBSD: cgdconfig.c,v 1.54 2022/08/12 10:48:27 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Roland C. Dowdeswell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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.
18 *
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.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 2002, 2003\
35 The NetBSD Foundation, Inc. All rights reserved.");
36 __RCSID("$NetBSD: cgdconfig.c,v 1.54 2022/08/12 10:48:27 riastradh Exp $");
37 #endif
38
39 #ifdef HAVE_ARGON2
40 #include <argon2.h>
41 #endif
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <libgen.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <util.h>
51 #include <paths.h>
52 #include <dirent.h>
53
54 /* base64 gunk */
55 #include <netinet/in.h>
56 #include <arpa/nameser.h>
57 #include <resolv.h>
58
59 #include <sys/ioctl.h>
60 #include <sys/stat.h>
61 #include <sys/bootblock.h>
62 #include <sys/disklabel.h>
63 #include <sys/disklabel_gpt.h>
64 #include <sys/mman.h>
65 #include <sys/param.h>
66 #include <sys/resource.h>
67 #include <sys/statvfs.h>
68 #include <sys/bitops.h>
69
70 #include <dev/cgdvar.h>
71
72 #include <ufs/ffs/fs.h>
73
74 #include "params.h"
75 #include "pkcs5_pbkdf2.h"
76 #include "utils.h"
77 #include "cgdconfig.h"
78 #include "prog_ops.h"
79
80 #define CGDCONFIG_CFILE CGDCONFIG_DIR "/cgd.conf"
81
82 enum action {
83 ACTION_DEFAULT, /* default -> configure */
84 ACTION_CONFIGURE, /* configure, with paramsfile */
85 ACTION_UNCONFIGURE, /* unconfigure */
86 ACTION_GENERATE, /* generate a paramsfile */
87 ACTION_GENERATE_CONVERT, /* generate a ``dup'' paramsfile */
88 ACTION_CONFIGALL, /* configure all from config file */
89 ACTION_UNCONFIGALL, /* unconfigure all from config file */
90 ACTION_CONFIGSTDIN, /* configure, key from stdin */
91 ACTION_LIST, /* list configured devices */
92 ACTION_PRINTKEY, /* print key to stdout */
93 };
94
95 /* if nflag is set, do not configure/unconfigure the cgd's */
96
97 int nflag = 0;
98
99 /* if pflag is set to PFLAG_STDIN read from stdin rather than getpass(3) */
100
101 #define PFLAG_GETPASS 0x01
102 #define PFLAG_GETPASS_ECHO 0x02
103 #define PFLAG_GETPASS_MASK 0x03
104 #define PFLAG_STDIN 0x04
105 int pflag = PFLAG_GETPASS;
106
107 static int configure(int, char **, struct params *, int);
108 static int configure_stdin(struct params *, int argc, char **);
109 static int generate(struct params *, int, char **, const char *);
110 static int generate_convert(struct params *, int, char **, const char *);
111 static int unconfigure(int, char **, struct params *, int);
112 static int do_all(const char *, int, char **,
113 int (*)(int, char **, struct params *, int));
114 static int do_list(int, char **);
115 static int do_printkey(int, char **);
116
117 #define CONFIG_FLAGS_FROMALL 1 /* called from configure_all() */
118 #define CONFIG_FLAGS_FROMMAIN 2 /* called from main() */
119
120 static int configure_params(int, const char *, const char *,
121 struct params *);
122 static void eliminate_cores(void);
123 static bits_t *getkey(const char *, struct keygen *, size_t);
124 static bits_t *getkey_storedkey(const char *, struct keygen *, size_t);
125 static bits_t *getkey_randomkey(const char *, struct keygen *, size_t, int);
126 #ifdef HAVE_ARGON2
127 static bits_t *getkey_argon2id(const char *, struct keygen *, size_t);
128 #endif
129 static bits_t *getkey_pkcs5_pbkdf2(const char *, struct keygen *, size_t,
130 int);
131 static bits_t *getkey_shell_cmd(const char *, struct keygen *, size_t);
132 static char *maybe_getpass(char *);
133 static int opendisk_werror(const char *, char *, size_t);
134 static int unconfigure_fd(int);
135 static int verify(struct params *, int);
136 static int verify_disklabel(int);
137 static int verify_ffs(int);
138 static int verify_reenter(struct params *);
139 static int verify_mbr(int);
140 static int verify_gpt(int);
141
142 __dead static void usage(void);
143
144 /* Verbose Framework */
145 unsigned verbose = 0;
146
147 #define VERBOSE(x,y) if (verbose >= x) y
148 #define VPRINTF(x,y) if (verbose >= x) (void)printf y
149
150 static void
151 usage(void)
152 {
153
154 (void)fprintf(stderr, "usage: %s [-enpv] [-V vmeth] cgd dev "
155 "[paramsfile]\n", getprogname());
156 (void)fprintf(stderr, " %s -C [-enpv] [-f configfile]\n",
157 getprogname());
158 (void)fprintf(stderr, " %s -G [-enpv] [-i ivmeth] [-k kgmeth] "
159 "[-o outfile] paramsfile\n", getprogname());
160 (void)fprintf(stderr, " %s -g [-v] [-i ivmeth] [-k kgmeth] "
161 "[-o outfile] alg [keylen]\n", getprogname());
162 (void)fprintf(stderr, " %s -l [-v[v]] [cgd]\n", getprogname());
163 (void)fprintf(stderr, " %s -s [-nv] [-i ivmeth] cgd dev alg "
164 "[keylen]\n", getprogname());
165 (void)fprintf(stderr, " %s -t paramsfile\n", getprogname());
166 (void)fprintf(stderr, " %s -U [-nv] [-f configfile]\n",
167 getprogname());
168 (void)fprintf(stderr, " %s -u [-nv] cgd\n", getprogname());
169 exit(EXIT_FAILURE);
170 }
171
172 static int
173 parse_size_t(const char *s, size_t *l)
174 {
175 char *endptr;
176 long v;
177
178 errno = 0;
179 v = strtol(s, &endptr, 10);
180 if ((v == LONG_MIN || v == LONG_MAX) && errno)
181 return -1;
182 if (v < INT_MIN || v > INT_MAX) {
183 errno = ERANGE;
184 return -1;
185 }
186 if (endptr == s) {
187 errno = EINVAL;
188 return -1;
189 }
190 *l = (size_t)v;
191 return 0;
192 }
193
194 static void
195 set_action(enum action *action, enum action value)
196 {
197 if (*action != ACTION_DEFAULT)
198 usage();
199 *action = value;
200 }
201
202 int
203 main(int argc, char **argv)
204 {
205 struct params *p;
206 struct params *tp;
207 struct keygen *kg;
208 enum action action = ACTION_DEFAULT;
209 int ch;
210 const char *cfile = NULL;
211 const char *outfile = NULL;
212
213 setprogname(*argv);
214 eliminate_cores();
215 if (mlockall(MCL_FUTURE))
216 err(EXIT_FAILURE, "Can't lock memory");
217 p = params_new();
218 kg = NULL;
219
220 while ((ch = getopt(argc, argv, "CGUV:b:ef:gi:k:lno:sptuv")) != -1)
221 switch (ch) {
222 case 'C':
223 set_action(&action, ACTION_CONFIGALL);
224 break;
225 case 'G':
226 set_action(&action, ACTION_GENERATE_CONVERT);
227 break;
228 case 'U':
229 set_action(&action, ACTION_UNCONFIGALL);
230 break;
231 case 'V':
232 tp = params_verify_method(string_fromcharstar(optarg));
233 if (!tp)
234 usage();
235 p = params_combine(p, tp);
236 break;
237 case 'b':
238 {
239 size_t size;
240
241 if (parse_size_t(optarg, &size) == -1)
242 usage();
243 tp = params_bsize(size);
244 if (!tp)
245 usage();
246 p = params_combine(p, tp);
247 }
248 break;
249 case 'e':
250 pflag = PFLAG_GETPASS_ECHO;
251 break;
252 case 'f':
253 if (cfile)
254 usage();
255 cfile = estrdup(optarg);
256 break;
257 case 'g':
258 set_action(&action, ACTION_GENERATE);
259 break;
260 case 'i':
261 tp = params_ivmeth(string_fromcharstar(optarg));
262 p = params_combine(p, tp);
263 break;
264 case 'k':
265 kg = keygen_method(string_fromcharstar(optarg));
266 if (!kg)
267 usage();
268 keygen_addlist(&p->keygen, kg);
269 break;
270 case 'l':
271 set_action(&action, ACTION_LIST);
272 break;
273 case 'n':
274 nflag = 1;
275 break;
276 case 'o':
277 if (outfile)
278 usage();
279 outfile = estrdup(optarg);
280 break;
281 case 'p':
282 pflag = PFLAG_STDIN;
283 break;
284 case 's':
285 set_action(&action, ACTION_CONFIGSTDIN);
286 break;
287 case 't':
288 set_action(&action, ACTION_PRINTKEY);
289 break;
290 case 'u':
291 set_action(&action, ACTION_UNCONFIGURE);
292 break;
293 case 'v':
294 verbose++;
295 break;
296 default:
297 usage();
298 /* NOTREACHED */
299 }
300
301 argc -= optind;
302 argv += optind;
303
304 if (!outfile)
305 outfile = "";
306 if (!cfile)
307 cfile = "";
308
309 if (prog_init && prog_init() == -1)
310 err(1, "init failed");
311
312 /* validate the consistency of the arguments */
313
314 switch (action) {
315 case ACTION_DEFAULT: /* ACTION_CONFIGURE is the default */
316 case ACTION_CONFIGURE:
317 return configure(argc, argv, p, CONFIG_FLAGS_FROMMAIN);
318 case ACTION_UNCONFIGURE:
319 return unconfigure(argc, argv, NULL, CONFIG_FLAGS_FROMMAIN);
320 case ACTION_GENERATE:
321 return generate(p, argc, argv, outfile);
322 case ACTION_GENERATE_CONVERT:
323 return generate_convert(p, argc, argv, outfile);
324 case ACTION_CONFIGALL:
325 return do_all(cfile, argc, argv, configure);
326 case ACTION_UNCONFIGALL:
327 return do_all(cfile, argc, argv, unconfigure);
328 case ACTION_CONFIGSTDIN:
329 return configure_stdin(p, argc, argv);
330 case ACTION_LIST:
331 return do_list(argc, argv);
332 case ACTION_PRINTKEY:
333 return do_printkey(argc, argv);
334 default:
335 errx(EXIT_FAILURE, "undefined action");
336 /* NOTREACHED */
337 }
338 }
339
340 static bits_t *
341 getkey(const char *dev, struct keygen *kg, size_t len)
342 {
343 bits_t *ret = NULL;
344 bits_t *tmp;
345
346 VPRINTF(3, ("getkey(\"%s\", %p, %zu) called\n", dev, kg, len));
347 for (; kg; kg=kg->next) {
348 switch (kg->kg_method) {
349 case KEYGEN_STOREDKEY:
350 tmp = getkey_storedkey(dev, kg, len);
351 break;
352 case KEYGEN_RANDOMKEY:
353 tmp = getkey_randomkey(dev, kg, len, 1);
354 break;
355 case KEYGEN_URANDOMKEY:
356 tmp = getkey_randomkey(dev, kg, len, 0);
357 break;
358 #ifdef HAVE_ARGON2
359 case KEYGEN_ARGON2ID:
360 tmp = getkey_argon2id(dev, kg, len);
361 break;
362 #endif
363 case KEYGEN_PKCS5_PBKDF2_SHA1:
364 tmp = getkey_pkcs5_pbkdf2(dev, kg, len, 0);
365 break;
366 /* provide backwards compatibility for old config files */
367 case KEYGEN_PKCS5_PBKDF2_OLD:
368 tmp = getkey_pkcs5_pbkdf2(dev, kg, len, 1);
369 break;
370 case KEYGEN_SHELL_CMD:
371 tmp = getkey_shell_cmd(dev, kg, len);
372 break;
373 default:
374 warnx("unrecognised keygen method %d in getkey()",
375 kg->kg_method);
376 if (ret)
377 bits_free(ret);
378 return NULL;
379 }
380
381 if (ret)
382 ret = bits_xor_d(tmp, ret);
383 else
384 ret = tmp;
385 }
386
387 return ret;
388 }
389
390 /*ARGSUSED*/
391 static bits_t *
392 getkey_storedkey(const char *target, struct keygen *kg, size_t keylen)
393 {
394 return bits_dup(kg->kg_key);
395 }
396
397 /*ARGSUSED*/
398 static bits_t *
399 getkey_randomkey(const char *target, struct keygen *kg, size_t keylen, int hard)
400 {
401 return bits_getrandombits(keylen, hard);
402 }
403
404 static char *
405 maybe_getpass(char *prompt)
406 {
407 char buf[1024];
408 char *p = NULL;
409 char *tmp, *pass;
410
411 switch (pflag) {
412 case PFLAG_GETPASS:
413 p = getpass_r(prompt, buf, sizeof(buf));
414 break;
415
416 case PFLAG_GETPASS_ECHO:
417 p = getpassfd(prompt, buf, sizeof(buf), NULL,
418 GETPASS_ECHO|GETPASS_ECHO_NL|GETPASS_NEED_TTY, 0);
419 break;
420
421 case PFLAG_STDIN:
422 p = fgets(buf, sizeof(buf), stdin);
423 if (p) {
424 tmp = strchr(p, '\n');
425 if (tmp)
426 *tmp = '\0';
427 }
428 break;
429
430 default:
431 errx(EXIT_FAILURE, "pflag set inappropriately?");
432 }
433
434 if (!p)
435 err(EXIT_FAILURE, "failed to read passphrase");
436
437 pass = estrdup(p);
438 explicit_memset(buf, 0, sizeof(buf));
439
440 return pass;
441 }
442
443 /*ARGSUSED*/
444 /*
445 * XXX take, and pass through, a compat flag that indicates whether we
446 * provide backwards compatibility with a previous bug. The previous
447 * behaviour is indicated by the keygen method pkcs5_pbkdf2, and a
448 * non-zero compat flag. The new default, and correct keygen method is
449 * called pcks5_pbkdf2/sha1. When the old method is removed, so will
450 * be the compat argument.
451 */
452 static bits_t *
453 getkey_pkcs5_pbkdf2(const char *target, struct keygen *kg, size_t keylen,
454 int compat)
455 {
456 bits_t *ret;
457 char *passp;
458 char buf[1024];
459 u_int8_t *tmp;
460
461 snprintf(buf, sizeof(buf), "%s's passphrase%s:", target,
462 pflag & PFLAG_GETPASS_ECHO ? " (echo)" : "");
463 passp = maybe_getpass(buf);
464 if (pkcs5_pbkdf2(&tmp, BITS2BYTES(keylen), (uint8_t *)passp,
465 strlen(passp),
466 bits_getbuf(kg->kg_salt), BITS2BYTES(bits_len(kg->kg_salt)),
467 kg->kg_iterations, compat)) {
468 warnx("failed to generate PKCS#5 PBKDF2 key");
469 return NULL;
470 }
471
472 ret = bits_new(tmp, keylen);
473 kg->kg_key = bits_dup(ret);
474 explicit_memset(passp, 0, strlen(passp));
475 free(passp);
476 free(tmp);
477 return ret;
478 }
479
480 #ifdef HAVE_ARGON2
481 static bits_t *
482 getkey_argon2id(const char *target, struct keygen *kg, size_t keylen)
483 {
484 bits_t *ret;
485 char *passp;
486 char buf[1024];
487 uint8_t raw[256];
488 int err;
489
490 snprintf(buf, sizeof(buf), "%s's passphrase%s:", target,
491 pflag & PFLAG_GETPASS_ECHO ? " (echo)" : "");
492 passp = maybe_getpass(buf);
493 if ((err = argon2_hash(kg->kg_iterations, kg->kg_memory,
494 kg->kg_parallelism,
495 passp, strlen(passp),
496 bits_getbuf(kg->kg_salt),
497 BITS2BYTES(bits_len(kg->kg_salt)),
498 raw, sizeof(raw),
499 NULL, 0,
500 Argon2_id, kg->kg_version)) != ARGON2_OK) {
501 warnx("failed to generate Argon2id key, error code %d", err);
502 return NULL;
503 }
504
505 ret = bits_new(raw, keylen);
506 kg->kg_key = bits_dup(ret);
507 explicit_memset(passp, 0, strlen(passp));
508 explicit_memset(raw, 0, sizeof(raw));
509 free(passp);
510 return ret;
511 }
512 #endif
513
514 /*ARGSUSED*/
515 static bits_t *
516 getkey_shell_cmd(const char *target, struct keygen *kg, size_t keylen)
517 {
518 FILE *f;
519 bits_t *ret;
520 int status;
521
522 if ((f = popen(string_tocharstar(kg->kg_cmd), "r")) == NULL)
523 errx(1, "command failed");
524 if ((ret = bits_fget(f, keylen)) == NULL)
525 errx(1, "command output too short");
526 if ((status = pclose(f)) != 0)
527 err(1, "command failed with status %d", status);
528
529 return ret;
530 }
531
532 /*ARGSUSED*/
533 static int
534 unconfigure(int argc, char **argv, struct params *inparams, int flags)
535 {
536 int fd;
537 int ret;
538 char buf[MAXPATHLEN] = "";
539
540 /* only complain about additional arguments, if called from main() */
541 if (flags == CONFIG_FLAGS_FROMMAIN && argc != 1)
542 usage();
543
544 /* if called from do_all(), then ensure that 2 or 3 args exist */
545 if (flags == CONFIG_FLAGS_FROMALL && (argc < 2 || argc > 3))
546 return -1;
547
548 fd = opendisk1(*argv, O_RDWR, buf, sizeof(buf), 1, prog_open);
549 if (fd == -1) {
550 int saved_errno = errno;
551
552 warn("can't open cgd \"%s\", \"%s\"", *argv, buf);
553
554 /* this isn't fatal with nflag != 0 */
555 if (!nflag)
556 return saved_errno;
557 }
558
559 VPRINTF(1, ("%s (%s): clearing\n", *argv, buf));
560
561 if (nflag)
562 return 0;
563
564 ret = unconfigure_fd(fd);
565 (void)prog_close(fd);
566 return ret;
567 }
568
569 static int
570 unconfigure_fd(int fd)
571 {
572 struct cgd_ioctl ci;
573
574 if (prog_ioctl(fd, CGDIOCCLR, &ci) == -1) {
575 warn("ioctl");
576 return -1;
577 }
578
579 return 0;
580 }
581
582 /*ARGSUSED*/
583 static int
584 configure(int argc, char **argv, struct params *inparams, int flags)
585 {
586 struct params *p;
587 struct keygen *kg;
588 int fd;
589 int loop = 0;
590 int ret;
591 char cgdname[PATH_MAX];
592 char devicename[PATH_MAX];
593 const char *dev = NULL; /* XXX: gcc */
594
595 if (argc < 2 || argc > 3) {
596 /* print usage and exit, only if called from main() */
597 if (flags == CONFIG_FLAGS_FROMMAIN) {
598 warnx("wrong number of args");
599 usage();
600 }
601 return -1;
602 }
603
604 if ((
605 fd = opendisk1(*argv, O_RDWR, cgdname, sizeof(cgdname), 1, prog_open)
606 ) != -1) {
607 struct cgd_user cgu;
608
609 cgu.cgu_unit = -1;
610 if (prog_ioctl(fd, CGDIOCGET, &cgu) != -1 && cgu.cgu_dev != 0) {
611 warnx("device %s already in use", *argv);
612 prog_close(fd);
613 return -1;
614 }
615 prog_close(fd);
616 }
617
618 dev = getfsspecname(devicename, sizeof(devicename), argv[1]);
619 if (dev == NULL) {
620 warnx("getfsspecname failed: %s", devicename);
621 return -1;
622 }
623
624 if (argc == 2) {
625 char pfile[MAXPATHLEN];
626
627 /* make string writable for basename */
628 strlcpy(pfile, dev, sizeof(pfile));
629 p = params_cget(basename(pfile));
630 } else
631 p = params_cget(argv[2]);
632
633 if (!p)
634 return -1;
635
636 /*
637 * over-ride with command line specifications and fill in default
638 * values.
639 */
640
641 p = params_combine(p, inparams);
642 ret = params_filldefaults(p);
643 if (ret) {
644 params_free(p);
645 return ret;
646 }
647
648 if (!params_verify(p)) {
649 warnx("params invalid");
650 return -1;
651 }
652
653 /*
654 * loop over configuring the disk and checking to see if it
655 * verifies properly. We open and close the disk device each
656 * time, because if the user passes us the block device we
657 * need to flush the buffer cache.
658 *
659 * We only loop if one of the verification methods prompts for
660 * a password.
661 */
662
663 for (kg = p->keygen;
664 (pflag & PFLAG_GETPASS_MASK) && kg;
665 kg = kg->next)
666 if (kg->kg_method == KEYGEN_ARGON2ID ||
667 kg->kg_method == KEYGEN_PKCS5_PBKDF2_SHA1 ||
668 kg->kg_method == KEYGEN_PKCS5_PBKDF2_OLD) {
669 loop = 1;
670 break;
671 }
672
673 for (;;) {
674 fd = opendisk_werror(argv[0], cgdname, sizeof(cgdname));
675 if (fd == -1)
676 return -1;
677
678 if (p->key)
679 bits_free(p->key);
680
681 p->key = getkey(argv[1], p->keygen, p->keylen);
682 if (!p->key)
683 goto bail_err;
684
685 ret = configure_params(fd, cgdname, dev, p);
686 if (ret)
687 goto bail_err;
688
689 ret = verify(p, fd);
690 if (ret == -1) {
691 (void)unconfigure_fd(fd);
692 goto bail_err;
693 }
694 if (ret == 0) /* success */
695 break;
696
697 (void)unconfigure_fd(fd);
698 (void)prog_close(fd);
699
700 if (!loop) {
701 warnx("verification failed permanently");
702 goto bail_err;
703 }
704
705 warnx("verification failed, please reenter passphrase");
706 }
707
708 params_free(p);
709 (void)prog_close(fd);
710 return 0;
711
712 bail_err:;
713 params_free(p);
714 (void)prog_close(fd);
715 return -1;
716 }
717
718 static int
719 configure_stdin(struct params *p, int argc, char **argv)
720 {
721 int fd;
722 int ret;
723 char cgdname[PATH_MAX];
724 char devicename[PATH_MAX];
725 const char *dev;
726
727 if (argc < 3 || argc > 4)
728 usage();
729
730 dev = getfsspecname(devicename, sizeof(devicename), argv[1]);
731 if (dev == NULL) {
732 warnx("getfsspecname failed: %s", devicename);
733 return -1;
734 }
735
736 p->algorithm = string_fromcharstar(argv[2]);
737 if (argc > 3) {
738 size_t keylen;
739
740 if (parse_size_t(argv[3], &keylen) == -1) {
741 warn("failed to parse key length");
742 return -1;
743 }
744 p->keylen = keylen;
745 }
746
747 ret = params_filldefaults(p);
748 if (ret)
749 return ret;
750
751 fd = opendisk_werror(argv[0], cgdname, sizeof(cgdname));
752 if (fd == -1)
753 return -1;
754
755 p->key = bits_fget(stdin, p->keylen);
756 if (!p->key) {
757 warnx("failed to read key from stdin");
758 return -1;
759 }
760
761 return configure_params(fd, cgdname, dev, p);
762 }
763
764 static int
765 opendisk_werror(const char *cgd, char *buf, size_t buflen)
766 {
767 int fd;
768
769 VPRINTF(3, ("opendisk_werror(%s, %s, %zu) called.\n", cgd,buf,buflen));
770
771 /* sanity */
772 if (!cgd || !buf)
773 return -1;
774
775 if (nflag) {
776 if (strlcpy(buf, cgd, buflen) >= buflen)
777 return -1;
778 return 0;
779 }
780
781 fd = opendisk1(cgd, O_RDWR, buf, buflen, 0, prog_open);
782 if (fd == -1)
783 warnx("can't open cgd \"%s\", \"%s\"", cgd, buf);
784
785 return fd;
786 }
787
788 static int
789 configure_params(int fd, const char *cgd, const char *dev, struct params *p)
790 {
791 struct cgd_ioctl ci;
792
793 /* sanity */
794 if (!cgd || !dev)
795 return -1;
796
797 (void)memset(&ci, 0x0, sizeof(ci));
798 ci.ci_disk = dev;
799 ci.ci_alg = string_tocharstar(p->algorithm);
800 ci.ci_ivmethod = string_tocharstar(p->ivmeth);
801 ci.ci_key = bits_getbuf(p->key);
802 ci.ci_keylen = p->keylen;
803 ci.ci_blocksize = p->bsize;
804
805 VPRINTF(1, (" with alg %s keylen %zu blocksize %zu ivmethod %s\n",
806 string_tocharstar(p->algorithm), p->keylen, p->bsize,
807 string_tocharstar(p->ivmeth)));
808 VPRINTF(2, ("key: "));
809 VERBOSE(2, bits_fprint(stdout, p->key));
810 VPRINTF(2, ("\n"));
811
812 if (nflag)
813 return 0;
814
815 if (prog_ioctl(fd, CGDIOCSET, &ci) == -1) {
816 int saved_errno = errno;
817 warn("ioctl");
818 return saved_errno;
819 }
820
821 return 0;
822 }
823
824 /*
825 * verify returns 0 for success, -1 for unrecoverable error, or 1 for retry.
826 */
827
828 #define SCANSIZE 8192
829
830 static int
831 verify(struct params *p, int fd)
832 {
833
834 switch (p->verify_method) {
835 case VERIFY_NONE:
836 return 0;
837 case VERIFY_DISKLABEL:
838 return verify_disklabel(fd);
839 case VERIFY_FFS:
840 return verify_ffs(fd);
841 case VERIFY_REENTER:
842 return verify_reenter(p);
843 case VERIFY_MBR:
844 return verify_mbr(fd);
845 case VERIFY_GPT:
846 return verify_gpt(fd);
847 default:
848 warnx("unimplemented verification method");
849 return -1;
850 }
851 }
852
853 static int
854 verify_disklabel(int fd)
855 {
856 struct disklabel l;
857 ssize_t ret;
858 char buf[SCANSIZE];
859
860 /*
861 * we simply scan the first few blocks for a disklabel, ignoring
862 * any MBR/filecore sorts of logic. MSDOS and RiscOS can't read
863 * a cgd, anyway, so it is unlikely that there will be non-native
864 * partition information.
865 */
866
867 ret = prog_pread(fd, buf, SCANSIZE, 0);
868 if (ret < 0) {
869 warn("can't read disklabel area");
870 return -1;
871 }
872
873 /* now scan for the disklabel */
874
875 return disklabel_scan(&l, buf, (size_t)ret);
876 }
877
878 static int
879 verify_mbr(int fd)
880 {
881 struct mbr_sector mbr;
882 ssize_t ret;
883 char buf[SCANSIZE];
884
885 /*
886 * we read the first blocks to avoid sector size issues and
887 * verify the MBR in the beginning
888 */
889
890 ret = prog_pread(fd, buf, SCANSIZE, 0);
891 if (ret < 0) {
892 warn("can't read mbr area");
893 return -1;
894 }
895
896 memcpy(&mbr, buf, sizeof(mbr));
897 if (le16toh(mbr.mbr_magic) != MBR_MAGIC)
898 return 1;
899
900 return 0;
901 }
902
903 static uint32_t crc32_tab[] = {
904 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
905 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
906 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
907 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
908 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
909 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
910 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
911 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
912 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
913 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
914 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
915 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
916 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
917 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
918 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
919 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
920 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
921 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
922 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
923 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
924 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
925 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
926 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
927 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
928 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
929 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
930 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
931 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
932 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
933 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
934 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
935 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
936 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
937 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
938 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
939 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
940 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
941 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
942 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
943 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
944 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
945 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
946 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
947 };
948
949 static uint32_t
950 crc32(const void *buf, size_t size)
951 {
952 const uint8_t *p;
953 uint32_t crc;
954
955 p = buf;
956 crc = ~0U;
957
958 while (size--)
959 crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
960
961 return crc ^ ~0U;
962 }
963
964 static int
965 verify_gpt(int fd)
966 {
967 struct gpt_hdr hdr;
968 ssize_t ret;
969 char buf[SCANSIZE];
970 unsigned blksize;
971 size_t off;
972
973 /*
974 * we read the first blocks to avoid sector size issues and
975 * verify the GPT header.
976 */
977
978 ret = prog_pread(fd, buf, SCANSIZE, 0);
979 if (ret < 0) {
980 warn("can't read gpt area");
981 return -1;
982 }
983
984 ret = 1;
985 for (blksize = DEV_BSIZE;
986 (off = (blksize * GPT_HDR_BLKNO)) <= SCANSIZE - sizeof(hdr);
987 blksize <<= 1) {
988
989 memcpy(&hdr, &buf[off], sizeof(hdr));
990 if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) == 0
991 && le32toh(hdr.hdr_revision) == GPT_HDR_REVISION
992 && le32toh(hdr.hdr_size) == GPT_HDR_SIZE) {
993
994 hdr.hdr_crc_self = 0;
995 if (crc32(&hdr, sizeof(hdr))) {
996 ret = 0;
997 break;
998 }
999 }
1000 }
1001
1002 return ret;
1003 }
1004
1005 static off_t sblock_try[] = SBLOCKSEARCH;
1006
1007 static int
1008 verify_ffs(int fd)
1009 {
1010 size_t i;
1011
1012 for (i = 0; sblock_try[i] != -1; i++) {
1013 union {
1014 char buf[SBLOCKSIZE];
1015 struct fs fs;
1016 } u;
1017 ssize_t ret;
1018
1019 ret = prog_pread(fd, &u, sizeof(u), sblock_try[i]);
1020 if (ret < 0) {
1021 warn("pread");
1022 break;
1023 } else if ((size_t)ret < sizeof(u)) {
1024 warnx("pread: incomplete block");
1025 break;
1026 }
1027 switch (u.fs.fs_magic) {
1028 case FS_UFS1_MAGIC:
1029 case FS_UFS2_MAGIC:
1030 case FS_UFS1_MAGIC_SWAPPED:
1031 case FS_UFS2_MAGIC_SWAPPED:
1032 return 0;
1033 default:
1034 continue;
1035 }
1036 }
1037
1038 return 1; /* failure */
1039 }
1040
1041 static int
1042 verify_reenter(struct params *p)
1043 {
1044 struct keygen *kg;
1045 bits_t *orig_key, *key = NULL;
1046 int ret;
1047
1048 ret = 0;
1049 for (kg = p->keygen; kg && !ret; kg = kg->next) {
1050 if (kg->kg_method != KEYGEN_ARGON2ID &&
1051 kg->kg_method != KEYGEN_PKCS5_PBKDF2_SHA1 &&
1052 kg->kg_method != KEYGEN_PKCS5_PBKDF2_OLD)
1053 continue;
1054
1055 orig_key = kg->kg_key;
1056 kg->kg_key = NULL;
1057
1058 switch (kg->kg_method) {
1059 #ifdef HAVE_ARGON2
1060 case KEYGEN_ARGON2ID:
1061 key = getkey_argon2id("re-enter device", kg,
1062 bits_len(orig_key));
1063 break;
1064 #endif
1065 case KEYGEN_PKCS5_PBKDF2_SHA1:
1066 key = getkey_pkcs5_pbkdf2("re-enter device", kg,
1067 bits_len(orig_key), 0);
1068 break;
1069 case KEYGEN_PKCS5_PBKDF2_OLD:
1070 key = getkey_pkcs5_pbkdf2("re-enter device", kg,
1071 bits_len(orig_key), 1);
1072 break;
1073 default:
1074 warnx("unsupported keygen method");
1075 kg->kg_key = orig_key;
1076 return -1;
1077 }
1078
1079 ret = !bits_match(key, orig_key);
1080
1081 bits_free(key);
1082 bits_free(kg->kg_key);
1083 kg->kg_key = orig_key;
1084 }
1085
1086 return ret;
1087 }
1088
1089 static int
1090 generate(struct params *p, int argc, char **argv, const char *outfile)
1091 {
1092 int ret;
1093
1094 if (argc < 1 || argc > 2)
1095 usage();
1096
1097 p->algorithm = string_fromcharstar(argv[0]);
1098 if (argc > 1) {
1099 size_t keylen;
1100
1101 if (parse_size_t(argv[1], &keylen) == -1) {
1102 warn("Failed to parse key length");
1103 return -1;
1104 }
1105 p->keylen = keylen;
1106 }
1107
1108 ret = params_filldefaults(p);
1109 if (ret)
1110 return ret;
1111
1112 if (!p->keygen) {
1113 p->keygen = keygen_generate(KEYGEN_PKCS5_PBKDF2_SHA1);
1114 if (!p->keygen)
1115 return -1;
1116 }
1117
1118 if (keygen_filldefaults(p->keygen, p->keylen)) {
1119 warnx("Failed to generate defaults for keygen");
1120 return -1;
1121 }
1122
1123 if (!params_verify(p)) {
1124 warnx("invalid parameters generated");
1125 return -1;
1126 }
1127
1128 return params_cput(p, outfile);
1129 }
1130
1131 static int
1132 generate_convert(struct params *p, int argc, char **argv, const char *outfile)
1133 {
1134 struct params *oldp;
1135 struct keygen *kg;
1136
1137 if (argc != 1)
1138 usage();
1139
1140 oldp = params_cget(*argv);
1141 if (!oldp)
1142 return -1;
1143
1144 /* for sanity, we ensure that none of the keygens are randomkey */
1145 for (kg=p->keygen; kg; kg=kg->next)
1146 if ((kg->kg_method == KEYGEN_RANDOMKEY) ||
1147 (kg->kg_method == KEYGEN_URANDOMKEY)) {
1148 warnx("can't preserve randomly generated key");
1149 goto bail;
1150 }
1151 for (kg=oldp->keygen; kg; kg=kg->next)
1152 if ((kg->kg_method == KEYGEN_RANDOMKEY) ||
1153 (kg->kg_method == KEYGEN_URANDOMKEY)) {
1154 warnx("can't preserve randomly generated key");
1155 goto bail;
1156 }
1157
1158 if (!params_verify(oldp)) {
1159 warnx("invalid old parameters file \"%s\"", *argv);
1160 return -1;
1161 }
1162
1163 oldp->key = getkey("old file", oldp->keygen, oldp->keylen);
1164
1165 /* we copy across the non-keygen info, here. */
1166
1167 string_free(p->algorithm);
1168 string_free(p->ivmeth);
1169
1170 p->algorithm = string_dup(oldp->algorithm);
1171 p->ivmeth = string_dup(oldp->ivmeth);
1172 p->keylen = oldp->keylen;
1173 p->bsize = oldp->bsize;
1174 if (p->verify_method == VERIFY_UNKNOWN)
1175 p->verify_method = oldp->verify_method;
1176
1177 params_free(oldp);
1178
1179 if (!p->keygen) {
1180 p->keygen = keygen_generate(KEYGEN_PKCS5_PBKDF2_SHA1);
1181 if (!p->keygen)
1182 return -1;
1183 }
1184 (void)params_filldefaults(p);
1185 (void)keygen_filldefaults(p->keygen, p->keylen);
1186 p->key = getkey("new file", p->keygen, p->keylen);
1187
1188 kg = keygen_generate(KEYGEN_STOREDKEY);
1189 kg->kg_key = bits_xor(p->key, oldp->key);
1190 keygen_addlist(&p->keygen, kg);
1191
1192 if (!params_verify(p)) {
1193 warnx("can't generate new parameters file");
1194 return -1;
1195 }
1196
1197 return params_cput(p, outfile);
1198 bail:;
1199 params_free(oldp);
1200 return -1;
1201 }
1202
1203 static int
1204 /*ARGSUSED*/
1205 do_all(const char *cfile, int argc, char **argv,
1206 int (*conf)(int, char **, struct params *, int))
1207 {
1208 FILE *f;
1209 size_t len;
1210 size_t lineno;
1211 int my_argc;
1212 int ret;
1213 const char *fn;
1214 char *line;
1215 char **my_argv;
1216
1217 if (argc > 0)
1218 usage();
1219
1220 if (!cfile[0])
1221 fn = CGDCONFIG_CFILE;
1222 else
1223 fn = cfile;
1224
1225 f = fopen(fn, "r");
1226 if (f == NULL) {
1227 warn("could not open config file \"%s\"", fn);
1228 return -1;
1229 }
1230
1231 ret = 0;
1232 lineno = 0;
1233 for (;;) {
1234 line = fparseln(f, &len, &lineno, "\\\\#", FPARSELN_UNESCALL);
1235 if (!line)
1236 break;
1237 if (!*line)
1238 continue;
1239
1240 my_argv = words(line, &my_argc);
1241 ret = conf(my_argc, my_argv, NULL, CONFIG_FLAGS_FROMALL);
1242 if (ret) {
1243 warnx("action failed on \"%s\" line %lu", fn,
1244 (u_long)lineno);
1245 break;
1246 }
1247 words_free(my_argv, my_argc);
1248 }
1249 return ret;
1250 }
1251
1252 static const char *
1253 iv_method(int mode)
1254 {
1255
1256 switch (mode) {
1257 case CGD_CIPHER_CBC_ENCBLKNO8:
1258 return "encblkno8";
1259 case CGD_CIPHER_CBC_ENCBLKNO1:
1260 return "encblkno1";
1261 default:
1262 return "unknown";
1263 }
1264 }
1265
1266
1267 static void
1268 show(const char *dev) {
1269 char path[64];
1270 struct cgd_user cgu;
1271 int fd;
1272
1273 fd = opendisk(dev, O_RDONLY, path, sizeof(path), 0);
1274 if (fd == -1) {
1275 warn("open: %s", dev);
1276 return;
1277 }
1278
1279 cgu.cgu_unit = -1;
1280 if (prog_ioctl(fd, CGDIOCGET, &cgu) == -1) {
1281 close(fd);
1282 err(1, "CGDIOCGET");
1283 }
1284
1285 printf("%s: ", dev);
1286
1287 if (cgu.cgu_dev == 0) {
1288 printf("not in use");
1289 goto out;
1290 }
1291
1292 dev = devname(cgu.cgu_dev, S_IFBLK);
1293 if (dev != NULL)
1294 printf("%s ", dev);
1295 else
1296 printf("dev %llu,%llu ", (unsigned long long)major(cgu.cgu_dev),
1297 (unsigned long long)minor(cgu.cgu_dev));
1298
1299 if (verbose)
1300 printf("%s ", cgu.cgu_alg);
1301 if (verbose > 1) {
1302 printf("keylen %d ", cgu.cgu_keylen);
1303 printf("blksize %zd ", cgu.cgu_blocksize);
1304 printf("%s ", iv_method(cgu.cgu_mode));
1305 }
1306
1307 out:;
1308 putchar('\n');
1309 close(fd);
1310 }
1311
1312 static int
1313 do_list(int argc, char **argv)
1314 {
1315
1316 if (argc != 0 && argc != 1)
1317 usage();
1318
1319 if (argc) {
1320 show(argv[0]);
1321 return 0;
1322 }
1323
1324 DIR *dirp;
1325 struct dirent *dp;
1326 __BITMAP_TYPE(, uint32_t, 65536) bm;
1327
1328 __BITMAP_ZERO(&bm);
1329
1330 if ((dirp = opendir(_PATH_DEV)) == NULL)
1331 err(1, "opendir: %s", _PATH_DEV);
1332
1333 while ((dp = readdir(dirp)) != NULL) {
1334 char *ep;
1335 if (strncmp(dp->d_name, "rcgd", 4) != 0)
1336 continue;
1337 errno = 0;
1338 int n = (int)strtol(dp->d_name + 4, &ep, 0);
1339 if (ep == dp->d_name + 4 || errno != 0) {
1340 warnx("bad name %s", dp->d_name);
1341 continue;
1342 }
1343 *ep = '\0';
1344 if (__BITMAP_ISSET(n, &bm))
1345 continue;
1346 __BITMAP_SET(n, &bm);
1347 show(dp->d_name + 1);
1348 }
1349
1350 closedir(dirp);
1351 return 0;
1352 }
1353
1354 static int
1355 do_printkey(int argc, char **argv)
1356 {
1357 struct params *p;
1358 const uint8_t *raw;
1359 size_t nbits, nbytes;
1360 size_t nb64;
1361 char *b64;
1362 int ret;
1363
1364 if (argc != 1)
1365 usage();
1366 p = params_cget(argv[0]);
1367 if (p == NULL)
1368 return -1;
1369 if (!params_verify(p)) {
1370 warnx("invalid parameters file \"%s\"", argv[0]);
1371 return -1;
1372 }
1373 p->key = getkey("key", p->keygen, p->keylen);
1374 raw = bits_getbuf(p->key);
1375 nbits = bits_len(p->key);
1376 assert(nbits <= INT_MAX - 7);
1377 nbytes = BITS2BYTES(nbits);
1378 assert(nbytes <= 3*(INT_MAX/4) - 2);
1379
1380 nb64 = 4*((nbytes + 2)/3);
1381 b64 = emalloc(nb64 + 2);
1382 ret = __b64_ntop(raw, nbytes, b64, nb64 + 1);
1383 assert(ret == (int)nb64);
1384 b64[nb64] = '\n';
1385 b64[nb64 + 1] = '\0';
1386
1387 if (fwrite(b64, nb64 + 1, 1, stdout) != 1)
1388 err(1, "fwrite");
1389 fflush(stdout);
1390 return ferror(stdout);
1391 }
1392
1393 static void
1394 eliminate_cores(void)
1395 {
1396 struct rlimit rlp;
1397
1398 rlp.rlim_cur = 0;
1399 rlp.rlim_max = 0;
1400 if (setrlimit(RLIMIT_CORE, &rlp) == -1)
1401 err(EXIT_FAILURE, "Can't disable cores");
1402 }
1403