From 3f07af118614ba2ed72fa61e20fd8ce2d9050732 Mon Sep 17 00:00:00 2001 From: Landon Fuller Date: Mon, 12 Dec 2005 03:27:12 +0000 Subject: [PATCH] Add PKE recipient structure creation w/ symmetric key & IV generation. Fix NULL dereference when no additional PKI Trusted Signers are specified. git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@2661 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/src/filed/backup.c | 12 ++ bacula/src/filed/filed.c | 62 +++++++--- bacula/src/filed/filed_conf.c | 15 +++ bacula/src/filed/filed_conf.h | 2 + bacula/src/filed/job.c | 1 + bacula/src/jcr.h | 2 + bacula/src/lib/crypto.c | 224 +++++++++++++++++++++++++++++++++- bacula/src/lib/crypto.h | 12 ++ bacula/src/lib/protos.h | 45 +++---- 9 files changed, 338 insertions(+), 37 deletions(-) diff --git a/bacula/src/filed/backup.c b/bacula/src/filed/backup.c index 0431a0fc0a..fe9d2e7d0d 100644 --- a/bacula/src/filed/backup.c +++ b/bacula/src/filed/backup.c @@ -47,6 +47,8 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) { BSOCK *sd; bool ok = true; + // TODO landonf: Allow user to specify encryption algorithm + crypto_cipher_t cipher = CRYPTO_CIPHER_AES_128_CBC; sd = jcr->store_bsock; @@ -79,6 +81,11 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) jcr->compress_buf_size = jcr->buf_size + ((jcr->buf_size+999) / 1000) + 30; jcr->compress_buf = get_memory(jcr->compress_buf_size); + if (jcr->pki_encrypt) { + /* Create per-job session encryption context */ + jcr->pki_recipients = crypto_recipients_new(cipher, jcr->pki_readers); + } + Dmsg1(300, "set_find_options ff=%p\n", jcr->ff); set_find_options((FF_PKT *)jcr->ff, jcr->incremental, jcr->mtime); Dmsg0(300, "start find files\n"); @@ -108,6 +115,11 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) free_pool_memory(jcr->compress_buf); jcr->compress_buf = NULL; } + + if (jcr->pki_recipients) { + crypto_recipients_free(jcr->pki_recipients); + } + Dmsg1(100, "end blast_data ok=%d\n", ok); return ok; } diff --git a/bacula/src/filed/filed.c b/bacula/src/filed/filed.c index fe6b33c37d..7bb2a3a2f8 100644 --- a/bacula/src/filed/filed.c +++ b/bacula/src/filed/filed.c @@ -372,27 +372,59 @@ static int check_resources() } /* - * Trusted Signers. We're always trusted. me->pki_keypair - * will be deallocated when me->pki_signers is deallocated. + * Trusted Signers. We're always trusted. */ me->pki_signers = New(alist(10, not_owned_by_alist)); - me->pki_signers->append(me->pki_keypair); + me->pki_signers->append(crypto_keypair_dup(me->pki_keypair)); /* If additional trusted keys have been specified, load them up */ - foreach_alist(filepath, me->pki_trustedkeys) { - X509_KEYPAIR *keypair; + if (me->pki_trustedkeys) { + foreach_alist(filepath, me->pki_trustedkeys) { + X509_KEYPAIR *keypair; - keypair = crypto_keypair_new(); - if (!keypair) { - Emsg0(M_FATAL, 0, _("Failed to allocate a new keypair object.\n")); - OK = false; - } else { - if (crypto_keypair_load_cert(keypair, filepath)) { - me->pki_signers->append(keypair); - } else { - Emsg3(M_FATAL, 0, _("Failed to load trusted signer certificate" - " from file %s for File daemon \"%s\" in %s.\n"), filepath, me->hdr.name, configfile); + keypair = crypto_keypair_new(); + if (!keypair) { + Emsg0(M_FATAL, 0, _("Failed to allocate a new keypair object.\n")); OK = false; + } else { + if (crypto_keypair_load_cert(keypair, filepath)) { + me->pki_signers->append(keypair); + } else { + Emsg3(M_FATAL, 0, _("Failed to load trusted signer certificate" + " from file %s for File daemon \"%s\" in %s.\n"), filepath, me->hdr.name, configfile); + OK = false; + } + } + } + } + + if (me->pki_encrypt) { + /* + * Trusted readers. We're always trusted. + * The symmetric session key will be encrypted for each of these readers. + */ + me->pki_readers = New(alist(10, not_owned_by_alist)); + me->pki_readers->append(crypto_keypair_dup(me->pki_keypair)); + + + /* If additional keys have been specified, load them up */ + if (me->pki_masterkeys) { + foreach_alist(filepath, me->pki_masterkeys) { + X509_KEYPAIR *keypair; + + keypair = crypto_keypair_new(); + if (!keypair) { + Emsg0(M_FATAL, 0, _("Failed to allocate a new keypair object.\n")); + OK = false; + } else { + if (crypto_keypair_load_cert(keypair, filepath)) { + me->pki_signers->append(keypair); + } else { + Emsg3(M_FATAL, 0, _("Failed to load master key certificate" + " from file %s for File daemon \"%s\" in %s.\n"), filepath, me->hdr.name, configfile); + OK = false; + } + } } } } diff --git a/bacula/src/filed/filed_conf.c b/bacula/src/filed/filed_conf.c index 242eab9ab8..442e6dd63c 100644 --- a/bacula/src/filed/filed_conf.c +++ b/bacula/src/filed/filed_conf.c @@ -93,6 +93,7 @@ static RES_ITEM cli_items[] = { {"pkiencryption", store_yesno, ITEM(res_client.pki_encrypt), 1, ITEM_DEFAULT, 0}, {"pkikeypair", store_dir, ITEM(res_client.pki_keypairfile), 0, 0, 0}, {"pkitrustedsigner", store_alist_str, ITEM(res_client.pki_trustedkeys), 0, 0, 0}, + {"pkimasterkey", store_alist_str, ITEM(res_client.pki_masterkeys), 0, 0, 0}, {"tlsenable", store_yesno, ITEM(res_client.tls_enable), 1, 0, 0}, {"tlsrequire", store_yesno, ITEM(res_client.tls_require), 1, 0, 0}, {"tlscacertificatefile", store_dir, ITEM(res_client.tls_ca_certfile), 0, 0, 0}, @@ -247,6 +248,9 @@ void free_resource(RES *sres, int type) if (res->res_client.pki_keypairfile) { free(res->res_client.pki_keypairfile); } + if (res->res_client.pki_keypair) { + crypto_keypair_free(res->res_client.pki_keypair); + } /* Also frees res_client.pki_keypair */ if (res->res_client.pki_trustedkeys) { delete res->res_client.pki_trustedkeys; @@ -258,6 +262,17 @@ void free_resource(RES *sres, int type) } delete res->res_client.pki_signers; } + if (res->res_client.pki_masterkeys) { + delete res->res_client.pki_masterkeys; + } + if (res->res_client.pki_readers) { + X509_KEYPAIR *keypair; + foreach_alist(keypair, res->res_client.pki_readers) { + crypto_keypair_free(keypair); + } + delete res->res_client.pki_signers; + } + if (res->res_client.tls_ctx) { free_tls_context(res->res_client.tls_ctx); } diff --git a/bacula/src/filed/filed_conf.h b/bacula/src/filed/filed_conf.h index 396fae14a7..5ba1737598 100644 --- a/bacula/src/filed/filed_conf.h +++ b/bacula/src/filed/filed_conf.h @@ -75,6 +75,7 @@ struct CLIENT { int pki_encrypt; /* Enable Data Encryption */ char *pki_keypairfile; /* PKI Key Pair File */ alist *pki_trustedkeys; /* PKI Trusted Public Keys */ + alist *pki_masterkeys; /* PKI Master Keys */ int tls_enable; /* Enable TLS */ int tls_require; /* Require TLS */ char *tls_ca_certfile; /* TLS CA Certificate File */ @@ -84,6 +85,7 @@ struct CLIENT { X509_KEYPAIR *pki_keypair; /* Shared PKI Public/Private Keypair */ alist *pki_signers; /* Shared PKI Trusted Signers */ + alist *pki_readers; /* Shared PKI Recipients */ TLS_CONTEXT *tls_ctx; /* Shared TLS Context */ }; diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index 7af7ce10b0..57dd05eca2 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -177,6 +177,7 @@ void *handle_client_request(void *dirp) jcr->pki_encrypt = me->pki_encrypt; jcr->pki_keypair = me->pki_keypair; jcr->pki_signers = me->pki_signers; + jcr->pki_readers = me->pki_readers; dir->jcr = jcr; enable_backup_privileges(NULL, 1 /* ignore_errors */); diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index 65d176357c..1907f633f9 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -234,6 +234,8 @@ public: DIGEST *digest; /* Last file's digest context */ 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 */ DIRRES* director; /* Director resource */ #endif /* FILE_DAEMON */ diff --git a/bacula/src/lib/crypto.c b/bacula/src/lib/crypto.c index 9860a4b1a9..327b1269b1 100644 --- a/bacula/src/lib/crypto.c +++ b/bacula/src/lib/crypto.c @@ -73,7 +73,8 @@ * * CryptoData ::= SEQUENCE { * version Version DEFAULT v0, - * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * iv InitializationVector, * recipientInfo RecipientInfo * } * @@ -104,6 +105,10 @@ * * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier * + * ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + * + * InitializationVector ::= OCTET STRING + * * SignatureValue ::= OCTET STRING * * EncryptedKey ::= OCTET STRING @@ -160,6 +165,7 @@ typedef struct { typedef struct { ASN1_INTEGER *version; ASN1_OBJECT *contentEncryptionAlgorithm; + ASN1_OCTET_STRING *iv; STACK_OF(RecipientInfo) *recipientInfo; } CryptoData; @@ -170,10 +176,12 @@ ASN1_SEQUENCE(SignatureData) = { ASN1_SEQUENCE(CryptoData) = { ASN1_SIMPLE(CryptoData, version, ASN1_INTEGER), + ASN1_SIMPLE(CryptoData, iv, ASN1_OCTET_STRING), ASN1_SET_OF(CryptoData, recipientInfo, RecipientInfo) } ASN1_SEQUENCE_END(CryptoData); IMPLEMENT_ASN1_FUNCTIONS(SignerInfo) +IMPLEMENT_ASN1_FUNCTIONS(RecipientInfo) IMPLEMENT_ASN1_FUNCTIONS(SignatureData) IMPLEMENT_ASN1_FUNCTIONS(CryptoData) IMPLEMENT_STACK_OF(SignerInfo) @@ -261,6 +269,14 @@ struct Signature { SignatureData *sigData; }; +/* Encryption Key Data */ +struct Crypto_Recipients { + CryptoData *cryptoData; /* ASN.1 Structure */ + EVP_CIPHER *openssl_cipher; /* OpenSSL Cipher Object */ + unsigned char session_key[EVP_MAX_KEY_LENGTH]; /* Private symmetric session key */ + size_t session_key_len; /* Symmetric session key length */ +}; + /* PEM Password Dispatch Context */ typedef struct PEM_CB_Context { CRYPTO_PEM_PASSWD_CB *pem_callback; @@ -344,6 +360,49 @@ X509_KEYPAIR *crypto_keypair_new (void) { return keypair; } +/* + * Create a copy of a keypair object. The underlying + * EVP objects are not duplicated, as no EVP_PKEY_dup() + * API is available. Instead, the reference count is + * incremented. + */ +X509_KEYPAIR *crypto_keypair_dup (X509_KEYPAIR *keypair) +{ + X509_KEYPAIR *newpair; + + newpair = crypto_keypair_new(); + + if (!newpair) { + /* Allocation failed */ + return NULL; + } + + /* Increment the public key ref count */ + if (keypair->pubkey) { + CRYPTO_add(&(keypair->pubkey->references), 1, CRYPTO_LOCK_EVP_PKEY); + newpair->pubkey = keypair->pubkey; + } + + /* Increment the private key ref count */ + if (keypair->privkey) { + CRYPTO_add(&(keypair->privkey->references), 1, CRYPTO_LOCK_EVP_PKEY); + 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; + } + } + + return newpair; +} + + /* * Load a public key from a PEM-encoded x509 certificate. * Returns: true on success @@ -815,6 +874,169 @@ void crypto_sign_free(SIGNATURE *sig) free (sig); } +/* + * Create a new encryption recipient. + * Returns: A pointer to a CRYPTO_RECIPIENTS object on success. + * NULL on failure. + */ +CRYPTO_RECIPIENTS *crypto_recipients_new (crypto_cipher_t cipher, alist *pubkeys) +{ + CRYPTO_RECIPIENTS *cr; + 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) { + return NULL; + } + + cr->cryptoData = CryptoData_new(); + + if (!cr->cryptoData) { + /* Allocation failed in OpenSSL */ + free(cr); + return NULL; + } + + /* Set the ASN.1 structure version number */ + ASN1_INTEGER_set(cr->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); + 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); + 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); + ec = EVP_aes_256_cbc(); + break; + case CRYPTO_CIPHER_BLOWFISH_CBC: + /* Blowfish CBC */ + cr->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); + 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) { + /* OpenSSL failure */ + crypto_recipients_free(cr); + 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_recipients_free(cr); + return NULL; + } + + /* Generate random IV */ + if (RAND_bytes(iv, iv_len) <= 0) { + /* OpenSSL failure */ + crypto_recipients_free(cr); + return NULL; + } + + /* Store it in our ASN.1 structure */ + if (!M_ASN1_OCTET_STRING_set(cr->cryptoData->iv, iv, iv_len)) { + /* Allocation failed in OpenSSL */ + crypto_recipients_free(cr); + return NULL; + } + } + + /* + * Create RecipientInfo structures for supplied + * public keys. + */ + foreach_alist(keypair, pubkeys) { + RecipientInfo *ri; + unsigned char *ekey; + int ekey_len; + + ri = RecipientInfo_new(); + if (!ri) { + /* Allocation failed in OpenSSL */ + crypto_recipients_free(cr); + return NULL; + } + + /* Set the ASN.1 structure version number */ + 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); + + /* Set our key encryption algorithm. We currently require RSA */ + assert(keypair->pubkey && EVP_PKEY_type(keypair->pubkey->type) == 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_recipients_free(cr); + return NULL; + } + + if ((ekey_len = EVP_PKEY_encrypt(ekey, cr->session_key, cr->session_key_len, keypair->pubkey)) <= 0) { + /* OpenSSL failure */ + RecipientInfo_free(ri); + crypto_recipients_free(cr); + free(ekey); + return NULL; + } + + /* Store it in our ASN.1 structure */ + if (!M_ASN1_OCTET_STRING_set(ri->encryptedKey, ekey, ekey_len)) { + /* Allocation failed in OpenSSL */ + RecipientInfo_free(ri); + crypto_recipients_free(cr); + free(ekey); + return NULL; + } + + /* Free the encrypted key buffer */ + free(ekey); + + /* Push the new RecipientInfo structure onto the stack */ + sk_RecipientInfo_push(cr->cryptoData->recipientInfo, ri); + } + + return cr; +} + +/* + * Free memory associated with a crypto recipient object. + */ +void crypto_recipients_free (CRYPTO_RECIPIENTS *cr) +{ + CryptoData_free(cr->cryptoData); + free(cr); +} + /* * Perform global initialization of OpenSSL * This function is not thread safe. diff --git a/bacula/src/lib/crypto.h b/bacula/src/lib/crypto.h index 1519cf426c..d9cafe794e 100644 --- a/bacula/src/lib/crypto.h +++ b/bacula/src/lib/crypto.h @@ -45,6 +45,9 @@ typedef struct Digest DIGEST; /* Opaque Message Signature Structure */ typedef struct Signature SIGNATURE; +/* Opaque PKI Symmetric Key Data Structure */ +typedef struct Crypto_Recipients CRYPTO_RECIPIENTS; + /* PEM Decryption Passphrase Callback */ typedef int (CRYPTO_PEM_PASSWD_CB) (char *buf, int size, const void *userdata); @@ -58,6 +61,15 @@ typedef enum { CRYPTO_DIGEST_SHA512 = 4 } crypto_digest_t; +/* Cipher Types */ +typedef enum { + /* These are not stored on disk */ + CRYPTO_CIPHER_AES_128_CBC, + CRYPTO_CIPHER_AES_192_CBC, + CRYPTO_CIPHER_AES_256_CBC, + CRYPTO_CIPHER_BLOWFISH_CBC +} crypto_cipher_t; + /* Crypto API Errors */ typedef enum { CRYPTO_ERROR_NONE = 0, /* No error */ diff --git a/bacula/src/lib/protos.h b/bacula/src/lib/protos.h index 130189729f..3d5601fe28 100644 --- a/bacula/src/lib/protos.h +++ b/bacula/src/lib/protos.h @@ -114,27 +114,30 @@ void hmac_md5(uint8_t* text, int text_len, uint8_t* key, uint32_t bcrc32(uint8_t *buf, int len); /* crypto.c */ -int init_crypto (void); -int cleanup_crypto (void); -DIGEST * crypto_digest_new (crypto_digest_t type); -bool crypto_digest_update (DIGEST *digest, const void *data, size_t length); -bool crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length); -void crypto_digest_free (DIGEST *digest); -SIGNATURE * crypto_sign_new (void); -crypto_error_t crypto_sign_get_digest (SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST **digest); -crypto_error_t crypto_sign_verify (SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST *digest); -int crypto_sign_add_signer (SIGNATURE *sig, DIGEST *digest, X509_KEYPAIR *keypair); -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); -X509_KEYPAIR * crypto_keypair_new (void); -int crypto_keypair_load_cert (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); -void crypto_keypair_free (X509_KEYPAIR *keypair); -int crypto_default_pem_callback (char *buf, int size, const void *userdata); -const char * crypto_digest_name (DIGEST *digest); -crypto_digest_t crypto_digest_stream_type (int stream); -const char * crypto_strerror (crypto_error_t error); +int init_crypto (void); +int cleanup_crypto (void); +DIGEST * crypto_digest_new (crypto_digest_t type); +bool crypto_digest_update (DIGEST *digest, const void *data, size_t length); +bool crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length); +void crypto_digest_free (DIGEST *digest); +SIGNATURE * crypto_sign_new (void); +crypto_error_t crypto_sign_get_digest (SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST **digest); +crypto_error_t crypto_sign_verify (SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST *digest); +int crypto_sign_add_signer (SIGNATURE *sig, DIGEST *digest, X509_KEYPAIR *keypair); +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); +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); +int crypto_keypair_load_key (X509_KEYPAIR *keypair, const char *file, CRYPTO_PEM_PASSWD_CB *pem_callback, const void *pem_userdata); +void crypto_keypair_free (X509_KEYPAIR *keypair); +int crypto_default_pem_callback (char *buf, int size, const void *userdata); +const char * crypto_digest_name (DIGEST *digest); +crypto_digest_t crypto_digest_stream_type (int stream); +const char * crypto_strerror (crypto_error_t error); /* daemon.c */ void daemon_start (); -- 2.39.5