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