]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] add hash to map files
authorEvan Hunt <each@isc.org>
Thu, 2 May 2013 05:20:02 +0000 (22:20 -0700)
committerEvan Hunt <each@isc.org>
Thu, 2 May 2013 05:20:02 +0000 (22:20 -0700)
3562. [func] Update map file header format to include a SHA-1 hash
of the database content, so that corrupted map files
can be rejected at load time. [RT #32459]

CHANGES
bin/tests/system/masterformat/clean.sh
bin/tests/system/masterformat/tests.sh
lib/dns/include/dns/rbt.h
lib/dns/master.c
lib/dns/rbt.c
lib/dns/rbtdb.c

diff --git a/CHANGES b/CHANGES
index 9ea6935f582e754c778fd1383db070acfe005dbd..8f5c44c628b3f386522e33ab8a8e18cf3f6d328c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+3562.  [func]          Update map file header format to include a SHA-1 hash
+                       of the database content, so that corrupted map files
+                       can be rejected at load time. [RT #32459]
+
 3561.  [bug]           dig: issue a warning if an EDNS query returns FORMERR
                        or NOTIMP.  Adjust usage message. [RT #33363]
                        
index 366f4b7b79c275514e50de9451214b0a0b655d56..740ddef7d7e880909d800c2fcb0cc82e7a9c6096 100755 (executable)
@@ -31,5 +31,4 @@ rm -f ns2/formerly-text.db
 rm -f ns2/db-*
 rm -f ns2/large.bk
 rm -f ns3/example.db.map ns3/dynamic.db.map
-rm -f baseline.txt text.1 text.2 raw.1 raw.2 map.1 map.2
-
+rm -f baseline.txt text.1 text.2 raw.1 raw.2 map.1 map.2 map.5 text.5 badmap
index ba4a218fc24df76b93a849b2745c45c2a68efdd7..63b7b4bdaf769e1d7365da0320aa4c6f041bb541 100755 (executable)
@@ -58,6 +58,16 @@ sourceserial () {
              }' < $1
 }
 
+stomp () {
+        perl -e 'open(my $file, "+<", $ARGV[0]);
+                 binmode $file;
+                 seek($file, $ARGV[1], 0);
+                 for (my $i = 0; $i < $ARGV[2]; $i++) {
+                         print $file pack('C', $ARGV[3]);
+                 }
+                 close($file);' $1 $2 $3 $4
+}
+
 restart () {
     sleep 1
     (cd ..; $PERL start.pl --noclean --restart masterformat ns3)
@@ -218,7 +228,28 @@ for i in 0 1 2 3 4 5 6 7 8 9; do
     [ $lret -eq 0 ] && break;
 done
 [ $lret -eq 1 ] && ret=1
+[ $ret -eq 0 ] || echo "I:failed"
+status=`expr $status + $ret`
 
+# stomp on the file data so it hashes differently.
+# these are small and subtle changes, so that the resulting file
+# would appear to be a legitimate map file and would not trigger an
+# assertion failure if loaded into memory, but should still fail to
+# load because of a SHA1 hash mismatch.
+echo "I:checking corrupt map files fail to load (bad node header)"
+ret=0
+./named-compilezone -D -f text -F map -o map.5 example.nil baseline.txt > /dev/null
+cp map.5 badmap
+stomp badmap 2754 2 99
+./named-compilezone -D -f map -F text -o text.5 example.nil badmap > /dev/null && ret=1
+[ $ret -eq 0 ] || echo "I:failed"
+status=`expr $status + $ret`
+
+echo "I:checking corrupt map files fail to load (bad node data)"
+ret=0
+cp map.5 badmap
+stomp badmap 2897 5 127
+./named-compilezone -D -f map -F text -o text.5 example.nil badmap > /dev/null && ret=1
 [ $ret -eq 0 ] || echo "I:failed"
 status=`expr $status + $ret`
 
index 3ea3021aa72aec022e2eaac4c219690685c5bf18..ef9f5e5f9873ac2682b6480d9d86e5a08b8033fc 100644 (file)
@@ -25,6 +25,7 @@
 #include <isc/lang.h>
 #include <isc/magic.h>
 #include <isc/refcount.h>
+#include <isc/sha1.h>
 
 #include <dns/types.h>
 
@@ -152,7 +153,10 @@ typedef isc_result_t (*dns_rbtfindcallback_t)(dns_rbtnode_t *node,
 
 typedef isc_result_t (*dns_rbtdatawriter_t)(FILE *file,
                                            unsigned char *data,
-                                           isc_uint32_t version);
+                                           isc_uint32_t version,
+                                           isc_sha1_t *sha1);
+
+typedef void (*dns_rbtdatafixer_t)(dns_rbtnode_t *rbtnode, isc_sha1_t *sha1);
 
 /*****
  *****  Chain Info
@@ -705,7 +709,7 @@ isc_result_t
 dns_rbt_deserialize_tree(void *base_address, off_t header_offset,
                         isc_mem_t *mctx,
                         void (*deleter)(void *, void *), void *deleter_arg,
-                        void (*data_fixer)(dns_rbtnode_t *),
+                        dns_rbtdatafixer_t datafixer,
                         dns_rbtnode_t **originp, dns_rbt_t **rbtp);
 /*%<
  * Read a RBT structure and its data from a file.
index 860f59e0bbe227136a6c00e9695e8107f10a0da0..05d28bc98e8bda0d58df187c0c8c8607beeb8739 100644 (file)
@@ -2193,7 +2193,7 @@ openfile_map(dns_loadctx_t *lctx, const char *master_file) {
  */
 static isc_result_t
 load_map(dns_loadctx_t *lctx) {
-       isc_result_t result;
+       isc_result_t result = ISC_R_SUCCESS;
        dns_rdatacallbacks_t *callbacks;
 
        REQUIRE(DNS_LCTX_VALID(lctx));
@@ -2205,12 +2205,12 @@ load_map(dns_loadctx_t *lctx) {
                if (result != ISC_R_SUCCESS)
                        return (result);
 
-               (*callbacks->deserialize)(callbacks->deserialize_private,
-                                         lctx->f,
-                                         sizeof(dns_masterrawheader_t));
+               result = (*callbacks->deserialize)
+                         (callbacks->deserialize_private,
+                          lctx->f, sizeof(dns_masterrawheader_t));
        }
 
-       return (ISC_R_SUCCESS);
+       return (result);
 }
 
 static isc_result_t
index c7578fa342920ae6a9786d07bd8498c348422861..675fc0331a77c756eecee3806b02d720bbb0dd31 100644 (file)
 #endif
 
 #include <isc/file.h>
+#include <isc/hex.h>
 #include <isc/mem.h>
 #include <isc/platform.h>
 #include <isc/print.h>
 #include <isc/refcount.h>
+#include <isc/sha1.h>
 #include <isc/socket.h>
 #include <isc/stdio.h>
 #include <isc/string.h>
@@ -117,6 +119,7 @@ struct file_header {
        unsigned int bigendian:1;       /* big or little endian system */
        unsigned int rdataset_fixed:1;  /* compiled with --enable-rrset-fixed */
        unsigned int nodecount;         /* shadow from rbt structure */
+       unsigned char digest[ISC_SHA1_DIGESTLENGTH];
        char version2[32];              /* repeated; must match version1 */
 };
 
@@ -143,17 +146,18 @@ static isc_result_t
 dns_rbt_zero_header(FILE *file);
 
 static isc_result_t
-write_header(FILE *file, dns_rbt_t *rbt, isc_uint64_t first_node_offset);
+write_header(FILE *file, dns_rbt_t *rbt, isc_uint64_t first_node_offset,
+       unsigned char *digest);
 
 static isc_result_t
 serialize_node(FILE *file, dns_rbtnode_t *node, uintptr_t left,
               uintptr_t right, uintptr_t down, uintptr_t parent,
-              uintptr_t data);
+              uintptr_t data, isc_sha1_t *sha1);
 
 static isc_result_t
 serialize_nodes(FILE *file, dns_rbtnode_t *node, uintptr_t parent,
                dns_rbtdatawriter_t datawriter, isc_uint32_t serial,
-               uintptr_t *where);
+               uintptr_t *where, isc_sha1_t *sha1);
 /*
  * The following functions allow you to get the actual address of a pointer
  * without having to use an if statement to check to see if that address is
@@ -302,6 +306,20 @@ Name(dns_rbtnode_t *node) {
 }
 
 static void printnodename(dns_rbtnode_t *node);
+
+static void
+hexdump(const char *desc, unsigned char *data, size_t size) {
+       char hexdump[BUFSIZ];
+       isc_buffer_t b;
+       isc_region_t r;
+
+       isc_buffer_init(&b, hexdump, sizeof(hexdump));
+       r.base = data;
+       r.length = size;
+       isc_hex_totext(&r, 0, "", &b);
+       isc_buffer_putuint8(&b, 0);
+       fprintf(stderr, "%s: %s\n", desc, hexdump);
+}
 #endif
 
 static inline dns_rbtnode_t *
@@ -349,7 +367,7 @@ deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp);
 
 static void
 treefix(dns_rbt_t *rbt, dns_rbtnode_t *n, dns_name_t *name,
-       void (*data_fixer)(dns_rbtnode_t *));
+       dns_rbtdatafixer_t datafixer, isc_sha1_t *sha1);
 
 static isc_result_t
 deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node);
@@ -393,7 +411,9 @@ dns_rbt_zero_header(FILE *file) {
  * here.
  */
 static isc_result_t
-write_header(FILE *file, dns_rbt_t *rbt, isc_uint64_t first_node_offset) {
+write_header(FILE *file, dns_rbt_t *rbt, isc_uint64_t first_node_offset,
+            unsigned char *digest)
+{
        file_header_t header;
        isc_result_t result;
        long location;
@@ -419,6 +439,8 @@ write_header(FILE *file, dns_rbt_t *rbt, isc_uint64_t first_node_offset) {
 
        header.nodecount = rbt->nodecount;
 
+       memcpy(header.digest, digest, sizeof(header.digest));
+
        location = ftell(file);
        if (location < 0)
                return (ISC_R_FAILURE);
@@ -437,13 +459,16 @@ write_header(FILE *file, dns_rbt_t *rbt, isc_uint64_t first_node_offset) {
 static isc_result_t
 serialize_node(FILE *file, dns_rbtnode_t *node, uintptr_t left,
               uintptr_t right, uintptr_t down, uintptr_t parent,
-              uintptr_t data)
+              uintptr_t data, isc_sha1_t *sha1)
 {
        dns_rbtnode_t temp_node;
        long file_position;
        unsigned char *node_data;
        size_t datasize;
        isc_result_t result;
+#ifdef DEBUG
+       dns_name_t nodename;
+#endif
 
        INSIST(node != NULL);
 
@@ -494,9 +519,23 @@ serialize_node(FILE *file, dns_rbtnode_t *node, uintptr_t left,
 
        CHECK(isc_stdio_write(&temp_node, 1, sizeof(dns_rbtnode_t),
                              file, NULL));
-
        CHECK(isc_stdio_write(node_data, 1, datasize, file, NULL));
 
+#ifdef DEBUG
+       dns_name_init(&nodename, NULL);
+       NODENAME(node, &nodename);
+       fprintf(stderr, "serialize ");
+       dns_name_print(&nodename, stderr);
+       fprintf(stderr, "\n");
+       hexdump("node header", (unsigned char*) &temp_node,
+               sizeof(dns_rbtnode_t));
+       hexdump("node data", node_data, datasize);
+#endif
+
+       isc_sha1_update(sha1, (const isc_uint8_t *) &temp_node,
+                       sizeof(dns_rbtnode_t));
+       isc_sha1_update(sha1, (const isc_uint8_t *) node_data, datasize);
+
  cleanup:
        return (result);
 }
@@ -504,7 +543,7 @@ serialize_node(FILE *file, dns_rbtnode_t *node, uintptr_t left,
 static isc_result_t
 serialize_nodes(FILE *file, dns_rbtnode_t *node, uintptr_t parent,
                dns_rbtdatawriter_t datawriter, isc_uint32_t serial,
-               uintptr_t *where)
+               uintptr_t *where, isc_sha1_t *sha1)
 {
        uintptr_t left = 0, right = 0, down = 0, data = 0;
        long location = 0;
@@ -517,7 +556,7 @@ serialize_nodes(FILE *file, dns_rbtnode_t *node, uintptr_t parent,
                return (ISC_R_SUCCESS);
        }
 
-       /* Reserve space for current node */
+       /* Reserve space for current node. */
        location = ftell(file);
        if (location < 0)
                return (ISC_R_FAILURE);
@@ -530,13 +569,18 @@ serialize_nodes(FILE *file, dns_rbtnode_t *node, uintptr_t parent,
        offset_adjust = dns_rbt_serialize_align(location + NODE_SIZE(node));
        CHECK(isc_stdio_seek(file, offset_adjust, SEEK_SET));
 
-       /* Serialize the rest of the tree */
+       /*
+        * Serialize the rest of the tree.
+        *
+        * WARNING: A change in the order (from left, right, down)
+        * will break the way the sha1 hash is computed.
+        */
        CHECK(serialize_nodes(file, getleft(node, NULL), location,
-                             datawriter, serial, &left));
+                             datawriter, serial, &left, sha1));
        CHECK(serialize_nodes(file, getright(node, NULL), location,
-                             datawriter, serial, &right));
+                             datawriter, serial, &right, sha1));
        CHECK(serialize_nodes(file, getdown(node, NULL), location,
-                             datawriter, serial, &down));
+                             datawriter, serial, &down, sha1));
 
        if (node->data != NULL) {
                long ret;
@@ -553,14 +597,15 @@ serialize_nodes(FILE *file, dns_rbtnode_t *node, uintptr_t parent,
                        return (ISC_R_FAILURE);
                data = ret;
 
-               datawriter(file, node->data, serial);
+               datawriter(file, node->data, serial, sha1);
        }
 
-       /* Seek back to reserved space */
+       /* Seek back to reserved space. */
        CHECK(isc_stdio_seek(file, location, SEEK_SET));
 
-       /* Serialize the current node */
-       CHECK(serialize_node(file, node, left, right, down, parent, data));
+       /* Serialize the current node. */
+       CHECK(serialize_node(file, node, left, right, down, parent, data,
+               sha1));
 
        /* Ensure we are always at the end of the file. */
        CHECK(isc_stdio_seek(file, 0, SEEK_END));
@@ -589,11 +634,15 @@ dns_rbt_serialize_tree(FILE *file, dns_rbt_t *rbt,
 {
        isc_result_t result;
        long header_position, node_position, end_position;
+       unsigned char digest[ISC_SHA1_DIGESTLENGTH];
+       isc_sha1_t sha1;
 
        REQUIRE(file != NULL);
 
        CHECK(isc_file_isplainfilefd(fileno(file)));
 
+       isc_sha1_init(&sha1);
+
        header_position = ftell(file);
        if (header_position < 0)
                return (ISC_R_FAILURE);
@@ -606,7 +655,8 @@ dns_rbt_serialize_tree(FILE *file, dns_rbt_t *rbt,
        if (node_position < 0)
                return (ISC_R_FAILURE);
 
-       CHECK(serialize_nodes(file, rbt->root, 0, datawriter, serial, NULL));
+       CHECK(serialize_nodes(file, rbt->root, 0, datawriter, serial, NULL,
+               &sha1));
        end_position = ftell(file);
        if (end_position < 0)
                return (ISC_R_FAILURE);
@@ -617,9 +667,14 @@ dns_rbt_serialize_tree(FILE *file, dns_rbt_t *rbt,
                return (ISC_R_SUCCESS);
        }
 
+       isc_sha1_final(&sha1, digest);
+#ifdef DEBUG
+       hexdump("serializing digest", digest, sizeof(digest));
+#endif
+
        /* Serialize header */
        CHECK(isc_stdio_seek(file, header_position, SEEK_SET));
-       CHECK(write_header(file, rbt, HEADER_LENGTH));
+       CHECK(write_header(file, rbt, HEADER_LENGTH, digest));
 
        /* Ensure we are always at the end of the file. */
        CHECK(isc_stdio_seek(file, 0, SEEK_END));
@@ -631,11 +686,14 @@ dns_rbt_serialize_tree(FILE *file, dns_rbt_t *rbt,
 
 static void
 treefix(dns_rbt_t *rbt, dns_rbtnode_t *n, dns_name_t *name,
-               void (*data_fixer)(dns_rbtnode_t *))
+       dns_rbtdatafixer_t datafixer, isc_sha1_t *sha1)
 {
        isc_result_t result;
        dns_fixedname_t fixed;
        dns_name_t nodename, *fullname;
+       unsigned char *node_data;
+       dns_rbtnode_t header;
+       size_t datasize;
 
        if (n == NULL)
                return;
@@ -652,6 +710,9 @@ treefix(dns_rbt_t *rbt, dns_rbtnode_t *n, dns_name_t *name,
                /* XXX: we need to catch errors better than this */
        }
 
+       /* memorize header contents prior to fixup */
+       memcpy(&header, n, sizeof(header));
+
        INSIST(!n->parent_is_relative || n->parent != NULL);
        INSIST(!n->right_is_relative || n->right != NULL);
        INSIST(!n->left_is_relative || n->left != NULL);
@@ -675,15 +736,32 @@ treefix(dns_rbt_t *rbt, dns_rbtnode_t *n, dns_name_t *name,
 
        hash_node(rbt, n, fullname);
 
-       if (data_fixer != NULL && n->data != NULL)
-               data_fixer(n);
-
-       if (n->right != NULL)
-               treefix(rbt, n->right, name, data_fixer);
+       /* a change in the order (from left, right, down) will break hashing*/
        if (n->left != NULL)
-               treefix(rbt, n->left, name, data_fixer);
+               treefix(rbt, n->left, name, datafixer, sha1);
+       if (n->right != NULL)
+               treefix(rbt, n->right, name, datafixer, sha1);
        if (n->down != NULL)
-               treefix(rbt, n->down, fullname, data_fixer);
+               treefix(rbt, n->down, fullname, datafixer, sha1);
+
+       if (datafixer != NULL && n->data != NULL)
+               datafixer(n, sha1);
+
+       node_data = (unsigned char *) n + sizeof(dns_rbtnode_t);
+       datasize = NODE_SIZE(n) - sizeof(dns_rbtnode_t);
+
+#ifdef DEBUG
+       fprintf(stderr, "deserialize ");
+       dns_name_print(&nodename, stderr);
+       fprintf(stderr, "\n");
+       hexdump("node header", (unsigned char *) &header,
+               sizeof(dns_rbtnode_t));
+       hexdump("node data", node_data, datasize);
+#endif
+       isc_sha1_update(sha1, (const isc_uint8_t *) &header,
+                       sizeof(dns_rbtnode_t));
+       isc_sha1_update(sha1, (const isc_uint8_t *) node_data,
+                       datasize);
 }
 
 isc_result_t
@@ -691,14 +769,18 @@ dns_rbt_deserialize_tree(void *base_address, off_t header_offset,
                         isc_mem_t *mctx,
                         void (*deleter)(void *, void *),
                         void *deleter_arg,
-                        void (*data_fixer)(dns_rbtnode_t *),
+                        dns_rbtdatafixer_t datafixer,
                         dns_rbtnode_t **originp, dns_rbt_t **rbtp)
 {
        isc_result_t result;
        file_header_t *header;
+       unsigned char digest[ISC_SHA1_DIGESTLENGTH];
+       isc_sha1_t sha1;
 
        REQUIRE(originp == NULL || *originp == NULL);
 
+       isc_sha1_init(&sha1);
+
        result = dns_rbt_create(mctx, deleter, deleter_arg, rbtp);
        if (result != ISC_R_SUCCESS)
                return (result);
@@ -729,7 +811,15 @@ dns_rbt_deserialize_tree(void *base_address, off_t header_offset,
        (*rbtp)->root = (dns_rbtnode_t *)((char *)base_address +
                                header_offset + header->first_node_offset);
        (*rbtp)->nodecount = header->nodecount;
-       treefix(*rbtp, (*rbtp)->root, dns_rootname, data_fixer);
+       treefix(*rbtp, (*rbtp)->root, dns_rootname, datafixer, &sha1);
+
+       isc_sha1_final(&sha1, digest);
+#ifdef DEBUG
+       hexdump("deserializing digest", digest, sizeof(digest));
+#endif
+
+       if (memcmp(header->digest, digest, sizeof(digest)) != 0)
+               return (ISC_R_INVALIDFILE);
 
        if (originp != NULL)
                *originp = (*rbtp)->root;
@@ -749,7 +839,6 @@ dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
 #endif
        dns_rbt_t *rbt;
 
-
        REQUIRE(mctx != NULL);
        REQUIRE(rbtp != NULL && *rbtp == NULL);
        REQUIRE(deleter == NULL ? deleter_arg == NULL : 1);
@@ -820,13 +909,17 @@ dns_rbt_destroy2(dns_rbt_t **rbtp, unsigned int quantum) {
 
 unsigned int
 dns_rbt_nodecount(dns_rbt_t *rbt) {
+
        REQUIRE(VALID_RBT(rbt));
+
        return (rbt->nodecount);
 }
 
 unsigned int
 dns_rbt_hashsize(dns_rbt_t *rbt) {
+
        REQUIRE(VALID_RBT(rbt));
+
        return (rbt->hashsize);
 }
 
@@ -2552,6 +2645,7 @@ deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp) {
 static isc_result_t
 deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node) {
        isc_result_t result = ISC_R_SUCCESS;
+
        REQUIRE(VALID_RBT(rbt));
 
        if (node == NULL)
@@ -2607,6 +2701,7 @@ static void
 deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, dns_rbtnode_t **nodep) {
        dns_rbtnode_t *parent;
        dns_rbtnode_t *node = *nodep;
+
        REQUIRE(VALID_RBT(rbt));
 
  again:
@@ -2760,6 +2855,7 @@ dns_rbt_printtree(dns_rbtnode_t *root, dns_rbtnode_t *parent,
 
 void
 dns_rbt_printall(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *)) {
+
        REQUIRE(VALID_RBT(rbt));
 
        dns_rbt_printtree(rbt->root, NULL, 0, "root", data_printer);
@@ -2771,12 +2867,12 @@ dns_rbt_printall(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *)) {
 
 void
 dns_rbtnodechain_init(dns_rbtnodechain_t *chain, isc_mem_t *mctx) {
-       /*
-        * Initialize 'chain'.
-        */
 
        REQUIRE(chain != NULL);
 
+       /*
+        * Initialize 'chain'.
+        */
        chain->mctx = mctx;
        chain->end = NULL;
        chain->level_count = 0;
@@ -3217,13 +3313,13 @@ dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
 
 void
 dns_rbtnodechain_reset(dns_rbtnodechain_t *chain) {
+
+       REQUIRE(VALID_CHAIN(chain));
+
        /*
         * Free any dynamic storage associated with 'chain', and then
         * reinitialize 'chain'.
         */
-
-       REQUIRE(VALID_CHAIN(chain));
-
        chain->end = NULL;
        chain->level_count = 0;
        chain->level_matches = 0;
index 2cdbfd3b615a3a67ca435e5814634b8404828ebf..e70342859c15df10fdf110651c2dbc8cfcec47f1 100644 (file)
@@ -30,6 +30,7 @@
 #include <isc/event.h>
 #include <isc/heap.h>
 #include <isc/file.h>
+#include <isc/hex.h>
 #include <isc/mem.h>
 #include <isc/mutex.h>
 #include <isc/platform.h>
@@ -728,6 +729,25 @@ static unsigned int init_count;
  * For zone databases the node for the origin of the zone MUST NOT be deleted.
  */
 
+/*
+ * Debugging routines
+ */
+#ifdef DEBUG
+static void
+hexdump(const char *desc, unsigned char *data, size_t size) {
+       char hexdump[BUFSIZ];
+       isc_buffer_t b;
+       isc_region_t r;
+
+       isc_buffer_init(&b, hexdump, sizeof(hexdump));
+       r.base = data;
+       r.length = size;
+       isc_hex_totext(&r, 0, "", &b);
+       isc_buffer_putuint8(&b, 0);
+       fprintf(stderr, "%s: %s\n", desc, hexdump);
+}
+#endif
+
 
 /*
  * DB Routines
@@ -6976,7 +6996,7 @@ loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) {
 }
 
 static void
-fix_data(dns_rbtnode_t *rbtnode) {
+rbt_datafixer(dns_rbtnode_t *rbtnode, isc_sha1_t *sha1) {
        rdatasetheader_t *header;
        unsigned char *p;
        size_t size;
@@ -6984,12 +7004,18 @@ fix_data(dns_rbtnode_t *rbtnode) {
        REQUIRE(rbtnode != NULL);
 
        for (header = rbtnode->data; header != NULL; header = header->next) {
+               p = (unsigned char *) header;
+               size = dns_rdataslab_size(p, sizeof(*header));
+               isc_sha1_update(sha1, p, size);
+#ifdef DEBUG
+               hexdump("hashing header", p, sizeof(rdatasetheader_t));
+               hexdump("hashing slab", p + sizeof(rdatasetheader_t),
+                       size - sizeof(rdatasetheader_t));
+#endif
                header->serial = 1;
                header->is_mmapped = 1;
                header->node = rbtnode;
                header->node_is_relative = 0;
-               p = (unsigned char *) header;
-               size = dns_rdataslab_size(p, sizeof(*header));
                if (header->next != NULL) {
                        header->next = (rdatasetheader_t *)(p + size);
                        header->next_is_relative = 0;
@@ -7002,6 +7028,7 @@ fix_data(dns_rbtnode_t *rbtnode) {
  */
 static isc_result_t
 deserialize(void *arg, FILE *f, off_t offset) {
+       isc_result_t result;
        rbtdb_load_t *loadctx = arg;
        dns_rbtdb_t *rbtdb = loadctx->rbtdb;
        rbtdb_file_header_t *header;
@@ -7039,11 +7066,12 @@ deserialize(void *arg, FILE *f, off_t offset) {
        rbtdb->origin_node = NULL;
 
        if (header->tree != 0) {
-               dns_rbt_deserialize_tree(base, header->tree,
-                                        rbtdb->common.mctx, delete_callback,
-                                        rbtdb, fix_data,
-                                        &rbtdb->origin_node,
-                                        &temporary_rbt);
+               result = dns_rbt_deserialize_tree(base, header->tree,
+                                                 rbtdb->common.mctx,
+                                                 delete_callback,
+                                                 rbtdb, rbt_datafixer,
+                                                 &rbtdb->origin_node,
+                                                 &temporary_rbt);
                if (temporary_rbt != NULL) {
                        dns_rbt_destroy(&rbtdb->tree);
                        rbtdb->tree = temporary_rbt;
@@ -7052,28 +7080,38 @@ deserialize(void *arg, FILE *f, off_t offset) {
                        rbtdb->origin_node =
                                (dns_rbtnode_t *)(header->tree + base + 1024);
                }
+               if (result != ISC_R_SUCCESS)
+                       return (result);
        }
 
        if (header->nsec != 0) {
-               dns_rbt_deserialize_tree(base, header->nsec,
-                                        rbtdb->common.mctx, delete_callback,
-                                        rbtdb, fix_data, NULL, &temporary_rbt);
+               result = dns_rbt_deserialize_tree(base, header->nsec,
+                                                 rbtdb->common.mctx,
+                                                 delete_callback,
+                                                 rbtdb, rbt_datafixer,
+                                                 NULL, &temporary_rbt);
                if (temporary_rbt != NULL) {
                        dns_rbt_destroy(&rbtdb->nsec);
                        rbtdb->nsec = temporary_rbt;
                        temporary_rbt = NULL;
                }
+               if (result != ISC_R_SUCCESS)
+                       return (result);
        }
 
        if (header->nsec3 != 0) {
-               dns_rbt_deserialize_tree(base, header->nsec3,
-                                        rbtdb->common.mctx, delete_callback,
-                                        rbtdb, fix_data, NULL, &temporary_rbt);
+               result = dns_rbt_deserialize_tree(base, header->nsec3,
+                                                 rbtdb->common.mctx,
+                                                 delete_callback,
+                                                 rbtdb, rbt_datafixer,
+                                                 NULL, &temporary_rbt);
                if (temporary_rbt != NULL) {
                        dns_rbt_destroy(&rbtdb->nsec3);
                        rbtdb->nsec3 = temporary_rbt;
                        temporary_rbt = NULL;
                }
+               if (result != ISC_R_SUCCESS)
+                       return (result);
        }
 
        return (ISC_R_SUCCESS);
@@ -7151,7 +7189,7 @@ endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
         * If there's a KEY rdataset at the zone origin containing a
         * zone key, we consider the zone secure.
         */
-       if (! IS_CACHE(rbtdb))
+       if (! IS_CACHE(rbtdb) && rbtdb->origin_node != NULL)
                iszonesecure(db, rbtdb->current_version, rbtdb->origin_node);
 
        callbacks->add = NULL;
@@ -7169,7 +7207,8 @@ endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
  * by the void *data pointer in the dns_rbtnode
  */
 static isc_result_t
-rbt_datawriter(FILE *rbtfile, unsigned char *data, isc_uint32_t serial) {
+rbt_datawriter(FILE *rbtfile, unsigned char *data, isc_uint32_t serial, 
+               isc_sha1_t *sha1) {
        rdatasetheader_t newheader;
        rdatasetheader_t *header = (rdatasetheader_t *) data, *next;
        size_t where, size;
@@ -7210,12 +7249,22 @@ rbt_datawriter(FILE *rbtfile, unsigned char *data, isc_uint32_t serial) {
                        newheader.next_is_relative = 1;
                }
 
+#ifdef DEBUG
+               hexdump("writing header", (unsigned char *) &newheader,
+                       sizeof(rdatasetheader_t));
+               hexdump("writing slab", p + sizeof(rdatasetheader_t),
+                       size - sizeof(rdatasetheader_t));
+#endif
+               isc_sha1_update(sha1, (unsigned char *) &newheader,
+                               sizeof(rdatasetheader_t));
                result = isc_stdio_write(&newheader,
                                         sizeof(rdatasetheader_t), 1,
                                         rbtfile, NULL);
                if (result != ISC_R_SUCCESS)
                        return (result);
 
+               isc_sha1_update(sha1, p + sizeof(rdatasetheader_t),
+                               size - sizeof(rdatasetheader_t));
                result = isc_stdio_write(p + sizeof(rdatasetheader_t),
                             size - sizeof(rdatasetheader_t), 1, rbtfile, NULL);
                if (result != ISC_R_SUCCESS)