]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/crypto.c
kes Add dynamic dll entry point for SHGetFolderPath to Win32 code.
[bacula/bacula] / bacula / src / lib / crypto.c
index 55839e50084eb0d6af53d8287887570a08680631..5ae8c2e1dfb2da4a3f469e04cf9c1c341e97a6ab 100644 (file)
@@ -5,8 +5,6 @@
  *
  * 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,
  * license please contact Landon Fuller <landonf@opendarwin.org>.
  */
 /*
-   Copyright (C) 2005 Kern Sibbald
+   Bacula® - The Network Backup Solution
 
-   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.
+   Copyright (C) 2005-2006 Free Software Foundation Europe e.V.
 
-   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.
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version two of the GNU General Public
+   License as published by the Free Software Foundation plus additions
+   that are listed in the file LICENSE.
 
- */
+   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 GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of John Walker.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
 
 
 #include "bacula.h"
@@ -176,6 +187,7 @@ ASN1_SEQUENCE(SignatureData) = {
 
 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);
@@ -276,6 +288,11 @@ struct Crypto_Session {
    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;
@@ -574,7 +591,7 @@ DIGEST *crypto_digest_new (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;
 
    /* Initialize the OpenSSL message digest context */
@@ -620,7 +637,8 @@ err:
  * Returns: true on success
  *          false on failure
  */
-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)
+{
    if (EVP_DigestUpdate(&digest->ctx, data, length) == 0) {
       return true;
    } else { 
@@ -635,8 +653,9 @@ bool crypto_digest_update (DIGEST *digest, const void *data, size_t length) {
  * 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)) {
       return false;
    } else {
       return true;
@@ -868,14 +887,14 @@ 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;
 }
 
@@ -888,16 +907,16 @@ int crypto_sign_encode(SIGNATURE *sig, void *dest, size_t *length)
 
  */
 
-SIGNATURE *crypto_sign_decode(const void *sigData, size_t length)
+SIGNATURE *crypto_sign_decode(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;
+   unsigned char *p = (unsigned char *)sigData;
 #endif
 
-   sig = (SIGNATURE *) malloc(sizeof(SIGNATURE));
+   sig = (SIGNATURE *)malloc(sizeof(SIGNATURE));
    if (!sig) {
       return NULL;
    }
@@ -908,6 +927,7 @@ SIGNATURE *crypto_sign_decode(const void *sigData, size_t length)
    if (!sig->sigData) {
       /* Allocation / Decoding failed in OpenSSL */
       openssl_post_errors(M_ERROR, _("Signature decoding failed"));
+      free(sig);
       return NULL;
    }
 
@@ -1094,14 +1114,14 @@ CRYPTO_SESSION *crypto_session_new (crypto_cipher_t cipher, alist *pubkeys)
  * 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;
 }
 
@@ -1114,23 +1134,31 @@ bool crypto_session_encode(CRYPTO_SESSION *cs, void *dest, size_t *length)
  * 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;
+   const unsigned char *p = (const unsigned char *)data;
 #else
-   unsigned char *p = (unsigned char *) data;
+   unsigned char *p = (unsigned char *)data;
 #endif
 
+   /* bacula-fd.conf doesn't contains any key */
+   if (!keypairs) {
+      return CRYPTO_ERROR_NORECIPIENT;
+   }
+
    cs = (CRYPTO_SESSION *) malloc(sizeof(CRYPTO_SESSION));
    if (!cs) {
       return CRYPTO_ERROR_INTERNAL;
    }
 
+   /* Initialize required fields */
+   cs->session_key = NULL;
+
    /* d2i_CryptoData modifies the supplied pointer */
    cs->cryptoData = d2i_CryptoData(NULL, &p, length);
 
@@ -1214,6 +1242,117 @@ void crypto_session_free (CRYPTO_SESSION *cs)
    free(cs);
 }
 
+/*
+ * 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.
+ */
+CIPHER_CONTEXT *crypto_cipher_new (CRYPTO_SESSION *cs, bool encrypt, uint32_t *blocksize)
+{
+   CIPHER_CONTEXT *cipher_ctx;
+   const EVP_CIPHER *ec;
+
+   cipher_ctx = (CIPHER_CONTEXT *) malloc(sizeof(CIPHER_CONTEXT));
+   if (!cipher_ctx) {
+      return NULL;
+   }
+
+   /*
+    * Acquire a cipher instance for the given ASN.1 cipher NID
+    */
+   if ((ec = EVP_get_cipherbyobj(cs->cryptoData->contentEncryptionAlgorithm)) == NULL) {
+      Emsg1(M_ERROR, 0, _("Unsupported contentEncryptionAlgorithm: %d\n"), OBJ_obj2nid(cs->cryptoData->contentEncryptionAlgorithm));
+      free(cipher_ctx);
+      return NULL;
+   }
+
+   /* Initialize the OpenSSL cipher context */
+   EVP_CIPHER_CTX_init(&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;
+   }
+
+   /* Validate the IV length */
+   if (EVP_CIPHER_iv_length(ec) != M_ASN1_STRING_length(cs->cryptoData->iv)) {
+      openssl_post_errors(M_ERROR, _("Encryption session provided an invalid IV"));
+      goto err;
+   }
+   
+   /* Add the key and IV to the cipher context */
+   if (!EVP_CipherInit_ex(&cipher_ctx->ctx, NULL, NULL, cs->session_key, M_ASN1_STRING_data(cs->cryptoData->iv), -1)) {
+      openssl_post_errors(M_ERROR, _("OpenSSL cipher context key/IV initialization failed"));
+      goto err;
+   }
+
+   *blocksize = EVP_CIPHER_CTX_block_size(&cipher_ctx->ctx);
+   return cipher_ctx;
+
+err:
+   crypto_cipher_free(cipher_ctx);
+   return NULL;
+}
+
+
+/*
+ * Encrypt/Decrypt length bytes of data using the provided cipher context
+ * Returns: true on success, number of bytes output in written
+ *          false on failure
+ */
+bool crypto_cipher_update(CIPHER_CONTEXT *cipher_ctx, const uint8_t *data, uint32_t length, const uint8_t *dest, uint32_t *written)
+{
+   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;
+   }
+}
+
+/*
+ * 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;
+   }
+}
+
+
+/*
+ * Free memory associated with a cipher context.
+ */
+void crypto_cipher_free (CIPHER_CONTEXT *cipher_ctx)
+{
+   EVP_CIPHER_CTX_cleanup(&cipher_ctx->ctx);
+   free (cipher_ctx);
+}
+
+
 /*
  * Perform global initialization of OpenSSL
  * This function is not thread safe.
@@ -1231,9 +1370,12 @@ int init_crypto (void)
    /* Load libssl and libcrypto human-readable error strings */
    SSL_load_error_strings();
 
-   /* Register OpenSSL ciphers */
+   /* Initialize OpenSSL SSL  library */
    SSL_library_init();
 
+   /* Register OpenSSL ciphers and digests */
+   OpenSSL_add_all_algorithms();
+
    if (!openssl_seed_prng()) {
       Emsg0(M_ERROR_TERM, 0, _("Failed to seed OpenSSL PRNG\n"));
    }
@@ -1269,6 +1411,9 @@ int cleanup_crypto (void)
    /* Free libssl and libcrypto error strings */
    ERR_free_strings();
 
+   /* Free all ciphers and digests */
+   EVP_cleanup();
+
    /* Free memory used by PRNG */
    RAND_cleanup();
 
@@ -1305,7 +1450,7 @@ DIGEST *crypto_digest_new (crypto_digest_t type)
 {
    DIGEST *digest;
 
-   digest = (DIGEST *) malloc(sizeof(DIGEST));
+   digest = (DIGEST *)malloc(sizeof(DIGEST));
    digest->type = type;
 
    switch (type) {
@@ -1324,7 +1469,8 @@ DIGEST *crypto_digest_new (crypto_digest_t type)
    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 ... */
@@ -1344,8 +1490,8 @@ bool crypto_digest_update (DIGEST *digest, const void *data, size_t length) {
    }
 }
 
-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
@@ -1353,7 +1499,7 @@ bool crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length) {
       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
@@ -1373,7 +1519,7 @@ bool crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length) {
    return false;
 }
 
-void crypto_digest_free (DIGEST *digest)
+void crypto_digest_free(DIGEST *digest)
 {
    free (digest);
 }
@@ -1388,9 +1534,9 @@ crypto_error_t crypto_sign_get_digest (SIGNATURE *sig, X509_KEYPAIR *keypair, DI
 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 (const uint8_t *sigData, uint32_t length) { return NULL; }
 void crypto_sign_free (SIGNATURE *sig) { }
 
 
@@ -1403,8 +1549,13 @@ void crypto_keypair_free (X509_KEYPAIR *keypair) { }
 
 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 */