/*** FIXME ***/
#define STREAM_SHA256_DIGEST 17 /* SHA-256 digest for the file */
#define STREAM_SHA512_DIGEST 18 /* SHA-512 digest for the file */
-#define STREAM_SIGNED_DIGEST 19 /* Signed File Digest, ASN.1 Encoded */
+#define STREAM_SIGNED_DIGEST 19 /* Signed File Digest, ASN.1, DER Encoded */
#define STREAM_ENCRYPTED_FILE_DATA 20 /* Encrypted, uncompressed data */
#define STREAM_ENCRYPTED_WIN32_DATA 21 /* Encrypted, uncompressed Win32 BackupRead data */
+#define STREAM_ENCRYPTED_SESSION_DATA 22 /* Encrypted Session Data, ASN.1, DER Encoded */
/*
jcr->compress_buf_size = jcr->buf_size + ((jcr->buf_size+999) / 1000) + 30;
jcr->compress_buf = get_memory(jcr->compress_buf_size);
+ /* Create encryption session data and a cached, DER-encoded session data
+ * structure. We use a single session key for each backup, so we'll encode
+ * the session data only once. */
if (jcr->pki_encrypt) {
+ size_t size = 0;
+
/* Create per-job session encryption context */
- jcr->pki_recipients = crypto_recipients_new(cipher, jcr->pki_readers);
+ jcr->pki_session = crypto_session_new(cipher, jcr->pki_readers);
+
+ /* Get the session data size */
+ if (crypto_session_encode(jcr->pki_session, NULL, &size) == false) {
+ Jmsg(jcr, M_FATAL, 0, _("An error occured while encrypting the stream.\n"));
+ return 0;
+ }
+
+ /* Allocate buffer */
+ jcr->pki_session_encoded = malloc(size);
+ if (!jcr->pki_session_encoded) {
+ return 0;
+ }
+
+ /* Encode session data */
+ if (crypto_session_encode(jcr->pki_session, jcr->pki_session_encoded, &size) == false) {
+ Jmsg(jcr, M_FATAL, 0, _("An error occured while encrypting the stream.\n"));
+ return 0;
+ }
+
+ /* ... and store the encoded size */
+ jcr->pki_session_encoded_size = size;
}
Dmsg1(300, "set_find_options ff=%p\n", jcr->ff);
jcr->compress_buf = NULL;
}
- if (jcr->pki_recipients) {
- crypto_recipients_free(jcr->pki_recipients);
+ if (jcr->pki_session) {
+ crypto_session_free(jcr->pki_session);
+ }
+ if (jcr->pki_session_encoded) {
+ free(jcr->pki_session_encoded);
}
Dmsg1(100, "end blast_data ok=%d\n", ok);
stop_thread_timer(tid);
tid = NULL;
}
+
+ /* Set up the encryption context, send the session data to the SD */
+ if (jcr->pki_encrypt) {
+ /* Send our header */
+ bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, STREAM_ENCRYPTED_SESSION_DATA);
+
+ /* Grow the bsock buffer to fit our message if necessary */
+ if ((size_t) sizeof_pool_memory(sd->msg) < jcr->pki_session_encoded_size) {
+ sd->msg = realloc_pool_memory(sd->msg, jcr->pki_session_encoded_size);
+ }
+
+ /* Copy our message over and send it */
+ memcpy(sd->msg, jcr->pki_session_encoded, jcr->pki_session_encoded_size);
+ sd->msglen = jcr->pki_session_encoded_size;
+ jcr->JobBytes += sd->msglen;
+
+ bnet_send(sd);
+ bnet_sig(sd, BNET_EOD);
+ }
+
stat = send_data(jcr, data_stream, ff_pkt, digest, signing_digest);
bclose(&ff_pkt->bfd);
if (!stat) {
/* Allocate signature data buffer */
buf = malloc(size);
if (!buf) {
- free(buf);
+ crypto_sign_free(sig);
return 0;
}
* 1. Stream record header
* 2. Stream data
* a. Attributes (Unix or Win32)
- * or b. File data for the file
- * or c. Alternate data stream (e.g. Resource Fork)
- * or d. Finder info
- * or e. ACLs
- * or f. Possibly a cryptographic signature
- * or g. Possibly MD5 or SHA1 record
+ * b. Possibly stream encryption session data (e.g., symmetric session key)
+ * or c. File data for the file
+ * or d. Alternate data stream (e.g. Resource Fork)
+ * or e. Finder info
+ * or f. ACLs
+ * or g. Possibly a cryptographic signature
+ * or h. Possibly MD5 or SHA1 record
* 3. Repeat step 1
*
* NOTE: We keep track of two bacula file descriptors:
break;
/* Data stream */
+ case STREAM_ENCRYPTED_SESSION_DATA:
+ // TODO landonf: Implement
+ // sig = crypto_sign_decode(sd->msg, (size_t) sd->msglen);
+ Dmsg1(30, "Stream=Encrypted Session Data, size: %d\n", sd->msglen);
+ break;
+
case STREAM_FILE_DATA:
case STREAM_SPARSE_DATA:
case STREAM_WIN32_DATA:
case STREAM_WIN32_GZIP_DATA:
/* Force an expected, consistent stream type here */
if (extract && (prev_stream == stream || prev_stream == STREAM_UNIX_ATTRIBUTES
- || prev_stream == STREAM_UNIX_ATTRIBUTES_EX)) {
+ || prev_stream == STREAM_UNIX_ATTRIBUTES_EX
+ || prev_stream == STREAM_ENCRYPTED_SESSION_DATA)) {
flags = 0;
if (stream == STREAM_SPARSE_DATA || stream == STREAM_SPARSE_GZIP_DATA) {
flags |= FO_SPARSE;
break;
/* Data streams to ignore */
+ case STREAM_ENCRYPTED_SESSION_DATA:
case STREAM_FILE_DATA:
case STREAM_SPARSE_DATA:
case STREAM_WIN32_DATA:
X509_KEYPAIR *pki_keypair; /* Encryption key pair */
alist *pki_signers; /* Trusted Signers */
alist *pki_readers; /* Trusted Readers */
- CRYPTO_RECIPIENTS *pki_recipients; /* PKE Public Keys + Symmetric Session Keys */
+ CRYPTO_SESSION *pki_session; /* PKE Public Keys + Symmetric Session Keys */
+ void *pki_session_encoded; /* Cached DER-encoded copy of pki_session */
+ size_t pki_session_encoded_size; /* Size of DER-encoded pki_session */
DIRRES* director; /* Director resource */
#endif /* FILE_DAEMON */
SignatureData *sigData;
};
-/* Encryption Key Data */
-struct Crypto_Recipients {
+/* Encryption Session Data */
+struct Crypto_Session {
CryptoData *cryptoData; /* ASN.1 Structure */
EVP_CIPHER *openssl_cipher; /* OpenSSL Cipher Object */
unsigned char session_key[EVP_MAX_KEY_LENGTH]; /* Private symmetric session key */
}
/*
- * Create a new encryption recipient.
- * Returns: A pointer to a CRYPTO_RECIPIENTS object on success.
+ * Create a new encryption session.
+ * Returns: A pointer to a CRYPTO_SESSION object on success.
* NULL on failure.
*/
-CRYPTO_RECIPIENTS *crypto_recipients_new (crypto_cipher_t cipher, alist *pubkeys)
+CRYPTO_SESSION *crypto_session_new (crypto_cipher_t cipher, alist *pubkeys)
{
- CRYPTO_RECIPIENTS *cr;
+ CRYPTO_SESSION *cs;
X509_KEYPAIR *keypair;
const EVP_CIPHER *ec;
unsigned char *iv;
int iv_len;
- /* Allocate our recipient description structures */
- cr = (CRYPTO_RECIPIENTS *) malloc(sizeof(CRYPTO_RECIPIENTS));
- if (!cr) {
+ /* Allocate our session description structures */
+ cs = (CRYPTO_SESSION *) malloc(sizeof(CRYPTO_SESSION));
+ if (!cs) {
return NULL;
}
- cr->cryptoData = CryptoData_new();
+ cs->cryptoData = CryptoData_new();
- if (!cr->cryptoData) {
+ if (!cs->cryptoData) {
/* Allocation failed in OpenSSL */
- free(cr);
+ free(cs);
return NULL;
}
/* Set the ASN.1 structure version number */
- ASN1_INTEGER_set(cr->cryptoData->version, BACULA_ASN1_VERSION);
+ ASN1_INTEGER_set(cs->cryptoData->version, BACULA_ASN1_VERSION);
/*
* Acquire a cipher instance and set the ASN.1 cipher NID
switch (cipher) {
case CRYPTO_CIPHER_AES_128_CBC:
/* AES 128 bit CBC */
- cr->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_128_cbc);
+ cs->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_128_cbc);
ec = EVP_aes_128_cbc();
break;
case CRYPTO_CIPHER_AES_192_CBC:
/* AES 192 bit CBC */
- cr->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_192_cbc);
+ cs->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_192_cbc);
ec = EVP_aes_192_cbc();
break;
case CRYPTO_CIPHER_AES_256_CBC:
/* AES 256 bit CBC */
- cr->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_256_cbc);
+ cs->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_256_cbc);
ec = EVP_aes_256_cbc();
break;
case CRYPTO_CIPHER_BLOWFISH_CBC:
/* Blowfish CBC */
- cr->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_bf_cbc);
+ cs->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_bf_cbc);
ec = EVP_bf_cbc();
break;
default:
Emsg0(M_ERROR, 0, _("Unsupported cipher type specified\n"));
- crypto_recipients_free(cr);
+ crypto_session_free(cs);
return NULL;
}
/* Generate a symmetric session key */
- cr->session_key_len = EVP_CIPHER_key_length(ec);
- if (RAND_bytes(cr->session_key, cr->session_key_len) <= 0) {
+ cs->session_key_len = EVP_CIPHER_key_length(ec);
+ if (RAND_bytes(cs->session_key, cs->session_key_len) <= 0) {
/* OpenSSL failure */
- crypto_recipients_free(cr);
+ crypto_session_free(cs);
return NULL;
}
iv = (unsigned char *) malloc(iv_len);
if (!iv) {
/* Malloc failure */
- crypto_recipients_free(cr);
+ crypto_session_free(cs);
return NULL;
}
/* Generate random IV */
if (RAND_bytes(iv, iv_len) <= 0) {
/* OpenSSL failure */
- crypto_recipients_free(cr);
+ crypto_session_free(cs);
return NULL;
}
/* Store it in our ASN.1 structure */
- if (!M_ASN1_OCTET_STRING_set(cr->cryptoData->iv, iv, iv_len)) {
+ if (!M_ASN1_OCTET_STRING_set(cs->cryptoData->iv, iv, iv_len)) {
/* Allocation failed in OpenSSL */
- crypto_recipients_free(cr);
+ crypto_session_free(cs);
return NULL;
}
}
ri = RecipientInfo_new();
if (!ri) {
/* Allocation failed in OpenSSL */
- crypto_recipients_free(cr);
+ crypto_session_free(cs);
return NULL;
}
ekey = (unsigned char *) malloc(EVP_PKEY_size(keypair->pubkey));
if (!ekey) {
RecipientInfo_free(ri);
- crypto_recipients_free(cr);
+ crypto_session_free(cs);
return NULL;
}
- if ((ekey_len = EVP_PKEY_encrypt(ekey, cr->session_key, cr->session_key_len, keypair->pubkey)) <= 0) {
+ if ((ekey_len = EVP_PKEY_encrypt(ekey, cs->session_key, cs->session_key_len, keypair->pubkey)) <= 0) {
/* OpenSSL failure */
RecipientInfo_free(ri);
- crypto_recipients_free(cr);
+ crypto_session_free(cs);
free(ekey);
return NULL;
}
if (!M_ASN1_OCTET_STRING_set(ri->encryptedKey, ekey, ekey_len)) {
/* Allocation failed in OpenSSL */
RecipientInfo_free(ri);
- crypto_recipients_free(cr);
+ crypto_session_free(cs);
free(ekey);
return NULL;
}
free(ekey);
/* Push the new RecipientInfo structure onto the stack */
- sk_RecipientInfo_push(cr->cryptoData->recipientInfo, ri);
+ sk_RecipientInfo_push(cs->cryptoData->recipientInfo, ri);
}
- return cr;
+ return cs;
}
/*
- * Free memory associated with a crypto recipient object.
+ * Encodes the CryptoData structure. The length argument is used to specify the
+ * size of dest. A length of 0 will cause no data to be written to dest, and the
+ * required length to be written to length. The caller can then allocate sufficient
+ * space for the output.
+ *
+ * Returns: true on success, stores the encoded data in dest, and the size in length.
+ * false on failure.
+ */
+bool crypto_session_encode(CRYPTO_SESSION *cs, void *dest, size_t *length)
+{
+ if (*length == 0) {
+ *length = i2d_CryptoData(cs->cryptoData, NULL);
+ return true;
+ }
+
+ *length = i2d_CryptoData(cs->cryptoData, (unsigned char **) &dest);
+ return true;
+}
+
+/*
+ * Decodes the CryptoData structure. The length argument is
+ * used to specify the size of data.
+ *
+ * Returns: CRYPTO_SESSION instance on success.
+ * NULL on failure.
+ */
+// TODO landonf: Unimplemented, requires a private key to decrypt session key
+CRYPTO_SESSION *crypto_session_decode(const void *data, size_t length)
+{
+ CRYPTO_SESSION *cs;
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800FL)
+ const unsigned char *p = (const unsigned char *) data;
+#else
+ unsigned char *p = (unsigned char *) data;
+#endif
+
+ cs = (CRYPTO_SESSION *) malloc(sizeof(CRYPTO_SESSION));
+ if (!cs) {
+ return NULL;
+ }
+
+ /* d2i_CryptoData modifies the supplied pointer */
+ cs->cryptoData = d2i_CryptoData(NULL, &p, length);
+
+ if (!cs->cryptoData) {
+ /* Allocation / Decoding failed in OpenSSL */
+ openssl_post_errors(M_ERROR, _("CryptoData decoding failed"));
+ return NULL;
+ }
+
+ return cs;
+}
+
+/*
+ * Free memory associated with a crypto session object.
*/
-void crypto_recipients_free (CRYPTO_RECIPIENTS *cr)
+void crypto_session_free (CRYPTO_SESSION *cs)
{
- CryptoData_free(cr->cryptoData);
- free(cr);
+ CryptoData_free(cs->cryptoData);
+ free(cs);
}
/*
int crypto_sign_add_signer (SIGNATURE *sig, DIGEST *digest, X509_KEYPAIR *keypair) { return false; }
int crypto_sign_encode (SIGNATURE *sig, void *dest, size_t *length) { return false; }
-SIGNATURE *crypto_sign_decode (const void *sigData, size_t length) { return false; }
+SIGNATURE *crypto_sign_decode (const void *sigData, size_t length) { return NULL; }
void crypto_sign_free (SIGNATURE *sig) { }
int crypto_keypair_load_key (X509_KEYPAIR *keypair, const char *file, CRYPTO_PEM_PASSWD_CB *pem_callback, const void *pem_userdata) { return false; }
void crypto_keypair_free (X509_KEYPAIR *keypair) { }
-CRYPTO_RECIPIENTS *crypto_recipients_new (crypto_cipher_t cipher, alist *pubkeys) { return NULL; }
-void crypto_recipients_free (CRYPTO_RECIPIENTS *cr) { }
+CRYPTO_SESSION *crypto_session_new (crypto_cipher_t cipher, alist *pubkeys) { return NULL; }
+void crypto_session_free (CRYPTO_SESSION *cs) { }
+bool crypto_session_encode(CRYPTO_SESSION *cs, void *dest, size_t *length) { return false; }
+CRYPTO_SESSION *crypto_session_decode(const void *data, size_t length) { return NULL; }
#endif /* HAVE_CRYPTO */
typedef struct Signature SIGNATURE;
/* Opaque PKI Symmetric Key Data Structure */
-typedef struct Crypto_Recipients CRYPTO_RECIPIENTS;
+typedef struct Crypto_Session CRYPTO_SESSION;
/* PEM Decryption Passphrase Callback */
typedef int (CRYPTO_PEM_PASSWD_CB) (char *buf, int size, const void *userdata);
int crypto_sign_encode (SIGNATURE *sig, void *dest, size_t *length);
SIGNATURE * crypto_sign_decode (const void *sigData, size_t length);
void crypto_sign_free (SIGNATURE *sig);
-CRYPTO_RECIPIENTS *crypto_recipients_new (crypto_cipher_t cipher, alist *pubkeys);
-void crypto_recipients_free (CRYPTO_RECIPIENTS *cr);
+CRYPTO_SESSION * crypto_session_new (crypto_cipher_t cipher, alist *pubkeys);
+void crypto_session_free (CRYPTO_SESSION *cs);
+bool crypto_session_encode (CRYPTO_SESSION *cs, void *dest, size_t *length);
+CRYPTO_SESSION * crypto_session_decode (const void *data, size_t length);
X509_KEYPAIR * crypto_keypair_new (void);
X509_KEYPAIR * crypto_keypair_dup (X509_KEYPAIR *keypair);
int crypto_keypair_load_cert (X509_KEYPAIR *keypair, const char *file);
break;
case STREAM_SIGNED_DIGEST:
- // TODO landonf: Investigate signed digest support in the storage daemon
+ case STREAM_ENCRYPTED_SESSION_DATA:
+ // TODO landonf: Investigate crypto support in the storage daemon
break;
case STREAM_PROGRAM_NAMES:
update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA512);
break;
+ case STREAM_ENCRYPTED_SESSION_DATA:
+ // TODO landonf: Investigate crypto support in bscan
+ if (verbose > 1) {
+ Pmsg0(000, _("Got signed digest record\n"));
+ }
+ break;
+
case STREAM_SIGNED_DIGEST:
- // TODO landonf: Investigate signed digest support in bscan
+ // TODO landonf: Investigate crypto support in bscan
if (verbose > 1) {
Pmsg0(000, _("Got signed digest record\n"));
}
return "SHA512";
case STREAM_SIGNED_DIGEST:
return "SIGNED-DIGEST";
+ case STREAM_ENCRYPTED_SESSION_DATA:
+ return "ENCRYPTED-SESSION-DATA";
case -STREAM_UNIX_ATTRIBUTES:
return "contUATTR";
case -STREAM_FILE_DATA:
return "contSHA512";
case -STREAM_SIGNED_DIGEST:
return "contSIGNED-DIGEST";
+ case -STREAM_ENCRYPTED_SESSION_DATA:
+ return "contENCRYPTED-SESSION-DATA";
default:
sprintf(buf, "%d", stream);
return buf;