/*
Bacula® - The Network Backup Solution
- Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
+ Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
The main author of Bacula is Kern Sibbald, with contributions from
many others, a complete list can be found in the file AUTHORS.
// TODO landonf: Allow user to specify encryption algorithm
crypto_cipher_t cipher = CRYPTO_CIPHER_AES_128_CBC;
+
sd = jcr->store_bsock;
set_jcr_job_status(jcr, JS_Running);
/* Get the session data size */
if (crypto_session_encode(jcr->pki_session, (uint8_t *)0, &size) == false) {
- Jmsg(jcr, M_FATAL, 0, _("An error occured while encrypting the stream.\n"));
+ Jmsg(jcr, M_FATAL, 0, _("An error occurred while encrypting the stream.\n"));
return 0;
}
/* Encode session data */
if (crypto_session_encode(jcr->pki_session, jcr->pki_session_encoded, &size) == false) {
- Jmsg(jcr, M_FATAL, 0, _("An error occured while encrypting the stream.\n"));
+ Jmsg(jcr, M_FATAL, 0, _("An error occurred while encrypting the stream.\n"));
return 0;
}
*/
static int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level)
{
- int stat, data_stream;
+ bool do_read = false;
+ int stat, data_stream;
+ int rtnstat = 0;
DIGEST *digest = NULL;
DIGEST *signing_digest = NULL;
int digest_stream = STREAM_NONE;
+ SIGNATURE *sig = NULL;
+ uint8_t *buf = NULL;
bool has_file_data = false;
// TODO landonf: Allow the user to specify the digest algorithm
#ifdef HAVE_SHA2
if (jcr->pki_sign) {
signing_digest = crypto_digest_new(signing_algorithm);
- /* Full-stop if a failure occured initializing the signature digest */
+ /* Full-stop if a failure occurred initializing the signature digest */
if (signing_digest == NULL) {
Jmsg(jcr, M_NOTSAVED, 0, _("%s signature digest initialization failed\n"),
stream_to_ascii(signing_algorithm));
jcr->Errors++;
- return 1;
+ goto good_rtn;
}
}
if (!set_prog(&ff_pkt->bfd, ff_pkt->reader, jcr)) {
Jmsg(jcr, M_FATAL, 0, _("Python reader program \"%s\" not found.\n"),
ff_pkt->reader);
- return 0;
+ goto bail_out;
}
}
/* Send attributes -- must be done after binit() */
if (!encode_and_send_attributes(jcr, ff_pkt, data_stream)) {
- return 0;
+ goto bail_out;
}
/*
* Note, if is_win32_backup, we must open the Directory so that
* the BackupRead will save its permissions and ownership streams.
*/
- if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) &&
- ff_pkt->statp.st_size > 0) ||
- ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO ||
+
+ if (ff_pkt->type != FT_LNKSAVED && S_ISREG(ff_pkt->statp.st_mode)) {
+#ifdef HAVE_WIN32
+ do_read = !is_portable_backup(&ff_pkt->bfd) || ff_pkt->statp.st_size > 0;
+#else
+ do_read = ff_pkt->statp.st_size > 0;
+#endif
+ } else if (ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO ||
(!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) {
+ do_read = true;
+ }
+
+ if (do_read) {
btimer_t *tid;
if (ff_pkt->type == FT_FIFO) {
tid = start_thread_timer(pthread_self(), 60);
stop_thread_timer(tid);
tid = NULL;
}
- return 1;
+ goto good_rtn;
}
if (tid) {
stop_thread_timer(tid);
/* Set up the encryption context, send the session data to the SD */
if (jcr->pki_encrypt) {
/* Send our header */
+ Dmsg2(100, "Send hdr fi=%ld stream=%d\n", jcr->JobFiles, STREAM_ENCRYPTED_SESSION_DATA);
bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, STREAM_ENCRYPTED_SESSION_DATA);
/* Grow the bsock buffer to fit our message if necessary */
sd->msglen = jcr->pki_session_encoded_size;
jcr->JobBytes += sd->msglen;
+ Dmsg1(100, "Send data len=%d\n", sd->msglen);
bnet_send(sd);
bnet_sig(sd, BNET_EOD);
}
stat = send_data(jcr, data_stream, ff_pkt, digest, signing_digest);
bclose(&ff_pkt->bfd);
if (!stat) {
- return 0;
+ goto bail_out;
}
}
ff_pkt->flags & FO_HFSPLUS)) {
if (ff_pkt->hfsinfo.rsrclength > 0) {
int flags;
+ int rsrc_stream;
if (!bopen_rsrc(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) {
ff_pkt->ff_errno = errno;
berrno be;
if (is_bopen(&ff_pkt->bfd)) {
bclose(&ff_pkt->bfd);
}
- return 1;
+ goto good_rtn;
}
flags = ff_pkt->flags;
ff_pkt->flags &= ~(FO_GZIP|FO_SPARSE);
- stat = send_data(jcr, STREAM_MACOS_FORK_DATA, ff_pkt, digest, signing_digest);
+ if (flags & FO_ENCRYPT) {
+ rsrc_stream = STREAM_ENCRYPTED_MACOS_FORK_DATA;
+ } else {
+ rsrc_stream = STREAM_MACOS_FORK_DATA;
+ }
+ stat = send_data(jcr, rsrc_stream, ff_pkt, digest, signing_digest);
ff_pkt->flags = flags;
bclose(&ff_pkt->bfd);
if (!stat) {
- return 0;
+ goto bail_out;
}
}
if (ff_pkt->flags & FO_ACL) {
/* Read access ACLs for files, dirs and links */
if (!read_and_send_acl(jcr, BACL_TYPE_ACCESS, STREAM_UNIX_ATTRIBUTES_ACCESS_ACL)) {
- return 0;
+ goto bail_out;
}
/* Directories can have default ACLs too */
if (ff_pkt->type == FT_DIREND && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
if (!read_and_send_acl(jcr, BACL_TYPE_DEFAULT, STREAM_UNIX_ATTRIBUTES_DEFAULT_ACL)) {
- return 0;
+ goto bail_out;
}
}
}
/* Terminate the signing digest and send it to the Storage daemon */
if (signing_digest) {
- SIGNATURE *sig;
uint32_t size = 0;
- uint8_t *buf;
if ((sig = crypto_sign_new()) == NULL) {
Jmsg(jcr, M_FATAL, 0, _("Failed to allocate memory for stream signature.\n"));
- return 0;
+ goto bail_out;
}
if (crypto_sign_add_signer(sig, signing_digest, jcr->pki_keypair) == false) {
- Jmsg(jcr, M_FATAL, 0, _("An error occured while signing the stream.\n"));
- return 0;
+ Jmsg(jcr, M_FATAL, 0, _("An error occurred while signing the stream.\n"));
+ goto bail_out;
}
/* Get signature size */
if (crypto_sign_encode(sig, NULL, &size) == false) {
- Jmsg(jcr, M_FATAL, 0, _("An error occured while signing the stream.\n"));
- return 0;
+ Jmsg(jcr, M_FATAL, 0, _("An error occurred while signing the stream.\n"));
+ goto bail_out;
}
/* Allocate signature data buffer */
buf = (uint8_t *)malloc(size);
if (!buf) {
- crypto_sign_free(sig);
- return 0;
+ goto bail_out;
}
/* Encode signature data */
if (crypto_sign_encode(sig, buf, &size) == false) {
- Jmsg(jcr, M_FATAL, 0, _("An error occured while signing the stream.\n"));
- return 0;
+ Jmsg(jcr, M_FATAL, 0, _("An error occurred while signing the stream.\n"));
+ goto bail_out;
}
/* Send our header */
sd->msglen = size;
bnet_send(sd);
bnet_sig(sd, BNET_EOD); /* end of checksum */
-
- crypto_digest_free(signing_digest);
- crypto_sign_free(sig);
- free(buf);
+ goto good_rtn;
}
/* Terminate any digest and send it to Storage daemon and the Director */
bnet_send(sd);
bnet_sig(sd, BNET_EOD); /* end of checksum */
}
+ }
+good_rtn:
+ rtnstat = 1; /* good return */
+
+bail_out:
+ if (digest) {
crypto_digest_free(digest);
}
-
- return 1;
+ if (signing_digest) {
+ crypto_digest_free(signing_digest);
+ }
+ if (sig) {
+ crypto_sign_free(sig);
+ }
+ if (buf) {
+ free(buf);
+ }
+ return rtnstat;
}
/*
* We return 1 on sucess and 0 on errors.
*
* ***FIXME***
- * We use ff_pkt->statp.st_size when FO_SPARSE.
+ * We use ff_pkt->statp.st_size when FO_SPARSE to know when to stop
+ * reading.
* Currently this is not a problem as the only other stream, resource forks,
* are not handled as sparse files.
*/
#endif
if (ff_pkt->flags & FO_ENCRYPT) {
+ if (ff_pkt->flags & FO_SPARSE) {
+ Jmsg0(jcr, M_FATAL, 0, _("Encrypting sparse data not supported.\n"));
+ goto err;
+ }
/* 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\n"));
+ Jmsg0(jcr, M_FATAL, 0, _("Failed to initialize encryption context.\n"));
goto err;
}
* Read the file data
*/
while ((sd->msglen=(uint32_t)bread(&ff_pkt->bfd, rbuf, rsize)) > 0) {
- int sparseBlock = 0;
/* Check for sparse blocks */
if (ff_pkt->flags & FO_SPARSE) {
ser_declare;
+ bool haveBlock = true;
if (sd->msglen == rsize &&
fileAddr+sd->msglen < (uint64_t)ff_pkt->statp.st_size ||
((ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO) &&
(uint64_t)ff_pkt->statp.st_size == 0)) {
- sparseBlock = is_buf_zero(rbuf, rsize);
+ haveBlock = !is_buf_zero(rbuf, rsize);
}
- if (!sparseBlock) {
+ if (haveBlock) {
ser_begin(wbuf, SPARSE_FADDR_SIZE);
ser_uint64(fileAddr); /* store fileAddr in begin of buffer */
}
+ fileAddr += sd->msglen; /* update file address */
+ if (!haveBlock) {
+ continue; /* skip block of zeros */
+ }
}
jcr->ReadBytes += sd->msglen; /* count bytes read */
- fileAddr += sd->msglen;
/* Uncompressed cipher input length */
cipher_input_len = sd->msglen;
#ifdef HAVE_LIBZ
/* Do compression if turned on */
- if (!sparseBlock && (ff_pkt->flags & FO_GZIP) && jcr->pZLIB_compress_workset) {
+ if (ff_pkt->flags & FO_GZIP && jcr->pZLIB_compress_workset) {
Dmsg3(400, "cbuf=0x%x rbuf=0x%x len=%u\n", cbuf, rbuf, sd->msglen);
((z_stream*)jcr->pZLIB_compress_workset)->next_in = (Bytef *)rbuf;
cipher_input_len = compress_len;
}
#endif
-
- if (!sparseBlock && (ff_pkt->flags & FO_ENCRYPT)) {
+ /*
+ * Note, here we prepend the current record length to the beginning
+ * of the encrypted data. This is because both sparse and compression
+ * restore handling want records returned to them with exactly the
+ * same number of bytes that were processed in the backup handling.
+ * That is, both are block filters rather than a stream. When doing
+ * compression, the compression routines may buffer data, so that for
+ * any one record compressed, when it is decompressed the same size
+ * will not be obtained. Of course, the buffered data eventually comes
+ * out in subsequent crypto_cipher_update() calls or at least
+ * when crypto_cipher_finalize() is called. Unfortunately, this
+ * "feature" of encryption enormously complicates the restore code.
+ */
+ if (ff_pkt->flags & FO_ENCRYPT) {
uint32_t initial_len = 0;
ser_declare;
uint8_t packet_len[sizeof(uint32_t)];
ser_begin(packet_len, sizeof(uint32_t));
- ser_uint32(cipher_input_len); /* store fileAddr in begin of buffer */
+ ser_uint32(cipher_input_len); /* store data len in begin of buffer */
+ Dmsg1(20, "Encrypt len=%d\n", cipher_input_len);
if (!crypto_cipher_update(cipher_ctx, packet_len, sizeof(packet_len),
- (u_int8_t *)jcr->crypto_buf, &initial_len)) {
+ (u_int8_t *)jcr->crypto_buf, &initial_len)) {
/* 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) {
- 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;
- }
+ 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);
/* #endif */
} /* end while read file data */
- /* Send any remaining encrypted data + padding */
- if (sd->msglen >= 0) {
- if (ff_pkt->flags & FO_ENCRYPT) {
- if (!crypto_cipher_finalize(cipher_ctx, (uint8_t *)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 */
-
- sd->msg = jcr->crypto_buf; /* 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 */
- }
- }
- } else {
+ if (sd->msglen < 0) { /* error */
berrno be;
Jmsg(jcr, M_ERROR, 0, _("Read error on file %s. ERR=%s\n"),
ff_pkt->fname, be.strerror(ff_pkt->bfd.berrno));
if (jcr->Errors++ > 1000) { /* insanity check */
Jmsg(jcr, M_FATAL, 0, _("Too many errors.\n"));
}
+ } else if (ff_pkt->flags & FO_ENCRYPT) {
+ /*
+ * For encryption, we must call finalize to push out any
+ * buffered data.
+ */
+ if (!crypto_cipher_finalize(cipher_ctx, (uint8_t *)jcr->crypto_buf,
+ &encrypted_len)) {
+ /* Padding failed. Shouldn't happen. */
+ Jmsg(jcr, M_FATAL, 0, _("Encryption padding error\n"));
+ goto err;
+ }
+
+ /* Note, on SSL pre-0.9.7, there is always some output */
+ if (encrypted_len > 0) {
+ sd->msglen = encrypted_len; /* set encrypted length */
+ sd->msg = jcr->crypto_buf; /* 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 (!bnet_sig(sd, BNET_EOD)) { /* indicate end of file data */