+ if (is_bopen(&altbfd)) {
+ bclose_chksize(jcr, &altbfd, alt_size);
+ }
+ if (extract) {
+ /* Flush and deallocate cipher context */
+ if (cipher_ctx) {
+ flush_cipher(jcr, &bfd, flags, cipher_ctx, cipher_block_size);
+ crypto_cipher_free(cipher_ctx);
+ cipher_ctx = NULL;
+ }
+ set_attributes(jcr, attr, &bfd);
+
+ /* Verify the cryptographic signature on the last file, if any */
+ if (jcr->pki_sign) {
+ if (sig) {
+ // Failure is reported in verify_signature() ...
+ verify_signature(jcr, sig);
+ } else {
+ Jmsg1(jcr, M_ERROR, 0, _("Missing cryptographic signature for %s\n"), jcr->last_fname);
+ }
+ }
+ }
+
+ if (is_bopen(&bfd)) {
+ bclose(&bfd);
+ }
+
+ set_jcr_job_status(jcr, JS_Terminated);
+ goto ok_out;
+
+bail_out:
+ set_jcr_job_status(jcr, JS_ErrorTerminated);
+ok_out:
+
+ /* Free Signature & Crypto Data */
+ if (sig) {
+ crypto_sign_free(sig);
+ sig = NULL;
+ }
+ if (cs) {
+ crypto_session_free(cs);
+ cs = NULL;
+ }
+ if (cipher_ctx) {
+ crypto_cipher_free(cipher_ctx);
+ cipher_ctx = NULL;
+ }
+ if (jcr->compress_buf) {
+ free(jcr->compress_buf);
+ jcr->compress_buf = NULL;
+ jcr->compress_buf_size = 0;
+ }
+ if (jcr->crypto_buf) {
+ free_pool_memory(jcr->crypto_buf);
+ jcr->crypto_buf = NULL;
+ }
+ bclose(&altbfd);
+ bclose(&bfd);
+ free_attr(attr);
+ free_pool_memory(jcr->acl_text);
+ Dmsg2(10, "End Do Restore. Files=%d Bytes=%s\n", jcr->JobFiles,
+ edit_uint64(jcr->JobBytes, ec1));
+ if (non_support_data > 1 || non_support_attr > 1) {
+ Jmsg(jcr, M_ERROR, 0, _("%d non-supported data streams and %d non-supported attrib streams ignored.\n"),
+ non_support_data, non_support_attr);
+ }
+ if (non_support_rsrc) {
+ Jmsg(jcr, M_INFO, 0, _("%d non-supported resource fork streams ignored.\n"), non_support_rsrc);
+ }
+ if (non_support_finfo) {
+ Jmsg(jcr, M_INFO, 0, _("%d non-supported Finder Info streams ignored.\n"), non_support_rsrc);
+ }
+ if (non_support_acl) {
+ Jmsg(jcr, M_INFO, 0, _("%d non-supported acl streams ignored.\n"), non_support_acl);
+ }
+
+}
+
+/*
+ * 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*");
+ }
+}
+
+static int do_file_digest(FF_PKT *ff_pkt, void *pkt, bool top_level)
+{
+ JCR *jcr = (JCR *)pkt;
+ return (digest_file(jcr, ff_pkt, jcr->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: Better signature failure handling.
+ */
+int verify_signature(JCR *jcr, SIGNATURE *sig)
+{
+ X509_KEYPAIR *keypair;
+ DIGEST *digest = NULL;
+ crypto_error_t err;
+
+ /* Iterate through the trusted signers */
+ foreach_alist(keypair, jcr->pki_signers) {
+ err = crypto_sign_get_digest(sig, jcr->pki_keypair, &digest);
+
+ switch (err) {
+ case CRYPTO_ERROR_NONE:
+ /* Signature found, digest allocated */
+ jcr->digest = digest;
+
+ /* Checksum the entire file */
+ if (find_one_file(jcr, jcr->ff, do_file_digest, jcr, jcr->last_fname, (dev_t)-1, 1) != 0) {
+ Qmsg(jcr, M_ERROR, 0, _("Signature validation failed for %s: \n"), jcr->last_fname);
+ return false;
+ }
+
+ /* Verify the signature */
+ if ((err = crypto_sign_verify(sig, keypair, digest)) != CRYPTO_ERROR_NONE) {
+ Dmsg1(100, "Bad signature on %s\n", jcr->last_fname);
+ Qmsg2(jcr, M_ERROR, 0, _("Signature validation failed for %s: %s\n"), jcr->last_fname, crypto_strerror(err));
+ crypto_digest_free(digest);
+ return false;
+ }
+
+ /* Valid signature */
+ Dmsg1(100, "Signature good on %s\n", jcr->last_fname);
+ crypto_digest_free(digest);
+ return true;
+
+ case CRYPTO_ERROR_NOSIGNER:
+ /* Signature not found, try again */
+ 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));
+ if (digest) {
+ crypto_digest_free(digest);
+ }
+ return false;
+ }
+ }
+
+ /* No signer */
+ Dmsg1(100, "Could not find a valid public key for signature on %s\n", jcr->last_fname);
+ crypto_digest_free(digest);
+ return false;
+}
+
+/*
+ * 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, CIPHER_CONTEXT *cipher, uint32_t cipher_block_size)
+{
+ int stat;
+ char *wbuf; /* write buffer */
+ uint32_t wsize; /* write size */
+ uint32_t rsize; /* read size */
+ char ec1[50]; /* Buffer printing huge values */
+ const uint8_t *cipher_input; /* Decryption input */
+ uint32_t cipher_input_len; /* Decryption input length */
+ uint32_t decrypted_len = 0; /* Decryption output length */
+
+ if (flags & FO_SPARSE) {
+ ser_declare;
+ uint64_t faddr;
+ char ec1[50];
+ wbuf = buf + SPARSE_FADDR_SIZE;
+ rsize = buflen - SPARSE_FADDR_SIZE;
+ ser_begin(buf, SPARSE_FADDR_SIZE);
+ unser_uint64(faddr);
+ if (*addr != faddr) {
+ *addr = faddr;
+ if (blseek(bfd, (off_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.strerror(bfd->berrno));
+ return -1;
+ }
+ }
+ } else {
+ wbuf = buf;
+ rsize = buflen;
+ }
+ wsize = rsize;
+ cipher_input = (uint8_t *)wbuf;
+ cipher_input_len = (uint32_t)wsize;
+
+ if (flags & FO_GZIP) {
+#ifdef HAVE_LIBZ
+ uLong compress_len;
+ /*
+ * 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, wsize);
+ if ((stat=uncompress((Byte *)jcr->compress_buf, &compress_len,
+ (const Byte *)wbuf, (uLong)rsize)) != Z_OK) {
+ Qmsg(jcr, M_ERROR, 0, _("Uncompression error on file %s. ERR=%s\n"),
+ jcr->last_fname, zlib_strerror(stat));
+ return -1;
+ }
+ wbuf = jcr->compress_buf;
+ wsize = compress_len;
+ cipher_input = (uint8_t *)jcr->compress_buf; /* decrypt decompressed data */
+ cipher_input_len = compress_len;
+ Dmsg2(100, "Write uncompressed %d bytes, total before write=%s\n", compress_len, edit_uint64(jcr->JobBytes, ec1));
+#else
+ Qmsg(jcr, M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
+ return -1;
+#endif
+ } else {
+ Dmsg2(30, "Write %u bytes, total before write=%s\n", wsize, edit_uint64(jcr->JobBytes, ec1));
+ }
+
+ if (flags & FO_ENCRYPT) {
+ ASSERT(cipher);
+
+ /*
+ * Grow the crypto buffer, if necessary.
+ * crypto_cipher_update() will process only whole blocks,
+ * buffering the remaining input.
+ */
+ jcr->crypto_buf = check_pool_memory_size(jcr->crypto_buf, cipher_input_len + cipher_block_size);
+
+
+ /* Encrypt the input block */
+ if (!crypto_cipher_update(cipher, cipher_input, cipher_input_len, (uint8_t *)jcr->crypto_buf, &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 data available, write more data */
+ goto ok;
+ }
+
+ Dmsg2(400, "decrypted len=%d undecrypted len=%d\n",
+ decrypted_len, cipher_input_len);
+ wsize = decrypted_len;
+ wbuf = jcr->crypto_buf; /* Decrypted, possibly decompressed output here. */
+ }
+
+
+ if (flags & FO_WIN32DECOMP) {
+ if (!processWin32BackupAPIBlock(bfd, wbuf, wsize)) {
+ berrno be;
+ Jmsg2(jcr, M_ERROR, 0, _("Write error in Win32 Block Decomposition on %s: %s\n"),
+ jcr->last_fname, be.strerror(bfd->berrno));
+ return -1;
+ }
+ } else if (bwrite(bfd, wbuf, wsize) != (ssize_t)wsize) {
+ berrno be;
+ Jmsg2(jcr, M_ERROR, 0, _("Write error on %s: %s\n"),
+ jcr->last_fname, be.strerror(bfd->berrno));
+ return -1;