]> git.sur5r.net Git - bacula/bacula/commitdiff
Support for FD-side file encryption.
authorLandon Fuller <landonf@opendarwin.org>
Tue, 17 Jan 2006 06:26:20 +0000 (06:26 +0000)
committerLandon Fuller <landonf@opendarwin.org>
Tue, 17 Jan 2006 06:26:20 +0000 (06:26 +0000)
- Added MIN() and MAX() macros to baconfig.h
- Added stream types for win32, file, and macos encrypted data (with and without gzip)
- Added FO_ENCRYPT file flag.
- Implement crypto_cipher_* encryption functions
- Add encryption logic to save_file()

Decryption support has not been committed (yet):
If you use this code to encrypt your data, you will be unable to decrypt it!

git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@2755 91ce42f0-d328-0410-95d8-f526ca767f89

bacula/src/baconfig.h
bacula/src/filed/backup.c
bacula/src/filed/restore.c
bacula/src/findlib/attribs.c
bacula/src/findlib/bfile.c
bacula/src/findlib/find.h
bacula/src/jcr.h
bacula/src/lib/crypto.c
bacula/src/lib/crypto.h
bacula/src/lib/protos.h
bacula/src/stored/record.c

index ca29db2897171adb63b53e2ef208f636545be36c..0e1ce6562784c6ee8553e4e0c8d06b8301b22e45 100644 (file)
 #define TRUE  1
 #define FALSE 0
 
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
 #ifdef HAVE_TLS
 #define have_tls 1
 #else
 #define STREAM_UNIX_ATTRIBUTES_ACCESS_ACL 15 /* Standard ACL attributes on UNIX */
 #define STREAM_UNIX_ATTRIBUTES_DEFAULT_ACL 16 /* Default ACL attributes on UNIX */
 /*** 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, 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 */
+#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, 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 */
+#define STREAM_ENCRYPTED_FILE_GZIP_DATA   23   /* Encrypted, compressed data */
+#define STREAM_ENCRYPTED_WIN32_GZIP_DATA  24   /* Encrypted, compressed Win32 BackupRead data */
+#define STREAM_ENCRYPTED_MACOS_FORK_DATA  25   /* Encrypted, uncompressed Mac resource fork */
 
 
 /*
index 6812d8cf7490ee08dcece12d34bcac0bde8c22c7..7f277ba9da78efa97593da7958d8cd6039626713 100644 (file)
@@ -110,6 +110,9 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
 
       /* ... and store the encoded size */
       jcr->pki_session_encoded_size = size;
+
+      /* Allocate the encryption/decryption buffer */
+      jcr->crypto_buf = get_memory(CRYPTO_CIPHER_MAX_BLOCK_SIZE);
    }
 
    Dmsg1(300, "set_find_options ff=%p\n", jcr->ff);
@@ -141,6 +144,10 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
       free_pool_memory(jcr->compress_buf);
       jcr->compress_buf = NULL;
    }
+   if (jcr->crypto_buf) {
+      free_pool_memory(jcr->crypto_buf);
+      jcr->crypto_buf = NULL;
+   }
 
    if (jcr->pki_session) {
       crypto_session_free(jcr->pki_session);
@@ -308,7 +315,6 @@ static int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level)
    if (jcr->pki_sign) {
       signing_digest = crypto_digest_new(signing_algorithm);
    }
-
    /* Full-stop if a failure occured initializing the signature digest */
    if (jcr->pki_sign && signing_digest == NULL) {
       Jmsg(jcr, M_NOTSAVED, 0, _("%s signature digest initialization failed\n"),
@@ -317,6 +323,11 @@ static int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level)
       return 1;
    }
 
+   /* Enable encryption */
+   if (jcr->pki_encrypt) {
+      ff_pkt->flags |= FO_ENCRYPT;
+   }
+
    /* Initialise the file descriptor we use for data and other streams. */
    binit(&ff_pkt->bfd);
    if (ff_pkt->flags & FO_PORTABLE) {
@@ -544,6 +555,11 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, DIGEST *sign
    char *rbuf, *wbuf;
    int rsize = jcr->buf_size;      /* read buffer size */
    POOLMEM *msgsave;
+   CIPHER_CONTEXT *cipher_ctx = NULL; /* Quell bogus uninitialized warnings */
+   const void *cipher_input;
+   size_t cipher_input_len;
+   size_t cipher_block_size;
+   size_t encrypted_len;
 #ifdef FD_NO_SEND_TEST
    return 1;
 #endif
@@ -551,11 +567,11 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, DIGEST *sign
    msgsave = sd->msg;
    rbuf = sd->msg;                    /* read buffer */
    wbuf = sd->msg;                    /* write buffer */
+   cipher_input = rbuf;               /* encrypt uncompressed data */
 
 
    Dmsg1(300, "Saving data, type=%d\n", ff_pkt->type);
 
-
 #ifdef HAVE_LIBZ
    uLong compress_len, max_compress_len = 0;
    const Bytef *cbuf = NULL;
@@ -569,9 +585,30 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, DIGEST *sign
          max_compress_len = jcr->compress_buf_size; /* set max length */
       }
       wbuf = jcr->compress_buf;    /* compressed output here */
+      cipher_input = jcr->compress_buf; /* encrypt compressed data */
    }
 #endif
 
+   if (ff_pkt->flags & FO_ENCRYPT) {
+      /* Allocate the cipher context */
+      if ((cipher_ctx = crypto_cipher_new(jcr->pki_session, true, &cipher_block_size)) == NULL) {
+         /* Shouldn't happen! */
+         Jmsg0(jcr, M_FATAL, 0, _("Failed to initialize encryption context"));
+         goto err;
+      }
+
+      /*
+       * Grow the crypto buffer, if necessary.
+       * crypto_cipher_update() will buffer up to (cipher_block_size - 1).
+       * We grow crypto_buf to the maximum number of blocks that
+       * could be returned for the given read buffer size.
+       * (Using the larger of either rsize or max_compress_len)
+       */
+      jcr->crypto_buf = check_pool_memory_size(jcr->crypto_buf, (MAX((size_t) rsize, max_compress_len) + cipher_block_size - 1) / cipher_block_size * cipher_block_size);
+
+      wbuf = jcr->crypto_buf; /* Encrypted, possibly compressed output here. */
+   }
+
    /*
     * Send Data header to Storage daemon
     *    <file-index> <stream> <info>
@@ -579,7 +616,7 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, DIGEST *sign
    if (!bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, stream)) {
       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
             bnet_strerror(sd));
-      return 0;
+      goto err;
    }
    Dmsg1(300, ">stored: datahdr %s\n", sd->msg);
 
@@ -628,6 +665,9 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, DIGEST *sign
       jcr->ReadBytes += sd->msglen;         /* count bytes read */
       fileAddr += sd->msglen;
 
+      /* Uncompressed cipher input length */
+      cipher_input_len = sd->msglen;
+
       /* Update checksum if requested */
       if (digest) {
          crypto_digest_update(digest, rbuf, sd->msglen);
@@ -650,18 +690,34 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, DIGEST *sign
                (const Bytef *)rbuf, (uLong)sd->msglen,
                ff_pkt->GZIP_level)) != Z_OK) {
             Jmsg(jcr, M_FATAL, 0, _("Compression error: %d\n"), zstat);
-            sd->msg = msgsave;
-            sd->msglen = 0;
             set_jcr_job_status(jcr, JS_ErrorTerminated);
-            return 0;
+            goto err;
          }
          Dmsg2(400, "compressed len=%d uncompressed len=%d\n",
             compress_len, sd->msglen);
 
          sd->msglen = compress_len;      /* set compressed length */
+         cipher_input_len = compress_len;
       }
 #endif
 
+      if (ff_pkt->flags & FO_ENCRYPT) {
+         /* Encrypt the input block */
+         if (crypto_cipher_update(cipher_ctx, cipher_input, cipher_input_len, jcr->crypto_buf, &encrypted_len)) {
+            if (encrypted_len == 0) {
+               /* No full block of data available, read more data */
+               continue;
+            }
+            Dmsg2(400, "encrypted len=%d unencrypted len=%d\n",
+               encrypted_len, sd->msglen);
+            sd->msglen = encrypted_len; /* set encrypted length */
+         } else {
+            /* Encryption failed. Shouldn't happen. */
+            Jmsg(jcr, M_FATAL, 0, _("Encryption error\n"));
+            goto err;
+         }
+      }
+
       /* Send the buffer to the Storage daemon */
       if (!sparseBlock) {
          if (ff_pkt->flags & FO_SPARSE) {
@@ -671,18 +727,42 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, DIGEST *sign
          if (!bnet_send(sd)) {
             Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
                   bnet_strerror(sd));
-            sd->msg = msgsave;     /* restore bnet buffer */
-            sd->msglen = 0;
-            return 0;
+            goto err;
          }
       }
       Dmsg1(130, "Send data to SD len=%d\n", sd->msglen);
       /*          #endif */
-      jcr->JobBytes += sd->msglen;      /* count bytes saved possibly compressed */
+      jcr->JobBytes += sd->msglen;      /* count bytes saved possibly compressed/encrypted */
       sd->msg = msgsave;                /* restore read buffer */
 
    } /* end while read file data */
 
+   /* Send any remaining encrypted data + padding */
+   if (ff_pkt->flags & FO_ENCRYPT) {
+      if (!crypto_cipher_finalize(cipher_ctx, jcr->crypto_buf, &encrypted_len)) {
+         /* Padding failed. Shouldn't happen. */
+         Jmsg(jcr, M_FATAL, 0, _("Encryption padding error\n"));
+         goto err;
+      }
+
+      if (encrypted_len > 0) {
+         sd->msglen = encrypted_len; /* set encrypted length */
+
+         /* Send remaining encrypted data to the SD */
+         if (ff_pkt->flags & FO_SPARSE) {
+            sd->msglen += SPARSE_FADDR_SIZE; /* include fileAddr in size */
+         }
+         sd->msg = wbuf;              /* set correct write buffer */
+         if (!bnet_send(sd)) {
+            Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
+                  bnet_strerror(sd));
+            goto err;
+         }
+         Dmsg1(130, "Send data to SD len=%d\n", sd->msglen);
+         jcr->JobBytes += sd->msglen;      /* count bytes saved possibly compressed/encrypted */
+         sd->msg = msgsave;                /* restore bnet buffer */
+      }
+   }
 
    if (sd->msglen < 0) {
       berrno be;
@@ -697,10 +777,23 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, DIGEST *sign
    if (!bnet_sig(sd, BNET_EOD)) {        /* indicate end of file data */
       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
             bnet_strerror(sd));
-      return 0;
+      goto err;
+   }
+
+   /* Free the cipher context */
+   if (cipher_ctx) {
+      crypto_cipher_free(cipher_ctx);
    }
 
    return 1;
+
+err:
+   if (cipher_ctx) {
+      crypto_cipher_free(cipher_ctx);
+   }
+   sd->msg = msgsave; /* restore bnet buffer */
+   sd->msglen = 0;
+   return 0;
 }
 
 /*
@@ -770,7 +863,11 @@ static bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_strea
 #endif
 
    /* Find what data stream we will use, then encode the attributes */
-   data_stream = select_data_stream(ff_pkt);
+   if ((data_stream = select_data_stream(ff_pkt)) == STREAM_NONE) {
+      /* This should not happen */
+      Jmsg0(jcr, M_FATAL, 0, _("Invalid file flags, no supported data stream type.\n"));
+      return false;
+   }
    encode_stat(attribs, ff_pkt, data_stream);
 
    /* Now possibly extend the attributes */
index 7a9cb99d1f6aa5b0984cf44497f15383476a130b..c28bcf2deefd84ef1c4bd46942beae1a6443ee2f 100644 (file)
@@ -329,16 +329,23 @@ void do_restore(JCR *jcr)
       case STREAM_GZIP_DATA:
       case STREAM_SPARSE_GZIP_DATA:
       case STREAM_WIN32_GZIP_DATA:
+      case STREAM_ENCRYPTED_FILE_DATA:
+      case STREAM_ENCRYPTED_WIN32_DATA:
+      case STREAM_ENCRYPTED_FILE_GZIP_DATA:
+      case STREAM_ENCRYPTED_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_ENCRYPTED_SESSION_DATA)) {
             flags = 0;
+
             if (stream == STREAM_SPARSE_DATA || stream == STREAM_SPARSE_GZIP_DATA) {
                flags |= FO_SPARSE;
             }
+
             if (stream == STREAM_GZIP_DATA || stream == STREAM_SPARSE_GZIP_DATA
-                  || stream == STREAM_WIN32_GZIP_DATA) {
+                  || stream == STREAM_WIN32_GZIP_DATA || stream == STREAM_ENCRYPTED_FILE_GZIP_DATA
+                  || stream == STREAM_ENCRYPTED_WIN32_GZIP_DATA) {
                flags |= FO_GZIP;
             }
 
@@ -357,6 +364,7 @@ void do_restore(JCR *jcr)
 
       /* Resource fork stream - only recorded after a file to be restored */
       /* Silently ignore if we cannot write - we already reported that */
+      case STREAM_ENCRYPTED_MACOS_FORK_DATA:
       case STREAM_MACOS_FORK_DATA:
 #ifdef HAVE_DARWIN_OS
          if (extract) {
index 678aa9c71a7b506fbeee90430f273c2d10b64584..3e4f7cd64c1f3eb1cb1e47a8fca7b5c373943175 100755 (executable)
@@ -56,6 +56,15 @@ int select_data_stream(FF_PKT *ff_pkt)
 {
    int stream;
 
+   /*
+    *  Fix all incompatible options
+    */
+
+   /* No sparse option for encrypted data */
+   if (ff_pkt->flags & FO_ENCRYPT) {
+      ff_pkt->flags &= ~FO_SPARSE;
+   }
+
    /* Note, no sparse option for win32_data */
    if (!is_portable_backup(&ff_pkt->bfd)) {
       stream = STREAM_WIN32_DATA;
@@ -65,17 +74,65 @@ int select_data_stream(FF_PKT *ff_pkt)
    } else {
       stream = STREAM_FILE_DATA;
    }
+
+   /* Encryption is only supported for file data */
+   if (stream != STREAM_FILE_DATA && stream != STREAM_WIN32_DATA &&
+         stream != STREAM_MACOS_FORK_DATA) {
+      ff_pkt->flags &= ~FO_ENCRYPT;
+   }
+
+   /* Compression is not supported for Mac fork data */
+   if (stream == STREAM_MACOS_FORK_DATA) {
+      ff_pkt->flags &= ~FO_GZIP;
+   }
+
+   /*
+    * Handle compression and encryption options
+    */
 #ifdef HAVE_LIBZ
    if (ff_pkt->flags & FO_GZIP) {
-      if (stream == STREAM_WIN32_DATA) {
+      switch (stream) {
+      case STREAM_WIN32_DATA:
          stream = STREAM_WIN32_GZIP_DATA;
-      } else if (stream == STREAM_FILE_DATA) {
-         stream = STREAM_GZIP_DATA;
-      } else {
+         break;
+      case STREAM_SPARSE_DATA:
          stream = STREAM_SPARSE_GZIP_DATA;
+         break;
+      case STREAM_FILE_DATA:
+         stream = STREAM_GZIP_DATA;
+         break;
+      default:
+         /* All stream types that do not support gzip should clear out
+          * FO_GZIP above, and this code block should be unreachable. */
+         ASSERT(!ff_pkt->flags & FO_GZIP);
+         return STREAM_NONE;
       }
    }
 #endif
+#ifdef HAVE_CRYPTO
+   if (ff_pkt->flags & FO_ENCRYPT) {
+      switch (stream) {
+      case STREAM_WIN32_DATA:
+         stream = STREAM_ENCRYPTED_WIN32_DATA;
+         break;
+      case STREAM_WIN32_GZIP_DATA:
+         stream = STREAM_ENCRYPTED_WIN32_GZIP_DATA;
+         break;
+      case STREAM_FILE_DATA:
+         stream = STREAM_ENCRYPTED_FILE_DATA;
+         break;
+      case STREAM_GZIP_DATA:
+         stream = STREAM_ENCRYPTED_FILE_GZIP_DATA;
+         break;
+      default:
+         /* All stream types that do not support encryption should clear out
+          * FO_ENCRYPT above, and this code block should be unreachable. */
+         ASSERT(!ff_pkt->flags & FO_ENCRYPT);
+         return STREAM_NONE;
+      }
+   }
+#endif
+
    return stream;
 }
 
index f9f66652d938616086978ce96715bd64b89e02eb..85b3ffe9e3b0dec4c75aebc0840934171b9db9eb 100644 (file)
@@ -48,6 +48,8 @@ bool is_win32_stream(int stream)
    switch (stream) {
    case STREAM_WIN32_DATA:
    case STREAM_WIN32_GZIP_DATA:
+   case STREAM_ENCRYPTED_WIN32_DATA:
+   case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
       return true;
    }
    return false;
@@ -92,6 +94,16 @@ const char *stream_to_ascii(int stream)
       return _("SHA512 digest");
    case STREAM_SIGNED_DIGEST:
       return _("Signed digest");
+   case STREAM_ENCRYPTED_FILE_DATA:
+      return _("Encrypted File data");
+   case STREAM_ENCRYPTED_FILE_GZIP_DATA:
+      return _("Encrypted GZIP data");
+   case STREAM_ENCRYPTED_WIN32_DATA:
+      return _("Encrypted Win32 data");
+   case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
+      return _("Encrypted Win32 GZIP data");
+   case STREAM_ENCRYPTED_MACOS_FORK_DATA:
+      return _("Encrypted HFS+ resource fork");
    default:
       sprintf(buf, "%d", stream);
       return (const char *)buf;
@@ -317,6 +329,7 @@ bool is_restore_stream_supported(int stream)
 #endif
    case STREAM_MACOS_FORK_DATA:
    case STREAM_HFSPLUS_ATTRIBUTES:
+   case STREAM_ENCRYPTED_MACOS_FORK_DATA:
       return false;
 
    /* Known streams */
@@ -340,6 +353,10 @@ bool is_restore_stream_supported(int stream)
 #endif
 #ifdef HAVE_CRYPTO
    case STREAM_SIGNED_DIGEST:
+   case STREAM_ENCRYPTED_FILE_DATA:
+   case STREAM_ENCRYPTED_FILE_GZIP_DATA:
+   case STREAM_ENCRYPTED_WIN32_DATA:
+   case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
 #endif
    case 0:                            /* compatibility with old tapes */
       return true;
@@ -714,10 +731,20 @@ bool is_restore_stream_supported(int stream)
    case STREAM_SHA256_DIGEST:
    case STREAM_SHA512_DIGEST:
 #endif
+#ifdef HAVE_CRYPTO
+   case STREAM_SIGNED_DIGEST:
+   case STREAM_ENCRYPTED_FILE_DATA:
+   case STREAM_ENCRYPTED_FILE_GZIP_DATA:
+   case STREAM_ENCRYPTED_WIN32_DATA:
+   case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
+#endif
 #ifdef HAVE_DARWIN_OS
    case STREAM_MACOS_FORK_DATA:
    case STREAM_HFSPLUS_ATTRIBUTES:
-#endif
+#ifdef HAVE_CRYPTO
+   case STREAM_ENCRYPTED_MACOS_FORK_DATA:
+#endif /* HAVE_CRYPTO */
+#endif /* HAVE_DARWIN_OS */
    case 0:   /* compatibility with old tapes */
       return true;
 
index 752d009fe00f35a584b3d3247e71913cb98d1f84..d5487aa539b46685aa3aa1a83f53b4c5972de07e 100755 (executable)
@@ -88,6 +88,7 @@ enum {
 #define FO_WIN32DECOMP  (1<<18)       /* Use BackupRead decomposition */
 #define FO_SHA256       (1<<19)       /* Do SHA256 checksum */
 #define FO_SHA512       (1<<20)       /* Do SHA512 checksum */
+#define FO_ENCRYPT      (1<<21)       /* Encrypt data stream */
 
 struct s_included_file {
    struct s_included_file *next;
index 413482063966aab5e6c0fbf042c5e5bedf5511a1..869bde86a7ba988c59f0fcf173ec9529c4ba6236 100644 (file)
@@ -240,6 +240,7 @@ public:
    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 */
+   POOLMEM *crypto_buf;               /* Encryption/Decryption buffer */
    DIRRES* director;                  /* Director resource */
 #endif /* FILE_DAEMON */
 
index 55839e50084eb0d6af53d8287887570a08680631..f1639e71d63ae8c9a05c7937d0c0c418f5fe1cc0 100644 (file)
@@ -276,6 +276,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;
@@ -1214,6 +1219,115 @@ 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, size_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));
+      crypto_cipher_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 void *data, size_t length, const void *dest, size_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, void *dest, size_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.
@@ -1403,8 +1517,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, 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; }
+
+CIPHER_CONTEXT *crypto_cipher_new (CRYPTO_SESSION *cs, bool encrypt, size_t *blocksize) { return NULL; }
+bool crypto_cipher_update (CIPHER_CONTEXT *cipher_ctx, const void *data, size_t length, const void *dest, size_t *written) { return false; }
+bool crypto_cipher_finalize (CIPHER_CONTEXT *cipher_ctx, void *dest, size_t *written) { return false; }
+void crypto_cipher_free (CIPHER_CONTEXT *cipher_ctx) { }
 
 #endif /* HAVE_CRYPTO */
 
index f681f349c9165354033b71970cbe419f2a8da12a..25c5260e251d75aafecba0a0c8c0d61a1057ccf8 100644 (file)
@@ -48,6 +48,9 @@ typedef struct Signature SIGNATURE;
 /* Opaque PKI Symmetric Key Data Structure */
 typedef struct Crypto_Session CRYPTO_SESSION;
 
+/* Opaque Encryption/Decryption Context Structure */
+typedef struct Cipher_Context CIPHER_CONTEXT;
+
 /* PEM Decryption Passphrase Callback */
 typedef int (CRYPTO_PEM_PASSWD_CB) (char *buf, int size, const void *userdata);
 
@@ -91,8 +94,9 @@ typedef enum {
 /* Maximum Message Digest Size */
 #ifdef HAVE_OPENSSL
 
-/* Let OpenSSL define it */
-#define CRYPTO_DIGEST_MAX_SIZE EVP_MAX_MD_SIZE
+/* Let OpenSSL define a few things */
+#define CRYPTO_DIGEST_MAX_SIZE         EVP_MAX_MD_SIZE
+#define CRYPTO_CIPHER_MAX_BLOCK_SIZE   EVP_MAX_BLOCK_LENGTH
 
 #else /* HAVE_OPENSSL */
 
index eb6d186345d08fee9574f232432273afacad13c6..4d54d50156a0473713ea42d8d227e1afef8ac180 100644 (file)
@@ -132,6 +132,10 @@ void               crypto_session_free         (CRYPTO_SESSION *cs);
 bool               crypto_session_encode       (CRYPTO_SESSION *cs, void *dest, size_t *length);
 crypto_error_t     crypto_session_decode       (const void *data, size_t length, alist *keypairs, CRYPTO_SESSION **session); 
 CRYPTO_SESSION *   crypto_session_decode       (const void *data, size_t length);
+CIPHER_CONTEXT *   crypto_cipher_new           (CRYPTO_SESSION *cs, bool encrypt, size_t *blocksize);
+bool               crypto_cipher_update        (CIPHER_CONTEXT *cipher_ctx, const void *data, size_t length, const void *dest, size_t *written);
+bool               crypto_cipher_finalize      (CIPHER_CONTEXT *cipher_ctx, void *dest, size_t *written);
+void               crypto_cipher_free          (CIPHER_CONTEXT *cipher_ctx);
 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);
index 05641be2b30112a97b88308188321c4d377f2044..130a2884b71a6f3e38e978aa0b60d48550662912 100644 (file)
@@ -117,6 +117,16 @@ const char *stream_to_ascii(char *buf, int stream, int fi)
        return "SIGNED-DIGEST";
     case STREAM_ENCRYPTED_SESSION_DATA:
        return "ENCRYPTED-SESSION-DATA";
+    case STREAM_ENCRYPTED_FILE_DATA:
+       return "ENCRYPTED-FILE";
+    case STREAM_ENCRYPTED_FILE_GZIP_DATA:
+       return "ENCRYPTED-GZIP";
+    case STREAM_ENCRYPTED_WIN32_DATA:
+       return "ENCRYPTED-WIN32-DATA";
+    case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
+       return "ENCRYPTED-WIN32-GZIP";
+    case STREAM_ENCRYPTED_MACOS_FORK_DATA:
+       return "ENCRYPTED-MACOS-RSRC";
     case -STREAM_UNIX_ATTRIBUTES:
        return "contUATTR";
     case -STREAM_FILE_DATA:
@@ -153,6 +163,16 @@ const char *stream_to_ascii(char *buf, int stream, int fi)
        return "contSIGNED-DIGEST";
     case -STREAM_ENCRYPTED_SESSION_DATA:
        return "contENCRYPTED-SESSION-DATA";
+    case -STREAM_ENCRYPTED_FILE_DATA:
+       return "contENCRYPTED-FILE";
+    case -STREAM_ENCRYPTED_FILE_GZIP_DATA:
+       return "contENCRYPTED-GZIP";
+    case -STREAM_ENCRYPTED_WIN32_DATA:
+       return "contENCRYPTED-WIN32-DATA";
+    case -STREAM_ENCRYPTED_WIN32_GZIP_DATA:
+       return "contENCRYPTED-WIN32-GZIP";
+    case -STREAM_ENCRYPTED_MACOS_FORK_DATA:
+       return "contENCRYPTED-MACOS-RSRC";
     default:
        sprintf(buf, "%d", stream);
        return buf;