Commit d8de4d6a authored by Mike Hibler's avatar Mike Hibler

Added compat option and assorted cleanups.

Added WITH_V3COMPAT to make sure that we can still generated V3 images
(for other sites) if checksum/encryption is not used.

Try to clean up the command line options some. Be more consistent by putting
generated uuid into a file instead of just spitting it out on stderr. Make
sure that if the decryption specifies signing and/or encryption that we
require the image to have that info. Add some more assertions. (Re)distinguish
checksums from signed-checksums.
parent 60a21143
......@@ -16,9 +16,18 @@ DISTFILES = global.h imagehdr.h queue.h sliceinfo.h \
EXPANDCOPYRIGHT = /usr/site/lib/copyright/expand-copyr
# Set to 1 for "secure frisbee" support. Requires openssl libraries.
#
# Support encrypted and signed-checksumed images.
# Requires openssl libraries.
#
WITH_CRYPTO = 1
WITH_SIGNING = 1
#
# Support for auto-generation of old image versions
#
WITH_V3COMPAT = 1
SYSTEM := $(shell uname -s)
# FreeBSD specific goop
......@@ -56,6 +65,9 @@ export HAVE_LOCALE = 1
endif
#
# Support for various filesystems
#
WITH_FFS = 1
WITH_EXTFS = 1
WITH_NTFS = @WINSUPPORT@
......@@ -95,10 +107,18 @@ HASHLIBS = $(LIBS) -lcrypto $(PTHREADLIBS)
# Secure images
ifeq ($(WITH_CRYPTO),1)
CFLAGS += -DWITH_CRYPTO -DSIGN_CHECKSUM
CFLAGS += -DWITH_CRYPTO
ifeq ($(WITH_SIGNING),1)
CFLAGS += -DSIGN_CHECKSUM
endif
LIBS += -lcrypto
endif
# Support generation of V3 images when "secure" features not used
ifeq ($(WITH_V3COMPAT),1)
CFLAGS += -DWITH_V3COMPAT
endif
# UFS/UFS2
ifeq ($(WITH_FFS),1)
CFLAGS += -DWITH_FFS
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2009-2010 University of Utah and the Flux Group.
* Copyright (c) 2009-2011 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -14,14 +14,15 @@
#include <string.h>
#include <unistd.h>
#include "imagehdr.h"
#include "checksum.h"
#ifdef WITH_CRYPTO
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/rsa.h>
#include "imagehdr.h"
#include "checksum.h"
#ifdef SIGN_CHECKSUM
static RSA *signature_key = NULL;
char *
......@@ -44,39 +45,37 @@ checksum_keyfile(char *imagename)
return fname;
}
static void
input_public_key(char *keyfile, RSA *key)
/*
* Initialize everything necessary to check the signature of the given image.
* Returns non-zero if successful, 0 otherwise.
*/
int
init_checksum(char *keyfile)
{
char str[1024];
FILE *file;
file = fopen(keyfile, "r");
if (keyfile == NULL || (file = fopen(keyfile, "r")) == NULL) {
fprintf(stderr, "%s: cannot open keyfile\n", keyfile);
return 0;
}
if ((signature_key = RSA_new()) == NULL) {
fprintf(stderr, "%s: cannot allocate RSA struct\n", keyfile);
return 0;
}
fscanf(file, "%1024s", str);
BN_hex2bn(&key->n, str);
BN_hex2bn(&signature_key->n, str);
fscanf(file, "%1024s", str);
BN_hex2bn(&key->e, str);
BN_hex2bn(&signature_key->e, str);
fscanf(file, "%1024s", str);
BN_hex2bn(&key->dmp1, str);
BN_hex2bn(&signature_key->dmp1, str);
fscanf(file, "%1024s", str);
BN_hex2bn(&key->dmq1, str);
BN_hex2bn(&signature_key->dmq1, str);
fscanf(file, "%1024s", str);
BN_hex2bn(&key->iqmp, str);
BN_hex2bn(&signature_key->iqmp, str);
fclose(file);
}
/*
* Initialize everything necessary to check the signature of the given image.
* Returns non-zero if successful, 0 otherwise.
*/
int
init_checksum(char *keyfile)
{
if (keyfile == NULL || access(keyfile, R_OK)) {
fprintf(stderr, "Cannot open keyfile\n");
return 0;
}
signature_key = RSA_new();
input_public_key(keyfile, signature_key);
return 1;
}
......@@ -94,18 +93,41 @@ decrypt_checksum(unsigned char *alleged_sum)
memcpy(raw_sum, alleged_sum, CSUM_MAX_LEN);
memset(alleged_sum, '\0', CSUM_MAX_LEN);
RSA_public_decrypt(CSUM_MAX_LEN, raw_sum, alleged_sum, signature_key,
RSA_PKCS1_PADDING);
RSA_public_decrypt(sizeof(raw_sum), raw_sum, alleged_sum,
signature_key, RSA_PKCS1_PADDING);
}
#endif
int
verify_checksum(blockhdr_t *blockhdr, const unsigned char *bodybufp)
verify_checksum(blockhdr_t *blockhdr, const unsigned char *bodybufp, int type)
{
SHA_CTX sum_context;
int sum_length;
unsigned char alleged_sum[CSUM_MAX_LEN];
unsigned char calculated_sum[CSUM_MAX_LEN];
if (type == CSUM_NONE) {
fprintf(stderr, "Chunk has no checksum\n");
return 0;
}
#ifdef SIGN_CHECKSUM
if ((type & CSUM_SIGNED) == 0) {
fprintf(stderr, "Chunk checksum must be signed\n");
return 0;
}
#else
if ((type & CSUM_SIGNED) != 0) {
fprintf(stderr, "Chunk checksum must not be signed\n");
return 0;
}
#endif
type &= CSUM_TYPE;
if (type != CSUM_SHA1) {
fprintf(stderr, "Chunk checksum type %d not supported\n",
type);
return 0;
}
/* initialize checksum state */
memcpy(alleged_sum, blockhdr->checksum, CSUM_MAX_LEN);
memset(blockhdr->checksum, '\0', CSUM_MAX_LEN);
......@@ -153,7 +175,7 @@ verify_checksum(blockhdr_t *blockhdr, const unsigned char *bodybufp)
int
encrypt_readkey(char *keyfile, unsigned char *keybuf, int buflen)
{
char akeybuf[ENC_MAX_KEYLEN*2+1];
char akeybuf[ENC_MAX_KEYLEN*2+1], *cp;
FILE *fp;
/* XXX */
......@@ -162,13 +184,17 @@ encrypt_readkey(char *keyfile, unsigned char *keybuf, int buflen)
fp = fopen(keyfile, "r");
if (fp == NULL) {
fprintf(stderr, "Cannot open encryption keyfile\n");
fprintf(stderr, "%s: cannot open encryption keyfile\n",
keyfile);
return 0;
}
fgets(akeybuf, sizeof(akeybuf), fp);
fclose(fp);
if ((cp = rindex(akeybuf, '\n')))
*cp = '\0';
if (!hexstr_to_mem(keybuf, akeybuf, buflen)) {
fprintf(stderr, "Cannot parse key in keyfile\n");
fprintf(stderr, "%s: cannot parse encryption key\n",
keyfile);
return 0;
}
......
......@@ -15,7 +15,7 @@ cleanup_checksum(void);
// Returns 1 if checksums matched, 0 if they failed to match.
// If the checksums fail to match, an error message is also printed.
int
verify_checksum(blockhdr_t *blockhdr, const unsigned char *bodybufp);
verify_checksum(blockhdr_t *blockhdr, const unsigned char *bodybufp, int type);
int
encrypt_readkey(char *keyfile, unsigned char *keybuf, int buflen);
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2010 University of Utah and the Flux Group.
* Copyright (c) 2000-2011 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -118,23 +118,27 @@ main(int argc, char **argv)
infd = fileno(stdin);
#ifdef WITH_CRYPTO
#ifdef SIGN_CHECKSUM
if (checksums > 0) {
char *keyfile = checksum_keyfile(argv[0]);
if (!init_checksum(keyfile)) {
fprintf(stderr,
"%s: Cannot validate signature\n",
"%s: Cannot validate checksum signing key\n",
argv[0]);
continue;
}
}
#endif
#endif
errors = dumpfile(isstdin ? "<stdin>" : argv[0], infd);
#ifdef WITH_CRYPTO
#ifdef SIGN_CHECKSUM
if (checksums > 0)
cleanup_checksum();
#endif
#endif
if (!isstdin)
......@@ -150,7 +154,7 @@ usage(void)
"imagedump options <image filename> ...\n"
" -v Print version info and exit\n"
" -d Turn on progressive levels of detail\n"
" -c Verify chunk signaturees\n");
" -c Verify chunk checksums\n");
exit(1);
}
......@@ -286,7 +290,7 @@ dumpfile(char *name, int fd)
(unsigned long)(filesize / CHUNKSIZE),
magic - COMPRESSED_MAGIC_BASE + 1);
if (magic >= COMPRESSED_V4) {
sigtype = hdr->checksumtype;
sigtype = hdr->csum_type;
if (sigtype != CSUM_NONE)
printf(", signed (%d)",
sigtype);
......@@ -440,8 +444,8 @@ dumpchunk(char *name, char *buf, int chunkno, int checkindex)
case COMPRESSED_V4:
reg = (struct region *)((struct blockhdr_V4 *)hdr + 1);
if (chunkno > 0) {
if (sigtype != hdr->checksumtype) {
printf("%s: wrong signature type in chunk %d\n",
if (sigtype != hdr->csum_type) {
printf("%s: wrong checksum type in chunk %d\n",
name, chunkno);
return 1;
}
......@@ -456,11 +460,11 @@ dumpchunk(char *name, char *buf, int chunkno, int checkindex)
return 1;
}
}
if (checksums && hdr->checksumtype != CSUM_NONE) {
if (hdr->checksumtype != CSUM_SHA1) {
if (checksums && hdr->csum_type != CSUM_NONE) {
if (hdr->csum_type != CSUM_SHA1) {
printf("%s: unsupported checksum type %d in "
"chunk %d", name,
hdr->checksumtype,
hdr->csum_type,
chunkno);
return 1;
}
......@@ -510,9 +514,9 @@ dumpchunk(char *name, char *buf, int chunkno, int checkindex)
if (hdr->magic >= COMPRESSED_V4) {
int len;
if (hdr->checksumtype != CSUM_NONE) {
if (hdr->csum_type != CSUM_NONE) {
len = 0;
switch (hdr->checksumtype) {
switch (hdr->csum_type) {
case CSUM_SHA1:
len = CSUM_SHA1_LEN;
break;
......@@ -715,8 +719,9 @@ dumpchunk(char *name, char *buf, int chunkno, int checkindex)
/*
* Checksum this image. Assumes SHA1, because we check for this above.
*/
if (checksums && hdr->checksumtype != CSUM_NONE) {
if (!verify_checksum(hdr, (unsigned char *)buf)) {
if (checksums && hdr->csum_type != CSUM_NONE) {
if (!verify_checksum(hdr, (unsigned char *)buf,
hdr->csum_type)) {
printf("ERROR: chunk %d fails checksum!\n", chunkno);
return 1;
}
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2010 University of Utah and the Flux Group.
* Copyright (c) 2000-2011 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -81,13 +81,12 @@ struct blockhdr_V2 {
* Version 4 of the block descriptor adds support for authentication,
* integrety protection and encryption.
*
* An encrypted hash (aka, signature) of each header+chunk is stored in
* the header (checksum). The hash algorithm used is likewise stored in
* the header (checksumtype). The signature key used to encrypt the hash
* is transfered out-of-band.
* An optionally-signed checksum (hash) of each header+chunk is stored in
* the header (checksum) along with the hash algorithm used (csum_type).
* The pubkey used to sign the hash is transfered out-of-band.
*
* To ensure that all valid signed chunks are part of the same image, a
* unique identifier is stored in the header (imageid) of each chunk
* To ensure that all valid signed chunks are part of the same image,
* a unique identifier is stored in the header (imageid) of each chunk
* associated with the same image.
*
* Optionally, the contents of each chunk (but not the header) is encrypted
......@@ -105,12 +104,12 @@ struct blockhdr_V4 {
uint32_t lastsect; /* last sector described by block */
int32_t reloccount; /* number of reloc entries */
/* V4 follows */
uint32_t enc_cipher; /* Which cipher was used to encrypt */
uint16_t enc_cipher; /* cipher was used to encrypt */
uint16_t csum_type; /* checksum algortihm used */
uint8_t enc_iv[ENC_MAX_KEYLEN];
/* Initialization vector */
uint32_t checksumtype; /* Which checksum was used */
unsigned char checksum[SIG_MAX_KEYLEN];
/* Checksum, leave room for 512 bits */
/* (Signed) checksum */
unsigned char imageid[UUID_LENGTH];
/* Unique ID for the whole image */
};
......@@ -122,6 +121,12 @@ struct blockhdr_V4 {
#define CSUM_SHA1 1 /* SHA1: default */
#define CSUM_SHA1_LEN 20
/* type field */
#define CSUM_TYPE 0xFF
/* flags */
#define CSUM_SIGNED 0x8000 /* checksum is signed */
/*
* Ciphers supported
*/
......
......@@ -200,6 +200,7 @@ unsigned long splits;
/* security */
static int do_checksum = 0;
static int do_decrypt = 0;
static char *cipherarg = NULL;
static unsigned char encryption_key[ENC_MAX_KEYLEN];
#endif
......@@ -547,8 +548,11 @@ usage(void)
" -d Turn on progressive levels of debugging\n"
" -r retries Number of image read retries to attempt\n"
" -W size MB of memory to use for write buffering\n"
" -c key Key used to authenticate a checksum (hex number)\n"
" -k key Key used for encryption/decryption (hex number)\n");
" -c Check per-chunk checksum\n"
" -a keyfile File containing pubkey used to sign checksum (implies -c)\n"
" -e cipher Decrypt the image with the given cipher\n"
" -k keyfile File containing key used for encryption/decryption\n"
" -u uuid UUID for image\n");
exit(1);
}
......@@ -563,7 +567,7 @@ main(int argc, char *argv[])
#ifdef NOTHREADS
nothreads = 1;
#endif
while ((ch = getopt(argc, argv, "vdhs:zp:oOnFD:W:Cr:Nck:u:")) != -1)
while ((ch = getopt(argc, argv, "vdhs:zp:oOnFD:W:Cr:Na:ck:eu:")) != -1)
switch(ch) {
#ifdef FAKEFRISBEE
case 'F':
......@@ -628,16 +632,41 @@ main(int argc, char *argv[])
#endif
#ifdef WITH_CRYPTO
case 'c':
#ifndef SIGN_CHECKSUM
do_checksum = 1;
break;
case 'k':
if (!hexstr_to_mem(encryption_key, optarg,
ENC_MAX_KEYLEN)) {
do_decrypt = 0;
#else
fprintf(stderr, "Unsigned checksums not supported\n");
exit(1);
#endif
case 'a':
#ifdef SIGN_CHECKSUM
if (!init_checksum(optarg))
exit(1);
do_checksum = 1;
break;
#else
fprintf(stderr, "Signed checksums not supported\n");
exit(1);
#endif
case 'e':
/* Encryption cipher */
cipherarg = optarg;
if (strcmp(cipherarg, "bf_cbc") != 0) {
fprintf(stderr,
"Only know \"bf_cbc\" (blowfish CBC)\n");
usage();
} else {
do_decrypt = 1;
}
do_decrypt = 1;
break;
case 'k':
if (!encrypt_readkey(optarg, encryption_key,
ENC_MAX_KEYLEN))
exit(1);
do_decrypt = 1;
break;
#endif
case 'u':
......@@ -689,17 +718,6 @@ main(int argc, char *argv[])
}
}
#ifdef WITH_CRYPTO
if (do_checksum > 0) {
char *keyfile = checksum_keyfile(argv[0]);
if (!init_checksum(keyfile)) {
fprintf(stderr,
"%s: Cannot validate signature\n", argv[0]);
exit(1);
}
}
#endif
if (docrconly)
outfd = -1;
else if (argc == 2 && strcmp(argv[1], "-")) {
......@@ -883,8 +901,10 @@ main(int argc, char *argv[])
fprintf(stderr, "%s: CRC=%u\n", argv[0], ~crc);
dump_writebufs();
#ifdef WITH_CRYPTO
if (do_checksum > 0)
#ifdef SIGN_CHECKSUM
if (do_checksum)
cleanup_checksum();
#endif
#endif
return 0;
}
......@@ -895,15 +915,21 @@ main(int argc, char *argv[])
int
ImageUnzipInitKeys(char *uuid, char *sig_keyfile, char *enc_keyfile)
{
#ifdef WITH_CRYPTO
if (uuid) {
memcpy(imageid, uuid, UUID_LENGTH);
has_id = 1;
}
#ifdef WITH_CRYPTO
if (sig_keyfile) {
#ifdef SIGN_CHECKSUM
if (!init_checksum(sig_keyfile))
exit(1);
do_checksum = 1;
#else
fprintf(stderr, "Signed checksums not supported\n");
exit(1);
#endif
}
if (enc_keyfile) {
if (!encrypt_readkey(enc_keyfile,
......@@ -1030,8 +1056,10 @@ ImageUnzipQuit(void)
fixmbr(slice, dostype);
#ifdef WITH_CRYPTO
if (do_checksum > 0)
#ifdef SIGN_CHECKSUM
if (do_checksum)
cleanup_checksum();
#endif
#endif
fprintf(stderr, "Wrote %qd bytes (%qd actual)\n",
......@@ -1197,7 +1225,8 @@ write_subblock(int chunkno, const char *chunkbufp, int chunksize)
#ifdef WITH_CRYPTO
/* returns the number of characters decrypted */
static int
decrypt_buffer(unsigned char *dest, const unsigned char *source, const blockhdr_t *header)
decrypt_buffer(unsigned char *dest, const unsigned char *source,
const blockhdr_t *header)
{
/* init */
int update_count = 0;
......@@ -1263,23 +1292,28 @@ inflate_subblock(const char *chunkbufp)
blockhdr = (const blockhdr_t *) chunkbufp;
chunkbufp += DEFAULTREGIONSIZE;
#ifdef WITH_CRYPTO
/*
* If decryption and/or signing is required and the image is too
* old, bail.
*
* XXX this assumes that version nums are monotonically increasing,
* I cannot imagine why they would not be in the future!
*/
if (blockhdr->magic < COMPRESSED_V4 &&
(do_checksum || do_decrypt)) {
fprintf(stderr,
"%s requested but image is old format (V%d)\n",
do_checksum ? "checksum" : "decryption",
blockhdr->magic - COMPRESSED_MAGIC_BASE);
exit(1);
}
#endif
switch (blockhdr->magic) {
case COMPRESSED_V1:
{
static int didwarn;
#ifdef WITH_CRYPTO
if (do_checksum) {
fprintf(stderr,
"signature checking requested but image is wrong format\n");
exit(1);
}
if (do_decrypt) {
fprintf(stderr,
"decryption requested but image is wrong format\n");
exit(1);
}
#endif
curregion = (struct region *)
((struct blockhdr_V1 *)blockhdr + 1);
if (dofill && !didwarn) {
......@@ -1293,18 +1327,6 @@ inflate_subblock(const char *chunkbufp)
case COMPRESSED_V2:
case COMPRESSED_V3:
#ifdef WITH_CRYPTO
if (do_checksum) {
fprintf(stderr,
"signature checking requested but image is wrong format\n");
exit(1);
}
if (do_decrypt) {
fprintf(stderr,
"decryption requested but image is wrong format\n");
exit(1);
}
#endif
imageversion = 2;
curregion = (struct region *)
((struct blockhdr_V2 *)blockhdr + 1);
......@@ -1317,11 +1339,12 @@ inflate_subblock(const char *chunkbufp)
case COMPRESSED_V4:
#ifdef WITH_CRYPTO
/*
* Verify the hash signature before looking at anything else.
* Verify the checksum before looking at anything else.
*/
if (do_checksum &&
!verify_checksum((blockhdr_t *)blockhdr,
(const unsigned char *)blockhdr))
(const unsigned char *)blockhdr,
blockhdr->csum_type))
exit(1);
#endif
......@@ -1329,9 +1352,12 @@ inflate_subblock(const char *chunkbufp)
* Track the current image UUID.
*
* If a UUID was specified on the command line, use that as
* the expected UUID. Otherwise, if this is the first chunk
* received, remember its UUID. All other chunks must match
* this UUID.
* the expected UUID. All chunks much match this UUID.
*
* XXX if no UUID was specified, we use the one from the
* first chunk received and match all others against that.
* This is not ideal, since it is vulnerable to spoofing if
* the client is not using a signature and/or encryption.
*/
if (has_id == 0) {
has_id = 1;
......@@ -1353,6 +1379,17 @@ inflate_subblock(const char *chunkbufp)
* Decrypt the rest of the chunk if encrypted.
*/
if (do_decrypt) {
if (blockhdr->enc_cipher == ENC_NONE) {
fprintf(stderr, "Chunk has no cipher\n");
exit(1);
}
if (blockhdr->enc_cipher != ENC_BLOWFISH_CBC) {
fprintf(stderr,
"Chunk cipher type %d not supported\n",
blockhdr->enc_cipher);
exit(1);
}
((blockhdr_t *)blockhdr)->size
= decrypt_buffer((unsigned char *)plaintext,
(unsigned char *)chunkbufp,
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment