Index: head/etc/defaults/rc.conf =================================================================== --- head/etc/defaults/rc.conf +++ head/etc/defaults/rc.conf @@ -607,6 +607,8 @@ chkprintcap_flags="-d" # Create missing directories by default. dumpdev="AUTO" # Device to crashdump to (device name, AUTO, or NO). dumpdir="/var/crash" # Directory where crash dumps are to be stored +dumppubkey="" # Public key for encrypted kernel crash dumps. + # See dumpon(8) for more details. savecore_enable="YES" # Extract core from dump devices if any savecore_flags="-m 10" # Used if dumpdev is enabled above, and present. # By default, only the 10 most recent kernel dumps Index: head/etc/rc.d/dumpon =================================================================== --- head/etc/rc.d/dumpon +++ head/etc/rc.d/dumpon @@ -16,7 +16,12 @@ dumpon_try() { - if /sbin/dumpon "${1}" ; then + if [ -n "${dumppubkey}" ]; then + /sbin/dumpon -k "${dumppubkey}" "${1}" + else + /sbin/dumpon "${1}" + fi + if [ $? -eq 0 ]; then # Make a symlink in devfs for savecore ln -fs "${1}" /dev/dumpdev return 0 Index: head/sbin/Makefile =================================================================== --- head/sbin/Makefile +++ head/sbin/Makefile @@ -83,6 +83,7 @@ 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 Index: head/sbin/decryptcore/Makefile =================================================================== --- head/sbin/decryptcore/Makefile +++ head/sbin/decryptcore/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD$ + +PROG= decryptcore + +LIBADD= crypto pjdlog + +MAN= decryptcore.8 + +CFLAGS+=-I${.CURDIR}/../../lib/libpjdlog + +WARNS?= 6 + +.include Index: head/sbin/decryptcore/decryptcore.8 =================================================================== --- head/sbin/decryptcore/decryptcore.8 +++ head/sbin/decryptcore/decryptcore.8 @@ -0,0 +1,114 @@ +.\" Copyright (c) 2016 Konrad Witaszczyk +.\" 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 . Index: head/sbin/decryptcore/decryptcore.c =================================================================== --- head/sbin/decryptcore/decryptcore.c +++ head/sbin/decryptcore/decryptcore.c @@ -0,0 +1,373 @@ +/*- + * Copyright (c) 2016 Konrad Witaszczyk + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pjdlog.h" + +#define DECRYPTCORE_CRASHDIR "/var/crash" + +static void +usage(void) +{ + + pjdlog_exitx(1, + "usage: decryptcore [-Lv] -p privatekeyfile -k keyfile -e encryptedcore -c core\n" + " decryptcore [-Lv] [-d crashdir] -p privatekeyfile -n dumpnr"); +} + +static int +wait_for_process(pid_t pid) +{ + int status; + + if (waitpid(pid, &status, WUNTRACED | WEXITED) == -1) { + pjdlog_errno(LOG_ERR, "Unable to wait for a child process"); + return (1); + } + + if (WIFEXITED(status)) + return (WEXITSTATUS(status)); + + return (1); +} + +static struct kerneldumpkey * +read_key(int kfd) +{ + struct kerneldumpkey *kdk; + ssize_t size; + size_t kdksize; + + PJDLOG_ASSERT(kfd >= 0); + + kdksize = sizeof(*kdk); + kdk = calloc(1, kdksize); + if (kdk == NULL) { + pjdlog_errno(LOG_ERR, "Unable to allocate kernel dump key"); + goto failed; + } + + size = read(kfd, kdk, kdksize); + if (size == (ssize_t)kdksize) { + kdk->kdk_encryptedkeysize = dtoh32(kdk->kdk_encryptedkeysize); + kdksize += (size_t)kdk->kdk_encryptedkeysize; + kdk = realloc(kdk, kdksize); + if (kdk == NULL) { + pjdlog_errno(LOG_ERR, "Unable to reallocate kernel dump key"); + goto failed; + } + size += read(kfd, &kdk->kdk_encryptedkey, + kdk->kdk_encryptedkeysize); + } + if (size != (ssize_t)kdksize) { + pjdlog_errno(LOG_ERR, "Unable to read key"); + goto failed; + } + + return (kdk); +failed: + free(kdk); + return (NULL); +} + +static bool +decrypt(const char *privkeyfile, const char *keyfile, const char *input, + const char *output) +{ + uint8_t buf[KERNELDUMP_BUFFER_SIZE], key[KERNELDUMP_KEY_MAX_SIZE]; + EVP_CIPHER_CTX ctx; + const EVP_CIPHER *cipher; + FILE *fp; + struct kerneldumpkey *kdk; + RSA *privkey; + int ifd, kfd, ofd, olen, privkeysize; + ssize_t bytes; + pid_t pid; + + PJDLOG_ASSERT(privkeyfile != NULL); + PJDLOG_ASSERT(keyfile != NULL); + PJDLOG_ASSERT(input != NULL); + PJDLOG_ASSERT(output != NULL); + + privkey = NULL; + + /* + * Decrypt a core dump in a child process so we can unlink a partially + * decrypted core if the child process fails. + */ + pid = fork(); + if (pid == -1) { + pjdlog_errno(LOG_ERR, "Unable to create child process"); + return (false); + } + + if (pid > 0) + return (wait_for_process(pid) == 0); + + kfd = open(keyfile, O_RDONLY); + if (kfd == -1) { + pjdlog_errno(LOG_ERR, "Unable to open %s", keyfile); + goto failed; + } + ifd = open(input, O_RDONLY); + if (ifd == -1) { + pjdlog_errno(LOG_ERR, "Unable to open %s", input); + goto failed; + } + ofd = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (ofd == -1) { + pjdlog_errno(LOG_ERR, "Unable to open %s", output); + goto failed; + } + fp = fopen(privkeyfile, "r"); + if (fp == NULL) { + pjdlog_errno(LOG_ERR, "Unable to open %s", privkeyfile); + goto failed; + } + + if (cap_enter() < 0 && errno != ENOSYS) { + pjdlog_errno(LOG_ERR, "Unable to enter capability mode"); + goto failed; + } + + privkey = RSA_new(); + if (privkey == NULL) { + pjdlog_error("Unable to allocate an RSA structure: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto failed; + } + EVP_CIPHER_CTX_init(&ctx); + + kdk = read_key(kfd); + close(kfd); + if (kdk == NULL) + goto failed; + + privkey = PEM_read_RSAPrivateKey(fp, &privkey, NULL, NULL); + fclose(fp); + if (privkey == NULL) { + pjdlog_error("Unable to read data from %s.", privkeyfile); + goto failed; + } + + privkeysize = RSA_size(privkey); + if (privkeysize != (int)kdk->kdk_encryptedkeysize) { + pjdlog_error("RSA modulus size mismatch: equals %db and should be %ub.", + 8 * privkeysize, 8 * kdk->kdk_encryptedkeysize); + goto failed; + } + + switch (kdk->kdk_encryption) { + case KERNELDUMP_ENC_AES_256_CBC: + cipher = EVP_aes_256_cbc(); + break; + default: + pjdlog_error("Invalid encryption algorithm."); + goto failed; + } + + if (RSA_private_decrypt(kdk->kdk_encryptedkeysize, + kdk->kdk_encryptedkey, key, privkey, + RSA_PKCS1_PADDING) != sizeof(key)) { + pjdlog_error("Unable to decrypt key: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto failed; + } + RSA_free(privkey); + privkey = NULL; + + EVP_DecryptInit_ex(&ctx, cipher, NULL, key, kdk->kdk_iv); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + explicit_bzero(key, sizeof(key)); + + do { + bytes = read(ifd, buf, sizeof(buf)); + if (bytes < 0) { + pjdlog_errno(LOG_ERR, "Unable to read data from %s", + input); + goto failed; + } else if (bytes == 0) { + break; + } + + if (bytes > 0) { + if (EVP_DecryptUpdate(&ctx, buf, &olen, buf, + bytes) == 0) { + pjdlog_error("Unable to decrypt core."); + goto failed; + } + } else { + if (EVP_DecryptFinal_ex(&ctx, buf, &olen) == 0) { + pjdlog_error("Unable to decrypt core."); + goto failed; + } + } + + if (olen == 0) + continue; + + if (write(ofd, buf, olen) != olen) { + pjdlog_errno(LOG_ERR, "Unable to write data to %s", + output); + goto failed; + } + } while (bytes > 0); + + explicit_bzero(buf, sizeof(buf)); + EVP_CIPHER_CTX_cleanup(&ctx); + exit(0); +failed: + explicit_bzero(key, sizeof(key)); + explicit_bzero(buf, sizeof(buf)); + RSA_free(privkey); + EVP_CIPHER_CTX_cleanup(&ctx); + exit(1); +} + +int +main(int argc, char **argv) +{ + char core[PATH_MAX], encryptedcore[PATH_MAX], keyfile[PATH_MAX]; + struct stat sb; + const char *crashdir, *dumpnr, *privatekey; + int ch, debug; + size_t ii; + bool usesyslog; + + pjdlog_init(PJDLOG_MODE_STD); + pjdlog_prefix_set("(decryptcore) "); + + debug = 0; + *core = '\0'; + crashdir = NULL; + dumpnr = NULL; + *encryptedcore = '\0'; + *keyfile = '\0'; + privatekey = NULL; + usesyslog = false; + while ((ch = getopt(argc, argv, "Lc:d:e:k:n:p:v")) != -1) { + switch (ch) { + case 'L': + usesyslog = true; + break; + case 'c': + strncpy(core, optarg, sizeof(core)); + break; + case 'd': + crashdir = optarg; + break; + case 'e': + strncpy(encryptedcore, optarg, sizeof(encryptedcore)); + break; + case 'k': + strncpy(keyfile, optarg, sizeof(keyfile)); + break; + case 'n': + dumpnr = optarg; + break; + case 'p': + privatekey = optarg; + break; + case 'v': + debug++; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 0) + usage(); + + /* Verify mutually exclusive options. */ + if ((crashdir != NULL || dumpnr != NULL) && + (*keyfile != '\0' || *encryptedcore != '\0' || *core != '\0')) { + usage(); + } + + /* + * Set key, encryptedcore and core file names using crashdir and dumpnr. + */ + if (dumpnr != NULL) { + for (ii = 0; ii < strnlen(dumpnr, PATH_MAX); ii++) { + if (isdigit((int)dumpnr[ii]) == 0) + usage(); + } + + if (crashdir == NULL) + crashdir = DECRYPTCORE_CRASHDIR; + PJDLOG_VERIFY(snprintf(keyfile, sizeof(keyfile), + "%s/key.%s", crashdir, dumpnr) > 0); + PJDLOG_VERIFY(snprintf(core, sizeof(core), + "%s/vmcore.%s", crashdir, dumpnr) > 0); + PJDLOG_VERIFY(snprintf(encryptedcore, sizeof(encryptedcore), + "%s/vmcore_encrypted.%s", crashdir, dumpnr) > 0); + } + + if (privatekey == NULL || *keyfile == '\0' || *encryptedcore == '\0' || + *core == '\0') { + usage(); + } + + if (usesyslog) + pjdlog_mode_set(PJDLOG_MODE_SYSLOG); + pjdlog_debug_set(debug); + + if (!decrypt(privatekey, keyfile, encryptedcore, core)) { + if (stat(core, &sb) == 0 && unlink(core) != 0) + pjdlog_exit(1, "Unable to remove core"); + exit(1); + } + + pjdlog_fini(); + + exit(0); +} Index: head/sbin/dumpon/Makefile =================================================================== --- head/sbin/dumpon/Makefile +++ head/sbin/dumpon/Makefile @@ -1,7 +1,15 @@ # $FreeBSD$ +.include + PACKAGE=runtime PROG= dumpon + +.if ${MK_OPENSSL} != "no" +LIBADD= crypto +CFLAGS+=-DHAVE_CRYPTO +.endif + MAN= dumpon.8 .include Index: head/sbin/dumpon/dumpon.8 =================================================================== --- head/sbin/dumpon/dumpon.8 +++ head/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 @@ .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 @@ 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 @@ .It Pa /etc/rc.conf boot-time system configuration .El +.Sh EXAMPLES +In order to generate an RSA private key a user can use the +.Xr genrsa 1 +tool: +.Pp +.Dl # openssl genrsa -out private.pem 4096 +.Pp +A public key can be extracted from the private key using the +.Xr rsa 1 +tool: +.Pp +.Dl # openssl rsa -in private.pem -out public.pem -pubout +.Pp +Once the RSA keys are created the private key should be moved to a safe place. +Now +.Pa public.pem +can be used by +.Nm +to configure encrypted kernel crash dumps: +.Pp +.Dl # dumpon -k public.pem /dev/ada0s1b +.Pp +It is recommended to test if the kernel saves encrypted crash dumps using the +current configuration. +The easiest way to do that is to cause a kernel panic using the +.Xr ddb 4 +debugger: +.Pp +.Dl # sysctl debug.kdb.panic=1 +.Pp +In the debugger the following commands should be typed to write a core dump and +reboot: +.Pp +.Dl db> call doadump(0) +.Dl db> reset +.Pp +After reboot +.Xr savecore 8 +should be able to save the core dump in the core directory which is +.Pa /var/crash +by default: +.Pp +.Dl # savecore /var/crash /dev/ada0s1b +.Pp +Three files should be created in the core directory: +.Pa info.# , +.Pa key.# +and +.Pa vmcore_encrypted.# +where +.Dq # +is the number of the last core dump saved by +.Xr savecore 8 . +The +.Pa vmcore_encrypted.# +can be decrypted using the +.Xr decryptcore 8 +utility: +.Pp +.Dl # decryptcore -p private.pem -k key.# -e vmcore_encrypted.# -c vmcore.# +.Pp +or shorter: +.Pp +.Dl # decryptcore -p private.pem -n # +.Pp +The +.Pa vmcore.# +can be now examined using +.Xr kgdb 1 : +.Pp +.Dl # kgdb /usr/obj/sys/GENERIC/kernel.debug vmcore.# +.Pp +or shorter: +.Pp +.Dl # kgdb -n # /usr/obj/sys/GENERIC/kernel.debug +.Pp +The core was decrypted properly if +.Xr kgdb 1 +does not print any errors. .Sh SEE ALSO +.Xr kgdb 1 , +.Xr ddb 4 , .Xr fstab 5 , .Xr rc.conf 5 , .Xr config 8 , .Xr init 8 , .Xr loader 8 , .Xr rc 8 , +.Xr decryptcore 8 , .Xr savecore 8 , .Xr swapon 8 , .Xr panic 9 Index: head/sbin/dumpon/dumpon.c =================================================================== --- head/sbin/dumpon/dumpon.c +++ head/sbin/dumpon/dumpon.c @@ -42,13 +42,16 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include +#include #include #include #include #include +#include #include #include #include @@ -56,13 +59,19 @@ #include #include +#ifdef HAVE_CRYPTO +#include +#include +#include +#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 @@ } } +#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 @@ 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 @@ 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 @@ if (fd < 0) err(EX_OSFILE, "%s", dumpdev); check_size(fd, dumpdev); - u = 0; - i = ioctl(fd, DIOCSKERNELDUMP, &u); - u = 1; - i = ioctl(fd, DIOCSKERNELDUMP, &u); + bzero(&kda, sizeof(kda)); + + kda.kda_enable = 0; + i = ioctl(fd, DIOCSKERNELDUMP, &kda); + explicit_bzero(&kda, sizeof(kda)); + +#ifdef HAVE_CRYPTO + if (pubkeyfile != NULL) + genkey(pubkeyfile, &kda); +#endif + + kda.kda_enable = 1; + i = ioctl(fd, DIOCSKERNELDUMP, &kda); + explicit_bzero(kda.kda_encryptedkey, kda.kda_encryptedkeysize); + free(kda.kda_encryptedkey); + explicit_bzero(&kda, sizeof(kda)); if (i == 0 && verbose) printf("kernel dumps on %s\n", dumpdev); } else { fd = open(_PATH_DEVNULL, O_RDONLY); if (fd < 0) err(EX_OSFILE, "%s", _PATH_DEVNULL); - u = 0; - i = ioctl(fd, DIOCSKERNELDUMP, &u); + + kda.kda_enable = 0; + i = ioctl(fd, DIOCSKERNELDUMP, &kda); + explicit_bzero(&kda, sizeof(kda)); if (i == 0 && verbose) printf("kernel dumps disabled\n"); } Index: head/sbin/savecore/savecore.8 =================================================================== --- head/sbin/savecore/savecore.8 +++ head/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 @@ .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 @@ .Xr xo_parse_args 3 , .Xr textdump 4 , .Xr tar 5 , +.Xr decryptcore 8 , .Xr dumpon 8 , .Xr syslogd 8 .Sh HISTORY Index: head/sbin/savecore/savecore.c =================================================================== --- head/sbin/savecore/savecore.c +++ head/sbin/savecore/savecore.c @@ -74,6 +74,7 @@ #include #include #include +#include #include #include #include @@ -183,6 +184,28 @@ 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 @@ { (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 @@ #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 @@ 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 @@ { 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 @@ int bounds, status; u_int sectorsize, xostyle; int istextdump; + uint32_t dumpkeysize; + bool isencrypted, ret; bounds = getbounds(); mediasize = 0; @@ -581,7 +611,8 @@ 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 @@ } 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 @@ 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 @@ syslog(LOG_WARNING, "unable to create symlink %s/%s: %m", savedir, "info.last"); } + if (isencrypted) { + if (symlink(keyname, "key.last") == -1) { + syslog(LOG_WARNING, + "unable to create symlink %s/%s: %m", savedir, + "key.last"); + } + } if (compress) { snprintf(linkname, sizeof(linkname), "%s.last.gz", - istextdump ? "textdump.tar" : "vmcore"); + istextdump ? "textdump.tar" : + (isencrypted ? "vmcore_encrypted" : "vmcore")); } else { snprintf(linkname, sizeof(linkname), "%s.last", - istextdump ? "textdump.tar" : "vmcore"); + istextdump ? "textdump.tar" : + (isencrypted ? "vmcore_encrypted" : "vmcore")); } if (symlink(corename, linkname) == -1) { syslog(LOG_WARNING, "unable to create symlink %s/%s: %m", Index: head/share/man/man5/rc.conf.5 =================================================================== --- head/share/man/man5/rc.conf.5 +++ head/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 @@ at boot time when .Va dumpdir is set. +.It Va dumppubkey +.Pq Vt str +Path to a public key. +It is used by +.Xr dumpon 8 +to encrypt a one-time key for a crash dump. +The public key has to match a private key used by +.Xr decryptcore 8 +to decrypt a crash dump after reboot. +See +.Xr dumpon 8 +for more details. .It Va savecore_enable .Pq Vt bool If set to Index: head/sys/amd64/amd64/minidump_machdep.c =================================================================== --- head/sys/amd64/amd64/minidump_machdep.c +++ head/sys/amd64/amd64/minidump_machdep.c @@ -223,7 +223,6 @@ 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 @@ 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 @@ 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 @@ goto fail; /* Dump trailer */ - error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size); + error = dump_write_header(di, &kdh, 0, dumplo); if (error) goto fail; - dumplo += size; + dumplo += di->blocksize; /* Signal completion, signoff and exit stage left. */ dump_write(di, NULL, 0, 0, 0); Index: head/sys/arm/arm/minidump_machdep.c =================================================================== --- head/sys/arm/arm/minidump_machdep.c +++ head/sys/arm/arm/minidump_machdep.c @@ -238,15 +238,22 @@ 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 @@ 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 @@ goto fail; /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_write_header(di, &kdh, 0, dumplo); if (error) goto fail; - dumplo += sizeof(kdh); + dumplo += di->blocksize; /* Signal completion, signoff and exit stage left. */ dump_write(di, NULL, 0, 0, 0); Index: head/sys/arm64/arm64/minidump_machdep.c =================================================================== --- head/sys/arm64/arm64/minidump_machdep.c +++ head/sys/arm64/arm64/minidump_machdep.c @@ -281,14 +281,21 @@ 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 @@ 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 @@ goto fail; /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_write_header(di, &kdh, 0, dumplo); if (error) goto fail; - dumplo += sizeof(kdh); + dumplo += di->blocksize; /* Signal completion, signoff and exit stage left. */ dump_write(di, NULL, 0, 0, 0); Index: head/sys/conf/NOTES =================================================================== --- head/sys/conf/NOTES +++ head/sys/conf/NOTES @@ -3072,3 +3072,6 @@ options EVDEV_DEBUG # enable event debug msgs device uinput # install /dev/uinput cdev options UINPUT_DEBUG # enable uinput debug msgs + +# Encrypted kernel crash dumps. +options EKCD Index: head/sys/conf/files =================================================================== --- head/sys/conf/files +++ head/sys/conf/files @@ -594,13 +594,13 @@ crypto/des/des_ecb.c optional crypto | ipsec | netsmb crypto/des/des_setkey.c optional crypto | ipsec | netsmb crypto/rc4/rc4.c optional netgraph_mppc_encryption | kgssapi -crypto/rijndael/rijndael-alg-fst.c optional crypto | geom_bde | \ +crypto/rijndael/rijndael-alg-fst.c optional crypto | ekcd | geom_bde | \ ipsec | random !random_loadable | wlan_ccmp -crypto/rijndael/rijndael-api-fst.c optional geom_bde | random !random_loadable +crypto/rijndael/rijndael-api-fst.c optional ekcd | geom_bde | random !random_loadable crypto/rijndael/rijndael-api.c optional crypto | ipsec | wlan_ccmp crypto/sha1.c optional carp | crypto | ipsec | \ netgraph_mppc_encryption | sctp -crypto/sha2/sha256c.c optional crypto | geom_bde | ipsec | random !random_loadable | \ +crypto/sha2/sha256c.c optional crypto | ekcd | geom_bde | ipsec | random !random_loadable | \ sctp | zfs crypto/sha2/sha512c.c optional crypto | geom_bde | ipsec | zfs crypto/skein/skein.c optional crypto | zfs Index: head/sys/conf/options =================================================================== --- head/sys/conf/options +++ head/sys/conf/options @@ -1004,3 +1004,6 @@ # Hyper-V network driver HN_DEBUG opt_hn.h + +# Encrypted kernel crash dumps +EKCD opt_ekcd.h Index: head/sys/ddb/db_textdump.c =================================================================== --- head/sys/ddb/db_textdump.c +++ head/sys/ddb/db_textdump.c @@ -427,6 +427,7 @@ void textdump_dumpsys(struct dumperinfo *di) { + struct kerneldumpcrypto *kdc; off_t dumplen, trailer_offset; if (di->blocksize != TEXTDUMP_BLOCKSIZE) { @@ -449,6 +450,12 @@ 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_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 @@ * 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 @@ else printf("Textdump complete.\n"); textdump_pending = 0; + + /* + * Restore EKCD status. + */ + di->kdc = kdc; } /*- Index: head/sys/dev/null/null.c =================================================================== --- head/sys/dev/null/null.c +++ head/sys/dev/null/null.c @@ -30,6 +30,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_compat.h" + #include #include #include @@ -108,8 +110,11 @@ error = 0; switch (cmd) { +#ifdef COMPAT_FREEBSD11 + case DIOCSKERNELDUMP_FREEBSD11: +#endif case DIOCSKERNELDUMP: - error = set_dumper(NULL, NULL, td); + error = set_dumper(NULL, NULL, td, 0, NULL, 0, NULL); break; case FIONBIO: break; Index: head/sys/geom/geom_dev.c =================================================================== --- head/sys/geom/geom_dev.c +++ head/sys/geom/geom_dev.c @@ -36,6 +36,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_compat.h" + #include #include #include @@ -128,36 +130,44 @@ } 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 @@ 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 @@ 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 @@ /* Reset any dump-area set on this device */ if (dev->si_flags & SI_DUMPDEV) - (void)set_dumper(NULL, NULL, curthread); + (void)set_dumper(NULL, NULL, curthread, 0, NULL, 0, NULL); /* Destroy the struct cdev *so we get no more requests */ destroy_dev_sched_cb(dev, g_dev_callback, cp); Index: head/sys/i386/i386/minidump_machdep.c =================================================================== --- head/sys/i386/i386/minidump_machdep.c +++ head/sys/i386/i386/minidump_machdep.c @@ -245,14 +245,21 @@ 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 @@ 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 @@ goto fail; /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_write_header(di, &kdh, 0, dumplo); if (error) goto fail; - dumplo += sizeof(kdh); + dumplo += di->blocksize; /* Signal completion, signoff and exit stage left. */ dump_write(di, NULL, 0, 0, 0); Index: head/sys/kern/kern_dump.c =================================================================== --- head/sys/kern/kern_dump.c +++ head/sys/kern/kern_dump.c @@ -117,6 +117,29 @@ #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 @@ Elf_Ehdr ehdr; uint64_t dumpsize; off_t hdrgap; - size_t hdrsz, size; + size_t hdrsz; int error; #ifndef __powerpc__ @@ -325,24 +348,37 @@ 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 @@ * 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 @@ goto fail; /* Dump trailer */ - error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size); + error = dump_write_header(di, &kdh, 0, dumplo); if (error) goto fail; + dumplo += di->blocksize; /* Signal completion, signoff and exit stage left. */ dump_write(di, NULL, 0, 0, 0); Index: head/sys/kern/kern_shutdown.c =================================================================== --- head/sys/kern/kern_shutdown.c +++ head/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 @@ #include #include +#include +#include + #include #include @@ -143,6 +147,22 @@ 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 @@ 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 @@ 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 @@ (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 @@ kdh->architectureversion = htod32(archver); kdh->dumplength = htod64(dumplen); kdh->dumptime = htod64(time_second); + kdh->dumpkeysize = htod32(dumpkeysize); kdh->blocksize = htod32(blksz); strlcpy(kdh->hostname, prison0.pr_hostname, sizeof(kdh->hostname)); strlcpy(kdh->versionstring, version, sizeof(kdh->versionstring)); Index: head/sys/mips/mips/minidump_machdep.c =================================================================== --- head/sys/mips/mips/minidump_machdep.c +++ head/sys/mips/mips/minidump_machdep.c @@ -219,15 +219,22 @@ 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 @@ 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 @@ } /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_write_header(di, &kdh, 0, dumplo); if (error) goto fail; - dumplo += sizeof(kdh); + dumplo += di->blocksize; /* Signal completion, signoff and exit stage left. */ dump_write(di, NULL, 0, 0, 0); Index: head/sys/sparc64/sparc64/dump_machdep.c =================================================================== --- head/sys/sparc64/sparc64/dump_machdep.c +++ head/sys/sparc64/sparc64/dump_machdep.c @@ -94,7 +94,8 @@ 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 @@ /* 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 @@ goto fail; /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_write_header(di, &kdh, 0, dumplo); if (error) goto fail; + dumplo += di->blocksize; /* Signal completion, signoff and exit stage left. */ dump_write(di, NULL, 0, 0, 0); Index: head/sys/sys/conf.h =================================================================== --- head/sys/sys/conf.h +++ head/sys/sys/conf.h @@ -325,6 +325,8 @@ 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 @@ off_t mediaoffset; /* Initial offset in bytes. */ off_t mediasize; /* Space available in bytes. */ void *blockbuf; /* Buffer for padding shorter dump blocks */ + struct kerneldumpcrypto *kdc; /* Kernel dump crypto. */ }; -int set_dumper(struct dumperinfo *, const char *_devname, struct thread *td); +int set_dumper(struct dumperinfo *di, const char *devname, struct thread *td, + uint8_t encrypt, const uint8_t *key, uint32_t encryptedkeysize, + const uint8_t *encryptedkey); int dump_write(struct dumperinfo *, void *, vm_offset_t, off_t, size_t); int dump_write_pad(struct dumperinfo *, void *, vm_offset_t, off_t, size_t, size_t *); +int dump_write_header(struct dumperinfo *di, struct kerneldumpheader *kdh, + vm_offset_t physical, off_t offset); +int dump_write_key(struct dumperinfo *di, vm_offset_t physical, off_t offset); int doadump(boolean_t); extern int dumping; /* system is dumping */ Index: head/sys/sys/disk.h =================================================================== --- head/sys/sys/disk.h +++ head/sys/sys/disk.h @@ -14,6 +14,7 @@ #define _SYS_DISK_H_ #include +#include #include #include @@ -54,7 +55,7 @@ * 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 @@ #define DIOCZONECMD _IOWR('d', 143, struct disk_zone_args) +struct diocskerneldump_arg { + uint8_t kda_enable; + uint8_t kda_encryption; + uint8_t kda_key[KERNELDUMP_KEY_MAX_SIZE]; + uint32_t kda_encryptedkeysize; + uint8_t *kda_encryptedkey; +}; +#define DIOCSKERNELDUMP _IOW('d', 144, struct diocskerneldump_arg) + /* + * Enable/Disable the device for kernel core dumps. + */ + #endif /* _SYS_DISK_H_ */ Index: head/sys/sys/kerneldump.h =================================================================== --- head/sys/sys/kerneldump.h +++ head/sys/sys/kerneldump.h @@ -38,6 +38,9 @@ #ifndef _SYS_KERNELDUMP_H #define _SYS_KERNELDUMP_H +#include +#include + #include #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 @@ #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 @@ #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 @@ 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 @@ 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 *);