xref: /freebsd/sbin/dumpon/dumpon.c (revision 52b63df9b6dfc157fb0b9f61a770b64e3663dee9)
18a16b7a1SPedro F. Giffuni /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
483f9dfabSGarrett Wollman  * Copyright (c) 1980, 1993
583f9dfabSGarrett Wollman  *	The Regents of the University of California.  All rights reserved.
683f9dfabSGarrett Wollman  *
783f9dfabSGarrett Wollman  * Redistribution and use in source and binary forms, with or without
883f9dfabSGarrett Wollman  * modification, are permitted provided that the following conditions
983f9dfabSGarrett Wollman  * are met:
1083f9dfabSGarrett Wollman  * 1. Redistributions of source code must retain the above copyright
1183f9dfabSGarrett Wollman  *    notice, this list of conditions and the following disclaimer.
1283f9dfabSGarrett Wollman  * 2. Redistributions in binary form must reproduce the above copyright
1383f9dfabSGarrett Wollman  *    notice, this list of conditions and the following disclaimer in the
1483f9dfabSGarrett Wollman  *    documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
1683f9dfabSGarrett Wollman  *    may be used to endorse or promote products derived from this software
1783f9dfabSGarrett Wollman  *    without specific prior written permission.
1883f9dfabSGarrett Wollman  *
1983f9dfabSGarrett Wollman  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2083f9dfabSGarrett Wollman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2183f9dfabSGarrett Wollman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2283f9dfabSGarrett Wollman  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2383f9dfabSGarrett Wollman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2483f9dfabSGarrett Wollman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2583f9dfabSGarrett Wollman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2683f9dfabSGarrett Wollman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2783f9dfabSGarrett Wollman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2883f9dfabSGarrett Wollman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2983f9dfabSGarrett Wollman  * SUCH DAMAGE.
3083f9dfabSGarrett Wollman  */
3183f9dfabSGarrett Wollman 
32c69284caSDavid E. O'Brien #if 0
3383f9dfabSGarrett Wollman #ifndef lint
348f034b11SPhilippe Charnier static const char copyright[] =
3583f9dfabSGarrett Wollman "@(#) Copyright (c) 1980, 1993\n\
3683f9dfabSGarrett Wollman 	The Regents of the University of California.  All rights reserved.\n";
3783f9dfabSGarrett Wollman #endif /* not lint */
3883f9dfabSGarrett Wollman 
3983f9dfabSGarrett Wollman #ifndef lint
408f034b11SPhilippe Charnier static char sccsid[] = "From: @(#)swapon.c	8.1 (Berkeley) 6/5/93";
4183f9dfabSGarrett Wollman #endif /* not lint */
42c69284caSDavid E. O'Brien #endif
43c69284caSDavid E. O'Brien #include <sys/cdefs.h>
44c69284caSDavid E. O'Brien __FBSDID("$FreeBSD$");
4583f9dfabSGarrett Wollman 
4683f9dfabSGarrett Wollman #include <sys/param.h>
47480f31c2SKonrad Witaszczyk #include <sys/capsicum.h>
482dd527b3SPoul-Henning Kamp #include <sys/disk.h>
490ff40d3dSMark Johnston #include <sys/socket.h>
50c0046e26SDag-Erling Smørgrav #include <sys/sysctl.h>
51372557d8SBryan Drewery #include <sys/wait.h>
5283f9dfabSGarrett Wollman 
53480f31c2SKonrad Witaszczyk #include <assert.h>
547672a014SMariusz Zaborski #include <capsicum_helpers.h>
55c0046e26SDag-Erling Smørgrav #include <err.h>
56f6848434SAlfred Perlstein #include <errno.h>
57c0046e26SDag-Erling Smørgrav #include <fcntl.h>
580ff40d3dSMark Johnston #include <ifaddrs.h>
590ff40d3dSMark Johnston #include <netdb.h>
60c0046e26SDag-Erling Smørgrav #include <paths.h>
61480f31c2SKonrad Witaszczyk #include <stdbool.h>
62c0046e26SDag-Erling Smørgrav #include <stdint.h>
63c0046e26SDag-Erling Smørgrav #include <stdio.h>
64c0046e26SDag-Erling Smørgrav #include <stdlib.h>
65c0046e26SDag-Erling Smørgrav #include <string.h>
66c0046e26SDag-Erling Smørgrav #include <sysexits.h>
67c0046e26SDag-Erling Smørgrav #include <unistd.h>
68c0046e26SDag-Erling Smørgrav 
690ff40d3dSMark Johnston #include <arpa/inet.h>
700ff40d3dSMark Johnston 
710ff40d3dSMark Johnston #include <net/if.h>
720ff40d3dSMark Johnston #include <net/if_dl.h>
730ff40d3dSMark Johnston #include <net/route.h>
740ff40d3dSMark Johnston 
750ff40d3dSMark Johnston #include <netinet/in.h>
760ff40d3dSMark Johnston #include <netinet/netdump/netdump.h>
770ff40d3dSMark Johnston 
78480f31c2SKonrad Witaszczyk #ifdef HAVE_CRYPTO
79480f31c2SKonrad Witaszczyk #include <openssl/err.h>
80480f31c2SKonrad Witaszczyk #include <openssl/pem.h>
814647ce4fSConrad Meyer #include <openssl/rand.h>
82480f31c2SKonrad Witaszczyk #include <openssl/rsa.h>
83480f31c2SKonrad Witaszczyk #endif
84480f31c2SKonrad Witaszczyk 
85c0046e26SDag-Erling Smørgrav static int	verbose;
86c0046e26SDag-Erling Smørgrav 
870ff40d3dSMark Johnston static void _Noreturn
88c0046e26SDag-Erling Smørgrav usage(void)
89c0046e26SDag-Erling Smørgrav {
900ff40d3dSMark Johnston 	fprintf(stderr,
916b6e2954SConrad Meyer     "usage: dumpon [-i index] [-r] [-v] [-k <pubkey>] [-Zz] <device>\n"
926b6e2954SConrad Meyer     "       dumpon [-i index] [-r] [-v] [-k <pubkey>] [-Zz]\n"
93e5fff57dSMark Johnston     "              [-g <gateway>] -s <server> -c <client> <iface>\n"
940ff40d3dSMark Johnston     "       dumpon [-v] off\n"
950ff40d3dSMark Johnston     "       dumpon [-v] -l\n");
96c0046e26SDag-Erling Smørgrav 	exit(EX_USAGE);
97c0046e26SDag-Erling Smørgrav }
98c0046e26SDag-Erling Smørgrav 
990ff40d3dSMark Johnston /*
1000ff40d3dSMark Johnston  * Look for a default route on the specified interface.
1010ff40d3dSMark Johnston  */
1020ff40d3dSMark Johnston static char *
1030ff40d3dSMark Johnston find_gateway(const char *ifname)
1040ff40d3dSMark Johnston {
1050ff40d3dSMark Johnston 	struct ifaddrs *ifa, *ifap;
1060ff40d3dSMark Johnston 	struct rt_msghdr *rtm;
1070ff40d3dSMark Johnston 	struct sockaddr *sa;
1080ff40d3dSMark Johnston 	struct sockaddr_dl *sdl;
1090ff40d3dSMark Johnston 	struct sockaddr_in *dst, *mask, *gw;
1100ff40d3dSMark Johnston 	char *buf, *next, *ret;
1110ff40d3dSMark Johnston 	size_t sz;
1120ff40d3dSMark Johnston 	int error, i, ifindex, mib[7];
1130ff40d3dSMark Johnston 
1140ff40d3dSMark Johnston 	/* First look up the interface index. */
1150ff40d3dSMark Johnston 	if (getifaddrs(&ifap) != 0)
1160ff40d3dSMark Johnston 		err(EX_OSERR, "getifaddrs");
1170ff40d3dSMark Johnston 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
1180ff40d3dSMark Johnston 		if (ifa->ifa_addr->sa_family != AF_LINK)
1190ff40d3dSMark Johnston 			continue;
1200ff40d3dSMark Johnston 		if (strcmp(ifa->ifa_name, ifname) == 0) {
1210ff40d3dSMark Johnston 			sdl = (struct sockaddr_dl *)(void *)ifa->ifa_addr;
1220ff40d3dSMark Johnston 			ifindex = sdl->sdl_index;
1230ff40d3dSMark Johnston 			break;
1240ff40d3dSMark Johnston 		}
1250ff40d3dSMark Johnston 	}
1260ff40d3dSMark Johnston 	if (ifa == NULL)
1270ff40d3dSMark Johnston 		errx(1, "couldn't find interface index for '%s'", ifname);
1280ff40d3dSMark Johnston 	freeifaddrs(ifap);
1290ff40d3dSMark Johnston 
1300ff40d3dSMark Johnston 	/* Now get the IPv4 routing table. */
1310ff40d3dSMark Johnston 	mib[0] = CTL_NET;
1320ff40d3dSMark Johnston 	mib[1] = PF_ROUTE;
1330ff40d3dSMark Johnston 	mib[2] = 0;
1340ff40d3dSMark Johnston 	mib[3] = AF_INET;
1350ff40d3dSMark Johnston 	mib[4] = NET_RT_DUMP;
1360ff40d3dSMark Johnston 	mib[5] = 0;
1370ff40d3dSMark Johnston 	mib[6] = -1; /* FIB */
1380ff40d3dSMark Johnston 
1390ff40d3dSMark Johnston 	for (;;) {
1400ff40d3dSMark Johnston 		if (sysctl(mib, nitems(mib), NULL, &sz, NULL, 0) != 0)
1410ff40d3dSMark Johnston 			err(EX_OSERR, "sysctl(NET_RT_DUMP)");
1420ff40d3dSMark Johnston 		buf = malloc(sz);
1430ff40d3dSMark Johnston 		error = sysctl(mib, nitems(mib), buf, &sz, NULL, 0);
1440ff40d3dSMark Johnston 		if (error == 0)
1450ff40d3dSMark Johnston 			break;
1460ff40d3dSMark Johnston 		if (errno != ENOMEM)
1470ff40d3dSMark Johnston 			err(EX_OSERR, "sysctl(NET_RT_DUMP)");
1480ff40d3dSMark Johnston 		free(buf);
1490ff40d3dSMark Johnston 	}
1500ff40d3dSMark Johnston 
151e5fff57dSMark Johnston 	ret = NULL;
1520ff40d3dSMark Johnston 	for (next = buf; next < buf + sz; next += rtm->rtm_msglen) {
1530ff40d3dSMark Johnston 		rtm = (struct rt_msghdr *)(void *)next;
1540ff40d3dSMark Johnston 		if (rtm->rtm_version != RTM_VERSION)
1550ff40d3dSMark Johnston 			continue;
1560ff40d3dSMark Johnston 		if ((rtm->rtm_flags & RTF_GATEWAY) == 0 ||
1570ff40d3dSMark Johnston 		    rtm->rtm_index != ifindex)
1580ff40d3dSMark Johnston 			continue;
1590ff40d3dSMark Johnston 
1600ff40d3dSMark Johnston 		dst = gw = mask = NULL;
1610ff40d3dSMark Johnston 		sa = (struct sockaddr *)(rtm + 1);
1620ff40d3dSMark Johnston 		for (i = 0; i < RTAX_MAX; i++) {
1630ff40d3dSMark Johnston 			if ((rtm->rtm_addrs & (1 << i)) != 0) {
1640ff40d3dSMark Johnston 				switch (i) {
1650ff40d3dSMark Johnston 				case RTAX_DST:
1660ff40d3dSMark Johnston 					dst = (void *)sa;
1670ff40d3dSMark Johnston 					break;
1680ff40d3dSMark Johnston 				case RTAX_GATEWAY:
1690ff40d3dSMark Johnston 					gw = (void *)sa;
1700ff40d3dSMark Johnston 					break;
1710ff40d3dSMark Johnston 				case RTAX_NETMASK:
1720ff40d3dSMark Johnston 					mask = (void *)sa;
1730ff40d3dSMark Johnston 					break;
1740ff40d3dSMark Johnston 				}
1750ff40d3dSMark Johnston 			}
1760ff40d3dSMark Johnston 			sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa));
1770ff40d3dSMark Johnston 		}
1780ff40d3dSMark Johnston 
1790ff40d3dSMark Johnston 		if (dst->sin_addr.s_addr == INADDR_ANY &&
1800ff40d3dSMark Johnston 		    mask->sin_addr.s_addr == 0) {
1810ff40d3dSMark Johnston 			ret = inet_ntoa(gw->sin_addr);
1820ff40d3dSMark Johnston 			break;
1830ff40d3dSMark Johnston 		}
1840ff40d3dSMark Johnston 	}
1850ff40d3dSMark Johnston 	free(buf);
1860ff40d3dSMark Johnston 	return (ret);
1870ff40d3dSMark Johnston }
1880ff40d3dSMark Johnston 
189c0046e26SDag-Erling Smørgrav static void
1906543fa5aSMitchell Horne check_link_status(const char *ifname)
1916543fa5aSMitchell Horne {
1926543fa5aSMitchell Horne 	struct ifaddrs *ifap, *ifa;
1936543fa5aSMitchell Horne 
1946543fa5aSMitchell Horne 	if (getifaddrs(&ifap) != 0)
1956543fa5aSMitchell Horne 		err(EX_OSERR, "getifaddrs");
1966543fa5aSMitchell Horne 
1976543fa5aSMitchell Horne 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
1986543fa5aSMitchell Horne 		if (strcmp(ifname, ifa->ifa_name) != 0)
1996543fa5aSMitchell Horne 			continue;
2006543fa5aSMitchell Horne 		if ((ifa->ifa_flags & IFF_UP) == 0) {
2016543fa5aSMitchell Horne 			warnx("warning: %s's link is down", ifname);
2026543fa5aSMitchell Horne 		}
2036543fa5aSMitchell Horne 		break;
2046543fa5aSMitchell Horne 	}
2056543fa5aSMitchell Horne 	freeifaddrs(ifap);
2066543fa5aSMitchell Horne }
2076543fa5aSMitchell Horne 
2086543fa5aSMitchell Horne static void
209c0046e26SDag-Erling Smørgrav check_size(int fd, const char *fn)
210c0046e26SDag-Erling Smørgrav {
211c0046e26SDag-Erling Smørgrav 	int name[] = { CTL_HW, HW_PHYSMEM };
2120bfc2a12SMarcelo Araujo 	size_t namelen = nitems(name);
213c0046e26SDag-Erling Smørgrav 	unsigned long physmem;
214ce893772SPaul Saab 	size_t len;
215c0046e26SDag-Erling Smørgrav 	off_t mediasize;
216ce893772SPaul Saab 	int minidump;
217c0046e26SDag-Erling Smørgrav 
218b54a17cdSJohn Baldwin 	len = sizeof(minidump);
219ce893772SPaul Saab 	if (sysctlbyname("debug.minidump", &minidump, &len, NULL, 0) == 0 &&
220ce893772SPaul Saab 	    minidump == 1)
221ce893772SPaul Saab 		return;
222b54a17cdSJohn Baldwin 	len = sizeof(physmem);
223c0046e26SDag-Erling Smørgrav 	if (sysctl(name, namelen, &physmem, &len, NULL, 0) != 0)
224c0046e26SDag-Erling Smørgrav 		err(EX_OSERR, "can't get memory size");
225c0046e26SDag-Erling Smørgrav 	if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0)
226c0046e26SDag-Erling Smørgrav 		err(EX_OSERR, "%s: can't get size", fn);
2277bef7073SEric van Gyzen 	if ((uintmax_t)mediasize < (uintmax_t)physmem)
2287bef7073SEric van Gyzen 		errx(EX_IOERR, "%s is smaller than physical memory", fn);
229c0046e26SDag-Erling Smørgrav }
23083f9dfabSGarrett Wollman 
231480f31c2SKonrad Witaszczyk #ifdef HAVE_CRYPTO
232480f31c2SKonrad Witaszczyk static void
233372557d8SBryan Drewery _genkey(const char *pubkeyfile, struct diocskerneldump_arg *kdap)
234480f31c2SKonrad Witaszczyk {
235480f31c2SKonrad Witaszczyk 	FILE *fp;
236480f31c2SKonrad Witaszczyk 	RSA *pubkey;
237480f31c2SKonrad Witaszczyk 
238480f31c2SKonrad Witaszczyk 	assert(pubkeyfile != NULL);
2390ff40d3dSMark Johnston 	assert(kdap != NULL);
240480f31c2SKonrad Witaszczyk 
241480f31c2SKonrad Witaszczyk 	fp = NULL;
242480f31c2SKonrad Witaszczyk 	pubkey = NULL;
243480f31c2SKonrad Witaszczyk 
244480f31c2SKonrad Witaszczyk 	fp = fopen(pubkeyfile, "r");
245480f31c2SKonrad Witaszczyk 	if (fp == NULL)
246480f31c2SKonrad Witaszczyk 		err(1, "Unable to open %s", pubkeyfile);
247480f31c2SKonrad Witaszczyk 
2484647ce4fSConrad Meyer 	/*
2494647ce4fSConrad Meyer 	 * Obsolescent OpenSSL only knows about /dev/random, and needs to
2504647ce4fSConrad Meyer 	 * pre-seed before entering cap mode.  For whatever reason,
2514647ce4fSConrad Meyer 	 * RSA_pub_encrypt uses the internal PRNG.
2524647ce4fSConrad Meyer 	 */
2534647ce4fSConrad Meyer #if OPENSSL_VERSION_NUMBER < 0x10100000L
2544647ce4fSConrad Meyer 	{
2554647ce4fSConrad Meyer 		unsigned char c[1];
2564647ce4fSConrad Meyer 		RAND_bytes(c, 1);
2574647ce4fSConrad Meyer 	}
2584647ce4fSConrad Meyer #endif
2594647ce4fSConrad Meyer 
2607672a014SMariusz Zaborski 	if (caph_enter() < 0)
261480f31c2SKonrad Witaszczyk 		err(1, "Unable to enter capability mode");
262480f31c2SKonrad Witaszczyk 
263480f31c2SKonrad Witaszczyk 	pubkey = RSA_new();
264480f31c2SKonrad Witaszczyk 	if (pubkey == NULL) {
265480f31c2SKonrad Witaszczyk 		errx(1, "Unable to allocate an RSA structure: %s",
266480f31c2SKonrad Witaszczyk 		    ERR_error_string(ERR_get_error(), NULL));
267480f31c2SKonrad Witaszczyk 	}
268480f31c2SKonrad Witaszczyk 
269480f31c2SKonrad Witaszczyk 	pubkey = PEM_read_RSA_PUBKEY(fp, &pubkey, NULL, NULL);
270480f31c2SKonrad Witaszczyk 	fclose(fp);
271480f31c2SKonrad Witaszczyk 	fp = NULL;
272480f31c2SKonrad Witaszczyk 	if (pubkey == NULL)
273*52b63df9SEnji Cooper 		errx(1, "Unable to read data from %s: %s", pubkeyfile,
274*52b63df9SEnji Cooper 		    ERR_error_string(ERR_get_error(), NULL));
275480f31c2SKonrad Witaszczyk 
276f27d255cSConrad Meyer 	/*
277f27d255cSConrad Meyer 	 * RSA keys under ~1024 bits are trivially factorable (2018).  OpenSSL
278f27d255cSConrad Meyer 	 * provides an API for RSA keys to estimate the symmetric-cipher
279f27d255cSConrad Meyer 	 * "equivalent" bits of security (defined in NIST SP800-57), which as
280f27d255cSConrad Meyer 	 * of this writing equates a 2048-bit RSA key to 112 symmetric cipher
281f27d255cSConrad Meyer 	 * bits.
282f27d255cSConrad Meyer 	 *
283f27d255cSConrad Meyer 	 * Use this API as a seatbelt to avoid suggesting to users that their
284f27d255cSConrad Meyer 	 * privacy is protected by encryption when the key size is insufficient
285f27d255cSConrad Meyer 	 * to prevent compromise via factoring.
286f27d255cSConrad Meyer 	 *
287f27d255cSConrad Meyer 	 * Future work: Sanity check for weak 'e', and sanity check for absence
288f27d255cSConrad Meyer 	 * of 'd' (i.e., the supplied key is a public key rather than a full
289f27d255cSConrad Meyer 	 * keypair).
290f27d255cSConrad Meyer 	 */
291f27d255cSConrad Meyer #if OPENSSL_VERSION_NUMBER >= 0x10100000L
292f27d255cSConrad Meyer 	if (RSA_security_bits(pubkey) < 112)
293f27d255cSConrad Meyer #else
294f27d255cSConrad Meyer 	if (RSA_size(pubkey) * 8 < 2048)
295f27d255cSConrad Meyer #endif
296f27d255cSConrad Meyer 		errx(1, "Small RSA keys (you provided: %db) can be "
297f27d255cSConrad Meyer 		    "factored cheaply.  Please generate a larger key.",
298f27d255cSConrad Meyer 		    RSA_size(pubkey) * 8);
299f27d255cSConrad Meyer 
3000ff40d3dSMark Johnston 	kdap->kda_encryptedkeysize = RSA_size(pubkey);
3010ff40d3dSMark Johnston 	if (kdap->kda_encryptedkeysize > KERNELDUMP_ENCKEY_MAX_SIZE) {
302480f31c2SKonrad Witaszczyk 		errx(1, "Public key has to be at most %db long.",
303480f31c2SKonrad Witaszczyk 		    8 * KERNELDUMP_ENCKEY_MAX_SIZE);
304480f31c2SKonrad Witaszczyk 	}
305480f31c2SKonrad Witaszczyk 
3060ff40d3dSMark Johnston 	kdap->kda_encryptedkey = calloc(1, kdap->kda_encryptedkeysize);
3070ff40d3dSMark Johnston 	if (kdap->kda_encryptedkey == NULL)
308480f31c2SKonrad Witaszczyk 		err(1, "Unable to allocate encrypted key");
309480f31c2SKonrad Witaszczyk 
31082985292SConrad Meyer 	/*
31182985292SConrad Meyer 	 * If no cipher was specified, choose a reasonable default.
31282985292SConrad Meyer 	 */
31382985292SConrad Meyer 	if (kdap->kda_encryption == KERNELDUMP_ENC_NONE)
31482985292SConrad Meyer 		kdap->kda_encryption = KERNELDUMP_ENC_CHACHA20;
31582985292SConrad Meyer 	else if (kdap->kda_encryption == KERNELDUMP_ENC_AES_256_CBC &&
31682985292SConrad Meyer 	    kdap->kda_compression != KERNELDUMP_COMP_NONE)
31782985292SConrad Meyer 		errx(EX_USAGE, "Unpadded AES256-CBC mode cannot be used "
31882985292SConrad Meyer 		    "with compression.");
31982985292SConrad Meyer 
3200ff40d3dSMark Johnston 	arc4random_buf(kdap->kda_key, sizeof(kdap->kda_key));
3210ff40d3dSMark Johnston 	if (RSA_public_encrypt(sizeof(kdap->kda_key), kdap->kda_key,
3220ff40d3dSMark Johnston 	    kdap->kda_encryptedkey, pubkey,
3234647ce4fSConrad Meyer 	    RSA_PKCS1_OAEP_PADDING) != (int)kdap->kda_encryptedkeysize) {
3244647ce4fSConrad Meyer 		errx(1, "Unable to encrypt the one-time key: %s",
3254647ce4fSConrad Meyer 		    ERR_error_string(ERR_get_error(), NULL));
326480f31c2SKonrad Witaszczyk 	}
327480f31c2SKonrad Witaszczyk 	RSA_free(pubkey);
328480f31c2SKonrad Witaszczyk }
329372557d8SBryan Drewery 
330372557d8SBryan Drewery /*
331372557d8SBryan Drewery  * Run genkey() in a child so it can use capability mode without affecting
332372557d8SBryan Drewery  * the rest of the runtime.
333372557d8SBryan Drewery  */
334372557d8SBryan Drewery static void
335372557d8SBryan Drewery genkey(const char *pubkeyfile, struct diocskerneldump_arg *kdap)
336372557d8SBryan Drewery {
337372557d8SBryan Drewery 	pid_t pid;
338372557d8SBryan Drewery 	int error, filedes[2], status;
339372557d8SBryan Drewery 	ssize_t bytes;
340372557d8SBryan Drewery 
341372557d8SBryan Drewery 	if (pipe2(filedes, O_CLOEXEC) != 0)
342372557d8SBryan Drewery 		err(1, "pipe");
343372557d8SBryan Drewery 	pid = fork();
344372557d8SBryan Drewery 	switch (pid) {
345372557d8SBryan Drewery 	case -1:
346372557d8SBryan Drewery 		err(1, "fork");
347372557d8SBryan Drewery 		break;
348372557d8SBryan Drewery 	case 0:
349372557d8SBryan Drewery 		close(filedes[0]);
350372557d8SBryan Drewery 		_genkey(pubkeyfile, kdap);
351372557d8SBryan Drewery 		/* Write the new kdap back to the parent. */
352372557d8SBryan Drewery 		bytes = write(filedes[1], kdap, sizeof(*kdap));
353372557d8SBryan Drewery 		if (bytes != sizeof(*kdap))
354372557d8SBryan Drewery 			err(1, "genkey pipe write");
35596f9bd46SEric van Gyzen 		bytes = write(filedes[1], kdap->kda_encryptedkey,
35696f9bd46SEric van Gyzen 		    kdap->kda_encryptedkeysize);
357080c6fdcSEric van Gyzen 		if (bytes != (ssize_t)kdap->kda_encryptedkeysize)
35896f9bd46SEric van Gyzen 			err(1, "genkey pipe write kda_encryptedkey");
359372557d8SBryan Drewery 		_exit(0);
360372557d8SBryan Drewery 	}
361372557d8SBryan Drewery 	close(filedes[1]);
362372557d8SBryan Drewery 	/* Read in the child's genkey() result into kdap. */
363372557d8SBryan Drewery 	bytes = read(filedes[0], kdap, sizeof(*kdap));
364372557d8SBryan Drewery 	if (bytes != sizeof(*kdap))
365372557d8SBryan Drewery 		errx(1, "genkey pipe read");
36696f9bd46SEric van Gyzen 	if (kdap->kda_encryptedkeysize > KERNELDUMP_ENCKEY_MAX_SIZE)
36796f9bd46SEric van Gyzen 		errx(1, "Public key has to be at most %db long.",
36896f9bd46SEric van Gyzen 		    8 * KERNELDUMP_ENCKEY_MAX_SIZE);
36996f9bd46SEric van Gyzen 	kdap->kda_encryptedkey = calloc(1, kdap->kda_encryptedkeysize);
37096f9bd46SEric van Gyzen 	if (kdap->kda_encryptedkey == NULL)
37196f9bd46SEric van Gyzen 		err(1, "Unable to allocate encrypted key");
37296f9bd46SEric van Gyzen 	bytes = read(filedes[0], kdap->kda_encryptedkey,
37396f9bd46SEric van Gyzen 	    kdap->kda_encryptedkeysize);
374080c6fdcSEric van Gyzen 	if (bytes != (ssize_t)kdap->kda_encryptedkeysize)
37596f9bd46SEric van Gyzen 		errx(1, "genkey pipe read kda_encryptedkey");
376372557d8SBryan Drewery 	error = waitpid(pid, &status, WEXITED);
377372557d8SBryan Drewery 	if (error == -1)
378372557d8SBryan Drewery 		err(1, "waitpid");
379372557d8SBryan Drewery 	if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
380372557d8SBryan Drewery 		errx(1, "genkey child exited with status %d",
381372557d8SBryan Drewery 		    WEXITSTATUS(status));
382372557d8SBryan Drewery 	else if (WIFSIGNALED(status))
383372557d8SBryan Drewery 		errx(1, "genkey child exited with signal %d",
384372557d8SBryan Drewery 		    WTERMSIG(status));
385372557d8SBryan Drewery 	close(filedes[0]);
386372557d8SBryan Drewery }
387480f31c2SKonrad Witaszczyk #endif
388480f31c2SKonrad Witaszczyk 
389f6848434SAlfred Perlstein static void
390f6848434SAlfred Perlstein listdumpdev(void)
391f6848434SAlfred Perlstein {
3926b6e2954SConrad Meyer 	static char ip[200];
3936b6e2954SConrad Meyer 
394f6848434SAlfred Perlstein 	char dumpdev[PATH_MAX];
3956b6e2954SConrad Meyer 	struct diocskerneldump_arg ndconf;
396f6848434SAlfred Perlstein 	size_t len;
397f6848434SAlfred Perlstein 	const char *sysctlname = "kern.shutdown.dumpdevname";
3980ff40d3dSMark Johnston 	int fd;
399f6848434SAlfred Perlstein 
400f6848434SAlfred Perlstein 	len = sizeof(dumpdev);
401f6848434SAlfred Perlstein 	if (sysctlbyname(sysctlname, &dumpdev, &len, NULL, 0) != 0) {
402f6848434SAlfred Perlstein 		if (errno == ENOMEM) {
403f6848434SAlfred Perlstein 			err(EX_OSERR, "Kernel returned too large of a buffer for '%s'\n",
404f6848434SAlfred Perlstein 				sysctlname);
405f6848434SAlfred Perlstein 		} else {
406f6848434SAlfred Perlstein 			err(EX_OSERR, "Sysctl get '%s'\n", sysctlname);
407f6848434SAlfred Perlstein 		}
408f6848434SAlfred Perlstein 	}
4090ff40d3dSMark Johnston 	if (strlen(dumpdev) == 0)
4100ff40d3dSMark Johnston 		(void)strlcpy(dumpdev, _PATH_DEVNULL, sizeof(dumpdev));
4110ff40d3dSMark Johnston 
4126b6e2954SConrad Meyer 	if (verbose) {
4136b6e2954SConrad Meyer 		char *ctx, *dd;
4146b6e2954SConrad Meyer 		unsigned idx;
4156b6e2954SConrad Meyer 
4166b6e2954SConrad Meyer 		printf("kernel dumps on priority: device\n");
4176b6e2954SConrad Meyer 		idx = 0;
4186b6e2954SConrad Meyer 		ctx = dumpdev;
4196b6e2954SConrad Meyer 		while ((dd = strsep(&ctx, ",")) != NULL)
4206b6e2954SConrad Meyer 			printf("%u: %s\n", idx++, dd);
4216b6e2954SConrad Meyer 	} else
422f6848434SAlfred Perlstein 		printf("%s\n", dumpdev);
4230ff40d3dSMark Johnston 
4240ff40d3dSMark Johnston 	/* If netdump is enabled, print the configuration parameters. */
4250ff40d3dSMark Johnston 	if (verbose) {
4260ff40d3dSMark Johnston 		fd = open(_PATH_NETDUMP, O_RDONLY);
4270ff40d3dSMark Johnston 		if (fd < 0) {
4280ff40d3dSMark Johnston 			if (errno != ENOENT)
4290ff40d3dSMark Johnston 				err(EX_OSERR, "opening %s", _PATH_NETDUMP);
4300ff40d3dSMark Johnston 			return;
431f6848434SAlfred Perlstein 		}
4326b6e2954SConrad Meyer 		if (ioctl(fd, DIOCGKERNELDUMP, &ndconf) != 0) {
4330ff40d3dSMark Johnston 			if (errno != ENXIO)
4346b6e2954SConrad Meyer 				err(EX_OSERR, "ioctl(DIOCGKERNELDUMP)");
4350ff40d3dSMark Johnston 			(void)close(fd);
4360ff40d3dSMark Johnston 			return;
4370ff40d3dSMark Johnston 		}
4380ff40d3dSMark Johnston 
4396b6e2954SConrad Meyer 		printf("server address: %s\n",
4406b6e2954SConrad Meyer 		    inet_ntop(ndconf.kda_af, &ndconf.kda_server, ip,
4416b6e2954SConrad Meyer 			sizeof(ip)));
4426b6e2954SConrad Meyer 		printf("client address: %s\n",
4436b6e2954SConrad Meyer 		    inet_ntop(ndconf.kda_af, &ndconf.kda_client, ip,
4446b6e2954SConrad Meyer 			sizeof(ip)));
4456b6e2954SConrad Meyer 		printf("gateway address: %s\n",
4466b6e2954SConrad Meyer 		    inet_ntop(ndconf.kda_af, &ndconf.kda_gateway, ip,
4476b6e2954SConrad Meyer 			sizeof(ip)));
4480ff40d3dSMark Johnston 		(void)close(fd);
4490ff40d3dSMark Johnston 	}
4500ff40d3dSMark Johnston }
4510ff40d3dSMark Johnston 
4520ff40d3dSMark Johnston static int
4530ff40d3dSMark Johnston opendumpdev(const char *arg, char *dumpdev)
4540ff40d3dSMark Johnston {
4550ff40d3dSMark Johnston 	int fd, i;
4560ff40d3dSMark Johnston 
4570ff40d3dSMark Johnston 	if (strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
4580ff40d3dSMark Johnston 		strlcpy(dumpdev, arg, PATH_MAX);
4590ff40d3dSMark Johnston 	else {
4600ff40d3dSMark Johnston 		i = snprintf(dumpdev, PATH_MAX, "%s%s", _PATH_DEV, arg);
4610ff40d3dSMark Johnston 		if (i < 0)
4620ff40d3dSMark Johnston 			err(EX_OSERR, "%s", arg);
4630ff40d3dSMark Johnston 		if (i >= PATH_MAX)
4640ff40d3dSMark Johnston 			errc(EX_DATAERR, EINVAL, "%s", arg);
4650ff40d3dSMark Johnston 	}
4660ff40d3dSMark Johnston 
4670ff40d3dSMark Johnston 	fd = open(dumpdev, O_RDONLY);
4680ff40d3dSMark Johnston 	if (fd < 0)
4690ff40d3dSMark Johnston 		err(EX_OSFILE, "%s", dumpdev);
4700ff40d3dSMark Johnston 	return (fd);
471f6848434SAlfred Perlstein }
472f6848434SAlfred Perlstein 
47383f9dfabSGarrett Wollman int
474d1a939c1SWarner Losh main(int argc, char *argv[])
47583f9dfabSGarrett Wollman {
4760ff40d3dSMark Johnston 	char dumpdev[PATH_MAX];
4776b6e2954SConrad Meyer 	struct diocskerneldump_arg ndconf, *kdap;
4780ff40d3dSMark Johnston 	struct addrinfo hints, *res;
4790ff40d3dSMark Johnston 	const char *dev, *pubkeyfile, *server, *client, *gateway;
48082985292SConrad Meyer 	int ch, error, fd, cipher;
4816b6e2954SConrad Meyer 	bool gzip, list, netdump, zstd, insert, rflag;
4826b6e2954SConrad Meyer 	uint8_t ins_idx;
48383f9dfabSGarrett Wollman 
4846b6e2954SConrad Meyer 	gzip = list = netdump = zstd = insert = rflag = false;
4850ff40d3dSMark Johnston 	kdap = NULL;
486480f31c2SKonrad Witaszczyk 	pubkeyfile = NULL;
4870ff40d3dSMark Johnston 	server = client = gateway = NULL;
4886b6e2954SConrad Meyer 	ins_idx = KDA_APPEND;
48982985292SConrad Meyer 	cipher = KERNELDUMP_ENC_NONE;
490480f31c2SKonrad Witaszczyk 
49182985292SConrad Meyer 	while ((ch = getopt(argc, argv, "C:c:g:i:k:lrs:vZz")) != -1)
49283f9dfabSGarrett Wollman 		switch ((char)ch) {
49382985292SConrad Meyer 		case 'C':
49482985292SConrad Meyer 			if (strcasecmp(optarg, "chacha") == 0 ||
49582985292SConrad Meyer 			    strcasecmp(optarg, "chacha20") == 0)
49682985292SConrad Meyer 				cipher = KERNELDUMP_ENC_CHACHA20;
49782985292SConrad Meyer 			else if (strcasecmp(optarg, "aes-cbc") == 0 ||
49882985292SConrad Meyer 			    strcasecmp(optarg, "aes256-cbc") == 0)
49982985292SConrad Meyer 				cipher = KERNELDUMP_ENC_AES_256_CBC;
50082985292SConrad Meyer 			else
50182985292SConrad Meyer 				errx(EX_USAGE, "Unrecognized cipher algorithm "
50282985292SConrad Meyer 				    "'%s'", optarg);
50382985292SConrad Meyer 			break;
5040ff40d3dSMark Johnston 		case 'c':
5050ff40d3dSMark Johnston 			client = optarg;
5060ff40d3dSMark Johnston 			break;
5070ff40d3dSMark Johnston 		case 'g':
5080ff40d3dSMark Johnston 			gateway = optarg;
5090ff40d3dSMark Johnston 			break;
5106b6e2954SConrad Meyer 		case 'i':
5116b6e2954SConrad Meyer 			{
5126b6e2954SConrad Meyer 			int i;
5136b6e2954SConrad Meyer 
5146b6e2954SConrad Meyer 			i = atoi(optarg);
5156b6e2954SConrad Meyer 			if (i < 0 || i >= KDA_APPEND - 1)
5166b6e2954SConrad Meyer 				errx(EX_USAGE,
5176b6e2954SConrad Meyer 				    "-i index must be between zero and %d.",
5186b6e2954SConrad Meyer 				    (int)KDA_APPEND - 2);
5196b6e2954SConrad Meyer 			insert = true;
5206b6e2954SConrad Meyer 			ins_idx = i;
5216b6e2954SConrad Meyer 			}
5226b6e2954SConrad Meyer 			break;
523480f31c2SKonrad Witaszczyk 		case 'k':
524480f31c2SKonrad Witaszczyk 			pubkeyfile = optarg;
525480f31c2SKonrad Witaszczyk 			break;
526f6848434SAlfred Perlstein 		case 'l':
5270ff40d3dSMark Johnston 			list = true;
5280ff40d3dSMark Johnston 			break;
5296b6e2954SConrad Meyer 		case 'r':
5306b6e2954SConrad Meyer 			rflag = true;
5316b6e2954SConrad Meyer 			break;
5320ff40d3dSMark Johnston 		case 's':
5330ff40d3dSMark Johnston 			server = optarg;
534f6848434SAlfred Perlstein 			break;
53583f9dfabSGarrett Wollman 		case 'v':
53683f9dfabSGarrett Wollman 			verbose = 1;
53783f9dfabSGarrett Wollman 			break;
5386026dcd7SMark Johnston 		case 'Z':
5396026dcd7SMark Johnston 			zstd = true;
5406026dcd7SMark Johnston 			break;
54164a16434SMark Johnston 		case 'z':
54264a16434SMark Johnston 			gzip = true;
54364a16434SMark Johnston 			break;
54483f9dfabSGarrett Wollman 		default:
54583f9dfabSGarrett Wollman 			usage();
54683f9dfabSGarrett Wollman 		}
547c0046e26SDag-Erling Smørgrav 
5486026dcd7SMark Johnston 	if (gzip && zstd)
5496026dcd7SMark Johnston 		errx(EX_USAGE, "The -z and -Z options are mutually exclusive.");
5506026dcd7SMark Johnston 
5516b6e2954SConrad Meyer 	if (insert && rflag)
5526b6e2954SConrad Meyer 		errx(EX_USAGE, "The -i and -r options are mutually exclusive.");
5536b6e2954SConrad Meyer 
554c0046e26SDag-Erling Smørgrav 	argc -= optind;
55583f9dfabSGarrett Wollman 	argv += optind;
55683f9dfabSGarrett Wollman 
5570ff40d3dSMark Johnston 	if (list) {
558f6848434SAlfred Perlstein 		listdumpdev();
559f6848434SAlfred Perlstein 		exit(EX_OK);
560f6848434SAlfred Perlstein 	}
561f6848434SAlfred Perlstein 
562c0046e26SDag-Erling Smørgrav 	if (argc != 1)
56383f9dfabSGarrett Wollman 		usage();
56483f9dfabSGarrett Wollman 
56582985292SConrad Meyer #ifdef HAVE_CRYPTO
5664647ce4fSConrad Meyer 	if (cipher != KERNELDUMP_ENC_NONE && pubkeyfile == NULL) {
56782985292SConrad Meyer 		errx(EX_USAGE, "-C option requires a public key file.");
5684647ce4fSConrad Meyer 	} else if (pubkeyfile != NULL) {
5694647ce4fSConrad Meyer 		ERR_load_crypto_strings();
5704647ce4fSConrad Meyer 	}
57182985292SConrad Meyer #else
5720ff40d3dSMark Johnston 	if (pubkeyfile != NULL)
57321a8e0b1SSean Bruno 		errx(EX_UNAVAILABLE,"Unable to use the public key."
57421a8e0b1SSean Bruno 				    " Recompile dumpon with OpenSSL support.");
575480f31c2SKonrad Witaszczyk #endif
576480f31c2SKonrad Witaszczyk 
5770ff40d3dSMark Johnston 	if (server != NULL && client != NULL) {
5780ff40d3dSMark Johnston 		dev = _PATH_NETDUMP;
5790ff40d3dSMark Johnston 		netdump = true;
5800ff40d3dSMark Johnston 	} else if (server == NULL && client == NULL && argc > 0) {
5816b6e2954SConrad Meyer 		if (strcmp(argv[0], "off") == 0) {
5826b6e2954SConrad Meyer 			rflag = true;
5836b6e2954SConrad Meyer 			dev = _PATH_DEVNULL;
5846b6e2954SConrad Meyer 		} else
5856b6e2954SConrad Meyer 			dev = argv[0];
5860ff40d3dSMark Johnston 		netdump = false;
587accff08cSBryan Drewery 
588accff08cSBryan Drewery 		if (strcmp(dev, _PATH_DEVNULL) == 0) {
589accff08cSBryan Drewery 			/*
590accff08cSBryan Drewery 			 * Netdump has its own configuration tracking that
591accff08cSBryan Drewery 			 * is not removed when using /dev/null.
592accff08cSBryan Drewery 			 */
593accff08cSBryan Drewery 			fd = open(_PATH_NETDUMP, O_RDONLY);
594accff08cSBryan Drewery 			if (fd != -1) {
595accff08cSBryan Drewery 				bzero(&ndconf, sizeof(ndconf));
596accff08cSBryan Drewery 				ndconf.kda_index = KDA_REMOVE_ALL;
597accff08cSBryan Drewery 				ndconf.kda_af = AF_INET;
598accff08cSBryan Drewery 				error = ioctl(fd, DIOCSKERNELDUMP, &ndconf);
599accff08cSBryan Drewery 				if (error != 0)
600accff08cSBryan Drewery 					err(1, "ioctl(%s, DIOCSKERNELDUMP)",
601accff08cSBryan Drewery 					    _PATH_NETDUMP);
602accff08cSBryan Drewery 				close(fd);
603accff08cSBryan Drewery 			}
604accff08cSBryan Drewery 		}
6050ff40d3dSMark Johnston 	} else
6060ff40d3dSMark Johnston 		usage();
60745a9027dSSteven Hartland 
6080ff40d3dSMark Johnston 	fd = opendumpdev(dev, dumpdev);
6097bef7073SEric van Gyzen 	if (!netdump && !gzip && !zstd && !rflag)
61064a16434SMark Johnston 		check_size(fd, dumpdev);
61164a16434SMark Johnston 
6126b6e2954SConrad Meyer 	kdap = &ndconf;
6130ff40d3dSMark Johnston 	bzero(kdap, sizeof(*kdap));
6140ff40d3dSMark Johnston 
6156b6e2954SConrad Meyer 	if (rflag)
6166b6e2954SConrad Meyer 		kdap->kda_index = KDA_REMOVE;
6176b6e2954SConrad Meyer 	else
6186b6e2954SConrad Meyer 		kdap->kda_index = ins_idx;
6196b6e2954SConrad Meyer 
6200ff40d3dSMark Johnston 	kdap->kda_compression = KERNELDUMP_COMP_NONE;
6210ff40d3dSMark Johnston 	if (zstd)
6220ff40d3dSMark Johnston 		kdap->kda_compression = KERNELDUMP_COMP_ZSTD;
6230ff40d3dSMark Johnston 	else if (gzip)
6240ff40d3dSMark Johnston 		kdap->kda_compression = KERNELDUMP_COMP_GZIP;
6250ff40d3dSMark Johnston 
6260ff40d3dSMark Johnston 	if (netdump) {
6270ff40d3dSMark Johnston 		memset(&hints, 0, sizeof(hints));
6280ff40d3dSMark Johnston 		hints.ai_family = AF_INET;
6290ff40d3dSMark Johnston 		hints.ai_protocol = IPPROTO_UDP;
6300ff40d3dSMark Johnston 		res = NULL;
6310ff40d3dSMark Johnston 		error = getaddrinfo(server, NULL, &hints, &res);
632576313b9SEric van Gyzen 		if (error != 0) {
633576313b9SEric van Gyzen 			if (error == EAI_SYSTEM)
634576313b9SEric van Gyzen 				err(EX_OSERR, "%s", gai_strerror(error));
635576313b9SEric van Gyzen 			errx(EX_NOHOST, "%s", gai_strerror(error));
636576313b9SEric van Gyzen 		}
6370ff40d3dSMark Johnston 		server = inet_ntoa(
6380ff40d3dSMark Johnston 		    ((struct sockaddr_in *)(void *)res->ai_addr)->sin_addr);
6390ff40d3dSMark Johnston 		freeaddrinfo(res);
6400ff40d3dSMark Johnston 
6416b6e2954SConrad Meyer 		if (strlcpy(ndconf.kda_iface, argv[0],
6426b6e2954SConrad Meyer 		    sizeof(ndconf.kda_iface)) >= sizeof(ndconf.kda_iface))
6430ff40d3dSMark Johnston 			errx(EX_USAGE, "invalid interface name '%s'", argv[0]);
6446b6e2954SConrad Meyer 		if (inet_aton(server, &ndconf.kda_server.in4) == 0)
6450ff40d3dSMark Johnston 			errx(EX_USAGE, "invalid server address '%s'", server);
6466b6e2954SConrad Meyer 		if (inet_aton(client, &ndconf.kda_client.in4) == 0)
6470ff40d3dSMark Johnston 			errx(EX_USAGE, "invalid client address '%s'", client);
6480ff40d3dSMark Johnston 
64979dd8f69SMark Johnston 		if (gateway == NULL) {
650e5fff57dSMark Johnston 			gateway = find_gateway(argv[0]);
651e5fff57dSMark Johnston 			if (gateway == NULL) {
652e5fff57dSMark Johnston 				if (verbose)
65379dd8f69SMark Johnston 					printf(
65479dd8f69SMark Johnston 				    "failed to look up gateway for %s\n",
655e5fff57dSMark Johnston 					    server);
6560ff40d3dSMark Johnston 				gateway = server;
657e5fff57dSMark Johnston 			}
65879dd8f69SMark Johnston 		}
6596b6e2954SConrad Meyer 		if (inet_aton(gateway, &ndconf.kda_gateway.in4) == 0)
6600ff40d3dSMark Johnston 			errx(EX_USAGE, "invalid gateway address '%s'", gateway);
6616b6e2954SConrad Meyer 		ndconf.kda_af = AF_INET;
6626b6e2954SConrad Meyer 	}
663480f31c2SKonrad Witaszczyk 
664480f31c2SKonrad Witaszczyk #ifdef HAVE_CRYPTO
66582985292SConrad Meyer 	if (pubkeyfile != NULL) {
66682985292SConrad Meyer 		kdap->kda_encryption = cipher;
6670ff40d3dSMark Johnston 		genkey(pubkeyfile, kdap);
66882985292SConrad Meyer 	}
669480f31c2SKonrad Witaszczyk #endif
6700ff40d3dSMark Johnston 	error = ioctl(fd, DIOCSKERNELDUMP, kdap);
6710ff40d3dSMark Johnston 	if (error != 0)
6720ff40d3dSMark Johnston 		error = errno;
6732b20327eSEd Maste 	if (error == EINVAL && (gzip || zstd)) {
6742b20327eSEd Maste 		/* Retry without compression in case kernel lacks support. */
6752b20327eSEd Maste 		kdap->kda_compression = KERNELDUMP_COMP_NONE;
6762b20327eSEd Maste 		error = ioctl(fd, DIOCSKERNELDUMP, kdap);
6772b20327eSEd Maste 		if (error == 0)
6782b20327eSEd Maste 			warnx("Compression disabled; kernel may lack gzip or zstd support.");
6792b20327eSEd Maste 		else
6802b20327eSEd Maste 			error = errno;
6812b20327eSEd Maste 	}
6826543fa5aSMitchell Horne 	/* Emit a warning if the user configured a downed interface. */
6836543fa5aSMitchell Horne 	if (error == 0 && netdump)
6846543fa5aSMitchell Horne 		check_link_status(kdap->kda_iface);
6856b6e2954SConrad Meyer 	explicit_bzero(kdap->kda_encryptedkey, kdap->kda_encryptedkeysize);
6860ff40d3dSMark Johnston 	free(kdap->kda_encryptedkey);
6870ff40d3dSMark Johnston 	explicit_bzero(kdap, sizeof(*kdap));
6886b6e2954SConrad Meyer 	if (error != 0) {
6896b6e2954SConrad Meyer 		if (netdump) {
6906b6e2954SConrad Meyer 			/*
6916b6e2954SConrad Meyer 			 * Be slightly less user-hostile for some common
6926b6e2954SConrad Meyer 			 * errors, especially as users don't have any great
6936b6e2954SConrad Meyer 			 * discoverability into which NICs support netdump.
6946b6e2954SConrad Meyer 			 */
6956543fa5aSMitchell Horne 			if (error == ENODEV)
6966b6e2954SConrad Meyer 				errx(EX_OSERR, "Unable to configure netdump "
6976b6e2954SConrad Meyer 				    "because the interface driver does not yet "
6986b6e2954SConrad Meyer 				    "support netdump.");
6996b6e2954SConrad Meyer 		}
7000ff40d3dSMark Johnston 		errc(EX_OSERR, error, "ioctl(DIOCSKERNELDUMP)");
70183f9dfabSGarrett Wollman 	}
7026b6e2954SConrad Meyer 
7030ff40d3dSMark Johnston 	if (verbose)
7046b6e2954SConrad Meyer 		listdumpdev();
70583f9dfabSGarrett Wollman 
7060ff40d3dSMark Johnston 	exit(EX_OK);
70783f9dfabSGarrett Wollman }
708