+ if (non_support_crypto) {
+ Jmsg(jcr, M_INFO, 0, _("%d non-supported crypto streams ignored.\n"), non_support_acl);
+ }
+
+}
+
+#ifdef HAVE_LIBZ
+/*
+ * Convert ZLIB error code into an ASCII message
+ */
+static const char *zlib_strerror(int stat)
+{
+ if (stat >= 0) {
+ return _("None");
+ }
+ switch (stat) {
+ case Z_ERRNO:
+ return _("Zlib errno");
+ case Z_STREAM_ERROR:
+ return _("Zlib stream error");
+ case Z_DATA_ERROR:
+ return _("Zlib data error");
+ case Z_MEM_ERROR:
+ return _("Zlib memory error");
+ case Z_BUF_ERROR:
+ return _("Zlib buffer error");
+ case Z_VERSION_ERROR:
+ return _("Zlib version error");
+ default:
+ return _("*none*");
+ }
+}
+#endif
+
+static int do_file_digest(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
+{
+ Dmsg1(50, "do_file_digest jcr=%p\n", jcr);
+ return (digest_file(jcr, ff_pkt, jcr->crypto.digest));
+}
+
+/*
+ * Verify the signature for the last restored file
+ * Return value is either true (signature correct)
+ * or false (signature could not be verified).
+ * TODO landonf: Implement without using find_one_file and
+ * without re-reading the file.
+ */
+static bool verify_signature(JCR *jcr, r_ctx &rctx)
+{
+ X509_KEYPAIR *keypair;
+ DIGEST *digest = NULL;
+ crypto_error_t err;
+ uint64_t saved_bytes;
+ crypto_digest_t signing_algorithm = have_sha2 ?
+ CRYPTO_DIGEST_SHA256 : CRYPTO_DIGEST_SHA1;
+ crypto_digest_t algorithm;
+ SIGNATURE *sig = rctx.sig;
+
+
+ if (!jcr->crypto.pki_sign) {
+ return true; /* no signature OK */
+ }
+ if (!sig) {
+ if (rctx.type == FT_REGE || rctx.type == FT_REG || rctx.type == FT_RAW) {
+ Jmsg1(jcr, M_ERROR, 0, _("Missing cryptographic signature for %s\n"),
+ jcr->last_fname);
+ goto bail_out;
+ }
+ return true;
+ }
+
+ /* Iterate through the trusted signers */
+ foreach_alist(keypair, jcr->crypto.pki_signers) {
+ err = crypto_sign_get_digest(sig, jcr->crypto.pki_keypair, algorithm, &digest);
+ switch (err) {
+ case CRYPTO_ERROR_NONE:
+ Dmsg0(50, "== Got digest\n");
+ /*
+ * We computed jcr->crypto.digest using signing_algorithm while writing
+ * the file. If it is not the same as the algorithm used for
+ * this file, punt by releasing the computed algorithm and
+ * computing by re-reading the file.
+ */
+ if (algorithm != signing_algorithm) {
+ if (jcr->crypto.digest) {
+ crypto_digest_free(jcr->crypto.digest);
+ jcr->crypto.digest = NULL;
+ }
+ }
+ if (jcr->crypto.digest) {
+ /* Use digest computed while writing the file to verify the signature */
+ if ((err = crypto_sign_verify(sig, keypair, jcr->crypto.digest)) != CRYPTO_ERROR_NONE) {
+ Dmsg1(50, "Bad signature on %s\n", jcr->last_fname);
+ Jmsg2(jcr, M_ERROR, 0, _("Signature validation failed for file %s: ERR=%s\n"),
+ jcr->last_fname, crypto_strerror(err));
+ goto bail_out;
+ }
+ } else {
+ /* Signature found, digest allocated. Old method,
+ * re-read the file and compute the digest
+ */
+ jcr->crypto.digest = digest;
+
+ /* Checksum the entire file */
+ /* Make sure we don't modify JobBytes by saving and restoring it */
+ saved_bytes = jcr->JobBytes;
+ if (find_one_file(jcr, jcr->ff, do_file_digest, jcr->last_fname, (dev_t)-1, 1) != 0) {
+ Jmsg(jcr, M_ERROR, 0, _("Digest one file failed for file: %s\n"),
+ jcr->last_fname);
+ jcr->JobBytes = saved_bytes;
+ goto bail_out;
+ }
+ jcr->JobBytes = saved_bytes;
+
+ /* Verify the signature */
+ if ((err = crypto_sign_verify(sig, keypair, digest)) != CRYPTO_ERROR_NONE) {
+ Dmsg1(50, "Bad signature on %s\n", jcr->last_fname);
+ Jmsg2(jcr, M_ERROR, 0, _("Signature validation failed for file %s: ERR=%s\n"),
+ jcr->last_fname, crypto_strerror(err));
+ goto bail_out;
+ }
+ jcr->crypto.digest = NULL;
+ }
+
+ /* Valid signature */
+ Dmsg1(50, "Signature good on %s\n", jcr->last_fname);
+ crypto_digest_free(digest);
+ return true;
+
+ case CRYPTO_ERROR_NOSIGNER:
+ /* Signature not found, try again */
+ if (digest) {
+ crypto_digest_free(digest);
+ digest = NULL;
+ }
+ continue;
+ default:
+ /* Something strange happened (that shouldn't happen!)... */
+ Qmsg2(jcr, M_ERROR, 0, _("Signature validation failed for %s: %s\n"), jcr->last_fname, crypto_strerror(err));
+ goto bail_out;
+ }
+ }
+
+ /* No signer */
+ Dmsg1(50, "Could not find a valid public key for signature on %s\n", jcr->last_fname);
+
+bail_out:
+ if (digest) {
+ crypto_digest_free(digest);
+ }
+ return false;
+}
+
+bool sparse_data(JCR *jcr, BFILE *bfd, uint64_t *addr, char **data, uint32_t *length)
+{
+ unser_declare;
+ uint64_t faddr;
+ char ec1[50];
+ unser_begin(*data, SPARSE_FADDR_SIZE);
+ unser_uint64(faddr);
+ if (*addr != faddr) {
+ *addr = faddr;
+ if (blseek(bfd, (boffset_t)*addr, SEEK_SET) < 0) {
+ berrno be;
+ Jmsg3(jcr, M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
+ edit_uint64(*addr, ec1), jcr->last_fname,
+ be.bstrerror(bfd->berrno));
+ return false;
+ }
+ }
+ *data += SPARSE_FADDR_SIZE;
+ *length -= SPARSE_FADDR_SIZE;
+ return true;
+}
+
+bool decompress_data(JCR *jcr, char **data, uint32_t *length)
+{
+#ifdef HAVE_LIBZ
+ uLong compress_len;
+ int stat;
+ char ec1[50]; /* Buffer printing huge values */
+
+ /*
+ * NOTE! We only use uLong and Byte because they are
+ * needed by the zlib routines, they should not otherwise
+ * be used in Bacula.
+ */
+ compress_len = jcr->compress_buf_size;
+ Dmsg2(100, "Comp_len=%d msglen=%d\n", compress_len, *length);
+ if ((stat=uncompress((Byte *)jcr->compress_buf, &compress_len,
+ (const Byte *)*data, (uLong)*length)) != Z_OK) {
+ Qmsg(jcr, M_ERROR, 0, _("Uncompression error on file %s. ERR=%s\n"),
+ jcr->last_fname, zlib_strerror(stat));
+ return false;
+ }
+ *data = jcr->compress_buf;
+ *length = compress_len;
+ Dmsg2(100, "Write uncompressed %d bytes, total before write=%s\n", compress_len, edit_uint64(jcr->JobBytes, ec1));
+ return true;
+#else
+ Qmsg(jcr, M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
+ return false;
+#endif
+}
+
+static void unser_crypto_packet_len(RESTORE_CIPHER_CTX *ctx)
+{
+ unser_declare;
+ if (ctx->packet_len == 0 && ctx->buf_len >= CRYPTO_LEN_SIZE) {
+ unser_begin(&ctx->buf[0], CRYPTO_LEN_SIZE);
+ unser_uint32(ctx->packet_len);
+ ctx->packet_len += CRYPTO_LEN_SIZE;
+ }
+}
+
+bool store_data(JCR *jcr, BFILE *bfd, char *data, const int32_t length, bool win32_decomp)
+{
+ if (jcr->crypto.digest) {
+ crypto_digest_update(jcr->crypto.digest, (uint8_t *)data, length);
+ }
+ if (win32_decomp) {
+ if (!processWin32BackupAPIBlock(bfd, data, length)) {
+ berrno be;
+ Jmsg2(jcr, M_ERROR, 0, _("Write error in Win32 Block Decomposition on %s: %s\n"),
+ jcr->last_fname, be.bstrerror(bfd->berrno));
+ return false;
+ }
+ } else if (bwrite(bfd, data, length) != (ssize_t)length) {
+ berrno be;
+ Jmsg2(jcr, M_ERROR, 0, _("Write error on %s: %s\n"),
+ jcr->last_fname, be.bstrerror(bfd->berrno));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * In the context of jcr, write data to bfd.
+ * We write buflen bytes in buf at addr. addr is updated in place.
+ * The flags specify whether to use sparse files or compression.
+ * Return value is the number of bytes written, or -1 on errors.
+ */
+int32_t extract_data(JCR *jcr, BFILE *bfd, POOLMEM *buf, int32_t buflen,
+ uint64_t *addr, int flags, RESTORE_CIPHER_CTX *cipher_ctx)
+{
+ char *wbuf; /* write buffer */
+ uint32_t wsize; /* write size */
+ uint32_t rsize; /* read size */
+ uint32_t decrypted_len = 0; /* Decryption output length */
+ char ec1[50]; /* Buffer printing huge values */
+
+ rsize = buflen;
+ jcr->ReadBytes += rsize;
+ wsize = rsize;
+ wbuf = buf;
+
+ if (flags & FO_ENCRYPT) {
+ ASSERT(cipher_ctx->cipher);
+
+ /* NOTE: We must implement block preserving semantics for the
+ * non-streaming compression and sparse code. */
+
+ /*
+ * Grow the crypto buffer, if necessary.
+ * crypto_cipher_update() will process only whole blocks,
+ * buffering the remaining input.
+ */
+ cipher_ctx->buf = check_pool_memory_size(cipher_ctx->buf,
+ cipher_ctx->buf_len + wsize + cipher_ctx->block_size);
+
+ /* Decrypt the input block */
+ if (!crypto_cipher_update(cipher_ctx->cipher,
+ (const u_int8_t *)wbuf,
+ wsize,
+ (u_int8_t *)&cipher_ctx->buf[cipher_ctx->buf_len],
+ &decrypted_len)) {
+ /* Decryption failed. Shouldn't happen. */
+ Jmsg(jcr, M_FATAL, 0, _("Decryption error\n"));
+ return -1;
+ }
+
+ if (decrypted_len == 0) {
+ /* No full block of encrypted data available, write more data */
+ return 0;
+ }
+
+ Dmsg2(100, "decrypted len=%d encrypted len=%d\n", decrypted_len, wsize);
+
+ cipher_ctx->buf_len += decrypted_len;
+ wbuf = cipher_ctx->buf;
+
+ /* If one full preserved block is available, write it to disk,
+ * and then buffer any remaining data. This should be effecient
+ * as long as Bacula's block size is not significantly smaller than the
+ * encryption block size (extremely unlikely!) */
+ unser_crypto_packet_len(cipher_ctx);
+ Dmsg1(500, "Crypto unser block size=%d\n", cipher_ctx->packet_len - CRYPTO_LEN_SIZE);
+
+ if (cipher_ctx->packet_len == 0 || cipher_ctx->buf_len < cipher_ctx->packet_len) {
+ /* No full preserved block is available. */
+ return 0;
+ }
+
+ /* We have one full block, set up the filter input buffers */
+ wsize = cipher_ctx->packet_len - CRYPTO_LEN_SIZE;
+ wbuf = &wbuf[CRYPTO_LEN_SIZE]; /* Skip the block length header */
+ cipher_ctx->buf_len -= cipher_ctx->packet_len;
+ Dmsg2(30, "Encryption writing full block, %u bytes, remaining %u bytes in buffer\n", wsize, cipher_ctx->buf_len);
+ }
+
+ if (flags & FO_SPARSE) {
+ if (!sparse_data(jcr, bfd, addr, &wbuf, &wsize)) {
+ return -1;
+ }
+ }
+
+ if (flags & FO_GZIP) {
+ if (!decompress_data(jcr, &wbuf, &wsize)) {
+ return -1;
+ }
+ }
+
+ if (!store_data(jcr, bfd, wbuf, wsize, (flags & FO_WIN32DECOMP) != 0)) {
+ return -1;
+ }
+ jcr->JobBytes += wsize;
+ *addr += wsize;
+ Dmsg2(30, "Write %u bytes, JobBytes=%s\n", wsize, edit_uint64(jcr->JobBytes, ec1));
+
+ /* Clean up crypto buffers */
+ if (flags & FO_ENCRYPT) {
+ /* Move any remaining data to start of buffer */
+ if (cipher_ctx->buf_len > 0) {
+ Dmsg1(30, "Moving %u buffered bytes to start of buffer\n", cipher_ctx->buf_len);
+ memmove(cipher_ctx->buf, &cipher_ctx->buf[cipher_ctx->packet_len],
+ cipher_ctx->buf_len);
+ }
+ /* The packet was successfully written, reset the length so that the next
+ * packet length may be re-read by unser_crypto_packet_len() */
+ cipher_ctx->packet_len = 0;
+ }
+ return wsize;
+}
+
+/*
+ * In the context of jcr, flush any remaining data from the cipher context,
+ * writing it to bfd.
+ * Return value is true on success, false on failure.
+ */
+bool flush_cipher(JCR *jcr, BFILE *bfd, uint64_t *addr, int flags,
+ RESTORE_CIPHER_CTX *cipher_ctx)
+{
+ uint32_t decrypted_len = 0;
+ char *wbuf; /* write buffer */
+ uint32_t wsize; /* write size */
+ char ec1[50]; /* Buffer printing huge values */
+ bool second_pass = false;
+
+again:
+ /* Write out the remaining block and free the cipher context */
+ cipher_ctx->buf = check_pool_memory_size(cipher_ctx->buf, cipher_ctx->buf_len +
+ cipher_ctx->block_size);
+
+ if (!crypto_cipher_finalize(cipher_ctx->cipher, (uint8_t *)&cipher_ctx->buf[cipher_ctx->buf_len],
+ &decrypted_len)) {
+ /* Writing out the final, buffered block failed. Shouldn't happen. */
+ Jmsg3(jcr, M_ERROR, 0, _("Decryption error. buf_len=%d decrypt_len=%d on file %s\n"),
+ cipher_ctx->buf_len, decrypted_len, jcr->last_fname);
+ }
+
+ Dmsg2(30, "Flush decrypt len=%d buf_len=%d\n", decrypted_len, cipher_ctx->buf_len);
+ /* If nothing new was decrypted, and our output buffer is empty, return */
+ if (decrypted_len == 0 && cipher_ctx->buf_len == 0) {
+ return true;
+ }
+
+ cipher_ctx->buf_len += decrypted_len;
+
+ unser_crypto_packet_len(cipher_ctx);
+ Dmsg1(500, "Crypto unser block size=%d\n", cipher_ctx->packet_len - CRYPTO_LEN_SIZE);
+ wsize = cipher_ctx->packet_len - CRYPTO_LEN_SIZE;
+ wbuf = &cipher_ctx->buf[CRYPTO_LEN_SIZE]; /* Decrypted, possibly decompressed output here. */
+ cipher_ctx->buf_len -= cipher_ctx->packet_len;
+ Dmsg2(30, "Encryption writing full block, %u bytes, remaining %u bytes in buffer\n", wsize, cipher_ctx->buf_len);
+
+ if (flags & FO_SPARSE) {
+ if (!sparse_data(jcr, bfd, addr, &wbuf, &wsize)) {
+ return false;
+ }
+ }
+
+ if (flags & FO_GZIP) {
+ if (!decompress_data(jcr, &wbuf, &wsize)) {
+ return false;
+ }
+ }
+
+ Dmsg0(30, "Call store_data\n");
+ if (!store_data(jcr, bfd, wbuf, wsize, (flags & FO_WIN32DECOMP) != 0)) {
+ return false;
+ }
+ jcr->JobBytes += wsize;
+ Dmsg2(30, "Flush write %u bytes, JobBytes=%s\n", wsize, edit_uint64(jcr->JobBytes, ec1));
+
+ /* Move any remaining data to start of buffer */
+ if (cipher_ctx->buf_len > 0) {
+ Dmsg1(30, "Moving %u buffered bytes to start of buffer\n", cipher_ctx->buf_len);
+ memmove(cipher_ctx->buf, &cipher_ctx->buf[cipher_ctx->packet_len],
+ cipher_ctx->buf_len);
+ }
+ /* The packet was successfully written, reset the length so that the next
+ * packet length may be re-read by unser_crypto_packet_len() */
+ cipher_ctx->packet_len = 0;
+
+ if (cipher_ctx->buf_len >0 && !second_pass) {
+ second_pass = true;
+ goto again;
+ }
+
+ /* Stop decryption */
+ cipher_ctx->buf_len = 0;
+ cipher_ctx->packet_len = 0;
+
+ return true;
+}
+
+static void deallocate_cipher(r_ctx &rctx)
+{
+ /* Flush and deallocate previous stream's cipher context */
+ if (rctx.cipher_ctx.cipher) {
+ flush_cipher(rctx.jcr, &rctx.bfd, &rctx.fileAddr, rctx.flags, &rctx.cipher_ctx);
+ crypto_cipher_free(rctx.cipher_ctx.cipher);
+ rctx.cipher_ctx.cipher = NULL;
+ }
+}
+
+static void deallocate_fork_cipher(r_ctx &rctx)
+{
+
+ /* Flush and deallocate previous stream's fork cipher context */
+ if (rctx.fork_cipher_ctx.cipher) {
+ flush_cipher(rctx.jcr, &rctx.forkbfd, &rctx.fork_addr, rctx.fork_flags, &rctx.fork_cipher_ctx);
+ crypto_cipher_free(rctx.fork_cipher_ctx.cipher);
+ rctx.fork_cipher_ctx.cipher = NULL;
+ }
+}
+
+static void free_signature(r_ctx &rctx)
+{
+ if (rctx.sig) {
+ crypto_sign_free(rctx.sig);
+ rctx.sig = NULL;
+ }
+}
+
+static void free_session(r_ctx &rctx)
+{
+ if (rctx.cs) {
+ crypto_session_free(rctx.cs);
+ rctx.cs = NULL;
+ }
+}
+
+
+/* This code if implemented goes above */
+#ifdef stbernard_implemented
+/ #if defined(HAVE_WIN32)
+ bool bResumeOfmOnExit = FALSE;
+ if (isOpenFileManagerRunning()) {
+ if ( pauseOpenFileManager() ) {
+ Jmsg(jcr, M_INFO, 0, _("Open File Manager paused\n") );
+ bResumeOfmOnExit = TRUE;
+ }
+ else {
+ Jmsg(jcr, M_ERROR, 0, _("FAILED to pause Open File Manager\n") );
+ }
+ }
+ {
+ char username[UNLEN+1];
+ DWORD usize = sizeof(username);
+ int privs = enable_backup_privileges(NULL, 1);
+ if (GetUserName(username, &usize)) {
+ Jmsg2(jcr, M_INFO, 0, _("Running as '%s'. Privmask=%#08x\n"), username,
+ } else {
+ Jmsg(jcr, M_WARNING, 0, _("Failed to retrieve current UserName\n"));
+ }
+ }
+#endif