]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/dumpon/dumpon.c
Merge llvm trunk r338150 (just before the 7.0.0 branch point), and
[FreeBSD/FreeBSD.git] / sbin / dumpon / dumpon.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1980, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #if 0
33 #ifndef lint
34 static const char copyright[] =
35 "@(#) Copyright (c) 1980, 1993\n\
36         The Regents of the University of California.  All rights reserved.\n";
37 #endif /* not lint */
38
39 #ifndef lint
40 static char sccsid[] = "From: @(#)swapon.c      8.1 (Berkeley) 6/5/93";
41 #endif /* not lint */
42 #endif
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45
46 #include <sys/param.h>
47 #include <sys/capsicum.h>
48 #include <sys/disk.h>
49 #include <sys/socket.h>
50 #include <sys/sysctl.h>
51
52 #include <assert.h>
53 #include <capsicum_helpers.h>
54 #include <err.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <ifaddrs.h>
58 #include <netdb.h>
59 #include <paths.h>
60 #include <stdbool.h>
61 #include <stdint.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <sysexits.h>
66 #include <unistd.h>
67
68 #include <arpa/inet.h>
69
70 #include <net/if.h>
71 #include <net/if_dl.h>
72 #include <net/route.h>
73
74 #include <netinet/in.h>
75 #include <netinet/netdump/netdump.h>
76
77 #ifdef HAVE_CRYPTO
78 #include <openssl/err.h>
79 #include <openssl/pem.h>
80 #include <openssl/rsa.h>
81 #endif
82
83 static int      verbose;
84
85 static void _Noreturn
86 usage(void)
87 {
88         fprintf(stderr,
89     "usage: dumpon [-v] [-k <pubkey>] [-Zz] <device>\n"
90     "       dumpon [-v] [-k <pubkey>] [-Zz]\n"
91     "              [-g <gateway>|default] -s <server> -c <client> <iface>\n"
92     "       dumpon [-v] off\n"
93     "       dumpon [-v] -l\n");
94         exit(EX_USAGE);
95 }
96
97 /*
98  * Look for a default route on the specified interface.
99  */
100 static char *
101 find_gateway(const char *ifname)
102 {
103         struct ifaddrs *ifa, *ifap;
104         struct rt_msghdr *rtm;
105         struct sockaddr *sa;
106         struct sockaddr_dl *sdl;
107         struct sockaddr_in *dst, *mask, *gw;
108         char *buf, *next, *ret;
109         size_t sz;
110         int error, i, ifindex, mib[7];
111
112         ret = NULL;
113
114         /* First look up the interface index. */
115         if (getifaddrs(&ifap) != 0)
116                 err(EX_OSERR, "getifaddrs");
117         for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
118                 if (ifa->ifa_addr->sa_family != AF_LINK)
119                         continue;
120                 if (strcmp(ifa->ifa_name, ifname) == 0) {
121                         sdl = (struct sockaddr_dl *)(void *)ifa->ifa_addr;
122                         ifindex = sdl->sdl_index;
123                         break;
124                 }
125         }
126         if (ifa == NULL)
127                 errx(1, "couldn't find interface index for '%s'", ifname);
128         freeifaddrs(ifap);
129
130         /* Now get the IPv4 routing table. */
131         mib[0] = CTL_NET;
132         mib[1] = PF_ROUTE;
133         mib[2] = 0;
134         mib[3] = AF_INET;
135         mib[4] = NET_RT_DUMP;
136         mib[5] = 0;
137         mib[6] = -1; /* FIB */
138
139         for (;;) {
140                 if (sysctl(mib, nitems(mib), NULL, &sz, NULL, 0) != 0)
141                         err(EX_OSERR, "sysctl(NET_RT_DUMP)");
142                 buf = malloc(sz);
143                 error = sysctl(mib, nitems(mib), buf, &sz, NULL, 0);
144                 if (error == 0)
145                         break;
146                 if (errno != ENOMEM)
147                         err(EX_OSERR, "sysctl(NET_RT_DUMP)");
148                 free(buf);
149         }
150
151         for (next = buf; next < buf + sz; next += rtm->rtm_msglen) {
152                 rtm = (struct rt_msghdr *)(void *)next;
153                 if (rtm->rtm_version != RTM_VERSION)
154                         continue;
155                 if ((rtm->rtm_flags & RTF_GATEWAY) == 0 ||
156                     rtm->rtm_index != ifindex)
157                         continue;
158
159                 dst = gw = mask = NULL;
160                 sa = (struct sockaddr *)(rtm + 1);
161                 for (i = 0; i < RTAX_MAX; i++) {
162                         if ((rtm->rtm_addrs & (1 << i)) != 0) {
163                                 switch (i) {
164                                 case RTAX_DST:
165                                         dst = (void *)sa;
166                                         break;
167                                 case RTAX_GATEWAY:
168                                         gw = (void *)sa;
169                                         break;
170                                 case RTAX_NETMASK:
171                                         mask = (void *)sa;
172                                         break;
173                                 }
174                         }
175                         sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa));
176                 }
177
178                 if (dst->sin_addr.s_addr == INADDR_ANY &&
179                     mask->sin_addr.s_addr == 0) {
180                         ret = inet_ntoa(gw->sin_addr);
181                         break;
182                 }
183         }
184         free(buf);
185         return (ret);
186 }
187
188 static void
189 check_size(int fd, const char *fn)
190 {
191         int name[] = { CTL_HW, HW_PHYSMEM };
192         size_t namelen = nitems(name);
193         unsigned long physmem;
194         size_t len;
195         off_t mediasize;
196         int minidump;
197
198         len = sizeof(minidump);
199         if (sysctlbyname("debug.minidump", &minidump, &len, NULL, 0) == 0 &&
200             minidump == 1)
201                 return;
202         len = sizeof(physmem);
203         if (sysctl(name, namelen, &physmem, &len, NULL, 0) != 0)
204                 err(EX_OSERR, "can't get memory size");
205         if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0)
206                 err(EX_OSERR, "%s: can't get size", fn);
207         if ((uintmax_t)mediasize < (uintmax_t)physmem) {
208                 if (verbose)
209                         printf("%s is smaller than physical memory\n", fn);
210                 exit(EX_IOERR);
211         }
212 }
213
214 #ifdef HAVE_CRYPTO
215 static void
216 genkey(const char *pubkeyfile, struct diocskerneldump_arg *kdap)
217 {
218         FILE *fp;
219         RSA *pubkey;
220
221         assert(pubkeyfile != NULL);
222         assert(kdap != NULL);
223
224         fp = NULL;
225         pubkey = NULL;
226
227         fp = fopen(pubkeyfile, "r");
228         if (fp == NULL)
229                 err(1, "Unable to open %s", pubkeyfile);
230
231         if (caph_enter() < 0)
232                 err(1, "Unable to enter capability mode");
233
234         pubkey = RSA_new();
235         if (pubkey == NULL) {
236                 errx(1, "Unable to allocate an RSA structure: %s",
237                     ERR_error_string(ERR_get_error(), NULL));
238         }
239
240         pubkey = PEM_read_RSA_PUBKEY(fp, &pubkey, NULL, NULL);
241         fclose(fp);
242         fp = NULL;
243         if (pubkey == NULL)
244                 errx(1, "Unable to read data from %s.", pubkeyfile);
245
246         kdap->kda_encryptedkeysize = RSA_size(pubkey);
247         if (kdap->kda_encryptedkeysize > KERNELDUMP_ENCKEY_MAX_SIZE) {
248                 errx(1, "Public key has to be at most %db long.",
249                     8 * KERNELDUMP_ENCKEY_MAX_SIZE);
250         }
251
252         kdap->kda_encryptedkey = calloc(1, kdap->kda_encryptedkeysize);
253         if (kdap->kda_encryptedkey == NULL)
254                 err(1, "Unable to allocate encrypted key");
255
256         kdap->kda_encryption = KERNELDUMP_ENC_AES_256_CBC;
257         arc4random_buf(kdap->kda_key, sizeof(kdap->kda_key));
258         if (RSA_public_encrypt(sizeof(kdap->kda_key), kdap->kda_key,
259             kdap->kda_encryptedkey, pubkey,
260             RSA_PKCS1_PADDING) != (int)kdap->kda_encryptedkeysize) {
261                 errx(1, "Unable to encrypt the one-time key.");
262         }
263         RSA_free(pubkey);
264 }
265 #endif
266
267 static void
268 listdumpdev(void)
269 {
270         char dumpdev[PATH_MAX];
271         struct netdump_conf ndconf;
272         size_t len;
273         const char *sysctlname = "kern.shutdown.dumpdevname";
274         int fd;
275
276         len = sizeof(dumpdev);
277         if (sysctlbyname(sysctlname, &dumpdev, &len, NULL, 0) != 0) {
278                 if (errno == ENOMEM) {
279                         err(EX_OSERR, "Kernel returned too large of a buffer for '%s'\n",
280                                 sysctlname);
281                 } else {
282                         err(EX_OSERR, "Sysctl get '%s'\n", sysctlname);
283                 }
284         }
285         if (strlen(dumpdev) == 0)
286                 (void)strlcpy(dumpdev, _PATH_DEVNULL, sizeof(dumpdev));
287
288         if (verbose)
289                 printf("kernel dumps on ");
290         printf("%s\n", dumpdev);
291
292         /* If netdump is enabled, print the configuration parameters. */
293         if (verbose) {
294                 fd = open(_PATH_NETDUMP, O_RDONLY);
295                 if (fd < 0) {
296                         if (errno != ENOENT)
297                                 err(EX_OSERR, "opening %s", _PATH_NETDUMP);
298                         return;
299                 }
300                 if (ioctl(fd, NETDUMPGCONF, &ndconf) != 0) {
301                         if (errno != ENXIO)
302                                 err(EX_OSERR, "ioctl(NETDUMPGCONF)");
303                         (void)close(fd);
304                         return;
305                 }
306
307                 printf("server address: %s\n", inet_ntoa(ndconf.ndc_server));
308                 printf("client address: %s\n", inet_ntoa(ndconf.ndc_client));
309                 printf("gateway address: %s\n", inet_ntoa(ndconf.ndc_gateway));
310                 (void)close(fd);
311         }
312 }
313
314 static int
315 opendumpdev(const char *arg, char *dumpdev)
316 {
317         int fd, i;
318
319         if (strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
320                 strlcpy(dumpdev, arg, PATH_MAX);
321         else {
322                 i = snprintf(dumpdev, PATH_MAX, "%s%s", _PATH_DEV, arg);
323                 if (i < 0)
324                         err(EX_OSERR, "%s", arg);
325                 if (i >= PATH_MAX)
326                         errc(EX_DATAERR, EINVAL, "%s", arg);
327         }
328
329         fd = open(dumpdev, O_RDONLY);
330         if (fd < 0)
331                 err(EX_OSFILE, "%s", dumpdev);
332         return (fd);
333 }
334
335 int
336 main(int argc, char *argv[])
337 {
338         char dumpdev[PATH_MAX];
339         struct diocskerneldump_arg _kda, *kdap;
340         struct netdump_conf ndconf;
341         struct addrinfo hints, *res;
342         const char *dev, *pubkeyfile, *server, *client, *gateway;
343         int ch, error, fd;
344         bool enable, gzip, list, netdump, zstd;
345
346         gzip = list = netdump = zstd = false;
347         kdap = NULL;
348         pubkeyfile = NULL;
349         server = client = gateway = NULL;
350
351         while ((ch = getopt(argc, argv, "c:g:k:ls:vZz")) != -1)
352                 switch ((char)ch) {
353                 case 'c':
354                         client = optarg;
355                         break;
356                 case 'g':
357                         gateway = optarg;
358                         break;
359                 case 'k':
360                         pubkeyfile = optarg;
361                         break;
362                 case 'l':
363                         list = true;
364                         break;
365                 case 's':
366                         server = optarg;
367                         break;
368                 case 'v':
369                         verbose = 1;
370                         break;
371                 case 'Z':
372                         zstd = true;
373                         break;
374                 case 'z':
375                         gzip = true;
376                         break;
377                 default:
378                         usage();
379                 }
380
381         if (gzip && zstd)
382                 errx(EX_USAGE, "The -z and -Z options are mutually exclusive.");
383
384         argc -= optind;
385         argv += optind;
386
387         if (list) {
388                 listdumpdev();
389                 exit(EX_OK);
390         }
391
392         if (argc != 1)
393                 usage();
394
395 #ifndef HAVE_CRYPTO
396         if (pubkeyfile != NULL)
397                 errx(EX_UNAVAILABLE,"Unable to use the public key."
398                                     " Recompile dumpon with OpenSSL support.");
399 #endif
400
401         if (server != NULL && client != NULL) {
402                 enable = true;
403                 dev = _PATH_NETDUMP;
404                 netdump = true;
405                 kdap = &ndconf.ndc_kda;
406         } else if (server == NULL && client == NULL && argc > 0) {
407                 enable = strcmp(argv[0], "off") != 0;
408                 dev = enable ? argv[0] : _PATH_DEVNULL;
409                 netdump = false;
410                 kdap = &_kda;
411         } else
412                 usage();
413
414         fd = opendumpdev(dev, dumpdev);
415         if (!netdump && !gzip)
416                 check_size(fd, dumpdev);
417
418         bzero(kdap, sizeof(*kdap));
419         kdap->kda_enable = 0;
420         if (ioctl(fd, DIOCSKERNELDUMP, kdap) != 0)
421                 err(EX_OSERR, "ioctl(DIOCSKERNELDUMP)");
422         if (!enable)
423                 exit(EX_OK);
424
425         explicit_bzero(kdap, sizeof(*kdap));
426         kdap->kda_enable = 1;
427         kdap->kda_compression = KERNELDUMP_COMP_NONE;
428         if (zstd)
429                 kdap->kda_compression = KERNELDUMP_COMP_ZSTD;
430         else if (gzip)
431                 kdap->kda_compression = KERNELDUMP_COMP_GZIP;
432
433         if (netdump) {
434                 memset(&hints, 0, sizeof(hints));
435                 hints.ai_family = AF_INET;
436                 hints.ai_protocol = IPPROTO_UDP;
437                 res = NULL;
438                 error = getaddrinfo(server, NULL, &hints, &res);
439                 if (error != 0)
440                         err(1, "%s", gai_strerror(error));
441                 if (res == NULL)
442                         errx(1, "failed to resolve '%s'", server);
443                 server = inet_ntoa(
444                     ((struct sockaddr_in *)(void *)res->ai_addr)->sin_addr);
445                 freeaddrinfo(res);
446
447                 if (strlcpy(ndconf.ndc_iface, argv[0],
448                     sizeof(ndconf.ndc_iface)) >= sizeof(ndconf.ndc_iface))
449                         errx(EX_USAGE, "invalid interface name '%s'", argv[0]);
450                 if (inet_aton(server, &ndconf.ndc_server) == 0)
451                         errx(EX_USAGE, "invalid server address '%s'", server);
452                 if (inet_aton(client, &ndconf.ndc_client) == 0)
453                         errx(EX_USAGE, "invalid client address '%s'", client);
454
455                 if (gateway == NULL)
456                         gateway = server;
457                 else if (strcmp(gateway, "default") == 0 &&
458                     (gateway = find_gateway(argv[0])) == NULL)
459                         errx(EX_NOHOST,
460                             "failed to look up next-hop router for %s", server);
461                 if (inet_aton(gateway, &ndconf.ndc_gateway) == 0)
462                         errx(EX_USAGE, "invalid gateway address '%s'", gateway);
463
464 #ifdef HAVE_CRYPTO
465                 if (pubkeyfile != NULL)
466                         genkey(pubkeyfile, kdap);
467 #endif
468                 error = ioctl(fd, NETDUMPSCONF, &ndconf);
469                 if (error != 0)
470                         error = errno;
471                 explicit_bzero(kdap->kda_encryptedkey,
472                     kdap->kda_encryptedkeysize);
473                 free(kdap->kda_encryptedkey);
474                 explicit_bzero(kdap, sizeof(*kdap));
475                 if (error != 0)
476                         errc(EX_OSERR, error, "ioctl(NETDUMPSCONF)");
477         } else {
478 #ifdef HAVE_CRYPTO
479                 if (pubkeyfile != NULL)
480                         genkey(pubkeyfile, kdap);
481 #endif
482                 error = ioctl(fd, DIOCSKERNELDUMP, kdap);
483                 if (error != 0)
484                         error = errno;
485                 explicit_bzero(kdap->kda_encryptedkey,
486                     kdap->kda_encryptedkeysize);
487                 free(kdap->kda_encryptedkey);
488                 explicit_bzero(kdap, sizeof(*kdap));
489                 if (error != 0)
490                         errc(EX_OSERR, error, "ioctl(DIOCSKERNELDUMP)");
491         }
492         if (verbose)
493                 printf("kernel dumps on %s\n", dumpdev);
494
495         exit(EX_OK);
496 }