Changeset View
Standalone View
sys/kern/kern_shutdown.c
Show First 20 Lines • Show All 889 Lines • ▼ Show 20 Lines | kerneldumpcrypto_create(size_t blocksize, uint8_t encryption, | ||||
memcpy(kdk->kdk_encryptedkey, encryptedkey, encryptedkeysize); | memcpy(kdk->kdk_encryptedkey, encryptedkey, encryptedkeysize); | ||||
return (kdc); | return (kdc); | ||||
failed: | failed: | ||||
explicit_bzero(kdc, sizeof(*kdc) + dumpkeysize); | explicit_bzero(kdc, sizeof(*kdc) + dumpkeysize); | ||||
free(kdc, M_EKCD); | free(kdc, M_EKCD); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
#endif /* EKCD */ | |||||
static int | static int | ||||
kerneldumpcrypto_init(struct kerneldumpcrypto *kdc) | kerneldumpcrypto_init(struct kerneldumpcrypto *kdc) | ||||
{ | { | ||||
#ifndef EKCD | |||||
return (0); | |||||
#else | |||||
uint8_t hash[SHA256_DIGEST_LENGTH]; | uint8_t hash[SHA256_DIGEST_LENGTH]; | ||||
SHA256_CTX ctx; | SHA256_CTX ctx; | ||||
struct kerneldumpkey *kdk; | struct kerneldumpkey *kdk; | ||||
int error; | int error; | ||||
error = 0; | error = 0; | ||||
if (kdc == NULL) | if (kdc == NULL) | ||||
Show All 23 Lines | kerneldumpcrypto_init(struct kerneldumpcrypto *kdc) | ||||
kdc->kdc_nextoffset = 0; | kdc->kdc_nextoffset = 0; | ||||
kdk = kdc->kdc_dumpkey; | kdk = kdc->kdc_dumpkey; | ||||
memcpy(kdk->kdk_iv, kdc->kdc_iv, sizeof(kdk->kdk_iv)); | memcpy(kdk->kdk_iv, kdc->kdc_iv, sizeof(kdk->kdk_iv)); | ||||
out: | out: | ||||
explicit_bzero(hash, sizeof(hash)); | explicit_bzero(hash, sizeof(hash)); | ||||
return (error); | return (error); | ||||
#endif | |||||
} | } | ||||
uint32_t | static uint32_t | ||||
kerneldumpcrypto_dumpkeysize(const struct kerneldumpcrypto *kdc) | kerneldumpcrypto_dumpkeysize(const struct kerneldumpcrypto *kdc) | ||||
{ | { | ||||
#ifdef EKCD | |||||
if (kdc == NULL) | if (kdc == NULL) | ||||
return (0); | return (0); | ||||
return (kdc->kdc_dumpkeysize); | return (kdc->kdc_dumpkeysize); | ||||
#else | |||||
return (0); | |||||
#endif | |||||
} | } | ||||
#endif /* EKCD */ | |||||
/* Registration of dumpers */ | /* Registration of dumpers */ | ||||
int | 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, | uint8_t encryption, const uint8_t *key, uint32_t encryptedkeysize, | ||||
const uint8_t *encryptedkey) | const uint8_t *encryptedkey) | ||||
{ | { | ||||
size_t wantcopy; | size_t wantcopy; | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | printf("Attempt to write outside dump device boundaries.\n" | ||||
(intmax_t)offset, (intmax_t)di->mediaoffset, | (intmax_t)offset, (intmax_t)di->mediaoffset, | ||||
(uintmax_t)length, (intmax_t)di->mediasize); | (uintmax_t)length, (intmax_t)di->mediasize); | ||||
return (ENOSPC); | return (ENOSPC); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* 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)); | |||||
} | |||||
#ifdef EKCD | #ifdef EKCD | ||||
static int | static int | ||||
dump_encrypt(struct kerneldumpcrypto *kdc, uint8_t *buf, size_t size) | dump_encrypt(struct kerneldumpcrypto *kdc, uint8_t *buf, size_t size) | ||||
{ | { | ||||
switch (kdc->kdc_encryption) { | switch (kdc->kdc_encryption) { | ||||
case KERNELDUMP_ENC_AES_256_CBC: | case KERNELDUMP_ENC_AES_256_CBC: | ||||
if (rijndael_blockEncrypt(&kdc->kdc_ci, &kdc->kdc_ki, buf, | if (rijndael_blockEncrypt(&kdc->kdc_ci, &kdc->kdc_ki, buf, | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | while (length > 0) { | ||||
virtual = (void *)((uint8_t *)virtual + nbytes); | virtual = (void *)((uint8_t *)virtual + nbytes); | ||||
length -= nbytes; | length -= nbytes; | ||||
} | } | ||||
kdc->kdc_nextoffset = nextoffset; | kdc->kdc_nextoffset = nextoffset; | ||||
return (0); | return (0); | ||||
} | } | ||||
#endif /* EKCD */ | |||||
/* Call dumper with bounds checking. */ | |||||
static int | static int | ||||
dump_raw_write(struct dumperinfo *di, void *virtual, vm_offset_t physical, | dump_write_key(struct dumperinfo *di, vm_offset_t physical, off_t offset) | ||||
off_t offset, size_t length) | |||||
{ | { | ||||
int error; | struct kerneldumpcrypto *kdc; | ||||
error = dump_check_bounds(di, offset, length); | kdc = di->kdc; | ||||
if (error != 0) | if (kdc == NULL) | ||||
return (error); | return (0); | ||||
return (di->dumper(di->priv, virtual, physical, offset, length)); | return (dump_raw_write(di, kdc->kdc_dumpkey, physical, offset, | ||||
kdc->kdc_dumpkeysize)); | |||||
} | } | ||||
#endif /* EKCD */ | |||||
int | int | ||||
dump_write(struct dumperinfo *di, void *virtual, vm_offset_t physical, | dump_write(struct dumperinfo *di, void *virtual, vm_offset_t physical, | ||||
off_t offset, size_t length) | off_t offset, size_t length) | ||||
{ | { | ||||
#ifdef EKCD | #ifdef EKCD | ||||
if (di->kdc != NULL) { | if (di->kdc != NULL) { | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | dump_write_header(struct dumperinfo *di, struct kerneldumpheader *kdh, | ||||
ret = dump_raw_write_pad(di, kdh, physical, offset, sizeof(*kdh), | ret = dump_raw_write_pad(di, kdh, physical, offset, sizeof(*kdh), | ||||
&size); | &size); | ||||
if (ret == 0 && size != di->blocksize) | if (ret == 0 && size != di->blocksize) | ||||
ret = EINVAL; | ret = EINVAL; | ||||
return (ret); | return (ret); | ||||
} | } | ||||
static 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 */ | |||||
} | |||||
/* | /* | ||||
* Do some preliminary setup for a kernel dump: verify that we have enough space | * Do some preliminary setup for a kernel dump: verify that we have enough space | ||||
* on the dump device, write the leading header, and optionally write the crypto | * on the dump device, write the leading header, and optionally write the crypto | ||||
* key. | * key. | ||||
*/ | */ | ||||
int | int | ||||
dump_start(struct dumperinfo *di, struct kerneldumpheader *kdh, off_t *dumplop) | dump_start(struct dumperinfo *di, struct kerneldumpheader *kdh, off_t *dumplop) | ||||
{ | { | ||||
uint64_t dumpsize; | uint64_t dumpsize; | ||||
off_t dumplo; | off_t dumplo; | ||||
cem: Maybe size_t or uint64_t (to match `dumpsize`) instead? | |||||
Not Done Inline ActionsI chose this to match the return type of kerneldumpcrypto_dumpkeysize(). I don't see any problem with keeping it a uint32_t? markj: I chose this to match the return type of kerneldumpcrypto_dumpkeysize(). I don't see any… | |||||
Not Done Inline ActionsThere's no problem with it staying uint32_t. cem: There's no problem with it staying uint32_t. | |||||
uint32_t keysize; | |||||
int error; | int error; | ||||
#ifdef EKCD | |||||
error = kerneldumpcrypto_init(di->kdc); | error = kerneldumpcrypto_init(di->kdc); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
keysize = kerneldumpcrypto_dumpkeysize(di->kdc); | |||||
#else | |||||
keysize = 0; | |||||
#endif | |||||
dumpsize = dtoh64(kdh->dumplength) + 2 * di->blocksize + | dumpsize = dtoh64(kdh->dumplength) + 2 * di->blocksize + keysize; | ||||
kerneldumpcrypto_dumpkeysize(di->kdc); | |||||
if (di->mediasize < KERNELDUMP_METADATA_SIZE + dumpsize) | if (di->mediasize < KERNELDUMP_METADATA_SIZE + dumpsize) | ||||
return (E2BIG); | return (E2BIG); | ||||
dumplo = di->mediaoffset + di->mediasize - dumpsize; | dumplo = di->mediaoffset + di->mediasize - dumpsize; | ||||
error = dump_write_header(di, kdh, 0, dumplo); | error = dump_write_header(di, kdh, 0, dumplo); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
dumplo += di->blocksize; | dumplo += di->blocksize; | ||||
error = dump_write_key(di, 0, dumplo); | #ifdef EKCD | ||||
error = dump_write_key(di, 0, *dumplop); | |||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
dumplo += kerneldumpcrypto_dumpkeysize(di->kdc); | dumplo += keysize; | ||||
#endif | |||||
*dumplop = dumplo; | *dumplop = dumplo; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Write the trailing kernel dump header and signal to the lower layers that the | * Write the trailing kernel dump header and signal to the lower layers that the | ||||
* dump has completed. | * dump has completed. | ||||
*/ | */ | ||||
int | int | ||||
dump_finish(struct dumperinfo *di, struct kerneldumpheader *kdh, off_t dumplo) | dump_finish(struct dumperinfo *di, struct kerneldumpheader *kdh, off_t dumplo) | ||||
{ | { | ||||
int error; | int error; | ||||
error = dump_write_header(di, kdh, 0, dumplo); | error = dump_write_header(di, kdh, 0, dumplo); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
(void)dump_write(di, NULL, 0, 0, 0); | (void)dump_write(di, NULL, 0, 0, 0); | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
mkdumpheader(struct kerneldumpheader *kdh, char *magic, uint32_t archver, | dump_init_header(const struct dumperinfo *di, struct kerneldumpheader *kdh, | ||||
uint64_t dumplen, uint32_t dumpkeysize, uint32_t blksz) | char *magic, uint32_t archver, uint64_t dumplen) | ||||
Not Done Inline Actionsdumpkeysize was passed to mkdumpheader() not to use the global dumper variable indirectly from MD code. I liked that we had only two functions where the dumper variable was used set_dumper() and doadump() which require some global state. The second one passes the dumper to MD code which calls dump API passing it as an argument. What do you think about introducing dump_init_header() function which would have di as an argument so we could extract a kernel dump key size and a block size from it? Note that it doesn't make sense to create a kernel dump header and write it to a dump device which has a different block size from the one included in the header. Currently mkdumpheader() allows to do so and savecore(8) gets a dump device block size using ioctl(2), not from a kernel dump header. This issue existed before but we could fix it too meanwhile we're here. def: dumpkeysize was passed to mkdumpheader() not to use the global dumper variable indirectly from… | |||||
Not Done Inline ActionsYup, that sounds reasonable to me. To be clear, you're talking about renaming mkdumpheader() and having it take di as an argument, rather than having both dump_init_header() and mkdumpheader()? markj: Yup, that sounds reasonable to me. To be clear, you're talking about renaming mkdumpheader()… | |||||
Done Inline ActionsYes, I'd like to rename mkdumpheader() to dump_init_header(), provide a const di pointer as the first argument and remove blksz from the arguments list. def: Yes, I'd like to rename mkdumpheader() to dump_init_header(), provide a const di pointer as the… | |||||
{ | { | ||||
size_t dstsize; | size_t dstsize; | ||||
bzero(kdh, sizeof(*kdh)); | bzero(kdh, sizeof(*kdh)); | ||||
strlcpy(kdh->magic, magic, sizeof(kdh->magic)); | strlcpy(kdh->magic, magic, sizeof(kdh->magic)); | ||||
strlcpy(kdh->architecture, MACHINE_ARCH, sizeof(kdh->architecture)); | strlcpy(kdh->architecture, MACHINE_ARCH, sizeof(kdh->architecture)); | ||||
kdh->version = htod32(KERNELDUMPVERSION); | kdh->version = htod32(KERNELDUMPVERSION); | ||||
kdh->architectureversion = htod32(archver); | kdh->architectureversion = htod32(archver); | ||||
kdh->dumplength = htod64(dumplen); | kdh->dumplength = htod64(dumplen); | ||||
kdh->dumptime = htod64(time_second); | kdh->dumptime = htod64(time_second); | ||||
kdh->dumpkeysize = htod32(dumpkeysize); | #ifdef EKCD | ||||
Not Done Inline Actions(Implicit zero-initialization of dumpkeysize in the non-EKCD case via bzero above.) cem: (Implicit zero-initialization of `dumpkeysize` in the non-EKCD case via bzero above.) | |||||
Done Inline ActionsYeah, I'll add an explicit initialization instead. markj: Yeah, I'll add an explicit initialization instead. | |||||
kdh->blocksize = htod32(blksz); | kdh->dumpkeysize = htod32(kerneldumpcrypto_dumpkeysize(di->kdc)); | ||||
#else | |||||
kdh->dumpkeysize = 0; | |||||
#endif | |||||
kdh->blocksize = htod32(di->blocksize); | |||||
strlcpy(kdh->hostname, prison0.pr_hostname, sizeof(kdh->hostname)); | strlcpy(kdh->hostname, prison0.pr_hostname, sizeof(kdh->hostname)); | ||||
dstsize = sizeof(kdh->versionstring); | dstsize = sizeof(kdh->versionstring); | ||||
if (strlcpy(kdh->versionstring, version, dstsize) >= dstsize) | if (strlcpy(kdh->versionstring, version, dstsize) >= dstsize) | ||||
kdh->versionstring[dstsize - 2] = '\n'; | kdh->versionstring[dstsize - 2] = '\n'; | ||||
if (panicstr != NULL) | if (panicstr != NULL) | ||||
strlcpy(kdh->panicstring, panicstr, sizeof(kdh->panicstring)); | strlcpy(kdh->panicstring, panicstr, sizeof(kdh->panicstring)); | ||||
kdh->parity = kerneldump_parity(kdh); | kdh->parity = kerneldump_parity(kdh); | ||||
} | } | ||||
Show All 11 Lines |
Maybe size_t or uint64_t (to match dumpsize) instead?