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
3283f9dfabSGarrett Wollman #include <sys/param.h>
33480f31c2SKonrad Witaszczyk #include <sys/capsicum.h>
342dd527b3SPoul-Henning Kamp #include <sys/disk.h>
350ff40d3dSMark Johnston #include <sys/socket.h>
36c0046e26SDag-Erling Smørgrav #include <sys/sysctl.h>
37372557d8SBryan Drewery #include <sys/wait.h>
3883f9dfabSGarrett Wollman
39480f31c2SKonrad Witaszczyk #include <assert.h>
407672a014SMariusz Zaborski #include <capsicum_helpers.h>
41c0046e26SDag-Erling Smørgrav #include <err.h>
42f6848434SAlfred Perlstein #include <errno.h>
43c0046e26SDag-Erling Smørgrav #include <fcntl.h>
440ff40d3dSMark Johnston #include <ifaddrs.h>
450ff40d3dSMark Johnston #include <netdb.h>
46c0046e26SDag-Erling Smørgrav #include <paths.h>
47480f31c2SKonrad Witaszczyk #include <stdbool.h>
48c0046e26SDag-Erling Smørgrav #include <stdint.h>
49c0046e26SDag-Erling Smørgrav #include <stdio.h>
50c0046e26SDag-Erling Smørgrav #include <stdlib.h>
51c0046e26SDag-Erling Smørgrav #include <string.h>
52c0046e26SDag-Erling Smørgrav #include <sysexits.h>
53c0046e26SDag-Erling Smørgrav #include <unistd.h>
54c0046e26SDag-Erling Smørgrav
550ff40d3dSMark Johnston #include <arpa/inet.h>
560ff40d3dSMark Johnston
570ff40d3dSMark Johnston #include <net/if.h>
580ff40d3dSMark Johnston #include <net/if_dl.h>
590ff40d3dSMark Johnston #include <net/route.h>
600ff40d3dSMark Johnston
610ff40d3dSMark Johnston #include <netinet/in.h>
620ff40d3dSMark Johnston #include <netinet/netdump/netdump.h>
630ff40d3dSMark Johnston
64480f31c2SKonrad Witaszczyk #ifdef HAVE_CRYPTO
65480f31c2SKonrad Witaszczyk #include <openssl/err.h>
66480f31c2SKonrad Witaszczyk #include <openssl/pem.h>
674647ce4fSConrad Meyer #include <openssl/rand.h>
68480f31c2SKonrad Witaszczyk #include <openssl/rsa.h>
69480f31c2SKonrad Witaszczyk #endif
70480f31c2SKonrad Witaszczyk
71c0046e26SDag-Erling Smørgrav static int verbose;
72c0046e26SDag-Erling Smørgrav
73*d0d7fcbaSJohn Baldwin static _Noreturn void
usage(void)74c0046e26SDag-Erling Smørgrav usage(void)
75c0046e26SDag-Erling Smørgrav {
760ff40d3dSMark Johnston fprintf(stderr,
776b6e2954SConrad Meyer "usage: dumpon [-i index] [-r] [-v] [-k <pubkey>] [-Zz] <device>\n"
786b6e2954SConrad Meyer " dumpon [-i index] [-r] [-v] [-k <pubkey>] [-Zz]\n"
79e5fff57dSMark Johnston " [-g <gateway>] -s <server> -c <client> <iface>\n"
800ff40d3dSMark Johnston " dumpon [-v] off\n"
810ff40d3dSMark Johnston " dumpon [-v] -l\n");
82c0046e26SDag-Erling Smørgrav exit(EX_USAGE);
83c0046e26SDag-Erling Smørgrav }
84c0046e26SDag-Erling Smørgrav
850ff40d3dSMark Johnston /*
860ff40d3dSMark Johnston * Look for a default route on the specified interface.
870ff40d3dSMark Johnston */
880ff40d3dSMark Johnston static char *
find_gateway(const char * ifname)890ff40d3dSMark Johnston find_gateway(const char *ifname)
900ff40d3dSMark Johnston {
910ff40d3dSMark Johnston struct ifaddrs *ifa, *ifap;
920ff40d3dSMark Johnston struct rt_msghdr *rtm;
930ff40d3dSMark Johnston struct sockaddr *sa;
940ff40d3dSMark Johnston struct sockaddr_dl *sdl;
950ff40d3dSMark Johnston struct sockaddr_in *dst, *mask, *gw;
960ff40d3dSMark Johnston char *buf, *next, *ret;
970ff40d3dSMark Johnston size_t sz;
980ff40d3dSMark Johnston int error, i, ifindex, mib[7];
990ff40d3dSMark Johnston
1000ff40d3dSMark Johnston /* First look up the interface index. */
1010ff40d3dSMark Johnston if (getifaddrs(&ifap) != 0)
1020ff40d3dSMark Johnston err(EX_OSERR, "getifaddrs");
1030ff40d3dSMark Johnston for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
1040ff40d3dSMark Johnston if (ifa->ifa_addr->sa_family != AF_LINK)
1050ff40d3dSMark Johnston continue;
1060ff40d3dSMark Johnston if (strcmp(ifa->ifa_name, ifname) == 0) {
1070ff40d3dSMark Johnston sdl = (struct sockaddr_dl *)(void *)ifa->ifa_addr;
1080ff40d3dSMark Johnston ifindex = sdl->sdl_index;
1090ff40d3dSMark Johnston break;
1100ff40d3dSMark Johnston }
1110ff40d3dSMark Johnston }
1120ff40d3dSMark Johnston if (ifa == NULL)
1130ff40d3dSMark Johnston errx(1, "couldn't find interface index for '%s'", ifname);
1140ff40d3dSMark Johnston freeifaddrs(ifap);
1150ff40d3dSMark Johnston
1160ff40d3dSMark Johnston /* Now get the IPv4 routing table. */
1170ff40d3dSMark Johnston mib[0] = CTL_NET;
1180ff40d3dSMark Johnston mib[1] = PF_ROUTE;
1190ff40d3dSMark Johnston mib[2] = 0;
1200ff40d3dSMark Johnston mib[3] = AF_INET;
1210ff40d3dSMark Johnston mib[4] = NET_RT_DUMP;
1220ff40d3dSMark Johnston mib[5] = 0;
1230ff40d3dSMark Johnston mib[6] = -1; /* FIB */
1240ff40d3dSMark Johnston
1250ff40d3dSMark Johnston for (;;) {
1260ff40d3dSMark Johnston if (sysctl(mib, nitems(mib), NULL, &sz, NULL, 0) != 0)
1270ff40d3dSMark Johnston err(EX_OSERR, "sysctl(NET_RT_DUMP)");
1280ff40d3dSMark Johnston buf = malloc(sz);
1290ff40d3dSMark Johnston error = sysctl(mib, nitems(mib), buf, &sz, NULL, 0);
1300ff40d3dSMark Johnston if (error == 0)
1310ff40d3dSMark Johnston break;
1320ff40d3dSMark Johnston if (errno != ENOMEM)
1330ff40d3dSMark Johnston err(EX_OSERR, "sysctl(NET_RT_DUMP)");
1340ff40d3dSMark Johnston free(buf);
1350ff40d3dSMark Johnston }
1360ff40d3dSMark Johnston
137e5fff57dSMark Johnston ret = NULL;
1380ff40d3dSMark Johnston for (next = buf; next < buf + sz; next += rtm->rtm_msglen) {
1390ff40d3dSMark Johnston rtm = (struct rt_msghdr *)(void *)next;
1400ff40d3dSMark Johnston if (rtm->rtm_version != RTM_VERSION)
1410ff40d3dSMark Johnston continue;
1420ff40d3dSMark Johnston if ((rtm->rtm_flags & RTF_GATEWAY) == 0 ||
1430ff40d3dSMark Johnston rtm->rtm_index != ifindex)
1440ff40d3dSMark Johnston continue;
1450ff40d3dSMark Johnston
1460ff40d3dSMark Johnston dst = gw = mask = NULL;
1470ff40d3dSMark Johnston sa = (struct sockaddr *)(rtm + 1);
1480ff40d3dSMark Johnston for (i = 0; i < RTAX_MAX; i++) {
1490ff40d3dSMark Johnston if ((rtm->rtm_addrs & (1 << i)) != 0) {
1500ff40d3dSMark Johnston switch (i) {
1510ff40d3dSMark Johnston case RTAX_DST:
1520ff40d3dSMark Johnston dst = (void *)sa;
1530ff40d3dSMark Johnston break;
1540ff40d3dSMark Johnston case RTAX_GATEWAY:
1550ff40d3dSMark Johnston gw = (void *)sa;
1560ff40d3dSMark Johnston break;
1570ff40d3dSMark Johnston case RTAX_NETMASK:
1580ff40d3dSMark Johnston mask = (void *)sa;
1590ff40d3dSMark Johnston break;
1600ff40d3dSMark Johnston }
1610ff40d3dSMark Johnston }
1620ff40d3dSMark Johnston sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa));
1630ff40d3dSMark Johnston }
1640ff40d3dSMark Johnston
1650ff40d3dSMark Johnston if (dst->sin_addr.s_addr == INADDR_ANY &&
1660ff40d3dSMark Johnston mask->sin_addr.s_addr == 0) {
1670ff40d3dSMark Johnston ret = inet_ntoa(gw->sin_addr);
1680ff40d3dSMark Johnston break;
1690ff40d3dSMark Johnston }
1700ff40d3dSMark Johnston }
1710ff40d3dSMark Johnston free(buf);
1720ff40d3dSMark Johnston return (ret);
1730ff40d3dSMark Johnston }
1740ff40d3dSMark Johnston
175c0046e26SDag-Erling Smørgrav static void
check_link_status(const char * ifname)1766543fa5aSMitchell Horne check_link_status(const char *ifname)
1776543fa5aSMitchell Horne {
1786543fa5aSMitchell Horne struct ifaddrs *ifap, *ifa;
1796543fa5aSMitchell Horne
1806543fa5aSMitchell Horne if (getifaddrs(&ifap) != 0)
1816543fa5aSMitchell Horne err(EX_OSERR, "getifaddrs");
1826543fa5aSMitchell Horne
1836543fa5aSMitchell Horne for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
1846543fa5aSMitchell Horne if (strcmp(ifname, ifa->ifa_name) != 0)
1856543fa5aSMitchell Horne continue;
1866543fa5aSMitchell Horne if ((ifa->ifa_flags & IFF_UP) == 0) {
1876543fa5aSMitchell Horne warnx("warning: %s's link is down", ifname);
1886543fa5aSMitchell Horne }
1896543fa5aSMitchell Horne break;
1906543fa5aSMitchell Horne }
1916543fa5aSMitchell Horne freeifaddrs(ifap);
1926543fa5aSMitchell Horne }
1936543fa5aSMitchell Horne
1946543fa5aSMitchell Horne static void
check_size(int fd,const char * fn)195c0046e26SDag-Erling Smørgrav check_size(int fd, const char *fn)
196c0046e26SDag-Erling Smørgrav {
197c0046e26SDag-Erling Smørgrav int name[] = { CTL_HW, HW_PHYSMEM };
1980bfc2a12SMarcelo Araujo size_t namelen = nitems(name);
199c0046e26SDag-Erling Smørgrav unsigned long physmem;
200ce893772SPaul Saab size_t len;
201c0046e26SDag-Erling Smørgrav off_t mediasize;
202ce893772SPaul Saab int minidump;
203c0046e26SDag-Erling Smørgrav
204b54a17cdSJohn Baldwin len = sizeof(minidump);
205ce893772SPaul Saab if (sysctlbyname("debug.minidump", &minidump, &len, NULL, 0) == 0 &&
206ce893772SPaul Saab minidump == 1)
207ce893772SPaul Saab return;
208b54a17cdSJohn Baldwin len = sizeof(physmem);
209c0046e26SDag-Erling Smørgrav if (sysctl(name, namelen, &physmem, &len, NULL, 0) != 0)
210c0046e26SDag-Erling Smørgrav err(EX_OSERR, "can't get memory size");
211c0046e26SDag-Erling Smørgrav if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0)
212c0046e26SDag-Erling Smørgrav err(EX_OSERR, "%s: can't get size", fn);
2137bef7073SEric van Gyzen if ((uintmax_t)mediasize < (uintmax_t)physmem)
2147bef7073SEric van Gyzen errx(EX_IOERR, "%s is smaller than physical memory", fn);
215c0046e26SDag-Erling Smørgrav }
21683f9dfabSGarrett Wollman
217480f31c2SKonrad Witaszczyk #ifdef HAVE_CRYPTO
218480f31c2SKonrad Witaszczyk static void
_genkey(const char * pubkeyfile,struct diocskerneldump_arg * kdap)219372557d8SBryan Drewery _genkey(const char *pubkeyfile, struct diocskerneldump_arg *kdap)
220480f31c2SKonrad Witaszczyk {
221480f31c2SKonrad Witaszczyk FILE *fp;
222480f31c2SKonrad Witaszczyk RSA *pubkey;
223480f31c2SKonrad Witaszczyk
224480f31c2SKonrad Witaszczyk assert(pubkeyfile != NULL);
2250ff40d3dSMark Johnston assert(kdap != NULL);
226480f31c2SKonrad Witaszczyk
227480f31c2SKonrad Witaszczyk fp = NULL;
228480f31c2SKonrad Witaszczyk pubkey = NULL;
229480f31c2SKonrad Witaszczyk
230480f31c2SKonrad Witaszczyk fp = fopen(pubkeyfile, "r");
231480f31c2SKonrad Witaszczyk if (fp == NULL)
232480f31c2SKonrad Witaszczyk err(1, "Unable to open %s", pubkeyfile);
233480f31c2SKonrad Witaszczyk
2344647ce4fSConrad Meyer /*
2354647ce4fSConrad Meyer * Obsolescent OpenSSL only knows about /dev/random, and needs to
2364647ce4fSConrad Meyer * pre-seed before entering cap mode. For whatever reason,
2374647ce4fSConrad Meyer * RSA_pub_encrypt uses the internal PRNG.
2384647ce4fSConrad Meyer */
2394647ce4fSConrad Meyer #if OPENSSL_VERSION_NUMBER < 0x10100000L
2404647ce4fSConrad Meyer {
2414647ce4fSConrad Meyer unsigned char c[1];
2424647ce4fSConrad Meyer RAND_bytes(c, 1);
2434647ce4fSConrad Meyer }
2444647ce4fSConrad Meyer #endif
2454647ce4fSConrad Meyer
2467672a014SMariusz Zaborski if (caph_enter() < 0)
247480f31c2SKonrad Witaszczyk err(1, "Unable to enter capability mode");
248480f31c2SKonrad Witaszczyk
249480f31c2SKonrad Witaszczyk pubkey = RSA_new();
250480f31c2SKonrad Witaszczyk if (pubkey == NULL) {
251480f31c2SKonrad Witaszczyk errx(1, "Unable to allocate an RSA structure: %s",
252480f31c2SKonrad Witaszczyk ERR_error_string(ERR_get_error(), NULL));
253480f31c2SKonrad Witaszczyk }
254480f31c2SKonrad Witaszczyk
255480f31c2SKonrad Witaszczyk pubkey = PEM_read_RSA_PUBKEY(fp, &pubkey, NULL, NULL);
256480f31c2SKonrad Witaszczyk fclose(fp);
257480f31c2SKonrad Witaszczyk fp = NULL;
258480f31c2SKonrad Witaszczyk if (pubkey == NULL)
25952b63df9SEnji Cooper errx(1, "Unable to read data from %s: %s", pubkeyfile,
26052b63df9SEnji Cooper ERR_error_string(ERR_get_error(), NULL));
261480f31c2SKonrad Witaszczyk
262f27d255cSConrad Meyer /*
263f27d255cSConrad Meyer * RSA keys under ~1024 bits are trivially factorable (2018). OpenSSL
264f27d255cSConrad Meyer * provides an API for RSA keys to estimate the symmetric-cipher
265f27d255cSConrad Meyer * "equivalent" bits of security (defined in NIST SP800-57), which as
266f27d255cSConrad Meyer * of this writing equates a 2048-bit RSA key to 112 symmetric cipher
267f27d255cSConrad Meyer * bits.
268f27d255cSConrad Meyer *
269f27d255cSConrad Meyer * Use this API as a seatbelt to avoid suggesting to users that their
270f27d255cSConrad Meyer * privacy is protected by encryption when the key size is insufficient
271f27d255cSConrad Meyer * to prevent compromise via factoring.
272f27d255cSConrad Meyer *
273f27d255cSConrad Meyer * Future work: Sanity check for weak 'e', and sanity check for absence
274f27d255cSConrad Meyer * of 'd' (i.e., the supplied key is a public key rather than a full
275f27d255cSConrad Meyer * keypair).
276f27d255cSConrad Meyer */
277f27d255cSConrad Meyer #if OPENSSL_VERSION_NUMBER >= 0x10100000L
278f27d255cSConrad Meyer if (RSA_security_bits(pubkey) < 112)
279f27d255cSConrad Meyer #else
280f27d255cSConrad Meyer if (RSA_size(pubkey) * 8 < 2048)
281f27d255cSConrad Meyer #endif
282f27d255cSConrad Meyer errx(1, "Small RSA keys (you provided: %db) can be "
283f27d255cSConrad Meyer "factored cheaply. Please generate a larger key.",
284f27d255cSConrad Meyer RSA_size(pubkey) * 8);
285f27d255cSConrad Meyer
2860ff40d3dSMark Johnston kdap->kda_encryptedkeysize = RSA_size(pubkey);
2870ff40d3dSMark Johnston if (kdap->kda_encryptedkeysize > KERNELDUMP_ENCKEY_MAX_SIZE) {
288480f31c2SKonrad Witaszczyk errx(1, "Public key has to be at most %db long.",
289480f31c2SKonrad Witaszczyk 8 * KERNELDUMP_ENCKEY_MAX_SIZE);
290480f31c2SKonrad Witaszczyk }
291480f31c2SKonrad Witaszczyk
2920ff40d3dSMark Johnston kdap->kda_encryptedkey = calloc(1, kdap->kda_encryptedkeysize);
2930ff40d3dSMark Johnston if (kdap->kda_encryptedkey == NULL)
294480f31c2SKonrad Witaszczyk err(1, "Unable to allocate encrypted key");
295480f31c2SKonrad Witaszczyk
29682985292SConrad Meyer /*
29782985292SConrad Meyer * If no cipher was specified, choose a reasonable default.
29882985292SConrad Meyer */
29982985292SConrad Meyer if (kdap->kda_encryption == KERNELDUMP_ENC_NONE)
30082985292SConrad Meyer kdap->kda_encryption = KERNELDUMP_ENC_CHACHA20;
30182985292SConrad Meyer else if (kdap->kda_encryption == KERNELDUMP_ENC_AES_256_CBC &&
30282985292SConrad Meyer kdap->kda_compression != KERNELDUMP_COMP_NONE)
30382985292SConrad Meyer errx(EX_USAGE, "Unpadded AES256-CBC mode cannot be used "
30482985292SConrad Meyer "with compression.");
30582985292SConrad Meyer
3060ff40d3dSMark Johnston arc4random_buf(kdap->kda_key, sizeof(kdap->kda_key));
3070ff40d3dSMark Johnston if (RSA_public_encrypt(sizeof(kdap->kda_key), kdap->kda_key,
3080ff40d3dSMark Johnston kdap->kda_encryptedkey, pubkey,
3094647ce4fSConrad Meyer RSA_PKCS1_OAEP_PADDING) != (int)kdap->kda_encryptedkeysize) {
3104647ce4fSConrad Meyer errx(1, "Unable to encrypt the one-time key: %s",
3114647ce4fSConrad Meyer ERR_error_string(ERR_get_error(), NULL));
312480f31c2SKonrad Witaszczyk }
313480f31c2SKonrad Witaszczyk RSA_free(pubkey);
314480f31c2SKonrad Witaszczyk }
315372557d8SBryan Drewery
316372557d8SBryan Drewery /*
317372557d8SBryan Drewery * Run genkey() in a child so it can use capability mode without affecting
318372557d8SBryan Drewery * the rest of the runtime.
319372557d8SBryan Drewery */
320372557d8SBryan Drewery static void
genkey(const char * pubkeyfile,struct diocskerneldump_arg * kdap)321372557d8SBryan Drewery genkey(const char *pubkeyfile, struct diocskerneldump_arg *kdap)
322372557d8SBryan Drewery {
323372557d8SBryan Drewery pid_t pid;
324372557d8SBryan Drewery int error, filedes[2], status;
325372557d8SBryan Drewery ssize_t bytes;
326372557d8SBryan Drewery
327372557d8SBryan Drewery if (pipe2(filedes, O_CLOEXEC) != 0)
328372557d8SBryan Drewery err(1, "pipe");
329372557d8SBryan Drewery pid = fork();
330372557d8SBryan Drewery switch (pid) {
331372557d8SBryan Drewery case -1:
332372557d8SBryan Drewery err(1, "fork");
333372557d8SBryan Drewery break;
334372557d8SBryan Drewery case 0:
335372557d8SBryan Drewery close(filedes[0]);
336372557d8SBryan Drewery _genkey(pubkeyfile, kdap);
337372557d8SBryan Drewery /* Write the new kdap back to the parent. */
338372557d8SBryan Drewery bytes = write(filedes[1], kdap, sizeof(*kdap));
339372557d8SBryan Drewery if (bytes != sizeof(*kdap))
340372557d8SBryan Drewery err(1, "genkey pipe write");
34196f9bd46SEric van Gyzen bytes = write(filedes[1], kdap->kda_encryptedkey,
34296f9bd46SEric van Gyzen kdap->kda_encryptedkeysize);
343080c6fdcSEric van Gyzen if (bytes != (ssize_t)kdap->kda_encryptedkeysize)
34496f9bd46SEric van Gyzen err(1, "genkey pipe write kda_encryptedkey");
345372557d8SBryan Drewery _exit(0);
346372557d8SBryan Drewery }
347372557d8SBryan Drewery close(filedes[1]);
348372557d8SBryan Drewery /* Read in the child's genkey() result into kdap. */
349372557d8SBryan Drewery bytes = read(filedes[0], kdap, sizeof(*kdap));
350372557d8SBryan Drewery if (bytes != sizeof(*kdap))
351372557d8SBryan Drewery errx(1, "genkey pipe read");
35296f9bd46SEric van Gyzen if (kdap->kda_encryptedkeysize > KERNELDUMP_ENCKEY_MAX_SIZE)
35396f9bd46SEric van Gyzen errx(1, "Public key has to be at most %db long.",
35496f9bd46SEric van Gyzen 8 * KERNELDUMP_ENCKEY_MAX_SIZE);
35596f9bd46SEric van Gyzen kdap->kda_encryptedkey = calloc(1, kdap->kda_encryptedkeysize);
35696f9bd46SEric van Gyzen if (kdap->kda_encryptedkey == NULL)
35796f9bd46SEric van Gyzen err(1, "Unable to allocate encrypted key");
35896f9bd46SEric van Gyzen bytes = read(filedes[0], kdap->kda_encryptedkey,
35996f9bd46SEric van Gyzen kdap->kda_encryptedkeysize);
360080c6fdcSEric van Gyzen if (bytes != (ssize_t)kdap->kda_encryptedkeysize)
36196f9bd46SEric van Gyzen errx(1, "genkey pipe read kda_encryptedkey");
362372557d8SBryan Drewery error = waitpid(pid, &status, WEXITED);
363372557d8SBryan Drewery if (error == -1)
364372557d8SBryan Drewery err(1, "waitpid");
365372557d8SBryan Drewery if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
366372557d8SBryan Drewery errx(1, "genkey child exited with status %d",
367372557d8SBryan Drewery WEXITSTATUS(status));
368372557d8SBryan Drewery else if (WIFSIGNALED(status))
369372557d8SBryan Drewery errx(1, "genkey child exited with signal %d",
370372557d8SBryan Drewery WTERMSIG(status));
371372557d8SBryan Drewery close(filedes[0]);
372372557d8SBryan Drewery }
373480f31c2SKonrad Witaszczyk #endif
374480f31c2SKonrad Witaszczyk
375f6848434SAlfred Perlstein static void
listdumpdev(void)376f6848434SAlfred Perlstein listdumpdev(void)
377f6848434SAlfred Perlstein {
3786b6e2954SConrad Meyer static char ip[200];
3796b6e2954SConrad Meyer
380f6848434SAlfred Perlstein char dumpdev[PATH_MAX];
3816b6e2954SConrad Meyer struct diocskerneldump_arg ndconf;
382f6848434SAlfred Perlstein size_t len;
383f6848434SAlfred Perlstein const char *sysctlname = "kern.shutdown.dumpdevname";
3840ff40d3dSMark Johnston int fd;
385f6848434SAlfred Perlstein
386f6848434SAlfred Perlstein len = sizeof(dumpdev);
387f6848434SAlfred Perlstein if (sysctlbyname(sysctlname, &dumpdev, &len, NULL, 0) != 0) {
388f6848434SAlfred Perlstein if (errno == ENOMEM) {
389f6848434SAlfred Perlstein err(EX_OSERR, "Kernel returned too large of a buffer for '%s'\n",
390f6848434SAlfred Perlstein sysctlname);
391f6848434SAlfred Perlstein } else {
392f6848434SAlfred Perlstein err(EX_OSERR, "Sysctl get '%s'\n", sysctlname);
393f6848434SAlfred Perlstein }
394f6848434SAlfred Perlstein }
3950ff40d3dSMark Johnston if (strlen(dumpdev) == 0)
3960ff40d3dSMark Johnston (void)strlcpy(dumpdev, _PATH_DEVNULL, sizeof(dumpdev));
3970ff40d3dSMark Johnston
3986b6e2954SConrad Meyer if (verbose) {
3996b6e2954SConrad Meyer char *ctx, *dd;
4006b6e2954SConrad Meyer unsigned idx;
4016b6e2954SConrad Meyer
4026b6e2954SConrad Meyer printf("kernel dumps on priority: device\n");
4036b6e2954SConrad Meyer idx = 0;
4046b6e2954SConrad Meyer ctx = dumpdev;
4056b6e2954SConrad Meyer while ((dd = strsep(&ctx, ",")) != NULL)
4066b6e2954SConrad Meyer printf("%u: %s\n", idx++, dd);
4076b6e2954SConrad Meyer } else
408f6848434SAlfred Perlstein printf("%s\n", dumpdev);
4090ff40d3dSMark Johnston
4100ff40d3dSMark Johnston /* If netdump is enabled, print the configuration parameters. */
4110ff40d3dSMark Johnston if (verbose) {
4120ff40d3dSMark Johnston fd = open(_PATH_NETDUMP, O_RDONLY);
4130ff40d3dSMark Johnston if (fd < 0) {
4140ff40d3dSMark Johnston if (errno != ENOENT)
4150ff40d3dSMark Johnston err(EX_OSERR, "opening %s", _PATH_NETDUMP);
4160ff40d3dSMark Johnston return;
417f6848434SAlfred Perlstein }
4186b6e2954SConrad Meyer if (ioctl(fd, DIOCGKERNELDUMP, &ndconf) != 0) {
4190ff40d3dSMark Johnston if (errno != ENXIO)
4206b6e2954SConrad Meyer err(EX_OSERR, "ioctl(DIOCGKERNELDUMP)");
4210ff40d3dSMark Johnston (void)close(fd);
4220ff40d3dSMark Johnston return;
4230ff40d3dSMark Johnston }
4240ff40d3dSMark Johnston
4256b6e2954SConrad Meyer printf("server address: %s\n",
4266b6e2954SConrad Meyer inet_ntop(ndconf.kda_af, &ndconf.kda_server, ip,
4276b6e2954SConrad Meyer sizeof(ip)));
4286b6e2954SConrad Meyer printf("client address: %s\n",
4296b6e2954SConrad Meyer inet_ntop(ndconf.kda_af, &ndconf.kda_client, ip,
4306b6e2954SConrad Meyer sizeof(ip)));
4316b6e2954SConrad Meyer printf("gateway address: %s\n",
4326b6e2954SConrad Meyer inet_ntop(ndconf.kda_af, &ndconf.kda_gateway, ip,
4336b6e2954SConrad Meyer sizeof(ip)));
4340ff40d3dSMark Johnston (void)close(fd);
4350ff40d3dSMark Johnston }
4360ff40d3dSMark Johnston }
4370ff40d3dSMark Johnston
4380ff40d3dSMark Johnston static int
opendumpdev(const char * arg,char * dumpdev)4390ff40d3dSMark Johnston opendumpdev(const char *arg, char *dumpdev)
4400ff40d3dSMark Johnston {
4410ff40d3dSMark Johnston int fd, i;
4420ff40d3dSMark Johnston
4430ff40d3dSMark Johnston if (strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
4440ff40d3dSMark Johnston strlcpy(dumpdev, arg, PATH_MAX);
4450ff40d3dSMark Johnston else {
4460ff40d3dSMark Johnston i = snprintf(dumpdev, PATH_MAX, "%s%s", _PATH_DEV, arg);
4470ff40d3dSMark Johnston if (i < 0)
4480ff40d3dSMark Johnston err(EX_OSERR, "%s", arg);
4490ff40d3dSMark Johnston if (i >= PATH_MAX)
4500ff40d3dSMark Johnston errc(EX_DATAERR, EINVAL, "%s", arg);
4510ff40d3dSMark Johnston }
4520ff40d3dSMark Johnston
4530ff40d3dSMark Johnston fd = open(dumpdev, O_RDONLY);
4540ff40d3dSMark Johnston if (fd < 0)
4550ff40d3dSMark Johnston err(EX_OSFILE, "%s", dumpdev);
4560ff40d3dSMark Johnston return (fd);
457f6848434SAlfred Perlstein }
458f6848434SAlfred Perlstein
45983f9dfabSGarrett Wollman int
main(int argc,char * argv[])460d1a939c1SWarner Losh main(int argc, char *argv[])
46183f9dfabSGarrett Wollman {
4620ff40d3dSMark Johnston char dumpdev[PATH_MAX];
4636b6e2954SConrad Meyer struct diocskerneldump_arg ndconf, *kdap;
4640ff40d3dSMark Johnston struct addrinfo hints, *res;
4650ff40d3dSMark Johnston const char *dev, *pubkeyfile, *server, *client, *gateway;
466464b1ab2Sinkeliz int ch, error, fd;
4676b6e2954SConrad Meyer bool gzip, list, netdump, zstd, insert, rflag;
4686b6e2954SConrad Meyer uint8_t ins_idx;
469464b1ab2Sinkeliz #ifdef HAVE_CRYPTO
470464b1ab2Sinkeliz int cipher = KERNELDUMP_ENC_NONE;
471464b1ab2Sinkeliz #endif
47283f9dfabSGarrett Wollman
4736b6e2954SConrad Meyer gzip = list = netdump = zstd = insert = rflag = false;
4740ff40d3dSMark Johnston kdap = NULL;
475480f31c2SKonrad Witaszczyk pubkeyfile = NULL;
4760ff40d3dSMark Johnston server = client = gateway = NULL;
4776b6e2954SConrad Meyer ins_idx = KDA_APPEND;
478480f31c2SKonrad Witaszczyk
47982985292SConrad Meyer while ((ch = getopt(argc, argv, "C:c:g:i:k:lrs:vZz")) != -1)
48083f9dfabSGarrett Wollman switch ((char)ch) {
48182985292SConrad Meyer case 'C':
482464b1ab2Sinkeliz #ifdef HAVE_CRYPTO
48382985292SConrad Meyer if (strcasecmp(optarg, "chacha") == 0 ||
48482985292SConrad Meyer strcasecmp(optarg, "chacha20") == 0)
48582985292SConrad Meyer cipher = KERNELDUMP_ENC_CHACHA20;
48682985292SConrad Meyer else if (strcasecmp(optarg, "aes-cbc") == 0 ||
48782985292SConrad Meyer strcasecmp(optarg, "aes256-cbc") == 0)
48882985292SConrad Meyer cipher = KERNELDUMP_ENC_AES_256_CBC;
48982985292SConrad Meyer else
49082985292SConrad Meyer errx(EX_USAGE, "Unrecognized cipher algorithm "
49182985292SConrad Meyer "'%s'", optarg);
49282985292SConrad Meyer break;
493464b1ab2Sinkeliz #else
494464b1ab2Sinkeliz errx(EX_USAGE,
495464b1ab2Sinkeliz "Built without crypto support, -C is unhandled.");
496464b1ab2Sinkeliz break;
497464b1ab2Sinkeliz #endif
4980ff40d3dSMark Johnston case 'c':
4990ff40d3dSMark Johnston client = optarg;
5000ff40d3dSMark Johnston break;
5010ff40d3dSMark Johnston case 'g':
5020ff40d3dSMark Johnston gateway = optarg;
5030ff40d3dSMark Johnston break;
5046b6e2954SConrad Meyer case 'i':
5056b6e2954SConrad Meyer {
5066b6e2954SConrad Meyer int i;
5076b6e2954SConrad Meyer
5086b6e2954SConrad Meyer i = atoi(optarg);
5096b6e2954SConrad Meyer if (i < 0 || i >= KDA_APPEND - 1)
5106b6e2954SConrad Meyer errx(EX_USAGE,
5116b6e2954SConrad Meyer "-i index must be between zero and %d.",
5126b6e2954SConrad Meyer (int)KDA_APPEND - 2);
5136b6e2954SConrad Meyer insert = true;
5146b6e2954SConrad Meyer ins_idx = i;
5156b6e2954SConrad Meyer }
5166b6e2954SConrad Meyer break;
517480f31c2SKonrad Witaszczyk case 'k':
518480f31c2SKonrad Witaszczyk pubkeyfile = optarg;
519480f31c2SKonrad Witaszczyk break;
520f6848434SAlfred Perlstein case 'l':
5210ff40d3dSMark Johnston list = true;
5220ff40d3dSMark Johnston break;
5236b6e2954SConrad Meyer case 'r':
5246b6e2954SConrad Meyer rflag = true;
5256b6e2954SConrad Meyer break;
5260ff40d3dSMark Johnston case 's':
5270ff40d3dSMark Johnston server = optarg;
528f6848434SAlfred Perlstein break;
52983f9dfabSGarrett Wollman case 'v':
53083f9dfabSGarrett Wollman verbose = 1;
53183f9dfabSGarrett Wollman break;
5326026dcd7SMark Johnston case 'Z':
5336026dcd7SMark Johnston zstd = true;
5346026dcd7SMark Johnston break;
53564a16434SMark Johnston case 'z':
53664a16434SMark Johnston gzip = true;
53764a16434SMark Johnston break;
53883f9dfabSGarrett Wollman default:
53983f9dfabSGarrett Wollman usage();
54083f9dfabSGarrett Wollman }
541c0046e26SDag-Erling Smørgrav
5426026dcd7SMark Johnston if (gzip && zstd)
5436026dcd7SMark Johnston errx(EX_USAGE, "The -z and -Z options are mutually exclusive.");
5446026dcd7SMark Johnston
5456b6e2954SConrad Meyer if (insert && rflag)
5466b6e2954SConrad Meyer errx(EX_USAGE, "The -i and -r options are mutually exclusive.");
5476b6e2954SConrad Meyer
548c0046e26SDag-Erling Smørgrav argc -= optind;
54983f9dfabSGarrett Wollman argv += optind;
55083f9dfabSGarrett Wollman
5510ff40d3dSMark Johnston if (list) {
552f6848434SAlfred Perlstein listdumpdev();
553f6848434SAlfred Perlstein exit(EX_OK);
554f6848434SAlfred Perlstein }
555f6848434SAlfred Perlstein
556c0046e26SDag-Erling Smørgrav if (argc != 1)
55783f9dfabSGarrett Wollman usage();
55883f9dfabSGarrett Wollman
55982985292SConrad Meyer #ifdef HAVE_CRYPTO
5604647ce4fSConrad Meyer if (cipher != KERNELDUMP_ENC_NONE && pubkeyfile == NULL) {
56182985292SConrad Meyer errx(EX_USAGE, "-C option requires a public key file.");
5624647ce4fSConrad Meyer } else if (pubkeyfile != NULL) {
56379d4d713SEd Maste #if OPENSSL_VERSION_NUMBER < 0x10100000L
5644647ce4fSConrad Meyer ERR_load_crypto_strings();
56579d4d713SEd Maste #else
56679d4d713SEd Maste if (!OPENSSL_init_crypto(0, NULL))
56779d4d713SEd Maste errx(EX_UNAVAILABLE, "Unable to initialize OpenSSL");
56879d4d713SEd Maste #endif
5694647ce4fSConrad Meyer }
57082985292SConrad Meyer #else
5710ff40d3dSMark Johnston if (pubkeyfile != NULL)
57221a8e0b1SSean Bruno errx(EX_UNAVAILABLE,"Unable to use the public key."
57321a8e0b1SSean Bruno " Recompile dumpon with OpenSSL support.");
574480f31c2SKonrad Witaszczyk #endif
575480f31c2SKonrad Witaszczyk
5760ff40d3dSMark Johnston if (server != NULL && client != NULL) {
5770ff40d3dSMark Johnston dev = _PATH_NETDUMP;
5780ff40d3dSMark Johnston netdump = true;
5790ff40d3dSMark Johnston } else if (server == NULL && client == NULL && argc > 0) {
5806b6e2954SConrad Meyer if (strcmp(argv[0], "off") == 0) {
5816b6e2954SConrad Meyer rflag = true;
5826b6e2954SConrad Meyer dev = _PATH_DEVNULL;
5836b6e2954SConrad Meyer } else
5846b6e2954SConrad Meyer dev = argv[0];
5850ff40d3dSMark Johnston netdump = false;
586accff08cSBryan Drewery
587accff08cSBryan Drewery if (strcmp(dev, _PATH_DEVNULL) == 0) {
588accff08cSBryan Drewery /*
589accff08cSBryan Drewery * Netdump has its own configuration tracking that
590accff08cSBryan Drewery * is not removed when using /dev/null.
591accff08cSBryan Drewery */
592accff08cSBryan Drewery fd = open(_PATH_NETDUMP, O_RDONLY);
593accff08cSBryan Drewery if (fd != -1) {
594accff08cSBryan Drewery bzero(&ndconf, sizeof(ndconf));
595accff08cSBryan Drewery ndconf.kda_index = KDA_REMOVE_ALL;
596accff08cSBryan Drewery ndconf.kda_af = AF_INET;
597accff08cSBryan Drewery error = ioctl(fd, DIOCSKERNELDUMP, &ndconf);
598accff08cSBryan Drewery if (error != 0)
599accff08cSBryan Drewery err(1, "ioctl(%s, DIOCSKERNELDUMP)",
600accff08cSBryan Drewery _PATH_NETDUMP);
601accff08cSBryan Drewery close(fd);
602accff08cSBryan Drewery }
603accff08cSBryan Drewery }
6040ff40d3dSMark Johnston } else
6050ff40d3dSMark Johnston usage();
60645a9027dSSteven Hartland
6070ff40d3dSMark Johnston fd = opendumpdev(dev, dumpdev);
6087bef7073SEric van Gyzen if (!netdump && !gzip && !zstd && !rflag)
60964a16434SMark Johnston check_size(fd, dumpdev);
61064a16434SMark Johnston
6116b6e2954SConrad Meyer kdap = &ndconf;
6120ff40d3dSMark Johnston bzero(kdap, sizeof(*kdap));
6130ff40d3dSMark Johnston
6146b6e2954SConrad Meyer if (rflag)
6156b6e2954SConrad Meyer kdap->kda_index = KDA_REMOVE;
6166b6e2954SConrad Meyer else
6176b6e2954SConrad Meyer kdap->kda_index = ins_idx;
6186b6e2954SConrad Meyer
6190ff40d3dSMark Johnston kdap->kda_compression = KERNELDUMP_COMP_NONE;
6200ff40d3dSMark Johnston if (zstd)
6210ff40d3dSMark Johnston kdap->kda_compression = KERNELDUMP_COMP_ZSTD;
6220ff40d3dSMark Johnston else if (gzip)
6230ff40d3dSMark Johnston kdap->kda_compression = KERNELDUMP_COMP_GZIP;
6240ff40d3dSMark Johnston
6250ff40d3dSMark Johnston if (netdump) {
6260ff40d3dSMark Johnston memset(&hints, 0, sizeof(hints));
6270ff40d3dSMark Johnston hints.ai_family = AF_INET;
6280ff40d3dSMark Johnston hints.ai_protocol = IPPROTO_UDP;
6290ff40d3dSMark Johnston res = NULL;
6300ff40d3dSMark Johnston error = getaddrinfo(server, NULL, &hints, &res);
631576313b9SEric van Gyzen if (error != 0) {
632576313b9SEric van Gyzen if (error == EAI_SYSTEM)
633576313b9SEric van Gyzen err(EX_OSERR, "%s", gai_strerror(error));
634576313b9SEric van Gyzen errx(EX_NOHOST, "%s", gai_strerror(error));
635576313b9SEric van Gyzen }
6360ff40d3dSMark Johnston server = inet_ntoa(
6370ff40d3dSMark Johnston ((struct sockaddr_in *)(void *)res->ai_addr)->sin_addr);
6380ff40d3dSMark Johnston freeaddrinfo(res);
6390ff40d3dSMark Johnston
6406b6e2954SConrad Meyer if (strlcpy(ndconf.kda_iface, argv[0],
6416b6e2954SConrad Meyer sizeof(ndconf.kda_iface)) >= sizeof(ndconf.kda_iface))
6420ff40d3dSMark Johnston errx(EX_USAGE, "invalid interface name '%s'", argv[0]);
6436b6e2954SConrad Meyer if (inet_aton(server, &ndconf.kda_server.in4) == 0)
6440ff40d3dSMark Johnston errx(EX_USAGE, "invalid server address '%s'", server);
6456b6e2954SConrad Meyer if (inet_aton(client, &ndconf.kda_client.in4) == 0)
6460ff40d3dSMark Johnston errx(EX_USAGE, "invalid client address '%s'", client);
6470ff40d3dSMark Johnston
64879dd8f69SMark Johnston if (gateway == NULL) {
649e5fff57dSMark Johnston gateway = find_gateway(argv[0]);
650e5fff57dSMark Johnston if (gateway == NULL) {
651e5fff57dSMark Johnston if (verbose)
65279dd8f69SMark Johnston printf(
65379dd8f69SMark Johnston "failed to look up gateway for %s\n",
654e5fff57dSMark Johnston server);
6550ff40d3dSMark Johnston gateway = server;
656e5fff57dSMark Johnston }
65779dd8f69SMark Johnston }
6586b6e2954SConrad Meyer if (inet_aton(gateway, &ndconf.kda_gateway.in4) == 0)
6590ff40d3dSMark Johnston errx(EX_USAGE, "invalid gateway address '%s'", gateway);
6606b6e2954SConrad Meyer ndconf.kda_af = AF_INET;
6616b6e2954SConrad Meyer }
662480f31c2SKonrad Witaszczyk
663480f31c2SKonrad Witaszczyk #ifdef HAVE_CRYPTO
66482985292SConrad Meyer if (pubkeyfile != NULL) {
66582985292SConrad Meyer kdap->kda_encryption = cipher;
6660ff40d3dSMark Johnston genkey(pubkeyfile, kdap);
66782985292SConrad Meyer }
668480f31c2SKonrad Witaszczyk #endif
6690ff40d3dSMark Johnston error = ioctl(fd, DIOCSKERNELDUMP, kdap);
6700ff40d3dSMark Johnston if (error != 0)
6710ff40d3dSMark Johnston error = errno;
6722b20327eSEd Maste if (error == EINVAL && (gzip || zstd)) {
6732b20327eSEd Maste /* Retry without compression in case kernel lacks support. */
6742b20327eSEd Maste kdap->kda_compression = KERNELDUMP_COMP_NONE;
6752b20327eSEd Maste error = ioctl(fd, DIOCSKERNELDUMP, kdap);
6762b20327eSEd Maste if (error == 0)
6772b20327eSEd Maste warnx("Compression disabled; kernel may lack gzip or zstd support.");
6782b20327eSEd Maste else
6792b20327eSEd Maste error = errno;
6802b20327eSEd Maste }
6816543fa5aSMitchell Horne /* Emit a warning if the user configured a downed interface. */
6826543fa5aSMitchell Horne if (error == 0 && netdump)
6836543fa5aSMitchell Horne check_link_status(kdap->kda_iface);
6846b6e2954SConrad Meyer explicit_bzero(kdap->kda_encryptedkey, kdap->kda_encryptedkeysize);
6850ff40d3dSMark Johnston free(kdap->kda_encryptedkey);
6860ff40d3dSMark Johnston explicit_bzero(kdap, sizeof(*kdap));
6876b6e2954SConrad Meyer if (error != 0) {
6886b6e2954SConrad Meyer if (netdump) {
6896b6e2954SConrad Meyer /*
6906b6e2954SConrad Meyer * Be slightly less user-hostile for some common
6916b6e2954SConrad Meyer * errors, especially as users don't have any great
6926b6e2954SConrad Meyer * discoverability into which NICs support netdump.
6936b6e2954SConrad Meyer */
6946543fa5aSMitchell Horne if (error == ENODEV)
6956b6e2954SConrad Meyer errx(EX_OSERR, "Unable to configure netdump "
6966b6e2954SConrad Meyer "because the interface driver does not yet "
6976b6e2954SConrad Meyer "support netdump.");
6986b6e2954SConrad Meyer }
6990ff40d3dSMark Johnston errc(EX_OSERR, error, "ioctl(DIOCSKERNELDUMP)");
70083f9dfabSGarrett Wollman }
7016b6e2954SConrad Meyer
7020ff40d3dSMark Johnston if (verbose)
7036b6e2954SConrad Meyer listdumpdev();
70483f9dfabSGarrett Wollman
7050ff40d3dSMark Johnston exit(EX_OK);
70683f9dfabSGarrett Wollman }
707