]> xenbits.xensource.com Git - people/royger/freebsd.git/commitdiff
Add support for encrypted kernel crash dumps.
authordef <def@FreeBSD.org>
Sat, 10 Dec 2016 16:20:39 +0000 (16:20 +0000)
committerdef <def@FreeBSD.org>
Sat, 10 Dec 2016 16:20:39 +0000 (16:20 +0000)
Changes include modifications in kernel crash dump routines, dumpon(8) and
savecore(8). A new tool called decryptcore(8) was added.

A new DIOCSKERNELDUMP I/O control was added to send a kernel crash dump
configuration in the diocskerneldump_arg structure to the kernel.
The old DIOCSKERNELDUMP I/O control was renamed to DIOCSKERNELDUMP_FREEBSD11 for
backward ABI compatibility.

dumpon(8) generates an one-time random symmetric key and encrypts it using
an RSA public key in capability mode. Currently only AES-256-CBC is supported
but EKCD was designed to implement support for other algorithms in the future.
The public key is chosen using the -k flag. The dumpon rc(8) script can do this
automatically during startup using the dumppubkey rc.conf(5) variable.  Once the
keys are calculated dumpon sends them to the kernel via DIOCSKERNELDUMP I/O
control.

When the kernel receives the DIOCSKERNELDUMP I/O control it generates a random
IV and sets up the key schedule for the specified algorithm. Each time the
kernel tries to write a crash dump to the dump device, the IV is replaced by
a SHA-256 hash of the previous value. This is intended to make a possible
differential cryptanalysis harder since it is possible to write multiple crash
dumps without reboot by repeating the following commands:
# sysctl debug.kdb.enter=1
db> call doadump(0)
db> continue
# savecore

A kernel dump key consists of an algorithm identifier, an IV and an encrypted
symmetric key. The kernel dump key size is included in a kernel dump header.
The size is an unsigned 32-bit integer and it is aligned to a block size.
The header structure has 512 bytes to match the block size so it was required to
make a panic string 4 bytes shorter to add a new field to the header structure.
If the kernel dump key size in the header is nonzero it is assumed that the
kernel dump key is placed after the first header on the dump device and the core
dump is encrypted.

Separate functions were implemented to write the kernel dump header and the
kernel dump key as they need to be unencrypted. The dump_write function encrypts
data if the kernel was compiled with the EKCD option. Encrypted kernel textdumps
are not supported due to the way they are constructed which makes it impossible
to use the CBC mode for encryption. It should be also noted that textdumps don't
contain sensitive data by design as a user decides what information should be
dumped.

savecore(8) writes the kernel dump key to a key.# file if its size in the header
is nonzero. # is the number of the current core dump.

decryptcore(8) decrypts the core dump using a private RSA key and the kernel
dump key. This is performed by a child process in capability mode.
If the decryption was not successful the parent process removes a partially
decrypted core dump.

Description on how to encrypt crash dumps was added to the decryptcore(8),
dumpon(8), rc.conf(5) and savecore(8) manual pages.

EKCD was tested on amd64 using bhyve and i386, mipsel and sparc64 using QEMU.
The feature still has to be tested on arm and arm64 as it wasn't possible to run
FreeBSD due to the problems with QEMU emulation and lack of hardware.

Designed by: def, pjd
Reviewed by: cem, oshogbo, pjd
Partial review: delphij, emaste, jhb, kib
Approved by: pjd (mentor)
Differential Revision: https://reviews.freebsd.org/D4712

29 files changed:
etc/defaults/rc.conf
etc/rc.d/dumpon
sbin/Makefile
sbin/decryptcore/Makefile [new file with mode: 0644]
sbin/decryptcore/decryptcore.8 [new file with mode: 0644]
sbin/decryptcore/decryptcore.c [new file with mode: 0644]
sbin/dumpon/Makefile
sbin/dumpon/dumpon.8
sbin/dumpon/dumpon.c
sbin/savecore/savecore.8
sbin/savecore/savecore.c
share/man/man5/rc.conf.5
sys/amd64/amd64/minidump_machdep.c
sys/arm/arm/minidump_machdep.c
sys/arm64/arm64/minidump_machdep.c
sys/conf/NOTES
sys/conf/files
sys/conf/options
sys/ddb/db_textdump.c
sys/dev/null/null.c
sys/geom/geom_dev.c
sys/i386/i386/minidump_machdep.c
sys/kern/kern_dump.c
sys/kern/kern_shutdown.c
sys/mips/mips/minidump_machdep.c
sys/sparc64/sparc64/dump_machdep.c
sys/sys/conf.h
sys/sys/disk.h
sys/sys/kerneldump.h

index f4fa7a8846df562146aa3a1ea9a5fdc81ccb1193..c8a3061b62ffd6f3e678acabdbf6fb2e54de4c91 100644 (file)
@@ -607,6 +607,8 @@ chkprintcap_enable="NO"     # Run chkprintcap(8) before running lpd.
 chkprintcap_flags="-d" # Create missing directories by default.
 dumpdev="AUTO"         # Device to crashdump to (device name, AUTO, or NO).
 dumpdir="/var/crash"   # Directory where crash dumps are to be stored
+dumppubkey=""          # Public key for encrypted kernel crash dumps.
+                       # See dumpon(8) for more details.
 savecore_enable="YES"  # Extract core from dump devices if any
 savecore_flags="-m 10" # Used if dumpdev is enabled above, and present.
                        # By default, only the 10 most recent kernel dumps
index 66276eb50535c18b25bcc06e745789da9098b26d..87bdd3eb0b39f485915d0575a940ea3bfa9ddf4a 100755 (executable)
@@ -16,7 +16,12 @@ stop_cmd="dumpon_stop"
 
 dumpon_try()
 {
-       if /sbin/dumpon "${1}" ; then
+       if [ -n "${dumppubkey}" ]; then
+               /sbin/dumpon -k "${dumppubkey}" "${1}"
+       else
+               /sbin/dumpon "${1}"
+       fi
+       if [ $? -eq 0 ]; then
                # Make a symlink in devfs for savecore
                ln -fs "${1}" /dev/dumpdev
                return 0
index 4826bc09b26d93b322c3e8f7ca53f02b9c0ac6a2..39e417acbacf97af3b623987dd42f88cb81b73ed 100644 (file)
@@ -83,6 +83,7 @@ SUBDIR.${MK_IPFW}+=   natd
 SUBDIR.${MK_ISCSI}+=   iscontrol
 SUBDIR.${MK_NAND}+=    nandfs
 SUBDIR.${MK_NAND}+=    newfs_nandfs
+SUBDIR.${MK_OPENSSL}+= decryptcore
 SUBDIR.${MK_PF}+=      pfctl
 SUBDIR.${MK_PF}+=      pflogd
 SUBDIR.${MK_QUOTAS}+=  quotacheck
diff --git a/sbin/decryptcore/Makefile b/sbin/decryptcore/Makefile
new file mode 100644 (file)
index 0000000..be8b6d1
--- /dev/null
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+PROG=  decryptcore
+
+LIBADD=        crypto pjdlog
+
+MAN=   decryptcore.8
+
+CFLAGS+=-I${.CURDIR}/../../lib/libpjdlog
+
+WARNS?=        6
+
+.include <bsd.prog.mk>
diff --git a/sbin/decryptcore/decryptcore.8 b/sbin/decryptcore/decryptcore.8
new file mode 100644 (file)
index 0000000..d21667c
--- /dev/null
@@ -0,0 +1,114 @@
+.\" Copyright (c) 2016 Konrad Witaszczyk <def@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 10, 2016
+.Dt DECRYPTCORE 8
+.Os
+.Sh NAME
+.Nm decryptcore
+.Nd "decrypt a core dump of the operating system"
+.Sh SYNOPSIS
+.Nm
+.Op Fl Lv
+.Fl p Ar privatekeyfile
+.Fl k Ar keyfile
+.Fl e Ar encryptedcore
+.Fl c Ar core
+.Nm
+.Op Fl Lv
+.Op Fl d Ar crashdir
+.Fl p Ar privatekeyfile
+.Fl n Ar dumpnr
+.Sh DESCRIPTION
+The
+.Nm
+first decrypts
+.Ar keyfile
+using
+.Ar privatekeyfile
+and then uses the resulting key to decrypt
+.Ar encryptedcore
+saved by
+.Xr savecore 8 .
+Result is saved in
+.Ar core .
+.Pp
+Alternatively a user can decrypt a core dump numbered
+.Ar dumpnr
+from the
+.Ar crashdir
+directory.
+In this case a dump key from the
+.Pa key.#
+file is used and the result is saved in the
+.Pa vmcore.#
+file where
+.Dq #
+corresponds to
+.Ar dumpnr .
+.Pp
+The
+.Nm
+utility can be started with the following command line arguments:
+.Bl -tag -width ".Fl e Ar encryptedcore"
+.It Fl L
+Write log messages to
+.Xr syslogd 8 .
+.It Fl v
+Print or log verbose/debugging information.
+This option can be specified multiple times to raise the verbosity
+level.
+.It Fl p Ar privatekeyfile
+Specify location of a private key file which will be used to decrypt a dump key
+file.
+.It Fl k Ar keyfile
+Specify location of a dump key file.
+.It Fl e Ar encrytpedcore
+Specify location of an encrypted core.
+.It Fl c Ar core
+Specify location of a resulting decrypted core dump.
+.It Fl d Ar crashdir
+Specify an alternative crash dump directory. The default crash dump directory is
+.Pa /var/crash .
+.It Fl n Ar dumpnr
+Specify a number of a crash dump to be decrypted.
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr capsicum 4 ,
+.Xr dumpon 8 ,
+.Xr kgdb 1 ,
+.Xr savecore 8 ,
+.Xr syslogd 8
+.Sh AUTHORS
+The
+.Nm
+was implemented by
+.An -nosplit
+.An Konrad Witaszczyk Aq Mt def@FreeBSD.org .
diff --git a/sbin/decryptcore/decryptcore.c b/sbin/decryptcore/decryptcore.c
new file mode 100644 (file)
index 0000000..758e4c8
--- /dev/null
@@ -0,0 +1,373 @@
+/*-
+ * Copyright (c) 2016 Konrad Witaszczyk <def@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/capsicum.h>
+#include <sys/endian.h>
+#include <sys/kerneldump.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/engine.h>
+
+#include "pjdlog.h"
+
+#define        DECRYPTCORE_CRASHDIR    "/var/crash"
+
+static void
+usage(void)
+{
+
+       pjdlog_exitx(1,
+           "usage: decryptcore [-Lv] -p privatekeyfile -k keyfile -e encryptedcore -c core\n"
+           "       decryptcore [-Lv] [-d crashdir] -p privatekeyfile -n dumpnr");
+}
+
+static int
+wait_for_process(pid_t pid)
+{
+       int status;
+
+       if (waitpid(pid, &status, WUNTRACED | WEXITED) == -1) {
+               pjdlog_errno(LOG_ERR, "Unable to wait for a child process");
+               return (1);
+       }
+
+       if (WIFEXITED(status))
+               return (WEXITSTATUS(status));
+
+       return (1);
+}
+
+static struct kerneldumpkey *
+read_key(int kfd)
+{
+       struct kerneldumpkey *kdk;
+       ssize_t size;
+       size_t kdksize;
+
+       PJDLOG_ASSERT(kfd >= 0);
+
+       kdksize = sizeof(*kdk);
+       kdk = calloc(1, kdksize);
+       if (kdk == NULL) {
+               pjdlog_errno(LOG_ERR, "Unable to allocate kernel dump key");
+               goto failed;
+       }
+
+       size = read(kfd, kdk, kdksize);
+       if (size == (ssize_t)kdksize) {
+               kdk->kdk_encryptedkeysize = dtoh32(kdk->kdk_encryptedkeysize);
+               kdksize += (size_t)kdk->kdk_encryptedkeysize;
+               kdk = realloc(kdk, kdksize);
+               if (kdk == NULL) {
+                       pjdlog_errno(LOG_ERR, "Unable to reallocate kernel dump key");
+                       goto failed;
+               }
+               size += read(kfd, &kdk->kdk_encryptedkey,
+                   kdk->kdk_encryptedkeysize);
+       }
+       if (size != (ssize_t)kdksize) {
+               pjdlog_errno(LOG_ERR, "Unable to read key");
+               goto failed;
+       }
+
+       return (kdk);
+failed:
+       free(kdk);
+       return (NULL);
+}
+
+static bool
+decrypt(const char *privkeyfile, const char *keyfile, const char *input,
+    const char *output)
+{
+       uint8_t buf[KERNELDUMP_BUFFER_SIZE], key[KERNELDUMP_KEY_MAX_SIZE];
+       EVP_CIPHER_CTX ctx;
+       const EVP_CIPHER *cipher;
+       FILE *fp;
+       struct kerneldumpkey *kdk;
+       RSA *privkey;
+       int ifd, kfd, ofd, olen, privkeysize;
+       ssize_t bytes;
+       pid_t pid;
+
+       PJDLOG_ASSERT(privkeyfile != NULL);
+       PJDLOG_ASSERT(keyfile != NULL);
+       PJDLOG_ASSERT(input != NULL);
+       PJDLOG_ASSERT(output != NULL);
+
+       privkey = NULL;
+
+       /*
+        * Decrypt a core dump in a child process so we can unlink a partially
+        * decrypted core if the child process fails.
+        */
+       pid = fork();
+       if (pid == -1) {
+               pjdlog_errno(LOG_ERR, "Unable to create child process");
+               return (false);
+       }
+
+       if (pid > 0)
+               return (wait_for_process(pid) == 0);
+
+       kfd = open(keyfile, O_RDONLY);
+       if (kfd == -1) {
+               pjdlog_errno(LOG_ERR, "Unable to open %s", keyfile);
+               goto failed;
+       }
+       ifd = open(input, O_RDONLY);
+       if (ifd == -1) {
+               pjdlog_errno(LOG_ERR, "Unable to open %s", input);
+               goto failed;
+       }
+       ofd = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+       if (ofd == -1) {
+               pjdlog_errno(LOG_ERR, "Unable to open %s", output);
+               goto failed;
+       }
+       fp = fopen(privkeyfile, "r");
+       if (fp == NULL) {
+               pjdlog_errno(LOG_ERR, "Unable to open %s", privkeyfile);
+               goto failed;
+       }
+
+       if (cap_enter() < 0 && errno != ENOSYS) {
+               pjdlog_errno(LOG_ERR, "Unable to enter capability mode");
+               goto failed;
+       }
+
+       privkey = RSA_new();
+       if (privkey == NULL) {
+               pjdlog_error("Unable to allocate an RSA structure: %s",
+                   ERR_error_string(ERR_get_error(), NULL));
+               goto failed;
+       }
+       EVP_CIPHER_CTX_init(&ctx);
+
+       kdk = read_key(kfd);
+       close(kfd);
+       if (kdk == NULL)
+               goto failed;
+
+       privkey = PEM_read_RSAPrivateKey(fp, &privkey, NULL, NULL);
+       fclose(fp);
+       if (privkey == NULL) {
+               pjdlog_error("Unable to read data from %s.", privkeyfile);
+               goto failed;
+       }
+
+       privkeysize = RSA_size(privkey);
+       if (privkeysize != (int)kdk->kdk_encryptedkeysize) {
+               pjdlog_error("RSA modulus size mismatch: equals %db and should be %ub.",
+                   8 * privkeysize, 8 * kdk->kdk_encryptedkeysize);
+               goto failed;
+       }
+
+       switch (kdk->kdk_encryption) {
+       case KERNELDUMP_ENC_AES_256_CBC:
+               cipher = EVP_aes_256_cbc();
+               break;
+       default:
+               pjdlog_error("Invalid encryption algorithm.");
+               goto failed;
+       }
+
+       if (RSA_private_decrypt(kdk->kdk_encryptedkeysize,
+           kdk->kdk_encryptedkey, key, privkey,
+           RSA_PKCS1_PADDING) != sizeof(key)) {
+               pjdlog_error("Unable to decrypt key: %s",
+                   ERR_error_string(ERR_get_error(), NULL));
+               goto failed;
+       }
+       RSA_free(privkey);
+       privkey = NULL;
+
+       EVP_DecryptInit_ex(&ctx, cipher, NULL, key, kdk->kdk_iv);
+       EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
+       explicit_bzero(key, sizeof(key));
+
+       do {
+               bytes = read(ifd, buf, sizeof(buf));
+               if (bytes < 0) {
+                       pjdlog_errno(LOG_ERR, "Unable to read data from %s",
+                           input);
+                       goto failed;
+               } else if (bytes == 0) {
+                       break;
+               }
+
+               if (bytes > 0) {
+                       if (EVP_DecryptUpdate(&ctx, buf, &olen, buf,
+                           bytes) == 0) {
+                               pjdlog_error("Unable to decrypt core.");
+                               goto failed;
+                       }
+               } else {
+                       if (EVP_DecryptFinal_ex(&ctx, buf, &olen) == 0) {
+                               pjdlog_error("Unable to decrypt core.");
+                               goto failed;
+                       }
+               }
+
+               if (olen == 0)
+                       continue;
+
+               if (write(ofd, buf, olen) != olen) {
+                       pjdlog_errno(LOG_ERR, "Unable to write data to %s",
+                           output);
+                       goto failed;
+               }
+       } while (bytes > 0);
+
+       explicit_bzero(buf, sizeof(buf));
+       EVP_CIPHER_CTX_cleanup(&ctx);
+       exit(0);
+failed:
+       explicit_bzero(key, sizeof(key));
+       explicit_bzero(buf, sizeof(buf));
+       RSA_free(privkey);
+       EVP_CIPHER_CTX_cleanup(&ctx);
+       exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+       char core[PATH_MAX], encryptedcore[PATH_MAX], keyfile[PATH_MAX];
+       struct stat sb;
+       const char *crashdir, *dumpnr, *privatekey;
+       int ch, debug;
+       size_t ii;
+       bool usesyslog;
+
+       pjdlog_init(PJDLOG_MODE_STD);
+       pjdlog_prefix_set("(decryptcore) ");
+
+       debug = 0;
+       *core = '\0';
+       crashdir = NULL;
+       dumpnr = NULL;
+       *encryptedcore = '\0';
+       *keyfile = '\0';
+       privatekey = NULL;
+       usesyslog = false;
+       while ((ch = getopt(argc, argv, "Lc:d:e:k:n:p:v")) != -1) {
+               switch (ch) {
+               case 'L':
+                       usesyslog = true;
+                       break;
+               case 'c':
+                       strncpy(core, optarg, sizeof(core));
+                       break;
+               case 'd':
+                       crashdir = optarg;
+                       break;
+               case 'e':
+                       strncpy(encryptedcore, optarg, sizeof(encryptedcore));
+                       break;
+               case 'k':
+                       strncpy(keyfile, optarg, sizeof(keyfile));
+                       break;
+               case 'n':
+                       dumpnr = optarg;
+                       break;
+               case 'p':
+                       privatekey = optarg;
+                       break;
+               case 'v':
+                       debug++;
+                       break;
+               default:
+                       usage();
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (argc != 0)
+               usage();
+
+       /* Verify mutually exclusive options. */
+       if ((crashdir != NULL || dumpnr != NULL) &&
+           (*keyfile != '\0' || *encryptedcore != '\0' || *core != '\0')) {
+               usage();
+       }
+
+       /*
+        * Set key, encryptedcore and core file names using crashdir and dumpnr.
+        */
+       if (dumpnr != NULL) {
+               for (ii = 0; ii < strnlen(dumpnr, PATH_MAX); ii++) {
+                       if (isdigit((int)dumpnr[ii]) == 0)
+                               usage();
+               }
+
+               if (crashdir == NULL)
+                       crashdir = DECRYPTCORE_CRASHDIR;
+               PJDLOG_VERIFY(snprintf(keyfile, sizeof(keyfile),
+                   "%s/key.%s", crashdir, dumpnr) > 0);
+               PJDLOG_VERIFY(snprintf(core, sizeof(core),
+                   "%s/vmcore.%s", crashdir, dumpnr) > 0);
+               PJDLOG_VERIFY(snprintf(encryptedcore, sizeof(encryptedcore),
+                   "%s/vmcore_encrypted.%s", crashdir, dumpnr) > 0);
+       }
+
+       if (privatekey == NULL || *keyfile == '\0' || *encryptedcore == '\0' ||
+           *core == '\0') {
+               usage();
+       }
+
+       if (usesyslog)
+               pjdlog_mode_set(PJDLOG_MODE_SYSLOG);
+       pjdlog_debug_set(debug);
+
+       if (!decrypt(privatekey, keyfile, encryptedcore, core)) {
+               if (stat(core, &sb) == 0 && unlink(core) != 0)
+                       pjdlog_exit(1, "Unable to remove core");
+               exit(1);
+       }
+
+       pjdlog_fini();
+
+       exit(0);
+}
index 782117c16ff0f7e5d940e7972e08e7c22092864b..d0700ab16a8921bc990c71d93bf09878e4c30568 100644 (file)
@@ -1,7 +1,15 @@
 # $FreeBSD$
 
+.include <src.opts.mk>
+
 PACKAGE=runtime
 PROG=  dumpon
+
+.if ${MK_OPENSSL} != "no"
+LIBADD=        crypto
+CFLAGS+=-DHAVE_CRYPTO
+.endif
+
 MAN=   dumpon.8
 
 .include <bsd.prog.mk>
index 81377124d8033e7db6482eba8e42487454bd68fb..14b0665ec1bbfb65de47c54fcc5877a37ed7803b 100644 (file)
@@ -28,7 +28,7 @@
 .\"     From: @(#)swapon.8     8.1 (Berkeley) 6/5/93
 .\" $FreeBSD$
 .\"
-.Dd October 3, 2016
+.Dd December 10, 2016
 .Dt DUMPON 8
 .Os
 .Sh NAME
@@ -37,6 +37,7 @@
 .Sh SYNOPSIS
 .Nm
 .Op Fl v
+.Op Fl k Ar public_key_file
 .Ar special_file
 .Nm
 .Op Fl v
@@ -56,7 +57,9 @@ normally occur from the system multi-user initialization file
 .Pa /etc/rc ,
 controlled by the
 .Dq dumpdev
-variable in the boot time configuration file
+and
+.Dq dumppubkey
+variables in the boot time configuration file
 .Pa /etc/rc.conf .
 .Pp
 The default type of kernel crash dump is the mini crash dump.
@@ -82,6 +85,35 @@ total amount of physical memory as reported by the
 variable.
 .Pp
 The
+.Op Fl k Ar public_key_file
+flag causes
+.Nm
+to generate a one-time key for kernel crash dump encryption.
+The key will be replaced by a new one when the
+.Nm
+utility is run again.
+The key is encrypted using
+.Ar public_key_file .
+This process is sandboxed using
+.Xr capsicum 4 .
+Both plain and encrypted keys are sent to the kernel using
+.Dv DIOCSKERNELDUMP
+.Xr ioctl 2 .
+A user can specify the
+.Ar public_key_file
+in the
+.Dq dumppubkey
+variable defined in
+.Pa /etc/rc.conf
+for use with the
+.Pa /etc/rc.d/dumpon
+.Xr rc 8
+script.
+This flag requires a kernel compiled with the
+.Dv EKCD
+kernel option.
+.Pp
+The
 .Fl l
 flag causes
 .Nm
@@ -140,13 +172,95 @@ standard swap areas
 .It Pa /etc/rc.conf
 boot-time system configuration
 .El
+.Sh EXAMPLES
+In order to generate an RSA private key a user can use the
+.Xr genrsa 1
+tool:
+.Pp
+.Dl # openssl genrsa -out private.pem 4096
+.Pp
+A public key can be extracted from the private key using the
+.Xr rsa 1
+tool:
+.Pp
+.Dl # openssl rsa -in private.pem -out public.pem -pubout
+.Pp
+Once the RSA keys are created the private key should be moved to a safe place.
+Now
+.Pa public.pem
+can be used by
+.Nm
+to configure encrypted kernel crash dumps:
+.Pp
+.Dl # dumpon -k public.pem /dev/ada0s1b
+.Pp
+It is recommended to test if the kernel saves encrypted crash dumps using the
+current configuration.
+The easiest way to do that is to cause a kernel panic using the
+.Xr ddb 4
+debugger:
+.Pp
+.Dl # sysctl debug.kdb.panic=1
+.Pp
+In the debugger the following commands should be typed to write a core dump and
+reboot:
+.Pp
+.Dl db> call doadump(0)
+.Dl db> reset
+.Pp
+After reboot
+.Xr savecore 8
+should be able to save the core dump in the core directory which is
+.Pa /var/crash
+by default:
+.Pp
+.Dl # savecore /var/crash /dev/ada0s1b
+.Pp
+Three files should be created in the core directory:
+.Pa info.# ,
+.Pa key.#
+and
+.Pa vmcore_encrypted.#
+where
+.Dq #
+is the number of the last core dump saved by
+.Xr savecore 8 .
+The
+.Pa vmcore_encrypted.#
+can be decrypted using the
+.Xr decryptcore 8
+utility:
+.Pp
+.Dl # decryptcore -p private.pem -k key.# -e vmcore_encrypted.# -c vmcore.#
+.Pp
+or shorter:
+.Pp
+.Dl # decryptcore -p private.pem -n #
+.Pp
+The
+.Pa vmcore.#
+can be now examined using
+.Xr kgdb 1 :
+.Pp
+.Dl # kgdb /usr/obj/sys/GENERIC/kernel.debug vmcore.#
+.Pp
+or shorter:
+.Pp
+.Dl # kgdb -n # /usr/obj/sys/GENERIC/kernel.debug
+.Pp
+The core was decrypted properly if
+.Xr kgdb 1
+does not print any errors.
 .Sh SEE ALSO
+.Xr kgdb 1 ,
+.Xr ddb 4 ,
 .Xr fstab 5 ,
 .Xr rc.conf 5 ,
 .Xr config 8 ,
 .Xr init 8 ,
 .Xr loader 8 ,
 .Xr rc 8 ,
+.Xr decryptcore 8 ,
 .Xr savecore 8 ,
 .Xr swapon 8 ,
 .Xr panic 9
index 8407111c7f65b0ed31adef5550be240dcae238d6..a1d77c852a48ddc72dcc26ae08c08610f1571bac 100644 (file)
@@ -42,13 +42,16 @@ static char sccsid[] = "From: @(#)swapon.c  8.1 (Berkeley) 6/5/93";
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
+#include <sys/capsicum.h>
 #include <sys/disk.h>
 #include <sys/sysctl.h>
 
+#include <assert.h>
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <paths.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -56,13 +59,19 @@ __FBSDID("$FreeBSD$");
 #include <sysexits.h>
 #include <unistd.h>
 
+#ifdef HAVE_CRYPTO
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#endif
+
 static int     verbose;
 
 static void
 usage(void)
 {
        fprintf(stderr, "%s\n%s\n%s\n",
-           "usage: dumpon [-v] special_file",
+           "usage: dumpon [-v] [-k public_key_file] special_file",
            "       dumpon [-v] off",
            "       dumpon [-v] -l");
        exit(EX_USAGE);
@@ -94,6 +103,59 @@ check_size(int fd, const char *fn)
        }
 }
 
+#ifdef HAVE_CRYPTO
+static void
+genkey(const char *pubkeyfile, struct diocskerneldump_arg *kda)
+{
+       FILE *fp;
+       RSA *pubkey;
+
+       assert(pubkeyfile != NULL);
+       assert(kda != NULL);
+
+       fp = NULL;
+       pubkey = NULL;
+
+       fp = fopen(pubkeyfile, "r");
+       if (fp == NULL)
+               err(1, "Unable to open %s", pubkeyfile);
+
+       if (cap_enter() < 0 && errno != ENOSYS)
+               err(1, "Unable to enter capability mode");
+
+       pubkey = RSA_new();
+       if (pubkey == NULL) {
+               errx(1, "Unable to allocate an RSA structure: %s",
+                   ERR_error_string(ERR_get_error(), NULL));
+       }
+
+       pubkey = PEM_read_RSA_PUBKEY(fp, &pubkey, NULL, NULL);
+       fclose(fp);
+       fp = NULL;
+       if (pubkey == NULL)
+               errx(1, "Unable to read data from %s.", pubkeyfile);
+
+       kda->kda_encryptedkeysize = RSA_size(pubkey);
+       if (kda->kda_encryptedkeysize > KERNELDUMP_ENCKEY_MAX_SIZE) {
+               errx(1, "Public key has to be at most %db long.",
+                   8 * KERNELDUMP_ENCKEY_MAX_SIZE);
+       }
+
+       kda->kda_encryptedkey = calloc(1, kda->kda_encryptedkeysize);
+       if (kda->kda_encryptedkey == NULL)
+               err(1, "Unable to allocate encrypted key");
+
+       kda->kda_encryption = KERNELDUMP_ENC_AES_256_CBC;
+       arc4random_buf(kda->kda_key, sizeof(kda->kda_key));
+       if (RSA_public_encrypt(sizeof(kda->kda_key), kda->kda_key,
+           kda->kda_encryptedkey, pubkey,
+           RSA_PKCS1_PADDING) != (int)kda->kda_encryptedkeysize) {
+               errx(1, "Unable to encrypt the one-time key.");
+       }
+       RSA_free(pubkey);
+}
+#endif
+
 static void
 listdumpdev(void)
 {
@@ -123,13 +185,20 @@ listdumpdev(void)
 int
 main(int argc, char *argv[])
 {
+       struct diocskerneldump_arg kda;
+       const char *pubkeyfile;
        int ch;
        int i, fd;
-       u_int u;
        int do_listdumpdev = 0;
+       bool enable;
 
-       while ((ch = getopt(argc, argv, "lv")) != -1)
+       pubkeyfile = NULL;
+
+       while ((ch = getopt(argc, argv, "k:lv")) != -1)
                switch((char)ch) {
+               case 'k':
+                       pubkeyfile = optarg;
+                       break;
                case 'l':
                        do_listdumpdev = 1;
                        break;
@@ -151,7 +220,15 @@ main(int argc, char *argv[])
        if (argc != 1)
                usage();
 
-       if (strcmp(argv[0], "off") != 0) {
+       enable = (strcmp(argv[0], "off") != 0);
+#ifndef HAVE_CRYPTO
+       if (pubkeyfile != NULL) {
+               enable = false;
+               warnx("Unable to use the public key. Recompile dumpon with OpenSSL support.");
+       }
+#endif
+
+       if (enable) {
                char tmp[PATH_MAX];
                char *dumpdev;
 
@@ -171,18 +248,32 @@ main(int argc, char *argv[])
                if (fd < 0)
                        err(EX_OSFILE, "%s", dumpdev);
                check_size(fd, dumpdev);
-               u = 0;
-               i = ioctl(fd, DIOCSKERNELDUMP, &u);
-               u = 1;
-               i = ioctl(fd, DIOCSKERNELDUMP, &u);
+               bzero(&kda, sizeof(kda));
+
+               kda.kda_enable = 0;
+               i = ioctl(fd, DIOCSKERNELDUMP, &kda);
+               explicit_bzero(&kda, sizeof(kda));
+
+#ifdef HAVE_CRYPTO
+               if (pubkeyfile != NULL)
+                       genkey(pubkeyfile, &kda);
+#endif
+
+               kda.kda_enable = 1;
+               i = ioctl(fd, DIOCSKERNELDUMP, &kda);
+               explicit_bzero(kda.kda_encryptedkey, kda.kda_encryptedkeysize);
+               free(kda.kda_encryptedkey);
+               explicit_bzero(&kda, sizeof(kda));
                if (i == 0 && verbose)
                        printf("kernel dumps on %s\n", dumpdev);
        } else {
                fd = open(_PATH_DEVNULL, O_RDONLY);
                if (fd < 0)
                        err(EX_OSFILE, "%s", _PATH_DEVNULL);
-               u = 0;
-               i = ioctl(fd, DIOCSKERNELDUMP, &u);
+
+               kda.kda_enable = 0;
+               i = ioctl(fd, DIOCSKERNELDUMP, &kda);
+               explicit_bzero(&kda, sizeof(kda));
                if (i == 0 && verbose)
                        printf("kernel dumps disabled\n");
        }
index af46d05e4d62bb7820a464274e7dfd93e7f36ce5..7d54c823affb9ff40de7d2d1e4fc31361eaf0e2d 100644 (file)
@@ -28,7 +28,7 @@
 .\"     From: @(#)savecore.8   8.1 (Berkeley) 6/5/93
 .\" $FreeBSD$
 .\"
-.Dd December 1, 2015
+.Dd December 10, 2016
 .Dt SAVECORE 8
 .Os
 .Sh NAME
@@ -119,6 +119,10 @@ If it passes these checks, it saves the core image in
 .Ar directory Ns Pa /vmcore.#
 and information about the core in
 .Ar directory Ns Pa /info.# .
+If the core is encrypted, it saves the dump key in
+.Ar directory Ns Pa /key.# .
+The core can be later decrypted using
+.Xr decryptcore 8 .
 For kernel textdumps generated with the
 .Xr textdump 4
 facility, output will be stored in the
@@ -166,6 +170,7 @@ is meant to be called near the end of the initialization file
 .Xr xo_parse_args 3 ,
 .Xr textdump 4 ,
 .Xr tar 5 ,
+.Xr decryptcore 8 ,
 .Xr dumpon 8 ,
 .Xr syslogd 8
 .Sh HISTORY
index 94b80090917416db76890133bc08fd38973d557c..b07e7e1e018c3a646739cd4ae6a4f0dd682c53bc 100644 (file)
@@ -74,6 +74,7 @@ __FBSDID("$FreeBSD$");
 #include <paths.h>
 #include <signal.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -183,6 +184,28 @@ writebounds(int bounds) {
        fclose(fp);
 }
 
+static bool
+writekey(const char *keyname, uint8_t *dumpkey, uint32_t dumpkeysize)
+{
+       int fd;
+
+       fd = open(keyname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+       if (fd == -1) {
+               syslog(LOG_ERR, "Unable to open %s to write the key: %m.",
+                   keyname);
+               return (false);
+       }
+
+       if (write(fd, dumpkey, dumpkeysize) != (ssize_t)dumpkeysize) {
+               syslog(LOG_ERR, "Unable to write the key to %s: %m.", keyname);
+               close(fd);
+               return (false);
+       }
+
+       close(fd);
+       return (true);
+}
+
 static off_t
 file_size(const char *path)
 {
@@ -238,8 +261,11 @@ symlinks_remove(void)
 {
 
        (void)unlink("info.last");
+       (void)unlink("key.last");
        (void)unlink("vmcore.last");
        (void)unlink("vmcore.last.gz");
+       (void)unlink("vmcore_encrypted.last");
+       (void)unlink("vmcore_encrypted.last.gz");
        (void)unlink("textdump.tar.last");
        (void)unlink("textdump.tar.last.gz");
 }
@@ -292,8 +318,8 @@ check_space(const char *savedir, off_t dumpsize, int bounds)
 #define BLOCKMASK (~(BLOCKSIZE-1))
 
 static int
-DoRegularFile(int fd, off_t dumpsize, char *buf, const char *device,
-    const char *filename, FILE *fp)
+DoRegularFile(int fd, bool isencrypted, off_t dumpsize, char *buf,
+    const char *device, const char *filename, FILE *fp)
 {
        int he, hs, nr, nw, wl;
        off_t dmpcnt, origsize;
@@ -315,7 +341,7 @@ DoRegularFile(int fd, off_t dumpsize, char *buf, const char *device,
                        nerr++;
                        return (-1);
                }
-               if (compress) {
+               if (compress || isencrypted) {
                        nw = fwrite(buf, 1, wl, fp);
                } else {
                        for (nw = 0; nw < nr; nw = he) {
@@ -436,9 +462,11 @@ DoFile(const char *savedir, const char *device)
 {
        xo_handle_t *xostdout, *xoinfo;
        static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX];
+       static char keyname[PATH_MAX];
        static char *buf = NULL;
        char *temp = NULL;
        struct kerneldumpheader kdhf, kdhl;
+       uint8_t *dumpkey;
        off_t mediasize, dumpsize, firsthd, lasthd;
        FILE *info, *fp;
        mode_t oumask;
@@ -446,6 +474,8 @@ DoFile(const char *savedir, const char *device)
        int bounds, status;
        u_int sectorsize, xostyle;
        int istextdump;
+       uint32_t dumpkeysize;
+       bool isencrypted, ret;
 
        bounds = getbounds();
        mediasize = 0;
@@ -581,7 +611,8 @@ DoFile(const char *savedir, const char *device)
                        goto closefd;
        }
        dumpsize = dtoh64(kdhl.dumplength);
-       firsthd = lasthd - dumpsize - sectorsize;
+       dumpkeysize = dtoh32(kdhl.dumpkeysize);
+       firsthd = lasthd - dumpsize - sectorsize - dumpkeysize;
        if (lseek(fd, firsthd, SEEK_SET) != firsthd ||
            read(fd, temp, sectorsize) != (ssize_t)sectorsize) {
                syslog(LOG_ERR,
@@ -649,13 +680,16 @@ DoFile(const char *savedir, const char *device)
        }
 
        oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
+       isencrypted = (dumpkeysize > 0);
        if (compress) {
                snprintf(corename, sizeof(corename), "%s.%d.gz",
-                   istextdump ? "textdump.tar" : "vmcore", bounds);
+                   istextdump ? "textdump.tar" :
+                   (isencrypted ? "vmcore_encrypted" : "vmcore"), bounds);
                fp = zopen(corename, "w");
        } else {
                snprintf(corename, sizeof(corename), "%s.%d",
-                   istextdump ? "textdump.tar" : "vmcore", bounds);
+                   istextdump ? "textdump.tar" :
+                   (isencrypted ? "vmcore_encrypted" : "vmcore"), bounds);
                fp = fopen(corename, "w");
        }
        if (fp == NULL) {
@@ -692,17 +726,42 @@ DoFile(const char *savedir, const char *device)
        xo_finish_h(xoinfo);
        fclose(info);
 
-       syslog(LOG_NOTICE, "writing %score to %s/%s",
-           compress ? "compressed " : "", savedir, corename);
+       if (isencrypted) {
+               dumpkey = calloc(1, dumpkeysize);
+               if (dumpkey == NULL) {
+                       syslog(LOG_ERR, "Unable to allocate kernel dump key.");
+                       nerr++;
+                       goto closeall;
+               }
+
+               if (read(fd, dumpkey, dumpkeysize) != (ssize_t)dumpkeysize) {
+                       syslog(LOG_ERR, "Unable to read kernel dump key: %m.");
+                       nerr++;
+                       goto closeall;
+               }
+
+               snprintf(keyname, sizeof(keyname), "key.%d", bounds);
+               ret = writekey(keyname, dumpkey, dumpkeysize);
+               explicit_bzero(dumpkey, dumpkeysize);
+               if (!ret) {
+                       nerr++;
+                       goto closeall;
+               }
+       }
+
+       syslog(LOG_NOTICE, "writing %s%score to %s/%s",
+           isencrypted ? "encrypted " : "", compress ? "compressed " : "",
+           savedir, corename);
 
        if (istextdump) {
                if (DoTextdumpFile(fd, dumpsize, lasthd, buf, device,
                    corename, fp) < 0)
                        goto closeall;
        } else {
-               if (DoRegularFile(fd, dumpsize, buf, device, corename, fp)
-                   < 0)
+               if (DoRegularFile(fd, isencrypted, dumpsize, buf, device,
+                   corename, fp) < 0) {
                        goto closeall;
+               }
        }
        if (verbose)
                printf("\n");
@@ -718,12 +777,21 @@ DoFile(const char *savedir, const char *device)
                syslog(LOG_WARNING, "unable to create symlink %s/%s: %m",
                    savedir, "info.last");
        }
+       if (isencrypted) {
+               if (symlink(keyname, "key.last") == -1) {
+                       syslog(LOG_WARNING,
+                           "unable to create symlink %s/%s: %m", savedir,
+                           "key.last");
+               }
+       }
        if (compress) {
                snprintf(linkname, sizeof(linkname), "%s.last.gz",
-                   istextdump ? "textdump.tar" : "vmcore");
+                   istextdump ? "textdump.tar" :
+                   (isencrypted ? "vmcore_encrypted" : "vmcore"));
        } else {
                snprintf(linkname, sizeof(linkname), "%s.last",
-                   istextdump ? "textdump.tar" : "vmcore");
+                   istextdump ? "textdump.tar" :
+                   (isencrypted ? "vmcore_encrypted" : "vmcore"));
        }
        if (symlink(corename, linkname) == -1) {
                syslog(LOG_WARNING, "unable to create symlink %s/%s: %m",
index ed04a77b7a04332435fc94aedeb8fe4b27f50607..46b6aa2063ef47545c6f315e35ce3bb67a03d78e 100644 (file)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd July 13, 2016
+.Dd December 10, 2016
 .Dt RC.CONF 5
 .Os
 .Sh NAME
@@ -3502,6 +3502,18 @@ to not run
 at boot time when
 .Va dumpdir
 is set.
+.It Va dumppubkey
+.Pq Vt str
+Path to a public key.
+It is used by
+.Xr dumpon 8
+to encrypt a one-time key for a crash dump.
+The public key has to match a private key used by
+.Xr decryptcore 8
+to decrypt a crash dump after reboot.
+See
+.Xr dumpon 8
+for more details.
 .It Va savecore_enable
 .Pq Vt bool
 If set to
index 11bcf5f863cadb1fc5af0d31aebdc2b359916079..a758c7b24d6a379a9fb31deed0af651cb7f5282b 100644 (file)
@@ -223,7 +223,6 @@ minidumpsys(struct dumperinfo *di)
        int error;
        uint64_t bits;
        uint64_t *pml4, *pdp, *pd, *pt, pa;
-       size_t size;
        int i, ii, j, k, n, bit;
        int retry_count;
        struct minidumphdr mdhdr;
@@ -321,14 +320,21 @@ minidumpsys(struct dumperinfo *di)
        dumpsize += PAGE_SIZE;
 
        /* Determine dump offset on device. */
-       if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2) {
+       if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2 +
+           kerneldumpcrypto_dumpkeysize(di->kdc)) {
                error = E2BIG;
                goto fail;
        }
        dumplo = di->mediaoffset + di->mediasize - dumpsize;
        dumplo -= di->blocksize * 2;
+       dumplo -= kerneldumpcrypto_dumpkeysize(di->kdc);
        progress = dumpsize;
 
+       /* Initialize kernel dump crypto. */
+       error = kerneldumpcrypto_init(di->kdc);
+       if (error)
+               goto fail;
+
        /* Initialize mdhdr */
        bzero(&mdhdr, sizeof(mdhdr));
        strcpy(mdhdr.magic, MINIDUMP_MAGIC);
@@ -340,16 +346,23 @@ minidumpsys(struct dumperinfo *di)
        mdhdr.dmapbase = DMAP_MIN_ADDRESS;
        mdhdr.dmapend = DMAP_MAX_ADDRESS;
 
-       mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_AMD64_VERSION, dumpsize, di->blocksize);
+       mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_AMD64_VERSION, dumpsize,
+           kerneldumpcrypto_dumpkeysize(di->kdc), di->blocksize);
 
        printf("Dumping %llu out of %ju MB:", (long long)dumpsize >> 20,
            ptoa((uintmax_t)physmem) / 1048576);
 
        /* Dump leader */
-       error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size);
+       error = dump_write_header(di, &kdh, 0, dumplo);
+       if (error)
+               goto fail;
+       dumplo += di->blocksize;
+
+       /* Dump key */
+       error = dump_write_key(di, 0, dumplo);
        if (error)
                goto fail;
-       dumplo += size;
+       dumplo += kerneldumpcrypto_dumpkeysize(di->kdc);
 
        /* Dump my header */
        bzero(&fakepd, sizeof(fakepd));
@@ -434,10 +447,10 @@ minidumpsys(struct dumperinfo *di)
                goto fail;
 
        /* Dump trailer */
-       error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size);
+       error = dump_write_header(di, &kdh, 0, dumplo);
        if (error)
                goto fail;
-       dumplo += size;
+       dumplo += di->blocksize;
 
        /* Signal completion, signoff and exit stage left. */
        dump_write(di, NULL, 0, 0, 0);
index e7340dcf33db019cb88ae3a193f0ca9706fb97b6..c5a139f747020ef6758ed111e0029f66c1fd1084 100644 (file)
@@ -238,15 +238,22 @@ minidumpsys(struct dumperinfo *di)
        dumpsize += PAGE_SIZE;
 
        /* Determine dump offset on device. */
-       if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+       if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2 +
+           kerneldumpcrypto_dumpkeysize(di->kdc)) {
                error = ENOSPC;
                goto fail;
        }
 
        dumplo = di->mediaoffset + di->mediasize - dumpsize;
-       dumplo -= sizeof(kdh) * 2;
+       dumplo -= di->blocksize * 2;
+       dumplo -= kerneldumpcrypto_dumpkeysize(di->kdc);
        progress = dumpsize;
 
+       /* Initialize kernel dump crypto. */
+       error = kerneldumpcrypto_init(di->kdc);
+       if (error)
+               goto fail;
+
        /* Initialize mdhdr */
        bzero(&mdhdr, sizeof(mdhdr));
        strcpy(mdhdr.magic, MINIDUMP_MAGIC);
@@ -262,16 +269,22 @@ minidumpsys(struct dumperinfo *di)
        mdhdr.mmuformat = MINIDUMP_MMU_FORMAT_V4;
 #endif
        mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION, dumpsize,
-           di->blocksize);
+           kerneldumpcrypto_dumpkeysize(di->kdc), di->blocksize);
 
        printf("Physical memory: %u MB\n", ptoa((uintmax_t)physmem) / 1048576);
        printf("Dumping %llu MB:", (long long)dumpsize >> 20);
 
        /* Dump leader */
-       error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
+       error = dump_write_header(di, &kdh, 0, dumplo);
+       if (error)
+               goto fail;
+       dumplo += di->blocksize;
+
+       /* Dump key */
+       error = dump_write_key(di, 0, dumplo);
        if (error)
                goto fail;
-       dumplo += sizeof(kdh);
+       dumplo += kerneldumpcrypto_dumpkeysize(di->kdc);
 
        /* Dump my header */
        bzero(dumpbuf, sizeof(dumpbuf));
@@ -348,10 +361,10 @@ minidumpsys(struct dumperinfo *di)
                goto fail;
 
        /* Dump trailer */
-       error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
+       error = dump_write_header(di, &kdh, 0, dumplo);
        if (error)
                goto fail;
-       dumplo += sizeof(kdh);
+       dumplo += di->blocksize;
 
        /* Signal completion, signoff and exit stage left. */
        dump_write(di, NULL, 0, 0, 0);
index 27c2081ef78d5fe0c8dff6ff0384edf5231493c4..2aeb07d07366d7ee7f203ee22fa6dbb42bb3abb1 100644 (file)
@@ -281,14 +281,21 @@ minidumpsys(struct dumperinfo *di)
        dumpsize += PAGE_SIZE;
 
        /* Determine dump offset on device. */
-       if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+       if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2 +
+           kerneldumpcrypto_dumpkeysize(di->kdc)) {
                error = E2BIG;
                goto fail;
        }
        dumplo = di->mediaoffset + di->mediasize - dumpsize;
-       dumplo -= sizeof(kdh) * 2;
+       dumplo -= di->blocksize * 2;
+       dumplo -= kerneldumpcrypto_dumpkeysize(di->kdc);
        progress = dumpsize;
 
+       /* Initialize kernel dump crypto. */
+       error = kerneldumpcrypto_init(di->kdc);
+       if (error)
+               goto fail;
+
        /* Initialize mdhdr */
        bzero(&mdhdr, sizeof(mdhdr));
        strcpy(mdhdr.magic, MINIDUMP_MAGIC);
@@ -302,16 +309,22 @@ minidumpsys(struct dumperinfo *di)
        mdhdr.dmapend = DMAP_MAX_ADDRESS;
 
        mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_AARCH64_VERSION,
-           dumpsize, di->blocksize);
+           dumpsize, kerneldumpcrypto_dumpkeysize(di->kdc), di->blocksize);
 
        printf("Dumping %llu out of %ju MB:", (long long)dumpsize >> 20,
            ptoa((uintmax_t)physmem) / 1048576);
 
        /* Dump leader */
-       error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
+       error = dump_write_header(di, &kdh, 0, dumplo);
+       if (error)
+               goto fail;
+       dumplo += di->blocksize;
+
+       /* Dump key */
+       error = dump_write_key(di, 0, dumplo);
        if (error)
                goto fail;
-       dumplo += sizeof(kdh);
+       dumplo += kerneldumpcrypto_dumpkeysize(di->kdc);
 
        /* Dump my header */
        bzero(&tmpbuffer, sizeof(tmpbuffer));
@@ -410,10 +423,10 @@ minidumpsys(struct dumperinfo *di)
                goto fail;
 
        /* Dump trailer */
-       error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
+       error = dump_write_header(di, &kdh, 0, dumplo);
        if (error)
                goto fail;
-       dumplo += sizeof(kdh);
+       dumplo += di->blocksize;
 
        /* Signal completion, signoff and exit stage left. */
        dump_write(di, NULL, 0, 0, 0);
index 06143861557098dc02bc548dcedd731f8d57c6da..cbd0934a032b73f88896f138317e5723d0cf5b15 100644 (file)
@@ -3072,3 +3072,6 @@ options   EVDEV_SUPPORT   # evdev support in legacy drivers
 options        EVDEV_DEBUG     # enable event debug msgs
 device         uinput          # install /dev/uinput cdev
 options        UINPUT_DEBUG    # enable uinput debug msgs
+
+# Encrypted kernel crash dumps.
+options        EKCD
index 9b4042726f68eedf2006e7ced9881e4d24a2159d..23e9c6f32a5693aea79aa668c123b64426627941 100644 (file)
@@ -594,13 +594,13 @@ crypto/camellia/camellia-api.c    optional crypto | ipsec
 crypto/des/des_ecb.c           optional crypto | ipsec | netsmb
 crypto/des/des_setkey.c                optional crypto | ipsec | netsmb
 crypto/rc4/rc4.c               optional netgraph_mppc_encryption | kgssapi
-crypto/rijndael/rijndael-alg-fst.c optional crypto | geom_bde | \
+crypto/rijndael/rijndael-alg-fst.c optional crypto | ekcd | geom_bde | \
                                         ipsec | random !random_loadable | wlan_ccmp
-crypto/rijndael/rijndael-api-fst.c optional geom_bde | random !random_loadable
+crypto/rijndael/rijndael-api-fst.c optional ekcd | geom_bde | random !random_loadable
 crypto/rijndael/rijndael-api.c optional crypto | ipsec | wlan_ccmp
 crypto/sha1.c                  optional carp | crypto | ipsec | \
                                         netgraph_mppc_encryption | sctp
-crypto/sha2/sha256c.c          optional crypto | geom_bde | ipsec | random !random_loadable | \
+crypto/sha2/sha256c.c          optional crypto | ekcd | geom_bde | ipsec | random !random_loadable | \
                                         sctp | zfs
 crypto/sha2/sha512c.c          optional crypto | geom_bde | ipsec | zfs
 crypto/skein/skein.c           optional crypto | zfs
index 07d76390505f07a575235370983c4bbdab96ea2f..1f20983c20978e7c67af8e47db4d61b2839b9da6 100644 (file)
@@ -1004,3 +1004,6 @@ UINPUT_DEBUG      opt_evdev.h
 
 # Hyper-V network driver
 HN_DEBUG       opt_hn.h
+
+# Encrypted kernel crash dumps
+EKCD           opt_ekcd.h
index aeeeec863cece1f63fb4036be55b0ca12e80273e..66d8d2c84032bb3e6697292aafe78e65593a5d7f 100644 (file)
@@ -427,6 +427,7 @@ textdump_dump_version(struct dumperinfo *di)
 void
 textdump_dumpsys(struct dumperinfo *di)
 {
+       struct kerneldumpcrypto *kdc;
        off_t dumplen, trailer_offset;
 
        if (di->blocksize != TEXTDUMP_BLOCKSIZE) {
@@ -448,6 +449,12 @@ textdump_dumpsys(struct dumperinfo *di)
        }
        textdump_error = 0;
 
+       /*
+        * Disable EKCD because we don't provide encrypted textdumps.
+        */
+       kdc = di->kdc;
+       di->kdc = NULL;
+
        /*
         * Position the start of the dump so that we'll write the kernel dump
         * trailer immediately before the end of the partition, and then work
@@ -456,7 +463,8 @@ textdump_dumpsys(struct dumperinfo *di)
         */
        textdump_offset = di->mediasize - sizeof(kdh);
        textdump_saveoff(&trailer_offset);
-       mkdumpheader(&kdh, TEXTDUMPMAGIC, KERNELDUMP_TEXT_VERSION, 0, TEXTDUMP_BLOCKSIZE);
+       mkdumpheader(&kdh, TEXTDUMPMAGIC, KERNELDUMP_TEXT_VERSION, 0, 0,
+           TEXTDUMP_BLOCKSIZE);
        (void)textdump_writenextblock(di, (char *)&kdh);
 
        /*
@@ -481,7 +489,7 @@ textdump_dumpsys(struct dumperinfo *di)
         * size.
         */
        dumplen = trailer_offset - (textdump_offset + TEXTDUMP_BLOCKSIZE);
-       mkdumpheader(&kdh, TEXTDUMPMAGIC, KERNELDUMP_TEXT_VERSION, dumplen,
+       mkdumpheader(&kdh, TEXTDUMPMAGIC, KERNELDUMP_TEXT_VERSION, dumplen, 0,
            TEXTDUMP_BLOCKSIZE);
        (void)textdump_writenextblock(di, (char *)&kdh);
        textdump_restoreoff(trailer_offset);
@@ -499,6 +507,11 @@ textdump_dumpsys(struct dumperinfo *di)
        else
                printf("Textdump complete.\n");
        textdump_pending = 0;
+
+       /*
+        * Restore EKCD status.
+        */
+       di->kdc = kdc;
 }
 
 /*-
index c8966df8ac4bbaa49308c14a14b0ce85df241630..d946da6208ffa93c5b70b0e4721d6f31da0e46d9 100644 (file)
@@ -30,6 +30,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_compat.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/conf.h>
@@ -108,8 +110,11 @@ null_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data __unused,
        error = 0;
 
        switch (cmd) {
+#ifdef COMPAT_FREEBSD11
+       case DIOCSKERNELDUMP_FREEBSD11:
+#endif
        case DIOCSKERNELDUMP:
-               error = set_dumper(NULL, NULL, td);
+               error = set_dumper(NULL, NULL, td, 0, NULL, 0, NULL);
                break;
        case FIONBIO:
                break;
index 3ccbd583fa2d215a8ca614ef7b4b14259a528904..82f7d4bdb9c32c5474b260daf4d9801301bf1ee3 100644 (file)
@@ -36,6 +36,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_compat.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
@@ -128,36 +130,44 @@ g_dev_fini(struct g_class *mp)
 }
 
 static int
-g_dev_setdumpdev(struct cdev *dev, struct thread *td)
+g_dev_setdumpdev(struct cdev *dev, struct diocskerneldump_arg *kda,
+    struct thread *td)
 {
        struct g_kerneldump kd;
        struct g_consumer *cp;
        int error, len;
 
-       if (dev == NULL)
-               return (set_dumper(NULL, NULL, td));
+       if (dev == NULL || kda == NULL)
+               return (set_dumper(NULL, NULL, td, 0, NULL, 0, NULL));
 
        cp = dev->si_drv2;
        len = sizeof(kd);
        kd.offset = 0;
        kd.length = OFF_MAX;
        error = g_io_getattr("GEOM::kerneldump", cp, &len, &kd);
-       if (error == 0) {
-               error = set_dumper(&kd.di, devtoname(dev), td);
-               if (error == 0)
-                       dev->si_flags |= SI_DUMPDEV;
-       }
+       if (error != 0)
+               return (error);
+
+       error = set_dumper(&kd.di, devtoname(dev), td, kda->kda_encryption,
+           kda->kda_key, kda->kda_encryptedkeysize, kda->kda_encryptedkey);
+       if (error == 0)
+               dev->si_flags |= SI_DUMPDEV;
+
        return (error);
 }
 
 static int
 init_dumpdev(struct cdev *dev)
 {
+       struct diocskerneldump_arg kda;
        struct g_consumer *cp;
        const char *devprefix = "/dev/", *devname;
        int error;
        size_t len;
 
+       bzero(&kda, sizeof(kda));
+       kda.kda_enable = 1;
+
        if (dumpdev == NULL)
                return (0);
 
@@ -173,7 +183,7 @@ init_dumpdev(struct cdev *dev)
        if (error != 0)
                return (error);
 
-       error = g_dev_setdumpdev(dev, curthread);
+       error = g_dev_setdumpdev(dev, &kda, curthread);
        if (error == 0) {
                freeenv(dumpdev);
                dumpdev = NULL;
@@ -493,12 +503,56 @@ g_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread
        case DIOCGFRONTSTUFF:
                error = g_io_getattr("GEOM::frontstuff", cp, &i, data);
                break;
-       case DIOCSKERNELDUMP:
-               if (*(u_int *)data == 0)
-                       error = g_dev_setdumpdev(NULL, td);
+#ifdef COMPAT_FREEBSD11
+       case DIOCSKERNELDUMP_FREEBSD11:
+           {
+               struct diocskerneldump_arg kda;
+
+               bzero(&kda, sizeof(kda));
+               kda.kda_encryption = KERNELDUMP_ENC_NONE;
+               kda.kda_enable = (uint8_t)*(u_int *)data;
+               if (kda.kda_enable == 0)
+                       error = g_dev_setdumpdev(NULL, NULL, td);
                else
-                       error = g_dev_setdumpdev(dev, td);
+                       error = g_dev_setdumpdev(dev, &kda, td);
+               break;
+           }
+#endif
+       case DIOCSKERNELDUMP:
+           {
+               struct diocskerneldump_arg *kda;
+               uint8_t *encryptedkey;
+
+               kda = (struct diocskerneldump_arg *)data;
+               if (kda->kda_enable == 0) {
+                       error = g_dev_setdumpdev(NULL, NULL, td);
+                       break;
+               }
+
+               if (kda->kda_encryption != KERNELDUMP_ENC_NONE) {
+                       if (kda->kda_encryptedkeysize <= 0 ||
+                           kda->kda_encryptedkeysize >
+                           KERNELDUMP_ENCKEY_MAX_SIZE) {
+                               return (EINVAL);
+                       }
+                       encryptedkey = malloc(kda->kda_encryptedkeysize, M_TEMP,
+                           M_WAITOK);
+                       error = copyin(kda->kda_encryptedkey, encryptedkey,
+                           kda->kda_encryptedkeysize);
+               } else {
+                       encryptedkey = NULL;
+               }
+               if (error == 0) {
+                       kda->kda_encryptedkey = encryptedkey;
+                       error = g_dev_setdumpdev(dev, kda, td);
+               }
+               if (encryptedkey != NULL) {
+                       explicit_bzero(encryptedkey, kda->kda_encryptedkeysize);
+                       free(encryptedkey, M_TEMP);
+               }
+               explicit_bzero(kda, sizeof(*kda));
                break;
+           }
        case DIOCGFLUSH:
                error = g_io_flush(cp);
                break;
@@ -756,7 +810,7 @@ g_dev_orphan(struct g_consumer *cp)
 
        /* Reset any dump-area set on this device */
        if (dev->si_flags & SI_DUMPDEV)
-               (void)set_dumper(NULL, NULL, curthread);
+               (void)set_dumper(NULL, NULL, curthread, 0, NULL, 0, NULL);
 
        /* Destroy the struct cdev *so we get no more requests */
        destroy_dev_sched_cb(dev, g_dev_callback, cp);
index 6fd11f3721ac2bd30ced46922c3739bd3778ec89..b50a0de82a6202bb878d43c7a23e9f3beee2ff26 100644 (file)
@@ -245,14 +245,21 @@ minidumpsys(struct dumperinfo *di)
        dumpsize += PAGE_SIZE;
 
        /* Determine dump offset on device. */
-       if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+       if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2 +
+           kerneldumpcrypto_dumpkeysize(di->kdc)) {
                error = ENOSPC;
                goto fail;
        }
        dumplo = di->mediaoffset + di->mediasize - dumpsize;
-       dumplo -= sizeof(kdh) * 2;
+       dumplo -= di->blocksize * 2;
+       dumplo -= kerneldumpcrypto_dumpkeysize(di->kdc);
        progress = dumpsize;
 
+       /* Initialize kernel dump crypto. */
+       error = kerneldumpcrypto_init(di->kdc);
+       if (error)
+               goto fail;
+
        /* Initialize mdhdr */
        bzero(&mdhdr, sizeof(mdhdr));
        strcpy(mdhdr.magic, MINIDUMP_MAGIC);
@@ -265,16 +272,23 @@ minidumpsys(struct dumperinfo *di)
        mdhdr.paemode = 1;
 #endif
 
-       mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_I386_VERSION, dumpsize, di->blocksize);
+       mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_I386_VERSION, dumpsize,
+           kerneldumpcrypto_dumpkeysize(di->kdc), di->blocksize);
 
        printf("Physical memory: %ju MB\n", ptoa((uintmax_t)physmem) / 1048576);
        printf("Dumping %llu MB:", (long long)dumpsize >> 20);
 
        /* Dump leader */
-       error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
+       error = dump_write_header(di, &kdh, 0, dumplo);
+       if (error)
+               goto fail;
+       dumplo += di->blocksize;
+
+       /* Dump key */
+       error = dump_write_key(di, 0, dumplo);
        if (error)
                goto fail;
-       dumplo += sizeof(kdh);
+       dumplo += kerneldumpcrypto_dumpkeysize(di->kdc);
 
        /* Dump my header */
        bzero(&fakept, sizeof(fakept));
@@ -349,10 +363,10 @@ minidumpsys(struct dumperinfo *di)
                goto fail;
 
        /* Dump trailer */
-       error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
+       error = dump_write_header(di, &kdh, 0, dumplo);
        if (error)
                goto fail;
-       dumplo += sizeof(kdh);
+       dumplo += di->blocksize;
 
        /* Signal completion, signoff and exit stage left. */
        dump_write(di, NULL, 0, 0, 0);
index 8e922a1e1bd705540d787aa326f9c52c338a6636..986149782e75ae97c41a4f47f98252ce257fdb12 100644 (file)
@@ -116,6 +116,29 @@ dumpsys_gen_write_aux_headers(struct dumperinfo *di)
 }
 #endif
 
+int
+dumpsys_buf_seek(struct dumperinfo *di, size_t sz)
+{
+       static uint8_t buf[DEV_BSIZE];
+       size_t nbytes;
+       int error;
+
+       bzero(buf, sizeof(buf));
+
+       while (sz > 0) {
+               nbytes = MIN(sz, sizeof(buf));
+
+               error = dump_write(di, buf, 0, dumplo, nbytes);
+               if (error)
+                       return (error);
+               dumplo += nbytes;
+
+               sz -= nbytes;
+       }
+
+       return (0);
+}
+
 int
 dumpsys_buf_write(struct dumperinfo *di, char *ptr, size_t sz)
 {
@@ -284,7 +307,7 @@ dumpsys_generic(struct dumperinfo *di)
        Elf_Ehdr ehdr;
        uint64_t dumpsize;
        off_t hdrgap;
-       size_t hdrsz, size;
+       size_t hdrsz;
        int error;
 
 #ifndef __powerpc__
@@ -325,24 +348,37 @@ dumpsys_generic(struct dumperinfo *di)
        hdrgap = fileofs - roundup2((off_t)hdrsz, di->blocksize);
 
        /* Determine dump offset on device. */
-       if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2) {
+       if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2 +
+           kerneldumpcrypto_dumpkeysize(di->kdc)) {
                error = ENOSPC;
                goto fail;
        }
        dumplo = di->mediaoffset + di->mediasize - dumpsize;
        dumplo -= di->blocksize * 2;
+       dumplo -= kerneldumpcrypto_dumpkeysize(di->kdc);
+
+       /* Initialize kernel dump crypto. */
+       error = kerneldumpcrypto_init(di->kdc);
+       if (error)
+               goto fail;
 
        mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARCH_VERSION, dumpsize,
-           di->blocksize);
+           kerneldumpcrypto_dumpkeysize(di->kdc), di->blocksize);
 
        printf("Dumping %ju MB (%d chunks)\n", (uintmax_t)dumpsize >> 20,
            ehdr.e_phnum - DUMPSYS_NUM_AUX_HDRS);
 
        /* Dump leader */
-       error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size);
+       error = dump_write_header(di, &kdh, 0, dumplo);
        if (error)
                goto fail;
-       dumplo += size;
+       dumplo += di->blocksize;
+
+       /* Dump key */
+       error = dump_write_key(di, 0, dumplo);
+       if (error)
+               goto fail;
+       dumplo += kerneldumpcrypto_dumpkeysize(di->kdc);
 
        /* Dump ELF header */
        error = dumpsys_buf_write(di, (char*)&ehdr, sizeof(ehdr));
@@ -365,7 +401,9 @@ dumpsys_generic(struct dumperinfo *di)
         * boundary. We cannot use MD_ALIGN on dumplo, because we don't
         * care and may very well be unaligned within the dump device.
         */
-       dumplo += hdrgap;
+       error = dumpsys_buf_seek(di, (size_t)hdrgap);
+       if (error)
+               goto fail;
 
        /* Dump memory chunks (updates dumplo) */
        error = dumpsys_foreach_chunk(dumpsys_cb_dumpdata, di);
@@ -373,9 +411,10 @@ dumpsys_generic(struct dumperinfo *di)
                goto fail;
 
        /* Dump trailer */
-       error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size);
+       error = dump_write_header(di, &kdh, 0, dumplo);
        if (error)
                goto fail;
+       dumplo += di->blocksize;
 
        /* Signal completion, signoff and exit stage left. */
        dump_write(di, NULL, 0, 0, 0);
index 79c4c30d7cc9b4d5dc94b806cc4b1646e00adad2..f981c80936232a06cf48d9340929dea55141fd65 100644 (file)
@@ -38,6 +38,7 @@
 __FBSDID("$FreeBSD$");
 
 #include "opt_ddb.h"
+#include "opt_ekcd.h"
 #include "opt_kdb.h"
 #include "opt_panic.h"
 #include "opt_sched.h"
@@ -71,6 +72,9 @@ __FBSDID("$FreeBSD$");
 #include <sys/vnode.h>
 #include <sys/watchdog.h>
 
+#include <crypto/rijndael/rijndael-api-fst.h>
+#include <crypto/sha2/sha256.h>
+
 #include <ddb/ddb.h>
 
 #include <machine/cpu.h>
@@ -143,6 +147,22 @@ int suspend_blocked = 0;
 SYSCTL_INT(_kern, OID_AUTO, suspend_blocked, CTLFLAG_RW,
        &suspend_blocked, 0, "Block suspend due to a pending shutdown");
 
+#ifdef EKCD
+FEATURE(ekcd, "Encrypted kernel crash dumps support");
+
+MALLOC_DEFINE(M_EKCD, "ekcd", "Encrypted kernel crash dumps data");
+
+struct kerneldumpcrypto {
+       uint8_t                 kdc_encryption;
+       uint8_t                 kdc_iv[KERNELDUMP_IV_MAX_SIZE];
+       keyInstance             kdc_ki;
+       cipherInstance          kdc_ci;
+       off_t                   kdc_nextoffset;
+       uint32_t                kdc_dumpkeysize;
+       struct kerneldumpkey    kdc_dumpkey[];
+};
+#endif
+
 /*
  * Variable panicstr contains argument to first call to panic; used as flag
  * to indicate that the kernel has already called panic.
@@ -838,9 +858,111 @@ static char dumpdevname[sizeof(((struct cdev*)NULL)->si_name)];
 SYSCTL_STRING(_kern_shutdown, OID_AUTO, dumpdevname, CTLFLAG_RD,
     dumpdevname, 0, "Device for kernel dumps");
 
+#ifdef EKCD
+static struct kerneldumpcrypto *
+kerneldumpcrypto_create(size_t blocksize, uint8_t encryption,
+    const uint8_t *key, uint32_t encryptedkeysize, const uint8_t *encryptedkey)
+{
+       struct kerneldumpcrypto *kdc;
+       struct kerneldumpkey *kdk;
+       uint32_t dumpkeysize;
+
+       dumpkeysize = roundup2(sizeof(*kdk) + encryptedkeysize, blocksize);
+       kdc = malloc(sizeof(*kdc) + dumpkeysize, M_EKCD, M_WAITOK | M_ZERO);
+
+       arc4rand(kdc->kdc_iv, sizeof(kdc->kdc_iv), 0);
+
+       kdc->kdc_encryption = encryption;
+       switch (kdc->kdc_encryption) {
+       case KERNELDUMP_ENC_AES_256_CBC:
+               if (rijndael_makeKey(&kdc->kdc_ki, DIR_ENCRYPT, 256, key) <= 0)
+                       goto failed;
+               break;
+       default:
+               goto failed;
+       }
+
+       kdc->kdc_dumpkeysize = dumpkeysize;
+       kdk = kdc->kdc_dumpkey;
+       kdk->kdk_encryption = kdc->kdc_encryption;
+       memcpy(kdk->kdk_iv, kdc->kdc_iv, sizeof(kdk->kdk_iv));
+       kdk->kdk_encryptedkeysize = htod32(encryptedkeysize);
+       memcpy(kdk->kdk_encryptedkey, encryptedkey, encryptedkeysize);
+
+       return (kdc);
+failed:
+       explicit_bzero(kdc, sizeof(*kdc) + dumpkeysize);
+       free(kdc, M_EKCD);
+       return (NULL);
+}
+#endif /* EKCD */
+
+int
+kerneldumpcrypto_init(struct kerneldumpcrypto *kdc)
+{
+#ifndef EKCD
+       return (0);
+#else
+       uint8_t hash[SHA256_DIGEST_LENGTH];
+       SHA256_CTX ctx;
+       struct kerneldumpkey *kdk;
+       int error;
+
+       error = 0;
+
+       if (kdc == NULL)
+               return (0);
+
+       /*
+        * When a user enters ddb it can write a crash dump multiple times.
+        * Each time it should be encrypted using a different IV.
+        */
+       SHA256_Init(&ctx);
+       SHA256_Update(&ctx, kdc->kdc_iv, sizeof(kdc->kdc_iv));
+       SHA256_Final(hash, &ctx);
+       bcopy(hash, kdc->kdc_iv, sizeof(kdc->kdc_iv));
+
+       switch (kdc->kdc_encryption) {
+       case KERNELDUMP_ENC_AES_256_CBC:
+               if (rijndael_cipherInit(&kdc->kdc_ci, MODE_CBC,
+                   kdc->kdc_iv) <= 0) {
+                       error = EINVAL;
+                       goto out;
+               }
+               break;
+       default:
+               error = EINVAL;
+               goto out;
+       }
+
+       kdc->kdc_nextoffset = 0;
+
+       kdk = kdc->kdc_dumpkey;
+       memcpy(kdk->kdk_iv, kdc->kdc_iv, sizeof(kdk->kdk_iv));
+out:
+       explicit_bzero(hash, sizeof(hash));
+       return (error);
+#endif
+}
+
+uint32_t
+kerneldumpcrypto_dumpkeysize(const struct kerneldumpcrypto *kdc)
+{
+
+#ifdef EKCD
+       if (kdc == NULL)
+               return (0);
+       return (kdc->kdc_dumpkeysize);
+#else
+       return (0);
+#endif
+}
+
 /* Registration of dumpers */
 int
-set_dumper(struct dumperinfo *di, const char *devname, struct thread *td)
+set_dumper(struct dumperinfo *di, const char *devname, struct thread *td,
+    uint8_t encryption, const uint8_t *key, uint32_t encryptedkeysize,
+    const uint8_t *encryptedkey)
 {
        size_t wantcopy;
        int error;
@@ -850,28 +972,56 @@ set_dumper(struct dumperinfo *di, const char *devname, struct thread *td)
                return (error);
 
        if (di == NULL) {
-               if (dumper.blockbuf != NULL)
-                       free(dumper.blockbuf, M_DUMPER);
-               bzero(&dumper, sizeof(dumper));
-               dumpdevname[0] = '\0';
-               return (0);
+               error = 0;
+               goto cleanup;
        }
        if (dumper.dumper != NULL)
                return (EBUSY);
        dumper = *di;
+       dumper.blockbuf = NULL;
+       dumper.kdc = NULL;
+
+       if (encryption != KERNELDUMP_ENC_NONE) {
+#ifdef EKCD
+               dumper.kdc = kerneldumpcrypto_create(di->blocksize, encryption,
+                   key, encryptedkeysize, encryptedkey);
+               if (dumper.kdc == NULL) {
+                       error = EINVAL;
+                       goto cleanup;
+               }
+#else
+               error = EOPNOTSUPP;
+               goto cleanup;
+#endif
+       }
+
        wantcopy = strlcpy(dumpdevname, devname, sizeof(dumpdevname));
        if (wantcopy >= sizeof(dumpdevname)) {
                printf("set_dumper: device name truncated from '%s' -> '%s'\n",
                        devname, dumpdevname);
        }
+
        dumper.blockbuf = malloc(di->blocksize, M_DUMPER, M_WAITOK | M_ZERO);
        return (0);
+cleanup:
+#ifdef EKCD
+       if (dumper.kdc != NULL) {
+               explicit_bzero(dumper.kdc, sizeof(*dumper.kdc) +
+                   dumper.kdc->kdc_dumpkeysize);
+               free(dumper.kdc, M_EKCD);
+       }
+#endif
+       if (dumper.blockbuf != NULL) {
+               explicit_bzero(dumper.blockbuf, dumper.blocksize);
+               free(dumper.blockbuf, M_DUMPER);
+       }
+       explicit_bzero(&dumper, sizeof(dumper));
+       dumpdevname[0] = '\0';
+       return (error);
 }
 
-/* Call dumper with bounds checking. */
-int
-dump_write(struct dumperinfo *di, void *virtual, vm_offset_t physical,
-    off_t offset, size_t length)
+static int
+dump_check_bounds(struct dumperinfo *di, off_t offset, size_t length)
 {
 
        if (length != 0 && (offset < di->mediaoffset ||
@@ -882,37 +1032,202 @@ dump_write(struct dumperinfo *di, void *virtual, vm_offset_t physical,
                    (uintmax_t)length, (intmax_t)di->mediasize);
                return (ENOSPC);
        }
-       return (di->dumper(di->priv, virtual, physical, offset, length));
+
+       return (0);
+}
+
+#ifdef EKCD
+static int
+dump_encrypt(struct kerneldumpcrypto *kdc, uint8_t *buf, size_t size)
+{
+
+       switch (kdc->kdc_encryption) {
+       case KERNELDUMP_ENC_AES_256_CBC:
+               if (rijndael_blockEncrypt(&kdc->kdc_ci, &kdc->kdc_ki, buf,
+                   8 * size, buf) <= 0) {
+                       return (EIO);
+               }
+               if (rijndael_cipherInit(&kdc->kdc_ci, MODE_CBC,
+                   buf + size - 16 /* IV size for AES-256-CBC */) <= 0) {
+                       return (EIO);
+               }
+               break;
+       default:
+               return (EINVAL);
+       }
+
+       return (0);
+}
+
+/* Encrypt data and call dumper. */
+static int
+dump_encrypted_write(struct dumperinfo *di, void *virtual, vm_offset_t physical,
+    off_t offset, size_t length)
+{
+       static uint8_t buf[KERNELDUMP_BUFFER_SIZE];
+       struct kerneldumpcrypto *kdc;
+       int error;
+       size_t nbytes;
+       off_t nextoffset;
+
+       kdc = di->kdc;
+
+       error = dump_check_bounds(di, offset, length);
+       if (error != 0)
+               return (error);
+
+       /* Signal completion. */
+       if (virtual == NULL && physical == 0 && offset == 0 && length == 0) {
+               return (di->dumper(di->priv, virtual, physical, offset,
+                   length));
+       }
+
+       /* Data have to be aligned to block size. */
+       if ((length % di->blocksize) != 0)
+               return (EINVAL);
+
+       /*
+        * Data have to be written continuously becase we're encrypting using
+        * CBC mode which has this assumption.
+        */
+       if (kdc->kdc_nextoffset != 0 && kdc->kdc_nextoffset != offset)
+               return (EINVAL);
+
+       nextoffset = offset + (off_t)length;
+
+       while (length > 0) {
+               nbytes = MIN(length, sizeof(buf));
+               bcopy(virtual, buf, nbytes);
+
+               if (dump_encrypt(kdc, buf, nbytes) != 0)
+                       return (EIO);
+
+               error = di->dumper(di->priv, buf, physical, offset, nbytes);
+               if (error != 0)
+                       return (error);
+
+               offset += nbytes;
+               virtual = (void *)((uint8_t *)virtual + nbytes);
+               length -= nbytes;
+       }
+
+       kdc->kdc_nextoffset = nextoffset;
+
+       return (0);
 }
+#endif /* EKCD */
 
 /* Call dumper with bounds checking. */
+static int
+dump_raw_write(struct dumperinfo *di, void *virtual, vm_offset_t physical,
+    off_t offset, size_t length)
+{
+       int error;
+
+       error = dump_check_bounds(di, offset, length);
+       if (error != 0)
+               return (error);
+
+       return (di->dumper(di->priv, virtual, physical, offset, length));
+}
+
 int
-dump_write_pad(struct dumperinfo *di, void *virtual, vm_offset_t physical,
-    off_t offset, size_t length, size_t *size)
+dump_write(struct dumperinfo *di, void *virtual, vm_offset_t physical,
+    off_t offset, size_t length)
+{
+
+#ifdef EKCD
+       if (di->kdc != NULL) {
+               return (dump_encrypted_write(di, virtual, physical, offset,
+                   length));
+       }
+#endif
+
+       return (dump_raw_write(di, virtual, physical, offset, length));
+}
+
+static int
+dump_pad(struct dumperinfo *di, void *virtual, size_t length, void **buf,
+    size_t *size)
 {
-       char *temp;
-       int ret;
 
        if (length > di->blocksize)
                return (ENOMEM);
 
        *size = di->blocksize;
-       if (length == di->blocksize)
-               temp = virtual;
-       else {
-               temp = di->blockbuf;
-               memset(temp + length, 0, di->blocksize - length);
-               memcpy(temp, virtual, length);
+       if (length == di->blocksize) {
+               *buf = virtual;
+       else {
+               *buf = di->blockbuf;
+               memcpy(*buf, virtual, length);
+               memset((uint8_t *)*buf + length, 0, di->blocksize - length);
        }
-       ret = dump_write(di, temp, physical, offset, *size);
 
+       return (0);
+}
+
+static int
+dump_raw_write_pad(struct dumperinfo *di, void *virtual, vm_offset_t physical,
+    off_t offset, size_t length, size_t *size)
+{
+       void *buf;
+       int error;
+
+       error = dump_pad(di, virtual, length, &buf, size);
+       if (error != 0)
+               return (error);
+
+       return (dump_raw_write(di, buf, physical, offset, *size));
+}
+
+int
+dump_write_pad(struct dumperinfo *di, void *virtual, vm_offset_t physical,
+    off_t offset, size_t length, size_t *size)
+{
+       void *buf;
+       int error;
+
+       error = dump_pad(di, virtual, length, &buf, size);
+       if (error != 0)
+               return (error);
+
+       return (dump_write(di, buf, physical, offset, *size));
+}
+
+int
+dump_write_header(struct dumperinfo *di, struct kerneldumpheader *kdh,
+    vm_offset_t physical, off_t offset)
+{
+       size_t size;
+       int ret;
+
+       ret = dump_raw_write_pad(di, kdh, physical, offset, sizeof(*kdh),
+           &size);
+       if (ret == 0 && size != di->blocksize)
+               ret = EINVAL;
        return (ret);
 }
 
+int
+dump_write_key(struct dumperinfo *di, vm_offset_t physical, off_t offset)
+{
+#ifndef EKCD
+       return (0);
+#else /* EKCD */
+       struct kerneldumpcrypto *kdc;
+
+       kdc = di->kdc;
+       if (kdc == NULL)
+               return (0);
+
+       return (dump_raw_write(di, kdc->kdc_dumpkey, physical, offset,
+           kdc->kdc_dumpkeysize));
+#endif /* !EKCD */
+}
 
 void
 mkdumpheader(struct kerneldumpheader *kdh, char *magic, uint32_t archver,
-    uint64_t dumplen, uint32_t blksz)
+    uint64_t dumplen, uint32_t dumpkeysize, uint32_t blksz)
 {
 
        bzero(kdh, sizeof(*kdh));
@@ -922,6 +1237,7 @@ mkdumpheader(struct kerneldumpheader *kdh, char *magic, uint32_t archver,
        kdh->architectureversion = htod32(archver);
        kdh->dumplength = htod64(dumplen);
        kdh->dumptime = htod64(time_second);
+       kdh->dumpkeysize = htod32(dumpkeysize);
        kdh->blocksize = htod32(blksz);
        strlcpy(kdh->hostname, prison0.pr_hostname, sizeof(kdh->hostname));
        strlcpy(kdh->versionstring, version, sizeof(kdh->versionstring));
index d9e3b47c555eed414b1df16d919f43a24cf34bf1..c0dba4c5da6d04491765d96e5fa8226f30662daa 100644 (file)
@@ -219,15 +219,22 @@ minidumpsys(struct dumperinfo *di)
        dumpsize += PAGE_SIZE;
 
        /* Determine dump offset on device. */
-       if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) {
+       if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2 +
+           kerneldumpcrypto_dumpkeysize(di->kdc)) {
                error = ENOSPC;
                goto fail;
        }
 
        origdumplo = dumplo = di->mediaoffset + di->mediasize - dumpsize;
-       dumplo -= sizeof(kdh) * 2;
+       dumplo -= di->blocksize * 2;
+       dumplo -= kerneldumpcrypto_dumpkeysize(di->kdc);
        progress = dumpsize;
 
+       /* Initialize kernel dump crypto. */
+       error = kerneldumpcrypto_init(di->kdc);
+       if (error)
+               goto fail;
+
        /* Initialize mdhdr */
        bzero(&mdhdr, sizeof(mdhdr));
        strcpy(mdhdr.magic, MINIDUMP_MAGIC);
@@ -238,17 +245,23 @@ minidumpsys(struct dumperinfo *di)
        mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS;
 
        mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_MIPS_VERSION, dumpsize,
-           di->blocksize);
+           kerneldumpcrypto_dumpkeysize(di->kdc), di->blocksize);
 
        printf("Physical memory: %ju MB\n", 
            (uintmax_t)ptoa((uintmax_t)physmem) / 1048576);
        printf("Dumping %llu MB:", (long long)dumpsize >> 20);
 
        /* Dump leader */
-       error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
+       error = dump_write_header(di, &kdh, 0, dumplo);
+       if (error)
+               goto fail;
+       dumplo += di->blocksize;
+
+       /* Dump key */
+       error = dump_write_key(di, 0, dumplo);
        if (error)
                goto fail;
-       dumplo += sizeof(kdh);
+       dumplo += kerneldumpcrypto_dumpkeysize(di->kdc);
 
        /* Dump my header */
        bzero(tmpbuffer, sizeof(tmpbuffer));
@@ -316,10 +329,10 @@ minidumpsys(struct dumperinfo *di)
        }
 
        /* Dump trailer */
-       error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
+       error = dump_write_header(di, &kdh, 0, dumplo);
        if (error)
                goto fail;
-       dumplo += sizeof(kdh);
+       dumplo += di->blocksize;
 
        /* Signal completion, signoff and exit stage left. */
        dump_write(di, NULL, 0, 0, 0);
index 9cb1aabd132ca633ce2b8b76ed09e0437d4c20ef..6dc1fa425b934d7108e78b0d5f6a095f8f80a6e8 100644 (file)
@@ -94,7 +94,8 @@ dumpsys(struct dumperinfo *di)
            DEV_BSIZE);
        size += hdrsize;
 
-       totsize = size + 2 * sizeof(kdh);
+       totsize = size + 2 * di->blocksize +
+           kerneldumpcrypto_dumpkeysize(di->kdc);
        if (totsize > di->mediasize) {
                printf("Insufficient space on device (need %ld, have %ld), "
                    "refusing to dump.\n", (long)totsize,
@@ -106,16 +107,27 @@ dumpsys(struct dumperinfo *di)
        /* Determine dump offset on device. */
        dumplo = di->mediaoffset + di->mediasize - totsize;
 
+       /* Initialize kernel dump crypto. */
+       error = kerneldumpcrypto_init(di->kdc);
+       if (error)
+               goto fail;
+
        mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_SPARC64_VERSION, size,
-           di->blocksize);
+           kerneldumpcrypto_dumpkeysize(di->kdc), di->blocksize);
 
        printf("Dumping %lu MB (%d chunks)\n", (u_long)(size >> 20), nreg);
 
        /* Dump leader */
-       error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
+       error = dump_write_header(di, &kdh, 0, dumplo);
+       if (error)
+               goto fail;
+       dumplo += di->blocksize;
+
+       /* Dump key */
+       error = dump_write_key(di, 0, dumplo);
        if (error)
                goto fail;
-       dumplo += sizeof(kdh);
+       dumplo += kerneldumpcrypto_dumpkeysize(di->kdc);
 
        /* Dump the private header. */
        hdr.dh_hdr_size = hdrsize;
@@ -143,9 +155,10 @@ dumpsys(struct dumperinfo *di)
                goto fail;
 
        /* Dump trailer */
-       error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh));
+       error = dump_write_header(di, &kdh, 0, dumplo);
        if (error)
                goto fail;
+       dumplo += di->blocksize;
 
        /* Signal completion, signoff and exit stage left. */
        dump_write(di, NULL, 0, 0, 0);
index 2bfc14f56e4f74d518f4100b344a48f275c64038..4cfb74476b0aa03a9c5f45fbee8a211fb994a200 100644 (file)
@@ -325,6 +325,8 @@ int dev_stdclone(char *_name, char **_namep, const char *_stem, int *_unit);
 EVENTHANDLER_DECLARE(dev_clone, dev_clone_fn);
 
 /* Stuff relating to kernel-dump */
+struct kerneldumpcrypto;
+struct kerneldumpheader;
 
 struct dumperinfo {
        dumper_t *dumper;       /* Dumping function. */
@@ -334,12 +336,18 @@ struct dumperinfo {
        off_t   mediaoffset;    /* Initial offset in bytes. */
        off_t   mediasize;      /* Space available in bytes. */
        void    *blockbuf;      /* Buffer for padding shorter dump blocks */
+       struct kerneldumpcrypto *kdc; /* Kernel dump crypto. */
 };
 
-int set_dumper(struct dumperinfo *, const char *_devname, struct thread *td);
+int set_dumper(struct dumperinfo *di, const char *devname, struct thread *td,
+    uint8_t encrypt, const uint8_t *key, uint32_t encryptedkeysize,
+    const uint8_t *encryptedkey);
 int dump_write(struct dumperinfo *, void *, vm_offset_t, off_t, size_t);
 int dump_write_pad(struct dumperinfo *, void *, vm_offset_t, off_t, size_t,
     size_t *);
+int dump_write_header(struct dumperinfo *di, struct kerneldumpheader *kdh,
+    vm_offset_t physical, off_t offset);
+int dump_write_key(struct dumperinfo *di, vm_offset_t physical, off_t offset);
 int doadump(boolean_t);
 extern int dumping;            /* system is dumping */
 
index 6b35d748ce7e1cdb2b78e91170851818e0fbd0b7..873efd1ee21300d3f4709bfaf092a020500ebb35 100644 (file)
@@ -14,6 +14,7 @@
 #define        _SYS_DISK_H_
 
 #include <sys/ioccom.h>
+#include <sys/kerneldump.h>
 #include <sys/types.h>
 #include <sys/disk_zone.h>
 
@@ -54,7 +55,7 @@ void disk_err(struct bio *bp, const char *what, int blkdone, int nl);
         * disk label formats.  Don't use it unless you have to.
         */
 
-#define        DIOCSKERNELDUMP _IOW('d', 133, u_int)   /* Set/Clear kernel dumps */
+#define        DIOCSKERNELDUMP_FREEBSD11 _IOW('d', 133, u_int)
        /*
         * Enable/Disable (the argument is boolean) the device for kernel
         * core dumps.
@@ -139,4 +140,16 @@ struct diocgattr_arg {
 
 #define        DIOCZONECMD     _IOWR('d', 143, struct disk_zone_args)
 
+struct diocskerneldump_arg {
+       uint8_t          kda_enable;
+       uint8_t          kda_encryption;
+       uint8_t          kda_key[KERNELDUMP_KEY_MAX_SIZE];
+       uint32_t         kda_encryptedkeysize;
+       uint8_t         *kda_encryptedkey;
+};
+#define        DIOCSKERNELDUMP _IOW('d', 144, struct diocskerneldump_arg)
+       /*
+        * Enable/Disable the device for kernel core dumps.
+        */
+
 #endif /* _SYS_DISK_H_ */
index 13bef698ffcb8716148eb0035ef146d19791aaee..3fb6a7ad18bf8a32d54ec006c944fc34b02a54c4 100644 (file)
@@ -38,6 +38,9 @@
 #ifndef _SYS_KERNELDUMP_H
 #define _SYS_KERNELDUMP_H
 
+#include <sys/param.h>
+#include <sys/conf.h>
+
 #include <machine/endian.h>
 
 #if BYTE_ORDER == LITTLE_ENDIAN
 #define        htod64(x)       (x)
 #endif
 
+#define        KERNELDUMP_ENC_NONE             0
+#define        KERNELDUMP_ENC_AES_256_CBC      1
+
+#define        KERNELDUMP_BUFFER_SIZE          1024
+#define        KERNELDUMP_IV_MAX_SIZE          32
+#define        KERNELDUMP_KEY_MAX_SIZE         64
+#define        KERNELDUMP_ENCKEY_MAX_SIZE      (16384 / 8)
+
 /*
  * All uintX_t fields are in dump byte order, which is the same as
  * network byte order. Use the macros defined above to read or
@@ -64,8 +75,8 @@ struct kerneldumpheader {
 #define        KERNELDUMPMAGIC_CLEARED "Cleared Kernel Dump"
        char            architecture[12];
        uint32_t        version;
-#define        KERNELDUMPVERSION               1
-#define        KERNELDUMP_TEXT_VERSION         1
+#define        KERNELDUMPVERSION               2
+#define        KERNELDUMP_TEXT_VERSION         2
        uint32_t        architectureversion;
 #define        KERNELDUMP_AARCH64_VERSION      1
 #define        KERNELDUMP_AMD64_VERSION        2
@@ -77,13 +88,21 @@ struct kerneldumpheader {
 #define        KERNELDUMP_SPARC64_VERSION      1
        uint64_t        dumplength;             /* excl headers */
        uint64_t        dumptime;
+       uint32_t        dumpkeysize;
        uint32_t        blocksize;
        char            hostname[64];
        char            versionstring[192];
-       char            panicstring[192];
+       char            panicstring[188];
        uint32_t        parity;
 };
 
+struct kerneldumpkey {
+       uint8_t         kdk_encryption;
+       uint8_t         kdk_iv[KERNELDUMP_IV_MAX_SIZE];
+       uint32_t        kdk_encryptedkeysize;
+       uint8_t         kdk_encryptedkey[];
+} __packed;
+
 /*
  * Parity calculation is endian insensitive.
  */
@@ -106,8 +125,11 @@ struct dump_pa {
        vm_paddr_t pa_size;
 };
 
+int kerneldumpcrypto_init(struct kerneldumpcrypto *kdc);
+uint32_t kerneldumpcrypto_dumpkeysize(const struct kerneldumpcrypto *kdc);
+
 void mkdumpheader(struct kerneldumpheader *kdh, char *magic, uint32_t archver,
-    uint64_t dumplen, uint32_t blksz);
+    uint64_t dumplen, uint32_t dumpkeysize, uint32_t blksz);
 
 int dumpsys_generic(struct dumperinfo *);
 
@@ -115,6 +137,7 @@ void dumpsys_map_chunk(vm_paddr_t, size_t, void **);
 typedef int dumpsys_callback_t(struct dump_pa *, int, void *);
 int dumpsys_foreach_chunk(dumpsys_callback_t, void *);
 int dumpsys_cb_dumpdata(struct dump_pa *, int, void *);
+int dumpsys_buf_seek(struct dumperinfo *, size_t);
 int dumpsys_buf_write(struct dumperinfo *, char *, size_t);
 int dumpsys_buf_flush(struct dumperinfo *);