Add support for encrypted kernel crash dumps.

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
This commit is contained in:
Konrad Witaszczyk 2016-12-10 16:20:39 +00:00
parent aced69428c
commit 480f31c214
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=309818
29 changed files with 1485 additions and 123 deletions

View 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

View File

@ -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

View 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

13
sbin/decryptcore/Makefile Normal file
View File

@ -0,0 +1,13 @@
# $FreeBSD$
PROG= decryptcore
LIBADD= crypto pjdlog
MAN= decryptcore.8
CFLAGS+=-I${.CURDIR}/../../lib/libpjdlog
WARNS?= 6
.include <bsd.prog.mk>

View File

@ -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 .

View File

@ -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);
}

View 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>

View 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

View 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");
}

View 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

View 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",

View 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

View 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 += size;
dumplo += di->blocksize;
/* Dump key */
error = dump_write_key(di, 0, dumplo);
if (error)
goto fail;
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);

View 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 += sizeof(kdh);
dumplo += di->blocksize;
/* Dump key */
error = dump_write_key(di, 0, dumplo);
if (error)
goto fail;
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);

View 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 += sizeof(kdh);
dumplo += di->blocksize;
/* Dump key */
error = dump_write_key(di, 0, dumplo);
if (error)
goto fail;
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);

View 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

View 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

View 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

View 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;
}
/*-

View 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;

View 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);

View 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 += sizeof(kdh);
dumplo += di->blocksize;
/* Dump key */
error = dump_write_key(di, 0, dumplo);
if (error)
goto fail;
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);

View 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);

View 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 (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));
}
/* Call dumper with bounds checking. */
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));

View 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 += sizeof(kdh);
dumplo += di->blocksize;
/* Dump key */
error = dump_write_key(di, 0, dumplo);
if (error)
goto fail;
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);

View 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 += sizeof(kdh);
dumplo += di->blocksize;
/* Dump key */
error = dump_write_key(di, 0, dumplo);
if (error)
goto fail;
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);

View 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 */

View 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_ */

View 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
@ -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 *);