aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/defaults/rc.conf2
-rwxr-xr-xetc/rc.d/dumpon7
-rw-r--r--sbin/Makefile1
-rw-r--r--sbin/decryptcore/Makefile13
-rw-r--r--sbin/decryptcore/decryptcore.8114
-rw-r--r--sbin/decryptcore/decryptcore.c373
-rw-r--r--sbin/dumpon/Makefile8
-rw-r--r--sbin/dumpon/dumpon.8118
-rw-r--r--sbin/dumpon/dumpon.c111
-rw-r--r--sbin/savecore/savecore.87
-rw-r--r--sbin/savecore/savecore.c92
-rw-r--r--share/man/man5/rc.conf.514
-rw-r--r--sys/amd64/amd64/minidump_machdep.c27
-rw-r--r--sys/arm/arm/minidump_machdep.c27
-rw-r--r--sys/arm64/arm64/minidump_machdep.c27
-rw-r--r--sys/conf/NOTES3
-rw-r--r--sys/conf/files6
-rw-r--r--sys/conf/options3
-rw-r--r--sys/ddb/db_textdump.c17
-rw-r--r--sys/dev/null/null.c7
-rw-r--r--sys/geom/geom_dev.c82
-rw-r--r--sys/i386/i386/minidump_machdep.c28
-rw-r--r--sys/kern/kern_dump.c53
-rw-r--r--sys/kern/kern_shutdown.c362
-rw-r--r--sys/mips/mips/minidump_machdep.c27
-rw-r--r--sys/sparc64/sparc64/dump_machdep.c23
-rw-r--r--sys/sys/conf.h10
-rw-r--r--sys/sys/disk.h15
-rw-r--r--sys/sys/kerneldump.h31
29 files changed, 1485 insertions, 123 deletions
diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf
index f4fa7a8846df..c8a3061b62ff 100644
--- a/etc/defaults/rc.conf
+++ b/etc/defaults/rc.conf
@@ -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
diff --git a/etc/rc.d/dumpon b/etc/rc.d/dumpon
index 66276eb50535..87bdd3eb0b39 100755
--- a/etc/rc.d/dumpon
+++ b/etc/rc.d/dumpon
@@ -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
diff --git a/sbin/Makefile b/sbin/Makefile
index 4826bc09b26d..39e417acbacf 100644
--- a/sbin/Makefile
+++ b/sbin/Makefile
@@ -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
index 000000000000..be8b6d1c5934
--- /dev/null
+++ b/sbin/decryptcore/Makefile
@@ -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
index 000000000000..d21667c0784f
--- /dev/null
+++ b/sbin/decryptcore/decryptcore.8
@@ -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
index 000000000000..758e4c8c0fea
--- /dev/null
+++ b/sbin/decryptcore/decryptcore.c
@@ -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);
+}
diff --git a/sbin/dumpon/Makefile b/sbin/dumpon/Makefile
index 782117c16ff0..d0700ab16a89 100644
--- a/sbin/dumpon/Makefile
+++ b/sbin/dumpon/Makefile
@@ -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>
diff --git a/sbin/dumpon/dumpon.8 b/sbin/dumpon/dumpon.8
index 81377124d803..14b0665ec1bb 100644
--- a/sbin/dumpon/dumpon.8
+++ b/sbin/dumpon/dumpon.8
@@ -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
diff --git a/sbin/dumpon/dumpon.c b/sbin/dumpon/dumpon.c
index 8407111c7f65..a1d77c852a48 100644
--- a/sbin/dumpon/dumpon.c
+++ b/sbin/dumpon/dumpon.c
@@ -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");
}
diff --git a/sbin/savecore/savecore.8 b/sbin/savecore/savecore.8
index af46d05e4d62..7d54c823affb 100644
--- a/sbin/savecore/savecore.8
+++ b/sbin/savecore/savecore.8
@@ -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
diff --git a/sbin/savecore/savecore.c b/sbin/savecore/savecore.c
index 94b800909174..b07e7e1e018c 100644
--- a/sbin/savecore/savecore.c
+++ b/sbin/savecore/savecore.c
@@ -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",
diff --git a/share/man/man5/rc.conf.5 b/share/man/man5/rc.conf.5
index ed04a77b7a04..46b6aa2063ef 100644
--- a/share/man/man5/rc.conf.5
+++ b/share/man/man5/rc.conf.5
@@ -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
diff --git a/sys/amd64/amd64/minidump_machdep.c b/sys/amd64/amd64/minidump_machdep.c
index 11bcf5f863ca..a758c7b24d6a 100644
--- a/sys/amd64/amd64/minidump_machdep.c
+++ b/sys/amd64/amd64/minidump_machdep.c
@@ -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);
diff --git a/sys/arm/arm/minidump_machdep.c b/sys/arm/arm/minidump_machdep.c
index e7340dcf33db..c5a139f74702 100644
--- a/sys/arm/arm/minidump_machdep.c
+++ b/sys/arm/arm/minidump_machdep.c
@@ -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);
diff --git a/sys/arm64/arm64/minidump_machdep.c b/sys/arm64/arm64/minidump_machdep.c
index 27c2081ef78d..2aeb07d07366 100644
--- a/sys/arm64/arm64/minidump_machdep.c
+++ b/sys/arm64/arm64/minidump_machdep.c
@@ -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);
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 061438615570..cbd0934a032b 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -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
diff --git a/sys/conf/files b/sys/conf/files
index 9b4042726f68..23e9c6f32a56 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -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
diff --git a/sys/conf/options b/sys/conf/options
index 07d76390505f..1f20983c2097 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -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
diff --git a/sys/ddb/db_textdump.c b/sys/ddb/db_textdump.c
index aeeeec863cec..66d8d2c84032 100644
--- a/sys/ddb/db_textdump.c
+++ b/sys/ddb/db_textdump.c
@@ -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) {
@@ -449,6 +450,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
* our way back. We will rewrite this header later to reflect the
@@ -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;
}
/*-
diff --git a/sys/dev/null/null.c b/sys/dev/null/null.c
index c8966df8ac4b..d946da6208ff 100644
--- a/sys/dev/null/null.c
+++ b/sys/dev/null/null.c
@@ -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;
diff --git a/sys/geom/geom_dev.c b/sys/geom/geom_dev.c
index 3ccbd583fa2d..82f7d4bdb9c3 100644
--- a/sys/geom/geom_dev.c
+++ b/sys/geom/geom_dev.c
@@ -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);
diff --git a/sys/i386/i386/minidump_machdep.c b/sys/i386/i386/minidump_machdep.c
index 6fd11f3721ac..b50a0de82a62 100644
--- a/sys/i386/i386/minidump_machdep.c
+++ b/sys/i386/i386/minidump_machdep.c
@@ -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);
diff --git a/sys/kern/kern_dump.c b/sys/kern/kern_dump.c
index 8e922a1e1bd7..986149782e75 100644
--- a/sys/kern/kern_dump.c
+++ b/sys/kern/kern_dump.c
@@ -117,6 +117,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)
{
size_t len;
@@ -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);
diff --git a/sys/kern/kern_shutdown.c b/sys/kern/kern_shutdown.c
index 79c4c30d7cc9..f981c8093623 100644
--- a/sys/kern/kern_shutdown.c
+++ b/sys/kern/kern_shutdown.c
@@ -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));
diff --git a/sys/mips/mips/minidump_machdep.c b/sys/mips/mips/minidump_machdep.c
index d9e3b47c555e..c0dba4c5da6d 100644
--- a/sys/mips/mips/minidump_machdep.c
+++ b/sys/mips/mips/minidump_machdep.c
@@ -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);
diff --git a/sys/sparc64/sparc64/dump_machdep.c b/sys/sparc64/sparc64/dump_machdep.c
index 9cb1aabd132c..6dc1fa425b93 100644
--- a/sys/sparc64/sparc64/dump_machdep.c
+++ b/sys/sparc64/sparc64/dump_machdep.c
@@ -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);
diff --git a/sys/sys/conf.h b/sys/sys/conf.h
index 2bfc14f56e4f..4cfb74476b0a 100644
--- a/sys/sys/conf.h
+++ b/sys/sys/conf.h
@@ -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 */
diff --git a/sys/sys/disk.h b/sys/sys/disk.h
index 6b35d748ce7e..873efd1ee213 100644
--- a/sys/sys/disk.h
+++ b/sys/sys/disk.h
@@ -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_ */
diff --git a/sys/sys/kerneldump.h b/sys/sys/kerneldump.h
index 13bef698ffcb..3fb6a7ad18bf 100644
--- a/sys/sys/kerneldump.h
+++ b/sys/sys/kerneldump.h
@@ -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
@@ -52,6 +55,14 @@
#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 *);