]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/filed/restore.c
18Jun08
[bacula/bacula] / bacula / src / filed / restore.c
index 2a197a51168d736063dd3b93552dd9f3b566e098..299b78f9e2a5b33902fbe92add40c289f14028d2 100644 (file)
@@ -1,14 +1,14 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2008 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.
    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.
+   License as published by the Free Software Foundation and included
+   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
@@ -56,6 +56,13 @@ const bool have_acl = true;
 const bool have_acl = false;
 #endif
 
+#ifdef HAVE_SHA2
+   const bool have_sha2 = true;
+#else
+   const bool have_sha2 = false;
+#endif
+
+
 /* Data received from Storage Daemon */
 static char rec_header[] = "rechdr %ld %ld %ld %ld %ld";
 
@@ -76,14 +83,15 @@ struct r_ctx {
    uint64_t fileAddr;                  /* file write address */
    uint32_t size;                      /* Size of file */
    int flags;                          /* Options for extract_data() */
-   BFILE forkbfd;                       /* Alternative data stream */
-   uint64_t fork_addr;                  /* Write address for alternative stream */
-   intmax_t fork_size;                  /* Size of alternate stream */
-   int fork_flags;                      /* Options for extract_data() */
+   BFILE forkbfd;                      /* Alternative data stream */
+   uint64_t fork_addr;                 /* Write address for alternative stream */
+   intmax_t fork_size;                 /* Size of alternate stream */
+   int fork_flags;                     /* Options for extract_data() */
+   int32_t type;                       /* file type FT_ */
 
    SIGNATURE *sig;                     /* Cryptographic signature (if any) for file */
    CRYPTO_SESSION *cs;                 /* Cryptographic session data (if any) for file */
-   RESTORE_CIPHER_CTX cipher_ctx;     /* Cryptographic restore context (if any) for file */
+   RESTORE_CIPHER_CTX cipher_ctx;      /* Cryptographic restore context (if any) for file */
    RESTORE_CIPHER_CTX fork_cipher_ctx; /* Cryptographic restore context (if any) for alternative stream */
 };
 
@@ -103,7 +111,7 @@ static void free_session(r_ctx &rctx);
 
 
 
-static bool verify_signature(JCR *jcr, SIGNATURE *sig);
+static bool verify_signature(JCR *jcr, r_ctx &rctx);
 int32_t extract_data(JCR *jcr, BFILE *bfd, POOLMEM *buf, int32_t buflen,
                      uint64_t *addr, int flags, RESTORE_CIPHER_CTX *cipher_ctx);
 bool flush_cipher(JCR *jcr, BFILE *bfd, uint64_t *addr, int flags, 
@@ -112,9 +120,9 @@ bool flush_cipher(JCR *jcr, BFILE *bfd, uint64_t *addr, int flags,
 
 /*
  * Close a bfd check that we are at the expected file offset.
- * Makes some code in set_attributes().
+ * Makes use of some code from set_attributes().
  */
-int bclose_chksize(JCR *jcr, BFILE *bfd, boffset_t osize)
+static int bclose_chksize(JCR *jcr, BFILE *bfd, boffset_t osize)
 {
    char ec1[50], ec2[50];
    boffset_t fsize;
@@ -147,7 +155,9 @@ void do_restore(JCR *jcr)
    ATTR *attr;
    intmax_t rsrc_len = 0;             /* Original length of resource fork */
    r_ctx rctx;
-
+   /* ***FIXME*** make configurable */
+   crypto_digest_t signing_algorithm = have_sha2 ? 
+                                       CRYPTO_DIGEST_SHA256 : CRYPTO_DIGEST_SHA1;
    memset(&rctx, 0, sizeof(rctx));
    rctx.jcr = jcr;
 
@@ -185,7 +195,7 @@ void do_restore(JCR *jcr)
    }
    jcr->buf_size = sd->msglen;
 
-   /* St Bernard code goes here if implemented */
+   /* St Bernard code goes here if implemented -- see end of file */
 
    if (have_libz) {
       uint32_t compress_buf_size = jcr->buf_size + 12 + ((jcr->buf_size+999) / 1000) + 100;
@@ -204,15 +214,15 @@ void do_restore(JCR *jcr)
     * Get a record from the Storage daemon. We are guaranteed to
     *   receive records in the following order:
     *   1. Stream record header
-    *   2. Stream data
+    *   2. Stream data (one or more of the following in the order given)
     *        a. Attributes (Unix or Win32)
     *        b. Possibly stream encryption session data (e.g., symmetric session key)
-    *    or  c. File data for the file
-    *    or  d. Alternate data stream (e.g. Resource Fork)
-    *    or  e. Finder info
-    *    or  f. ACLs
-    *    or  g. Possibly a cryptographic signature
-    *    or  h. Possibly MD5 or SHA1 record
+    *        c. File data for the file
+    *        d. Alternate data stream (e.g. Resource Fork)
+    *        e. Finder info
+    *        f. ACLs
+    *        g. Possibly a cryptographic signature
+    *        h. Possibly MD5 or SHA1 record
     *   3. Repeat step 1
     *
     * NOTE: We keep track of two bacula file descriptors:
@@ -225,12 +235,16 @@ void do_restore(JCR *jcr)
     *      close it again.
     *      The expected size of the stream, fork_len, should be set when
     *      opening the fd.
+    *   3. Not all the stream data records are required -- e.g. if there
+    *      is no fork, there is no alternate data stream, no ACL, ...
     */
    binit(&rctx.bfd);
    binit(&rctx.forkbfd);
-   attr = new_attr();
+   attr = new_attr(jcr);
    jcr->acl_text = get_pool_memory(PM_MESSAGE);
 
+   
+
    while (bget_msg(sd) >= 0 && !job_canceled(jcr)) {
       /* Remember previous stream type */
       rctx.prev_stream = rctx.stream;
@@ -246,7 +260,7 @@ void do_restore(JCR *jcr)
 
       /* * Now we expect the Stream Data */
       if (bget_msg(sd) < 0) {
-         Jmsg1(jcr, M_FATAL, 0, _("Data record error. ERR=%s\n"), bnet_strerror(sd));
+         Jmsg1(jcr, M_FATAL, 0, _("Data record error. ERR=%s\n"), sd->bstrerror());
          goto bail_out;
       }
       if (rctx.size != (uint32_t)sd->msglen) {
@@ -285,11 +299,16 @@ void do_restore(JCR *jcr)
                deallocate_fork_cipher(rctx);
             }
 
-            set_attributes(jcr, attr, &rctx.bfd);
+            if (jcr->plugin) {
+               plugin_set_attributes(jcr, attr, &rctx.bfd);
+            } else {
+               set_attributes(jcr, attr, &rctx.bfd);
+            }
             extract = false;
 
             /* Verify the cryptographic signature, if any */
-            verify_signature(jcr, rctx.sig);
+            rctx.type = attr->type;
+            verify_signature(jcr, rctx);
 
             /* Free Signature */
             free_signature(rctx);
@@ -301,6 +320,11 @@ void do_restore(JCR *jcr)
             bclose(&rctx.bfd);
          }
 
+         /* TODO: manage deleted files */
+         if (rctx.type == FT_DELETED) { /* deleted file */
+            continue;
+         }
+
          /*
           * Unpack attributes and do sanity check them
           */
@@ -330,11 +354,16 @@ void do_restore(JCR *jcr)
          build_attr_output_fnames(jcr, attr);
 
          /*
-          * Now determine if we are extracting or not.
+          * Try to actually create the file, which returns a status telling
+          *  us if we need to extract or not.
           */
          jcr->num_files_examined++;
          extract = false;
-         stat = create_file(jcr, attr, &rctx.bfd, jcr->replace);
+         if (jcr->plugin) {
+            stat = plugin_create_file(jcr, attr, &rctx.bfd, jcr->replace);
+         } else {
+            stat = create_file(jcr, attr, &rctx.bfd, jcr->replace);
+         }
          Dmsg2(30, "Outfile=%s create_file stat=%d\n", attr->ofname, stat);
          switch (stat) {
          case CF_ERROR:
@@ -361,7 +390,11 @@ void do_restore(JCR *jcr)
             }
             if (!extract) {
                /* set attributes now because file will not be extracted */
-               set_attributes(jcr, attr, &rctx.bfd);
+               if (jcr->plugin) {
+                  plugin_set_attributes(jcr, attr, &rctx.bfd);
+               } else {
+                  set_attributes(jcr, attr, &rctx.bfd);
+               }
             }
             break;
          }
@@ -380,16 +413,27 @@ void do_restore(JCR *jcr)
          }
 
          /* Do we have any keys at all? */
-         if (!jcr->pki_recipients) {
+         if (!jcr->crypto.pki_recipients) {
             Jmsg(jcr, M_ERROR, 0, _("No private decryption keys have been defined to decrypt encrypted backup data.\n"));
             extract = false;
             bclose(&rctx.bfd);
             break;
          }
 
+         if (jcr->crypto.digest) {
+            crypto_digest_free(jcr->crypto.digest);
+         }  
+         jcr->crypto.digest = crypto_digest_new(jcr, signing_algorithm);
+         if (!jcr->crypto.digest) {
+            Jmsg0(jcr, M_FATAL, 0, _("Could not create digest.\n"));
+            extract = false;
+            bclose(&rctx.bfd);
+            break;
+         }
+
          /* Decode and save session keys. */
          cryptoerr = crypto_session_decode((uint8_t *)sd->msg, (uint32_t)sd->msglen, 
-                        jcr->pki_recipients, &rctx.cs);
+                        jcr->crypto.pki_recipients, &rctx.cs);
          switch(cryptoerr) {
          case CRYPTO_ERROR_NONE:
             /* Success */
@@ -488,22 +532,22 @@ void do_restore(JCR *jcr)
       case STREAM_ENCRYPTED_MACOS_FORK_DATA:
       case STREAM_MACOS_FORK_DATA:
 #ifdef HAVE_DARWIN_OS
-         fork_flags = 0;
+         rctx.fork_flags = 0;
          jcr->ff->flags |= FO_HFSPLUS;
 
-         if (stream == STREAM_ENCRYPTED_MACOS_FORK_DATA) {
-            fork_flags |= FO_ENCRYPT;
+         if (rctx.stream == STREAM_ENCRYPTED_MACOS_FORK_DATA) {
+            rctx.fork_flags |= FO_ENCRYPT;
 
             /* Set up a decryption context */
             if (extract && !rctx.fork_cipher_ctx.cipher) {
-               if (!cs) {
+               if (!rctx.cs) {
                   Jmsg1(jcr, M_ERROR, 0, _("Missing encryption session data stream for %s\n"), jcr->last_fname);
                   extract = false;
                   bclose(&rctx.bfd);
                   continue;
                }
 
-               if ((rctx.fork_cipher_ctx.cipher = crypto_cipher_new(cs, false, &rctx.fork_cipher_ctx.block_size)) == NULL) {
+               if ((rctx.fork_cipher_ctx.cipher = crypto_cipher_new(rctx.cs, false, &rctx.fork_cipher_ctx.block_size)) == NULL) {
                   Jmsg1(jcr, M_ERROR, 0, _("Failed to initialize decryption context for %s\n"), jcr->last_fname);
                   free_session(rctx);
                   extract = false;
@@ -514,21 +558,21 @@ void do_restore(JCR *jcr)
          }
 
          if (extract) {
-            if (prev_stream != stream) {
-               if (bopen_rsrc(&forkbfd, jcr->last_fname, O_WRONLY | O_TRUNC | O_BINARY, 0) < 0) {
+            if (rctx.prev_stream != rctx.stream) {
+               if (bopen_rsrc(&rctx.forkbfd, jcr->last_fname, O_WRONLY | O_TRUNC | O_BINARY, 0) < 0) {
                   Jmsg(jcr, M_ERROR, 0, _("     Cannot open resource fork for %s.\n"), jcr->last_fname);
                   extract = false;
                   continue;
                }
 
-               fork_size = rsrc_len;
+               rctx.fork_size = rsrc_len;
                Dmsg0(30, "Restoring resource fork\n");
             }
 
-            if (extract_data(jcr, &forkbfd, sd->msg, sd->msglen, &rctx.fork_addr, fork_flags, 
-                             &rctxfork_cipher_ctx) < 0) {
+            if (extract_data(jcr, &rctx.forkbfd, sd->msg, sd->msglen, &rctx.fork_addr, rctx.fork_flags, 
+                             &rctx.fork_cipher_ctx) < 0) {
                extract = false;
-               bclose(&forkbfd);
+               bclose(&rctx.forkbfd);
                continue;
             }
          }
@@ -554,7 +598,7 @@ void do_restore(JCR *jcr)
 #endif
          break;
 
-      case STREAM_UNIX_ATTRIBUTES_ACCESS_ACL:
+      case STREAM_UNIX_ACCESS_ACL:
          if (have_acl) {
             pm_strcpy(jcr->acl_text, sd->msg);
             Dmsg2(400, "Restoring ACL type 0x%2x <%s>\n", BACL_TYPE_ACCESS, jcr->acl_text);
@@ -566,7 +610,7 @@ void do_restore(JCR *jcr)
          }
          break;
 
-      case STREAM_UNIX_ATTRIBUTES_DEFAULT_ACL:
+      case STREAM_UNIX_DEFAULT_ACL:
          if (have_acl) {
             pm_strcpy(jcr->acl_text, sd->msg);
             Dmsg2(400, "Restoring ACL type 0x%2x <%s>\n", BACL_TYPE_DEFAULT, jcr->acl_text);
@@ -605,6 +649,11 @@ void do_restore(JCR *jcr)
          }
          break;
 
+      case STREAM_PLUGIN_NAME:
+         Dmsg1(000, "restore stream_plugin_name=%s\n", sd->msg);
+         plugin_name_stream(jcr, sd->msg);
+         break;
+
       default:
          /* If extracting, wierd stream (not 1 or 2), close output file anyway */
          if (extract) {
@@ -616,10 +665,15 @@ void do_restore(JCR *jcr)
             deallocate_cipher(rctx);
             deallocate_fork_cipher(rctx);
 
-            set_attributes(jcr, attr, &rctx.bfd);
+            if (jcr->plugin) {
+               plugin_set_attributes(jcr, attr, &rctx.bfd);
+            } else {
+               set_attributes(jcr, attr, &rctx.bfd);
+            }
 
             /* Verify the cryptographic signature if any */
-            verify_signature(jcr, rctx.sig);
+            rctx.type = attr->type;
+            verify_signature(jcr, rctx);
             extract = false;
          } else if (is_bopen(&rctx.bfd)) {
             Jmsg0(jcr, M_ERROR, 0, _("Logic error: output file should not be open\n"));
@@ -627,7 +681,7 @@ void do_restore(JCR *jcr)
          }
          Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
               rctx.stream);
-         Dmsg2(0, "None of above!!! stream=%d data=%s\n", rctx.stream,sd->msg);
+         Dmsg2(0, "Unknown stream=%d data=%s\n", rctx.stream, sd->msg);
          break;
       } /* end switch(stream) */
 
@@ -644,10 +698,15 @@ void do_restore(JCR *jcr)
       deallocate_cipher(rctx);
       deallocate_fork_cipher(rctx);
 
-      set_attributes(jcr, attr, &rctx.bfd);
+      if (jcr->plugin) {
+         plugin_set_attributes(jcr, attr, &rctx.bfd);
+      } else {
+         set_attributes(jcr, attr, &rctx.bfd);
+      }
 
       /* Verify the cryptographic signature on the last file, if any */
-      verify_signature(jcr, rctx.sig);
+      rctx.type = attr->type;
+      verify_signature(jcr, rctx);
    }
 
    if (is_bopen(&rctx.bfd)) {
@@ -664,6 +723,10 @@ ok_out:
    /* Free Signature & Crypto Data */
    free_signature(rctx);
    free_session(rctx);
+   if (jcr->crypto.digest) {
+      crypto_digest_free(jcr->crypto.digest);
+      jcr->crypto.digest = NULL;
+   }
 
    /* Free file cipher restore context */
    if (rctx.cipher_ctx.cipher) {
@@ -743,11 +806,10 @@ static const char *zlib_strerror(int stat)
 }
 #endif
 
-static int do_file_digest(FF_PKT *ff_pkt, void *pkt, bool top_level) 
+static int do_file_digest(JCR *jcr, FF_PKT *ff_pkt, bool top_level) 
 {
-   JCR *jcr = (JCR *)pkt;
    Dmsg1(50, "do_file_digest jcr=%p\n", jcr);
-   return (digest_file(jcr, ff_pkt, jcr->digest));
+   return (digest_file(jcr, ff_pkt, jcr->crypto.digest));
 }
 
 /*
@@ -757,60 +819,93 @@ static int do_file_digest(FF_PKT *ff_pkt, void *pkt, bool top_level)
  * TODO landonf: Implement without using find_one_file and
  * without re-reading the file.
  */
-static bool verify_signature(JCR *jcr, SIGNATURE *sig)
+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->pki_sign) {
+
+   if (!jcr->crypto.pki_sign) {
       return true;                    /* no signature OK */
    }
    if (!sig) {
-      Jmsg1(jcr, M_ERROR, 0, _("Missing cryptographic signature for %s\n"), 
-            jcr->last_fname);
+      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->pki_signers) {
-      err = crypto_sign_get_digest(sig, jcr->pki_keypair, &digest);
+   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");
-         /* Signature found, digest allocated */
-         jcr->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, 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;
+         /*
+          * 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;
+            }  
          }
-         jcr->JobBytes = saved_bytes;
+         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;
+            /* 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);
-         jcr->digest = NULL;
          return true;
 
       case CRYPTO_ERROR_NOSIGNER:
          /* Signature not found, try again */
          if (digest) {
             crypto_digest_free(digest);
-            jcr->digest = NULL;
+            digest = NULL;
          }
          continue;
       default:
@@ -826,7 +921,6 @@ static bool verify_signature(JCR *jcr, SIGNATURE *sig)
 bail_out:
    if (digest) {
       crypto_digest_free(digest);
-      jcr->digest = NULL;
    }
    return false;
 }
@@ -895,6 +989,9 @@ static void unser_crypto_packet_len(RESTORE_CIPHER_CTX *ctx)
 
 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;
@@ -1017,7 +1114,6 @@ int32_t extract_data(JCR *jcr, BFILE *bfd, POOLMEM *buf, int32_t buflen,
        * packet length may be re-read by unser_crypto_packet_len() */
       cipher_ctx->packet_len = 0;
    }
-
    return wsize;
 }