+/*
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2016 Kern Sibbald
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
+*/
/*
* crypto.c Encryption support functions
*
* Author: Landon Fuller <landonf@opendarwin.org>
*
- * Version $Id$
- *
- * Copyright (C) 2005 Kern Sibbald
- *
* This file was contributed to the Bacula project by Landon Fuller.
*
* Landon Fuller has been granted a perpetual, worldwide, non-exclusive,
* If you wish to license these contributions under an alternate open source
* license please contact Landon Fuller <landonf@opendarwin.org>.
*/
-/*
- Copyright (C) 2005 Kern Sibbald
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- version 2 as amended with additional clauses defined in the
- file LICENSE in the main source directory.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- the file LICENSE for additional details.
-
- */
#include "bacula.h"
+#include "jcr.h"
#include <assert.h>
+/**
+ * For OpenSSL version 1.x, EVP_PKEY_encrypt no longer
+ * exists. It was not an official API.
+ */
+#ifdef HAVE_OPENSSLv1
+#define EVP_PKEY_encrypt EVP_PKEY_encrypt_old
+#define EVP_PKEY_decrypt EVP_PKEY_decrypt_old
+#endif
+
/*
* Bacula ASN.1 Syntax
*
#ifdef HAVE_CRYPTO /* Is encryption enabled? */
#ifdef HAVE_OPENSSL /* How about OpenSSL? */
-/* Are we initialized? */
-static int crypto_initialized = false;
+#include "openssl-compat.h"
/* ASN.1 Declarations */
#define BACULA_ASN1_VERSION 0
ASN1_SEQUENCE(CryptoData) = {
ASN1_SIMPLE(CryptoData, version, ASN1_INTEGER),
+ ASN1_SIMPLE(CryptoData, contentEncryptionAlgorithm, ASN1_OBJECT),
ASN1_SIMPLE(CryptoData, iv, ASN1_OCTET_STRING),
ASN1_SET_OF(CryptoData, recipientInfo, RecipientInfo)
} ASN1_SEQUENCE_END(CryptoData);
IMPLEMENT_ASN1_FUNCTIONS(RecipientInfo)
IMPLEMENT_ASN1_FUNCTIONS(SignatureData)
IMPLEMENT_ASN1_FUNCTIONS(CryptoData)
-IMPLEMENT_STACK_OF(SignerInfo)
-IMPLEMENT_STACK_OF(RecipientInfo)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+DEFINE_STACK_OF(SignerInfo);
+DEFINE_STACK_OF(RecipientInfo);
+#else
/*
* SignerInfo and RecipientInfo stack macros, generated by OpenSSL's util/mkstack.pl.
*/
#define sk_SignerInfo_is_sorted(st) SKM_sk_is_sorted(SignerInfo, (st))
#define d2i_ASN1_SET_OF_SignerInfo(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \
- SKM_ASN1_SET_OF_d2i(SignerInfo, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class))
+ SKM_ASN1_SET_OF_d2i(SignerInfo, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class))
#define i2d_ASN1_SET_OF_SignerInfo(st, pp, i2d_func, ex_tag, ex_class, is_set) \
SKM_ASN1_SET_OF_i2d(SignerInfo, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set))
#define ASN1_seq_pack_SignerInfo(st, i2d_func, buf, len) \
#define sk_RecipientInfo_is_sorted(st) SKM_sk_is_sorted(RecipientInfo, (st))
#define d2i_ASN1_SET_OF_RecipientInfo(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \
- SKM_ASN1_SET_OF_d2i(RecipientInfo, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class))
+ SKM_ASN1_SET_OF_d2i(RecipientInfo, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class))
#define i2d_ASN1_SET_OF_RecipientInfo(st, pp, i2d_func, ex_tag, ex_class, is_set) \
SKM_ASN1_SET_OF_i2d(RecipientInfo, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set))
#define ASN1_seq_pack_RecipientInfo(st, i2d_func, buf, len) \
#define ASN1_seq_unpack_RecipientInfo(buf, len, d2i_func, free_func) \
SKM_ASN1_seq_unpack(RecipientInfo, (buf), (len), (d2i_func), (free_func))
/* End of util/mkstack.pl block */
+#endif
/* X509 Public/Private Key Pair Structure */
struct X509_Keypair {
/* Message Digest Structure */
struct Digest {
crypto_digest_t type;
- EVP_MD_CTX ctx;
+ JCR *jcr;
+ EVP_MD_CTX *ctx;
};
/* Message Signature Structure */
struct Signature {
SignatureData *sigData;
+ JCR *jcr;
};
/* Encryption Session Data */
size_t session_key_len; /* Symmetric session key length */
};
+/* Symmetric Cipher Context */
+struct Cipher_Context {
+ EVP_CIPHER_CTX *ctx;
+};
+
/* PEM Password Dispatch Context */
typedef struct PEM_CB_Context {
CRYPTO_PEM_PASSWD_CB *pem_callback;
/*
* Extract subjectKeyIdentifier from x509 certificate.
- * Returns: On success, an ASN1_OCTET_STRING that must be freed via M_ASN1_OCTET_STRING_free().
+ * Returns: On success, an ASN1_OCTET_STRING that must be freed via ASN1_OCTET_STRING_free().
* NULL on failure.
*/
-static ASN1_OCTET_STRING *openssl_cert_keyid(X509 *cert){
+static ASN1_OCTET_STRING *openssl_cert_keyid(X509 *cert) {
X509_EXTENSION *ext;
- X509V3_EXT_METHOD *method;
+ const X509V3_EXT_METHOD *method;
ASN1_OCTET_STRING *keyid;
int i;
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800FL)
+ const ASN1_STRING *asn1_ext_val;
const unsigned char *ext_value_data;
-#else
- unsigned char *ext_value_data;
-#endif
-
/* Find the index to the subjectKeyIdentifier extension */
i = X509_get_ext_by_NID(cert, NID_subject_key_identifier, -1);
return NULL;
}
- ext_value_data = ext->value->data;
+ asn1_ext_val = X509_EXTENSION_get_data(ext);
+ ext_value_data = ASN1_STRING_get0_data(asn1_ext_val);
-#if (OPENSSL_VERSION_NUMBER > 0x00907000L)
if (method->it) {
/* New style ASN1 */
/* Decode ASN1 item in data */
- keyid = (ASN1_OCTET_STRING *) ASN1_item_d2i(NULL, &ext_value_data, ext->value->length,
+ keyid = (ASN1_OCTET_STRING *) ASN1_item_d2i(NULL, &ext_value_data, ASN1_STRING_length(asn1_ext_val),
ASN1_ITEM_ptr(method->it));
} else {
/* Old style ASN1 */
/* Decode ASN1 item in data */
- keyid = (ASN1_OCTET_STRING *) method->d2i(NULL, &ext_value_data, ext->value->length);
+ keyid = (ASN1_OCTET_STRING *) method->d2i(NULL, &ext_value_data, ASN1_STRING_length(asn1_ext_val));
}
-#else
- keyid = (ASN1_OCTET_STRING *) method->d2i(NULL, &ext_value_data, ext->value->length);
-#endif
-
return keyid;
}
* Returns: A pointer to a X509 KEYPAIR object on success.
* NULL on failure.
*/
-X509_KEYPAIR *crypto_keypair_new (void) {
+X509_KEYPAIR *crypto_keypair_new(void)
+{
X509_KEYPAIR *keypair;
/* Allocate our keypair structure */
- keypair = (X509_KEYPAIR *) malloc(sizeof(X509_KEYPAIR));
- if (!keypair) {
- return NULL;
- }
+ keypair = (X509_KEYPAIR *)malloc(sizeof(X509_KEYPAIR));
/* Initialize our keypair structure */
keypair->keyid = NULL;
* API is available. Instead, the reference count is
* incremented.
*/
-X509_KEYPAIR *crypto_keypair_dup (X509_KEYPAIR *keypair)
+X509_KEYPAIR *crypto_keypair_dup(X509_KEYPAIR *keypair)
{
X509_KEYPAIR *newpair;
+ int ret;
newpair = crypto_keypair_new();
/* Increment the public key ref count */
if (keypair->pubkey) {
- CRYPTO_add(&(keypair->pubkey->references), 1, CRYPTO_LOCK_EVP_PKEY);
+ ret = EVP_PKEY_up_ref(keypair->pubkey);
+ if (ret == 0)
+ goto out_free_new;
newpair->pubkey = keypair->pubkey;
}
/* Increment the private key ref count */
if (keypair->privkey) {
- CRYPTO_add(&(keypair->privkey->references), 1, CRYPTO_LOCK_EVP_PKEY);
+ ret = EVP_PKEY_up_ref(keypair->privkey);
+ if (ret == 0)
+ goto out_free_new;
newpair->privkey = keypair->privkey;
}
/* Duplicate the keyid */
if (keypair->keyid) {
- newpair->keyid = M_ASN1_OCTET_STRING_dup(keypair->keyid);
- if (!newpair->keyid) {
- /* Allocation failed */
- crypto_keypair_free(newpair);
- return NULL;
- }
+ newpair->keyid = ASN1_OCTET_STRING_dup(keypair->keyid);
+ if (!newpair->keyid)
+ goto out_free_new;
}
return newpair;
+
+out_free_new:
+ crypto_keypair_free(newpair);
+ return NULL;
}
* Returns: true on success
* false on failure
*/
-int crypto_keypair_load_cert (X509_KEYPAIR *keypair, const char *file)
+int crypto_keypair_load_cert(X509_KEYPAIR *keypair, const char *file)
{
BIO *bio;
X509 *cert;
/* Extract the subjectKeyIdentifier extension field */
if ((keypair->keyid = openssl_cert_keyid(cert)) == NULL) {
- Emsg0(M_ERROR, 0, _("Provided certificate does not include the required subjectKeyIdentifier extension."));
+ Jmsg0(NULL, M_ERROR, 0,
+ _("Provided certificate does not include the required subjectKeyIdentifier extension."));
goto err;
}
/* Validate the public key type (only RSA is supported) */
- if (EVP_PKEY_type(keypair->pubkey->type) != EVP_PKEY_RSA) {
- Emsg1(M_ERROR, 0, _("Unsupported key type provided: %d\n"), EVP_PKEY_type(keypair->pubkey->type));
+ if (EVP_PKEY_base_id(keypair->pubkey) != EVP_PKEY_RSA) {
+ Jmsg1(NULL, M_ERROR, 0,
+ _("Unsupported key type provided: %d\n"), EVP_PKEY_id(keypair->pubkey));
goto err;
}
+ X509_free(cert);
return true;
err:
* Returns: true if a private key is found
* false otherwise
*/
-bool crypto_keypair_has_key (const char *file) {
+bool crypto_keypair_has_key(const char *file) {
BIO *bio;
char *name = NULL;
char *header = NULL;
* Returns: true on success
* false on failure
*/
-int crypto_keypair_load_key (X509_KEYPAIR *keypair, const char *file,
+int crypto_keypair_load_key(X509_KEYPAIR *keypair, const char *file,
CRYPTO_PEM_PASSWD_CB *pem_callback,
const void *pem_userdata)
{
/*
* Free memory associated with a keypair object.
*/
-void crypto_keypair_free (X509_KEYPAIR *keypair)
+void crypto_keypair_free(X509_KEYPAIR *keypair)
{
if (keypair->pubkey) {
EVP_PKEY_free(keypair->pubkey);
EVP_PKEY_free(keypair->privkey);
}
if (keypair->keyid) {
- M_ASN1_OCTET_STRING_free(keypair->keyid);
+ ASN1_OCTET_STRING_free(keypair->keyid);
}
free(keypair);
}
* Returns: A pointer to a DIGEST object on success.
* NULL on failure.
*/
-DIGEST *crypto_digest_new (crypto_digest_t type)
+DIGEST *crypto_digest_new(JCR *jcr, crypto_digest_t type)
{
DIGEST *digest;
const EVP_MD *md = NULL; /* Quell invalid uninitialized warnings */
- digest = (DIGEST *) malloc(sizeof(DIGEST));
+ digest = (DIGEST *)malloc(sizeof(DIGEST));
digest->type = type;
+ digest->jcr = jcr;
+ Dmsg1(150, "crypto_digest_new jcr=%p\n", jcr);
/* Initialize the OpenSSL message digest context */
- EVP_MD_CTX_init(&digest->ctx);
+ digest->ctx = EVP_MD_CTX_new();
+ if (!digest->ctx)
+ goto err;
+ EVP_MD_CTX_reset(digest->ctx);
/* Determine the correct OpenSSL message digest type */
switch (type) {
break;
#endif
default:
- Emsg1(M_ERROR, 0, _("Unsupported digest type: %d\n"), type);
+ Jmsg1(jcr, M_ERROR, 0, _("Unsupported digest type: %d\n"), type);
goto err;
}
/* Initialize the backing OpenSSL context */
- if (EVP_DigestInit_ex(&digest->ctx, md, NULL) == 0) {
+ if (EVP_DigestInit_ex(digest->ctx, md, NULL) == 0) {
goto err;
}
err:
/* This should not happen, but never say never ... */
- openssl_post_errors(M_ERROR, _("OpenSSL digest initialization failed"));
+ Dmsg0(150, "Digest init failed.\n");
+ openssl_post_errors(jcr, M_ERROR, _("OpenSSL digest initialization failed"));
crypto_digest_free(digest);
return NULL;
}
* Returns: true on success
* false on failure
*/
-bool crypto_digest_update (DIGEST *digest, const void *data, size_t length) {
- if (EVP_DigestUpdate(&digest->ctx, data, length) == 0) {
- return true;
- } else {
+bool crypto_digest_update(DIGEST *digest, const uint8_t *data, uint32_t length)
+{
+ if (EVP_DigestUpdate(digest->ctx, data, length) == 0) {
+ Dmsg0(150, "digest update failed\n");
+ openssl_post_errors(digest->jcr, M_ERROR, _("OpenSSL digest update failed"));
return false;
+ } else {
+ return true;
}
}
* Returns: true on success
* false on failure
*/
-bool crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length) {
- if (!EVP_DigestFinal(&digest->ctx, (unsigned char *) dest, (unsigned int *) length)) {
+bool crypto_digest_finalize(DIGEST *digest, uint8_t *dest, uint32_t *length)
+{
+ if (!EVP_DigestFinal(digest->ctx, dest, (unsigned int *)length)) {
+ Dmsg0(150, "digest finalize failed\n");
+ openssl_post_errors(digest->jcr, M_ERROR, _("OpenSSL digest finalize failed"));
return false;
} else {
return true;
/*
* Free memory associated with a digest object.
*/
-void crypto_digest_free (DIGEST *digest)
+void crypto_digest_free(DIGEST *digest)
{
- EVP_MD_CTX_cleanup(&digest->ctx);
- free (digest);
+ EVP_MD_CTX_free(digest->ctx);
+ free(digest);
}
/*
* Returns: A pointer to a SIGNATURE object on success.
* NULL on failure.
*/
-SIGNATURE *crypto_sign_new (void)
+SIGNATURE *crypto_sign_new(JCR *jcr)
{
SIGNATURE *sig;
- sig = (SIGNATURE *) malloc(sizeof(SIGNATURE));
+ sig = (SIGNATURE *)malloc(sizeof(SIGNATURE));
if (!sig) {
return NULL;
}
sig->sigData = SignatureData_new();
+ sig->jcr = jcr;
+ Dmsg1(150, "crypto_sign_new jcr=%p\n", jcr);
if (!sig->sigData) {
/* Allocation failed in OpenSSL */
/*
* For a given public key, find the associated SignatureInfo record
- * and create a digest context for signature validation
+ * and create a digest context for signature validation
+ *
* Returns: CRYPTO_ERROR_NONE on success, with the newly allocated DIGEST in digest.
* A crypto_error_t value on failure.
*/
-crypto_error_t crypto_sign_get_digest(SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST **digest)
+crypto_error_t crypto_sign_get_digest(SIGNATURE *sig, X509_KEYPAIR *keypair,
+ crypto_digest_t &type, DIGEST **digest)
{
STACK_OF(SignerInfo) *signers;
SignerInfo *si;
for (i = 0; i < sk_SignerInfo_num(signers); i++) {
si = sk_SignerInfo_value(signers, i);
- if (M_ASN1_OCTET_STRING_cmp(keypair->keyid, si->subjectKeyIdentifier) == 0) {
+ if (ASN1_OCTET_STRING_cmp(keypair->keyid, si->subjectKeyIdentifier) == 0) {
/* Get the digest algorithm and allocate a digest context */
+ Dmsg1(150, "crypto_sign_get_digest jcr=%p\n", sig->jcr);
switch (OBJ_obj2nid(si->digestAlgorithm)) {
case NID_md5:
- *digest = crypto_digest_new(CRYPTO_DIGEST_MD5);
+ Dmsg0(100, "sign digest algorithm is MD5\n");
+ type = CRYPTO_DIGEST_MD5;
+ *digest = crypto_digest_new(sig->jcr, CRYPTO_DIGEST_MD5);
break;
case NID_sha1:
- *digest = crypto_digest_new(CRYPTO_DIGEST_SHA1);
+ Dmsg0(100, "sign digest algorithm is SHA1\n");
+ type = CRYPTO_DIGEST_SHA1;
+ *digest = crypto_digest_new(sig->jcr, CRYPTO_DIGEST_SHA1);
break;
#ifdef HAVE_SHA2
case NID_sha256:
- *digest = crypto_digest_new(CRYPTO_DIGEST_SHA256);
+ Dmsg0(100, "sign digest algorithm is SHA256\n");
+ type = CRYPTO_DIGEST_SHA256;
+ *digest = crypto_digest_new(sig->jcr, CRYPTO_DIGEST_SHA256);
break;
case NID_sha512:
- *digest = crypto_digest_new(CRYPTO_DIGEST_SHA512);
+ Dmsg0(100, "sign digest algorithm is SHA512\n");
+ type = CRYPTO_DIGEST_SHA512;
+ *digest = crypto_digest_new(sig->jcr, CRYPTO_DIGEST_SHA512);
break;
#endif
default:
+ type = CRYPTO_DIGEST_NONE;
*digest = NULL;
return CRYPTO_ERROR_INVALID_DIGEST;
}
/* Shouldn't happen */
if (*digest == NULL) {
+ openssl_post_errors(sig->jcr, M_ERROR, _("OpenSSL digest_new failed"));
return CRYPTO_ERROR_INVALID_DIGEST;
} else {
return CRYPTO_ERROR_NONE;
}
+ } else {
+ openssl_post_errors(sig->jcr, M_ERROR, _("OpenSSL sign get digest failed"));
}
+
}
return CRYPTO_ERROR_NOSIGNER;
SignerInfo *si;
int ok, i;
unsigned int sigLen;
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800FL)
const unsigned char *sigData;
-#else
- unsigned char *sigData;
-#endif
signers = sig->sigData->signerInfo;
/* Find the signer */
for (i = 0; i < sk_SignerInfo_num(signers); i++) {
si = sk_SignerInfo_value(signers, i);
- if (M_ASN1_OCTET_STRING_cmp(keypair->keyid, si->subjectKeyIdentifier) == 0) {
+ if (ASN1_OCTET_STRING_cmp(keypair->keyid, si->subjectKeyIdentifier) == 0) {
/* Extract the signature data */
- sigLen = M_ASN1_STRING_length(si->signature);
- sigData = M_ASN1_STRING_data(si->signature);
+ sigLen = ASN1_STRING_length(si->signature);
+ sigData = ASN1_STRING_get0_data(si->signature);
- ok = EVP_VerifyFinal(&digest->ctx, sigData, sigLen, keypair->pubkey);
+ ok = EVP_VerifyFinal(digest->ctx, sigData, sigLen, keypair->pubkey);
if (ok >= 1) {
return CRYPTO_ERROR_NONE;
} else if (ok == 0) {
+ openssl_post_errors(sig->jcr, M_ERROR, _("OpenSSL digest Verify final failed"));
return CRYPTO_ERROR_BAD_SIGNATURE;
} else if (ok < 0) {
/* Shouldn't happen */
- openssl_post_errors(M_ERROR, _("OpenSSL error occured"));
+ openssl_post_errors(sig->jcr, M_ERROR, _("OpenSSL digest Verify final failed"));
return CRYPTO_ERROR_INTERNAL;
}
}
}
-
+ Jmsg(sig->jcr, M_ERROR, 0, _("No signers found for crypto verify.\n"));
/* Signer wasn't found. */
return CRYPTO_ERROR_NOSIGNER;
}
}
/* Drop the string allocated by OpenSSL, and add our subjectKeyIdentifier */
- M_ASN1_OCTET_STRING_free(si->subjectKeyIdentifier);
- si->subjectKeyIdentifier = M_ASN1_OCTET_STRING_dup(keypair->keyid);
+ ASN1_OCTET_STRING_free(si->subjectKeyIdentifier);
+ si->subjectKeyIdentifier = ASN1_OCTET_STRING_dup(keypair->keyid);
/* Set our signature algorithm. We currently require RSA */
- assert(EVP_PKEY_type(keypair->pubkey->type) == EVP_PKEY_RSA);
+ assert(EVP_PKEY_base_id(keypair->pubkey) == EVP_PKEY_RSA);
/* This is slightly evil. Reach into the MD structure and grab the key type */
- si->signatureAlgorithm = OBJ_nid2obj(digest->ctx.digest->pkey_type);
+ si->signatureAlgorithm = OBJ_nid2obj(EVP_MD_pkey_type(EVP_MD_CTX_md(digest->ctx)));
/* Finalize/Sign our Digest */
len = EVP_PKEY_size(keypair->privkey);
buf = (unsigned char *) malloc(len);
- if (!EVP_SignFinal(&digest->ctx, buf, &len, keypair->privkey)) {
+ if (!EVP_SignFinal(digest->ctx, buf, &len, keypair->privkey)) {
openssl_post_errors(M_ERROR, _("Signature creation failed"));
goto err;
}
/* Add the signature to the SignerInfo structure */
- if (!M_ASN1_OCTET_STRING_set(si->signature, buf, len)) {
+ if (!ASN1_OCTET_STRING_set(si->signature, buf, len)) {
/* Allocation failed in OpenSSL */
goto err;
}
* Returns: true on success, stores the encoded data in dest, and the size in length.
* false on failure.
*/
-int crypto_sign_encode(SIGNATURE *sig, void *dest, size_t *length)
+int crypto_sign_encode(SIGNATURE *sig, uint8_t *dest, uint32_t *length)
{
if (*length == 0) {
*length = i2d_SignatureData(sig->sigData, NULL);
return true;
}
- *length = i2d_SignatureData(sig->sigData, (unsigned char **) &dest);
+ *length = i2d_SignatureData(sig->sigData, (unsigned char **)&dest);
return true;
}
*/
-SIGNATURE *crypto_sign_decode(const void *sigData, size_t length)
+SIGNATURE *crypto_sign_decode(JCR *jcr, const uint8_t *sigData, uint32_t length)
{
SIGNATURE *sig;
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800FL)
const unsigned char *p = (const unsigned char *) sigData;
-#else
- unsigned char *p = (unsigned char *) sigData;
-#endif
- sig = (SIGNATURE *) malloc(sizeof(SIGNATURE));
+ sig = (SIGNATURE *)malloc(sizeof(SIGNATURE));
if (!sig) {
return NULL;
}
+ sig->jcr = jcr;
/* d2i_SignatureData modifies the supplied pointer */
sig->sigData = d2i_SignatureData(NULL, &p, length);
if (!sig->sigData) {
/* Allocation / Decoding failed in OpenSSL */
- openssl_post_errors(M_ERROR, _("Signature decoding failed"));
+ openssl_post_errors(jcr, M_ERROR, _("Signature decoding failed"));
+ free(sig);
return NULL;
}
* Create a new encryption session.
* Returns: A pointer to a CRYPTO_SESSION object on success.
* NULL on failure.
+ *
+ * Note! Bacula malloc() fails if out of memory.
*/
CRYPTO_SESSION *crypto_session_new (crypto_cipher_t cipher, alist *pubkeys)
{
int iv_len;
/* Allocate our session description structures */
- cs = (CRYPTO_SESSION *) malloc(sizeof(CRYPTO_SESSION));
- if (!cs) {
- return NULL;
- }
+ cs = (CRYPTO_SESSION *)malloc(sizeof(CRYPTO_SESSION));
/* Initialize required fields */
cs->session_key = NULL;
cs->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_128_cbc);
ec = EVP_aes_128_cbc();
break;
+#ifndef HAVE_OPENSSL_EXPORT_LIBRARY
case CRYPTO_CIPHER_AES_192_CBC:
/* AES 192 bit CBC */
cs->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_192_cbc);
cs->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_aes_256_cbc);
ec = EVP_aes_256_cbc();
break;
+#endif
case CRYPTO_CIPHER_BLOWFISH_CBC:
/* Blowfish CBC */
cs->cryptoData->contentEncryptionAlgorithm = OBJ_nid2obj(NID_bf_cbc);
ec = EVP_bf_cbc();
break;
default:
- Emsg0(M_ERROR, 0, _("Unsupported cipher type specified\n"));
+ Jmsg0(NULL, M_ERROR, 0, _("Unsupported cipher type specified\n"));
crypto_session_free(cs);
return NULL;
}
/* Generate an IV if possible */
if ((iv_len = EVP_CIPHER_iv_length(ec))) {
- iv = (unsigned char *) malloc(iv_len);
- if (!iv) {
- /* Malloc failure */
- crypto_session_free(cs);
- return NULL;
- }
+ iv = (unsigned char *)malloc(iv_len);
/* Generate random IV */
if (RAND_bytes(iv, iv_len) <= 0) {
}
/* Store it in our ASN.1 structure */
- if (!M_ASN1_OCTET_STRING_set(cs->cryptoData->iv, iv, iv_len)) {
+ if (!ASN1_OCTET_STRING_set(cs->cryptoData->iv, iv, iv_len)) {
/* Allocation failed in OpenSSL */
crypto_session_free(cs);
free(iv);
ASN1_INTEGER_set(ri->version, BACULA_ASN1_VERSION);
/* Drop the string allocated by OpenSSL, and add our subjectKeyIdentifier */
- M_ASN1_OCTET_STRING_free(ri->subjectKeyIdentifier);
- ri->subjectKeyIdentifier = M_ASN1_OCTET_STRING_dup(keypair->keyid);
+ ASN1_OCTET_STRING_free(ri->subjectKeyIdentifier);
+ ri->subjectKeyIdentifier = ASN1_OCTET_STRING_dup(keypair->keyid);
/* Set our key encryption algorithm. We currently require RSA */
- assert(keypair->pubkey && EVP_PKEY_type(keypair->pubkey->type) == EVP_PKEY_RSA);
+ assert(keypair->pubkey && EVP_PKEY_base_id(keypair->pubkey) == EVP_PKEY_RSA);
ri->keyEncryptionAlgorithm = OBJ_nid2obj(NID_rsaEncryption);
/* Encrypt the session key */
- ekey = (unsigned char *) malloc(EVP_PKEY_size(keypair->pubkey));
- if (!ekey) {
- RecipientInfo_free(ri);
- crypto_session_free(cs);
- return NULL;
- }
+ ekey = (unsigned char *)malloc(EVP_PKEY_size(keypair->pubkey));
if ((ekey_len = EVP_PKEY_encrypt(ekey, cs->session_key, cs->session_key_len, keypair->pubkey)) <= 0) {
/* OpenSSL failure */
}
/* Store it in our ASN.1 structure */
- if (!M_ASN1_OCTET_STRING_set(ri->encryptedKey, ekey, ekey_len)) {
+ if (!ASN1_OCTET_STRING_set(ri->encryptedKey, ekey, ekey_len)) {
/* Allocation failed in OpenSSL */
RecipientInfo_free(ri);
crypto_session_free(cs);
* 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)
+bool crypto_session_encode(CRYPTO_SESSION *cs, uint8_t *dest, uint32_t *length)
{
if (*length == 0) {
*length = i2d_CryptoData(cs->cryptoData, NULL);
return true;
}
- *length = i2d_CryptoData(cs->cryptoData, (unsigned char **) &dest);
+ *length = i2d_CryptoData(cs->cryptoData, &dest);
return true;
}
* Returns: CRYPTO_ERROR_NONE and a pointer to a newly allocated CRYPTO_SESSION structure in *session on success.
* A crypto_error_t value on failure.
*/
-crypto_error_t crypto_session_decode(const void *data, size_t length, alist *keypairs, CRYPTO_SESSION **session)
+crypto_error_t crypto_session_decode(const uint8_t *data, uint32_t length, alist *keypairs, CRYPTO_SESSION **session)
{
CRYPTO_SESSION *cs;
X509_KEYPAIR *keypair;
STACK_OF(RecipientInfo) *recipients;
crypto_error_t retval = CRYPTO_ERROR_NONE;
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800FL)
- const unsigned char *p = (const unsigned char *) data;
-#else
- unsigned char *p = (unsigned char *) data;
-#endif
+ const unsigned char *p = (const unsigned char *)data;
- cs = (CRYPTO_SESSION *) malloc(sizeof(CRYPTO_SESSION));
- if (!cs) {
- return CRYPTO_ERROR_INTERNAL;
+ /* bacula-fd.conf doesn't contains any key */
+ if (!keypairs) {
+ return CRYPTO_ERROR_NORECIPIENT;
}
+ cs = (CRYPTO_SESSION *)malloc(sizeof(CRYPTO_SESSION));
+
+ /* Initialize required fields */
+ cs->session_key = NULL;
+
/* d2i_CryptoData modifies the supplied pointer */
cs->cryptoData = d2i_CryptoData(NULL, &p, length);
ri = sk_RecipientInfo_value(recipients, i);
/* Match against the subjectKeyIdentifier */
- if (M_ASN1_OCTET_STRING_cmp(keypair->keyid, ri->subjectKeyIdentifier) == 0) {
+ if (ASN1_OCTET_STRING_cmp(keypair->keyid, ri->subjectKeyIdentifier) == 0) {
/* Match found, extract symmetric encryption session data */
-
+
/* RSA is required. */
- assert(EVP_PKEY_type(keypair->privkey->type) == EVP_PKEY_RSA);
+ assert(EVP_PKEY_base_id(keypair->privkey) == EVP_PKEY_RSA);
/* If we recieve a RecipientInfo structure that does not use
* RSA, return an error */
/* Decrypt the session key */
/* Allocate sufficient space for the largest possible decrypted data */
- cs->session_key = (unsigned char *) malloc(EVP_PKEY_size(keypair->privkey));
- cs->session_key_len = EVP_PKEY_decrypt(cs->session_key, M_ASN1_STRING_data(ri->encryptedKey),
- M_ASN1_STRING_length(ri->encryptedKey), keypair->privkey);
+ cs->session_key = (unsigned char *)malloc(EVP_PKEY_size(keypair->privkey));
+ cs->session_key_len = EVP_PKEY_decrypt(cs->session_key, ASN1_STRING_get0_data(ri->encryptedKey),
+ ASN1_STRING_length(ri->encryptedKey), keypair->privkey);
if (cs->session_key_len <= 0) {
openssl_post_errors(M_ERROR, _("Failure decrypting the session key"));
/*
* Free memory associated with a crypto session object.
*/
-void crypto_session_free (CRYPTO_SESSION *cs)
+void crypto_session_free(CRYPTO_SESSION *cs)
{
if (cs->cryptoData) {
CryptoData_free(cs->cryptoData);
}
/*
- * Perform global initialization of OpenSSL
- * This function is not thread safe.
- * Returns: 0 on success
- * errno on failure
+ * Create a new crypto cipher context with the specified session object
+ * Returns: A pointer to a CIPHER_CONTEXT object on success. The cipher block size is returned in blocksize.
+ * NULL on failure.
*/
-int init_crypto (void)
+CIPHER_CONTEXT *crypto_cipher_new(CRYPTO_SESSION *cs, bool encrypt, uint32_t *blocksize)
{
- int stat;
+ CIPHER_CONTEXT *cipher_ctx;
+ const EVP_CIPHER *ec;
+
+ cipher_ctx = (CIPHER_CONTEXT *)malloc(sizeof(CIPHER_CONTEXT));
+ if (!cipher_ctx)
+ return NULL;
+
+ cipher_ctx->ctx = EVP_CIPHER_CTX_new();
+ if (!cipher_ctx->ctx)
+ goto err;
- if ((stat = openssl_init_threads()) != 0) {
- Emsg1(M_ABORT, 0, _("Unable to init OpenSSL threading: ERR=%s\n"), strerror(stat));
+ /*
+ * Acquire a cipher instance for the given ASN.1 cipher NID
+ */
+ if ((ec = EVP_get_cipherbyobj(cs->cryptoData->contentEncryptionAlgorithm)) == NULL) {
+ Jmsg1(NULL, M_ERROR, 0,
+ _("Unsupported contentEncryptionAlgorithm: %d\n"), OBJ_obj2nid(cs->cryptoData->contentEncryptionAlgorithm));
+ free(cipher_ctx);
+ return NULL;
}
- /* Load libssl and libcrypto human-readable error strings */
- SSL_load_error_strings();
+ /* Initialize the OpenSSL cipher context */
+ EVP_CIPHER_CTX_reset(cipher_ctx->ctx);
+ if (encrypt) {
+ /* Initialize for encryption */
+ if (!EVP_CipherInit_ex(cipher_ctx->ctx, ec, NULL, NULL, NULL, 1)) {
+ openssl_post_errors(M_ERROR, _("OpenSSL cipher context initialization failed"));
+ goto err;
+ }
+ } else {
+ /* Initialize for decryption */
+ if (!EVP_CipherInit_ex(cipher_ctx->ctx, ec, NULL, NULL, NULL, 0)) {
+ openssl_post_errors(M_ERROR, _("OpenSSL cipher context initialization failed"));
+ goto err;
+ }
+ }
+
+ /* Set the key size */
+ if (!EVP_CIPHER_CTX_set_key_length(cipher_ctx->ctx, cs->session_key_len)) {
+ openssl_post_errors(M_ERROR, _("Encryption session provided an invalid symmetric key"));
+ goto err;
+ }
- /* Register OpenSSL ciphers */
- SSL_library_init();
+ /* Validate the IV length */
+ if (EVP_CIPHER_iv_length(ec) != ASN1_STRING_length(cs->cryptoData->iv)) {
+ openssl_post_errors(M_ERROR, _("Encryption session provided an invalid IV"));
+ goto err;
+ }
- if (!openssl_seed_prng()) {
- Emsg0(M_ERROR_TERM, 0, _("Failed to seed OpenSSL PRNG\n"));
+ /* Add the key and IV to the cipher context */
+ if (!EVP_CipherInit_ex(cipher_ctx->ctx, NULL, NULL, cs->session_key, ASN1_STRING_get0_data(cs->cryptoData->iv), -1)) {
+ openssl_post_errors(M_ERROR, _("OpenSSL cipher context key/IV initialization failed"));
+ goto err;
}
- crypto_initialized = true;
+ *blocksize = EVP_CIPHER_CTX_block_size(cipher_ctx->ctx);
+ return cipher_ctx;
- return stat;
+err:
+ crypto_cipher_free(cipher_ctx);
+ return NULL;
}
+
/*
- * Perform global cleanup of OpenSSL
- * All cryptographic operations must be completed before calling this function.
- * This function is not thread safe.
- * Returns: 0 on success
- * errno on failure
+ * Encrypt/Decrypt length bytes of data using the provided cipher context
+ * Returns: true on success, number of bytes output in written
+ * false on failure
*/
-int cleanup_crypto (void)
+bool crypto_cipher_update(CIPHER_CONTEXT *cipher_ctx, const uint8_t *data, uint32_t length, const uint8_t *dest, uint32_t *written)
{
- /*
- * Ensure that we've actually been initialized; Doing this here decreases the
- * complexity of client's termination/cleanup code.
- */
- if (!crypto_initialized) {
- return 0;
+ if (!EVP_CipherUpdate(cipher_ctx->ctx, (unsigned char *)dest, (int *)written, (const unsigned char *)data, length)) {
+ /* This really shouldn't fail */
+ return false;
+ } else {
+ return true;
}
+}
- if (!openssl_save_prng()) {
- Emsg0(M_ERROR, 0, _("Failed to save OpenSSL PRNG\n"));
+/*
+ * Finalize the cipher context, writing any remaining data and necessary padding
+ * to dest, and the size in written.
+ * The result size will either be one block of data or zero.
+ *
+ * Returns: true on success
+ * false on failure
+ */
+bool crypto_cipher_finalize (CIPHER_CONTEXT *cipher_ctx, uint8_t *dest, uint32_t *written)
+{
+ if (!EVP_CipherFinal_ex(cipher_ctx->ctx, (unsigned char *)dest, (int *) written)) {
+ /* This really shouldn't fail */
+ return false;
+ } else {
+ return true;
}
+}
- openssl_cleanup_threads();
-
- /* Free libssl and libcrypto error strings */
- ERR_free_strings();
-
- /* Free memory used by PRNG */
- RAND_cleanup();
-
- crypto_initialized = false;
- return 0;
+/*
+ * Free memory associated with a cipher context.
+ */
+void crypto_cipher_free (CIPHER_CONTEXT *cipher_ctx)
+{
+ EVP_CIPHER_CTX_free(cipher_ctx->ctx);
+ free (cipher_ctx);
}
-
#else /* HAVE_OPENSSL */
# error No encryption library available
#endif /* HAVE_OPENSSL */
/* Message Digest Structure */
struct Digest {
crypto_digest_t type;
+ JCR *jcr;
union {
SHA1Context sha1;
MD5Context md5;
/* Dummy Signature Structure */
struct Signature {
+ JCR *jcr;
};
-DIGEST *crypto_digest_new (crypto_digest_t type)
+DIGEST *crypto_digest_new(JCR *jcr, crypto_digest_t type)
{
DIGEST *digest;
- digest = (DIGEST *) malloc(sizeof(DIGEST));
+ digest = (DIGEST *)malloc(sizeof(DIGEST));
digest->type = type;
+ digest->jcr = jcr;
switch (type) {
case CRYPTO_DIGEST_MD5:
SHA1Init(&digest->sha1);
break;
default:
- Emsg0(M_ERROR, 0, _("Unsupported digest type specified\n"));
+ Jmsg1(jcr, M_ERROR, 0, _("Unsupported digest type=%d specified\n"), type);
free(digest);
return NULL;
}
return (digest);
}
-bool crypto_digest_update (DIGEST *digest, const void *data, size_t length) {
+bool crypto_digest_update(DIGEST *digest, const uint8_t *data, uint32_t length)
+{
switch (digest->type) {
case CRYPTO_DIGEST_MD5:
/* Doesn't return anything ... */
if ((ret = SHA1Update(&digest->sha1, (const u_int8_t *) data, length)) == shaSuccess) {
return true;
} else {
- Emsg1(M_ERROR, 0, _("SHA1Update() returned an error: %d\n"), ret);
+ Jmsg1(NULL, M_ERROR, 0, _("SHA1Update() returned an error: %d\n"), ret);
return false;
}
break;
}
}
-bool crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length) {
-
+bool crypto_digest_finalize(DIGEST *digest, uint8_t *dest, uint32_t *length)
+{
switch (digest->type) {
case CRYPTO_DIGEST_MD5:
/* Guard against programmer error by either the API client or
assert(*length >= CRYPTO_DIGEST_MD5_SIZE);
*length = CRYPTO_DIGEST_MD5_SIZE;
/* Doesn't return anything ... */
- MD5Final((unsigned char *) dest, &digest->md5);
+ MD5Final((unsigned char *)dest, &digest->md5);
return true;
case CRYPTO_DIGEST_SHA1:
/* Guard against programmer error by either the API client or
return false;
}
-void crypto_digest_free (DIGEST *digest)
+void crypto_digest_free(DIGEST *digest)
{
- free (digest);
+ free(digest);
}
-/* Dummy routines */
-int init_crypto (void) { return 0; }
-int cleanup_crypto (void) { return 0; }
+SIGNATURE *crypto_sign_new(JCR *jcr) { return NULL; }
-SIGNATURE *crypto_sign_new (void) { return NULL; }
+crypto_error_t crypto_sign_get_digest (SIGNATURE *sig, X509_KEYPAIR *keypair,
+ crypto_digest_t &type, DIGEST **digest)
+ { return CRYPTO_ERROR_INTERNAL; }
-crypto_error_t crypto_sign_get_digest (SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST **digest) { return CRYPTO_ERROR_INTERNAL; }
crypto_error_t crypto_sign_verify (SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST *digest) { return CRYPTO_ERROR_INTERNAL; }
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; }
+int crypto_sign_encode (SIGNATURE *sig, uint8_t *dest, uint32_t *length) { return false; }
-SIGNATURE *crypto_sign_decode (const void *sigData, size_t length) { return NULL; }
+SIGNATURE *crypto_sign_decode (JCR *jcr, const uint8_t *sigData, uint32_t length) { return NULL; }
void crypto_sign_free (SIGNATURE *sig) { }
-X509_KEYPAIR *crypto_keypair_new (void) { return NULL; }
+X509_KEYPAIR *crypto_keypair_new(void) { return NULL; }
X509_KEYPAIR *crypto_keypair_dup (X509_KEYPAIR *keypair) { return NULL; }
int crypto_keypair_load_cert (X509_KEYPAIR *keypair, const char *file) { return false; }
bool crypto_keypair_has_key (const char *file) { return false; }
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_error_t crypto_session_decode(const void *data, size_t length, alist *keypairs, CRYPTO_SESSION **session) { return CRYPTO_ERROR_INTERNAL; }
+bool crypto_session_encode (CRYPTO_SESSION *cs, uint8_t *dest, uint32_t *length) { return false; }
+crypto_error_t crypto_session_decode(const uint8_t *data, uint32_t length, alist *keypairs, CRYPTO_SESSION **session) { return CRYPTO_ERROR_INTERNAL; }
+
+CIPHER_CONTEXT *crypto_cipher_new (CRYPTO_SESSION *cs, bool encrypt, uint32_t *blocksize) { return NULL; }
+bool crypto_cipher_update (CIPHER_CONTEXT *cipher_ctx, const uint8_t *data, uint32_t length, const uint8_t *dest, uint32_t *written) { return false; }
+bool crypto_cipher_finalize (CIPHER_CONTEXT *cipher_ctx, uint8_t *dest, uint32_t *written) { return false; }
+void crypto_cipher_free (CIPHER_CONTEXT *cipher_ctx) { }
#endif /* HAVE_CRYPTO */
* Returns the ASCII name of the digest type.
* Returns: ASCII name of digest type.
*/
-const char *crypto_digest_name (DIGEST *digest) {
+const char *crypto_digest_name(DIGEST *digest)
+{
switch (digest->type) {
case CRYPTO_DIGEST_MD5:
return "MD5";
* Given a stream type, returns the associated
* crypto_digest_t value.
*/
-crypto_digest_t crypto_digest_stream_type (int stream) {
+crypto_digest_t crypto_digest_stream_type(int stream)
+{
switch (stream) {
case STREAM_MD5_DIGEST:
return CRYPTO_DIGEST_MD5;
const char *crypto_strerror(crypto_error_t error) {
switch (error) {
case CRYPTO_ERROR_NONE:
- return "No error";
+ return _("No error");
case CRYPTO_ERROR_NOSIGNER:
- return "Signer not found";
+ return _("Signer not found");
case CRYPTO_ERROR_NORECIPIENT:
- return "Recipient not found";
+ return _("Recipient not found");
case CRYPTO_ERROR_INVALID_DIGEST:
- return "Unsupported digest algorithm";
+ return _("Unsupported digest algorithm");
case CRYPTO_ERROR_INVALID_CRYPTO:
- return "Unsupported encryption algorithm";
+ return _("Unsupported encryption algorithm");
case CRYPTO_ERROR_BAD_SIGNATURE:
- return "Signature is invalid";
+ return _("Signature is invalid");
case CRYPTO_ERROR_DECRYPTION:
- return "Decryption error";
+ return _("Decryption error");
case CRYPTO_ERROR_INTERNAL:
/* This shouldn't happen */
- return "Internal error";
+ return _("Internal error");
default:
- return "Unknown error";
+ return _("Unknown error");
}
}