summaryrefslogtreecommitdiff
path: root/usr/src/cmd/savecore/savecore.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/savecore/savecore.c')
-rw-r--r--usr/src/cmd/savecore/savecore.c172
1 files changed, 157 insertions, 15 deletions
diff --git a/usr/src/cmd/savecore/savecore.c b/usr/src/cmd/savecore/savecore.c
index b5ac0397aa..16529d8d9f 100644
--- a/usr/src/cmd/savecore/savecore.c
+++ b/usr/src/cmd/savecore/savecore.c
@@ -42,6 +42,8 @@
#include <atomic.h>
#include <libnvpair.h>
#include <libintl.h>
+#include <assert.h>
+#include <strings.h>
#include <sys/mem.h>
#include <sys/statvfs.h>
#include <sys/dumphdr.h>
@@ -55,6 +57,7 @@
#include <sys/fm/util.h>
#include <fm/libfmevent.h>
#include <sys/int_fmtio.h>
+#include <crypto/chacha/chacha.h>
/* fread/fwrite buffer size */
@@ -74,6 +77,8 @@ static long pagesize; /* dump pagesize */
static int dumpfd = -1; /* dumpfile descriptor */
static boolean_t have_dumpfile = B_TRUE; /* dumpfile existence */
static dumphdr_t corehdr, dumphdr; /* initial and terminal dumphdrs */
+static dump_crypt_t dcrypt; /* dump encryption header */
+static size_t dumphdr_size; /* size of dump header */
static boolean_t dump_incomplete; /* dumphdr indicates incomplete */
static boolean_t fm_panic; /* dump is the result of fm_panic */
static offset_t endoff; /* offset of end-of-dump header */
@@ -165,7 +170,8 @@ static void
usage(void)
{
(void) fprintf(stderr,
- "usage: %s [-L | -r] [-vd] [-f dumpfile] [dirname]\n", progname);
+ "usage: %s [-L | -r] [-vd] [-k keyfile] [-f dumpfile] [dirname]\n",
+ progname);
exit(1);
}
@@ -329,6 +335,19 @@ Pwrite(int fd, void *buf, size_t size, off64_t off)
strerror(errno));
}
+static void
+Read(int fd, void *buf, size_t size)
+{
+ ssize_t sz = read(fd, buf, size);
+
+ if (sz < 0)
+ logprint(SC_SL_ERR | SC_EXIT_ERR,
+ "read: %s", strerror(errno));
+ else if (sz != size)
+ logprint(SC_SL_ERR | SC_EXIT_ERR,
+ "read: size %ld != %ld", sz, size);
+}
+
static void *
Zalloc(size_t size)
{
@@ -362,9 +381,9 @@ read_dumphdr(void)
dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
endoff = llseek(dumpfd, -DUMP_OFFSET, SEEK_END) & -DUMP_OFFSET;
Pread(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
- Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + sizeof (dumphdr));
pagesize = dumphdr.dump_pagesize;
+ dumphdr_size = sizeof (dumphdr);
if (dumphdr.dump_magic != DUMP_MAGIC)
logprint(SC_SL_NONE | SC_EXIT_PEND, "bad magic number %x",
@@ -384,6 +403,20 @@ read_dumphdr(void)
"dump is from %u-bit kernel - cannot save on %u-bit kernel",
dumphdr.dump_wordsize, DUMP_WORDSIZE);
+ if (dumphdr.dump_flags & DF_ENCRYPTED) {
+ /*
+ * If our dump is encrypted, our encryption header follows
+ * our dump header. Read it, and then increment our
+ * dumphdr_size to assure that reads of data following the
+ * encryption header account for it.
+ */
+ Pread(dumpfd, &dcrypt, sizeof (dcrypt),
+ endoff + sizeof (dumphdr));
+ dumphdr_size += sizeof (dcrypt);
+ }
+
+ Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + dumphdr_size);
+
if (datahdr.dump_datahdr_magic == DUMP_DATAHDR_MAGIC) {
if (datahdr.dump_datahdr_version != DUMP_DATAHDR_VERSION)
logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_PEND,
@@ -500,19 +533,76 @@ build_dump_map(int corefd, const pfn_t *pfn_table)
free(inbuf);
}
+static void
+Decrypt(offset_t dumpoff, len_t nb, char *buf, size_t sz, uint8_t *key)
+{
+ size_t nelems = nb / sizeof (uint64_t), i;
+ uint64_t *clear = (uint64_t *)buf;
+ uint64_t *stream = (uint64_t *)(buf + sz);
+ uint64_t *crypt = (uint64_t *)(buf + (2 * sz));
+ uint64_t ctr = dumpoff >> DUMP_CRYPT_BLOCKSHIFT;
+ chacha_ctx_t ctx;
+
+ /*
+ * If our size is not 8-byte aligned, prepare our ciphertext to the
+ * next 8-byte boundary.
+ */
+ if (nb & (sizeof (uint64_t) - 1)) {
+ assert(nb < sz);
+ nelems++;
+ }
+
+ chacha_keysetup(&ctx, key, DUMP_CRYPT_KEYLEN * 8, 0);
+ chacha_ivsetup(&ctx, dcrypt.dump_crypt_nonce, (uint8_t *)&ctr);
+
+ for (i = 0; i < nelems; i++) {
+ stream[i] = dumpoff;
+ dumpoff += sizeof (uint64_t);
+ }
+
+ chacha_encrypt_bytes(&ctx, (uint8_t *)stream, (uint8_t *)crypt, nb);
+
+ for (i = 0; i < nelems; i++)
+ clear[i] ^= crypt[i];
+}
+
+static void
+Verify(uint8_t *key)
+{
+ chacha_ctx_t ctx;
+ uint8_t hmac[DUMP_CRYPT_HMACLEN];
+
+ chacha_keysetup(&ctx, key, DUMP_CRYPT_KEYLEN * 8, 0);
+ chacha_ivsetup(&ctx, dcrypt.dump_crypt_nonce, NULL);
+
+ chacha_encrypt_bytes(&ctx, (uint8_t *)&dumphdr.dump_utsname,
+ (uint8_t *)hmac, DUMP_CRYPT_HMACLEN);
+
+ if (bcmp(hmac, &dcrypt.dump_crypt_hmac, DUMP_CRYPT_HMACLEN) == 0)
+ return;
+
+ logprint(SC_SL_NONE | SC_EXIT_ERR,
+ "provided key does not match encryption key");
+}
+
/*
* Copy whole sections of the dump device to the file.
*/
static void
Copy(offset_t dumpoff, len_t nb, offset_t *offp, int fd, char *buf,
- size_t sz)
+ size_t sz, uint8_t *key)
{
size_t nr;
offset_t off = *offp;
while (nb > 0) {
nr = sz < nb ? sz : (size_t)nb;
+
Pread(dumpfd, buf, nr, dumpoff);
+
+ if (dumphdr.dump_flags & DF_ENCRYPTED)
+ Decrypt(dumpoff, nr, buf, sz, key);
+
Pwrite(fd, buf, nr, off);
off += nr;
dumpoff += nr;
@@ -567,21 +657,56 @@ CopyPages(offset_t *offp, int fd, char *buf, size_t sz)
* Update corehdr with new offsets.
*/
static void
-copy_crashfile(const char *corefile)
+copy_crashfile(const char *corefile, const char *keyfile)
{
int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ uint8_t keybuf[DUMP_CRYPT_KEYLEN];
size_t bufsz = FBUFSIZE;
- char *inbuf = Zalloc(bufsz);
+ char *inbuf;
offset_t coreoff;
size_t nb;
+ uint8_t *key = NULL;
+
+ if (dumphdr.dump_flags & DF_ENCRYPTED) {
+ int keyfd;
+
+ if (dcrypt.dump_crypt_algo != DUMP_CRYPT_ALGO_CHACHA20) {
+ logprint(SC_SL_NONE | SC_EXIT_ERR, "unrecognized dump "
+ "encryption algorithm %u", dcrypt.dump_crypt_algo);
+ }
+
+ if (keyfile == NULL) {
+ logprint(SC_SL_NONE | SC_EXIT_ERR, "dump is encrypted; "
+ "key must be provided");
+ }
- logprint(SC_SL_ERR | SC_IF_VERBOSE,
- "Copying %s to %s/%s\n", dumpfile, savedir, corefile);
+ keyfd = Open(keyfile, O_RDONLY, 0600);
+ Read(keyfd, keybuf, sizeof (keybuf));
+ (void) close(keyfd);
+
+ /*
+ * For the encrypted case, we triple our buffer size to
+ * allow for the stream buffer and the ciphertext buffer.
+ */
+ inbuf = Zalloc(bufsz * 3);
+ key = keybuf;
+ Verify(key);
+
+ logprint(SC_SL_ERR | SC_IF_VERBOSE, "Decrypting and copying "
+ "%s to %s/%s\n", dumpfile, savedir, corefile);
+ } else {
+ inbuf = Zalloc(bufsz);
+
+ logprint(SC_SL_ERR | SC_IF_VERBOSE,
+ "Copying %s to %s/%s\n", dumpfile, savedir, corefile);
+ }
/*
- * This dump file is still compressed
+ * This dump file is still compressed -- but it will no longer be
+ * encrypted.
*/
corehdr.dump_flags |= DF_COMPRESSED | DF_VALID;
+ corehdr.dump_flags &= ~DF_ENCRYPTED;
/*
* Leave room for corehdr, it is updated and written last
@@ -595,7 +720,7 @@ copy_crashfile(const char *corefile)
coreoff = roundup(coreoff, pagesize);
corehdr.dump_ksyms = coreoff;
Copy(dumphdr.dump_ksyms, dumphdr.dump_ksyms_csize, &coreoff, corefd,
- inbuf, bufsz);
+ inbuf, bufsz, key);
/*
* Save the pfn table.
@@ -603,7 +728,7 @@ copy_crashfile(const char *corefile)
coreoff = roundup(coreoff, pagesize);
corehdr.dump_pfn = coreoff;
Copy(dumphdr.dump_pfn, dumphdr.dump_npages * sizeof (pfn_t), &coreoff,
- corefd, inbuf, bufsz);
+ corefd, inbuf, bufsz, key);
/*
* Save the dump map.
@@ -611,7 +736,7 @@ copy_crashfile(const char *corefile)
coreoff = roundup(coreoff, pagesize);
corehdr.dump_map = coreoff;
Copy(dumphdr.dump_map, dumphdr.dump_nvtop * sizeof (mem_vtop_t),
- &coreoff, corefd, inbuf, bufsz);
+ &coreoff, corefd, inbuf, bufsz, key);
/*
* Save the data pages.
@@ -620,7 +745,7 @@ copy_crashfile(const char *corefile)
corehdr.dump_data = coreoff;
if (datahdr.dump_data_csize != 0)
Copy(dumphdr.dump_data, datahdr.dump_data_csize, &coreoff,
- corefd, inbuf, bufsz);
+ corefd, inbuf, bufsz, key);
else
CopyPages(&coreoff, corefd, inbuf, bufsz);
@@ -1657,6 +1782,8 @@ main(int argc, char *argv[])
struct rlimit rl;
long filebounds = -1;
char namelist[30], corefile[30], boundstr[30];
+ char *keyfile = NULL;
+
dumpfile = NULL;
startts = gethrtime();
@@ -1672,7 +1799,7 @@ main(int argc, char *argv[])
if (savedir != NULL)
savedir = strdup(savedir);
- while ((c = getopt(argc, argv, "Lvcdmf:r")) != EOF) {
+ while ((c = getopt(argc, argv, "Lvcdmf:rk:")) != EOF) {
switch (c) {
case 'L':
livedump++;
@@ -1696,6 +1823,9 @@ main(int argc, char *argv[])
dumpfile = optarg;
filebounds = getbounds(dumpfile);
break;
+ case 'k':
+ keyfile = optarg;
+ break;
case '?':
usage();
}
@@ -1756,6 +1886,18 @@ main(int argc, char *argv[])
read_dumphdr();
+ if (dumphdr.dump_flags & DF_ENCRYPTED) {
+ if (filemode) {
+ logprint(SC_SL_NONE | SC_EXIT_ERR, "saved dump file is "
+ "erroneously encrypted");
+ }
+
+ if (!csave) {
+ logprint(SC_SL_NONE | SC_EXIT_ERR, "dump is encrypted; "
+ "cannot be saved uncompressed");
+ }
+ }
+
/*
* We want this message to go to the log file, but not the console.
* There's no good way to do that with the existing syslog facility.
@@ -1860,7 +2002,7 @@ main(int argc, char *argv[])
"Saving compressed system crash dump in %s/%s",
savedir, corefile);
- copy_crashfile(corefile);
+ copy_crashfile(corefile, keyfile);
/*
* Raise a fault management event that indicates the system
@@ -1876,7 +2018,7 @@ main(int argc, char *argv[])
char *metrics = Zalloc(metrics_size + 1);
Pread(dumpfd, metrics, metrics_size, endoff +
- sizeof (dumphdr) + sizeof (datahdr));
+ dumphdr_size + sizeof (datahdr));
if (sec < 1)
sec = 1;