]> git.ipfire.org Git - thirdparty/ldns.git/commitdiff
new tool to verify a DNSSEC signed zone (no context, only internals
authorJelte Jansen <jelte@NLnetLabs.nl>
Mon, 21 Apr 2008 09:58:50 +0000 (09:58 +0000)
committerJelte Jansen <jelte@NLnetLabs.nl>
Mon, 21 Apr 2008 09:58:50 +0000 (09:58 +0000)
are checked: is every RRSET signed, do the signatures actually verify
with any of the keys at the apex, etc). Not every possible problem
will be detected, but it's a start

examples/Makefile.in
examples/ldns-verifyzone.c [new file with mode: 0644]

index 8b2c7fdbb718a2d205bc76ca6cb24616a7eff5cc..37de52c550bc637264ea12e01f1ccfee78041fc9 100644 (file)
@@ -46,7 +46,8 @@ MAIN_SOURCES = ldns-read-zone.c \
                  ldns-keyfetcher.c \
                  ldns-notify.c \
                  ldns-testns.c \
-                 ldns-compare-zones.c
+                 ldns-compare-zones.c \
+                  ldns-verifyzone.c
 OTHER_SOURCES = ldns-testpkts.c
 
 PROGRAMS=$(MAIN_SOURCES:.c=)
diff --git a/examples/ldns-verifyzone.c b/examples/ldns-verifyzone.c
new file mode 100644 (file)
index 0000000..bae1f03
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * read a zone file from disk and prints it, one RR per line
+ *
+ * (c) NLnetLabs 2005-2008
+ *
+ * See the file LICENSE for the license
+ */
+
+#include "config.h"
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <ldns/ldns.h>
+
+#include <errno.h>
+
+#ifdef HAVE_SSL
+#include <openssl/err.h>
+#endif
+
+int verbosity = 3;
+
+bool
+ldns_rr_list_contains_name(const ldns_rr_list *rr_list,
+                                         const ldns_rdf *name)
+{
+       size_t i;
+       for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) {
+               if (ldns_dname_compare(name, 
+                                                  ldns_rr_owner(ldns_rr_list_rr(rr_list, 
+                                                                                                  i))
+                                                  )
+                   ) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+void
+print_type(ldns_rr_type type)
+{
+       const ldns_rr_descriptor *descriptor;
+
+       descriptor = ldns_rr_descript(type);
+       if (descriptor && descriptor->_name) {
+               fprintf(stdout, "%s", descriptor->_name);
+       } else {
+               fprintf(stdout, "TYPE%u",
+                          type);
+       }
+
+}
+
+ldns_dnssec_zone *
+create_dnssec_zone(ldns_zone *orig_zone)
+{
+       size_t i;
+       ldns_dnssec_zone *dnssec_zone;
+
+       dnssec_zone = ldns_dnssec_zone_new();
+       if (ldns_dnssec_zone_add_rr(dnssec_zone, ldns_zone_soa(orig_zone)) !=
+           LDNS_STATUS_OK) {
+               fprintf(stderr, "Error adding SOA to dnssec zone, skipping record\n");
+       }
+
+       for (i = 0; i < ldns_rr_list_rr_count(ldns_zone_rrs(orig_zone)); i++) {
+               if (ldns_dnssec_zone_add_rr(dnssec_zone, 
+                                                          ldns_rr_list_rr(ldns_zone_rrs(orig_zone), 
+                                                                                   i)) !=
+                   LDNS_STATUS_OK) {
+                       fprintf(stderr, "Error adding RR to dnssec zone");
+                       fprintf(stderr, ", skipping record:\n");
+                       ldns_rr_print(stderr, 
+                                           ldns_rr_list_rr(ldns_zone_rrs(orig_zone), i));
+               }
+       }
+
+       return dnssec_zone;
+}
+
+ldns_status
+verify_dnssec_rrset(ldns_dnssec_rrsets *rrset, ldns_rr_list *keys)
+{
+       ldns_rr_list *rrset_rrs;
+       ldns_dnssec_rrs *cur_rr, *cur_sig;
+       ldns_status status;
+       ldns_rr_list *good_keys;
+       ldns_status result = LDNS_STATUS_OK;
+       
+       rrset_rrs = ldns_rr_list_new();
+       cur_rr = rrset->rrs;
+       while(cur_rr) {
+               ldns_rr_list_push_rr(rrset_rrs, cur_rr->rr);
+               cur_rr = cur_rr->next;
+       }
+       cur_sig = rrset->signatures;
+       if (cur_sig) {
+               while (cur_sig) {
+                       good_keys = ldns_rr_list_new();
+                       status = ldns_verify_rrsig_keylist(rrset_rrs,
+                                                                               cur_sig->rr,
+                                                                               keys,
+                                                                               good_keys);
+                       if (status != LDNS_STATUS_OK) {
+                               printf("Error: %s", ldns_get_errorstr_by_id(status));
+                               printf(" for ");
+                               ldns_rdf_print(stdout, ldns_rr_owner(rrset->rrs->rr));
+                               printf("\t");
+                               print_type(rrset->type);
+                               printf("\n");
+                               if (result == LDNS_STATUS_OK) {
+                                       result = status;
+                               }
+#ifdef HAVE_SSL
+                               if (status == LDNS_STATUS_SSL_ERR) {
+                                       ERR_load_crypto_strings();
+                                       ERR_print_errors_fp(stdout);
+                               }
+#endif
+                       }
+                       ldns_rr_list_free(good_keys);
+                       cur_sig = cur_sig->next;
+               }
+       } else {
+               printf("Error: no signatures for ");
+               ldns_rdf_print(stdout, ldns_rr_owner(rrset->rrs->rr));
+               printf("\t");
+               print_type(rrset->type);
+               printf("\n");
+       }
+       return result;
+}
+
+ldns_status
+verify_single_rr(ldns_rr *rr,
+                         ldns_dnssec_rrs *signature_rrs,
+                         ldns_rr_list *keys)
+{
+       ldns_rr_list *rrset_rrs;
+       ldns_rr_list *good_keys;
+       ldns_dnssec_rrs *cur_sig;
+       ldns_status status;
+       ldns_status result = LDNS_STATUS_OK;
+    
+       rrset_rrs = ldns_rr_list_new();
+       ldns_rr_list_push_rr(rrset_rrs, rr);
+
+       cur_sig = signature_rrs;
+       while (cur_sig) {
+               good_keys = ldns_rr_list_new();
+               status = ldns_verify_rrsig_keylist(rrset_rrs,
+                                                                       cur_sig->rr,
+                                                                       keys,
+                                                                       good_keys);
+               if (status != LDNS_STATUS_OK) {
+                       printf("Error: %s ", ldns_get_errorstr_by_id(status));
+                       if (result == LDNS_STATUS_OK) {
+                               result = status;
+                       }
+                       printf("for ");
+                       ldns_rdf_print(stdout, ldns_rr_owner(rr));
+                       printf("\t");
+                       print_type(ldns_rr_get_type(rr));
+                       printf("\n");
+#ifdef HAVE_SSL
+                       if (status == LDNS_STATUS_SSL_ERR) {
+                               ERR_load_crypto_strings();
+                               ERR_print_errors_fp(stdout);
+                       }
+#endif
+               }
+               ldns_rr_list_free(good_keys);
+               cur_sig = cur_sig->next;
+       }
+
+       return result;
+
+}
+
+ldns_status
+verify_dnssec_name(ldns_dnssec_name *name,
+                           ldns_rr_list *keys,
+                           ldns_rr_list *glue_rrs)
+{
+       ldns_status result = LDNS_STATUS_OK;
+       ldns_status status;
+       ldns_dnssec_rrsets *cur_rrset;
+
+       printf("Checking ");
+       ldns_rdf_print(stdout, name->name);
+       printf("\n");
+
+       if (ldns_rr_list_contains_name(glue_rrs, name->name)) {
+               /* glue */
+               cur_rrset = name->rrsets;
+               while (cur_rrset) {
+                       if (cur_rrset->signatures) {
+                               printf("Error: ");
+                               ldns_rdf_print(stdout, name->name);
+                               printf("\t");
+                               print_type(cur_rrset->type);
+                               printf(" has signature(s), but is glue\n");
+                               result = LDNS_STATUS_ERR;
+                       }
+                       cur_rrset = cur_rrset->next;
+               }
+               if (name->nsec) {
+                       printf("Error: ");
+                       ldns_rdf_print(stdout, name->name);
+                       printf("\t");
+                       print_type(cur_rrset->type);
+                       printf(" has an NSEC(3), but is glue\n");
+                       result = LDNS_STATUS_ERR;
+               }
+       } else {
+               /* not glue, do real verify */
+               cur_rrset = name->rrsets;
+               while(cur_rrset) {
+                       status = verify_dnssec_rrset(cur_rrset, keys);
+                       if (status != LDNS_STATUS_OK && result == LDNS_STATUS_OK) {
+                               result = status;
+                       }
+                       cur_rrset = cur_rrset->next;
+               }
+
+               if (name->nsec) {
+                       if (name->nsec_signatures) {
+                               status = verify_single_rr(name->nsec,
+                                                                        name->nsec_signatures,
+                                                                        keys);
+                       } else {
+                               printf("Error: the NSEC(3) record of ");
+                               ldns_rdf_print(stdout, name->name);
+                               printf(" has no signatures\n");
+                               if (result == LDNS_STATUS_OK) {
+                                       result = LDNS_STATUS_ERR;
+                               }
+                       }
+               } else {
+                       printf("Error: there is no NSEC(3) for ");
+                       ldns_rdf_print(stdout, name->name);
+                       printf("\n");
+                       if (result == LDNS_STATUS_OK) {
+                               result = LDNS_STATUS_ERR;
+                       }
+               }
+       }
+       return result;
+}
+
+ldns_status
+verify_dnssec_zone(ldns_dnssec_zone *dnssec_zone,
+                           ldns_rdf *zone_name,
+                           ldns_rr_list *glue_rrs)
+{
+       ldns_rr_list *keys;
+       ldns_rbnode_t *cur_node;
+       ldns_dnssec_rrsets *cur_key_rrset;
+       ldns_dnssec_rrs *cur_key;
+       ldns_dnssec_name *cur_name;
+       ldns_status status;
+       ldns_status result = LDNS_STATUS_OK;
+
+       keys = ldns_rr_list_new();
+       cur_key_rrset = ldns_dnssec_zone_find_rrset(dnssec_zone,
+                                                                           zone_name,
+                                                                           LDNS_RR_TYPE_DNSKEY);
+       if (!cur_key_rrset || !cur_key_rrset->rrs) {
+               printf("No DNSKEY records at zone apex\n");
+               result = LDNS_STATUS_ERR;
+       } else {
+               cur_key = cur_key_rrset->rrs;
+               while (cur_key) {
+                       ldns_rr_list_push_rr(keys, cur_key->rr);
+                       cur_key = cur_key->next;
+               }
+
+               cur_node = ldns_rbtree_first(dnssec_zone->names);
+               if (cur_node == LDNS_RBTREE_NULL) {
+                       printf("Empty zone?\n");
+               }
+               while (cur_node != LDNS_RBTREE_NULL) {
+                       cur_name = (ldns_dnssec_name *) cur_node->data;
+                       status = verify_dnssec_name(cur_name, keys, glue_rrs);
+                       if (status != LDNS_STATUS_OK && result == LDNS_STATUS_OK) {
+                               result = status;
+                       }
+                       cur_node = ldns_rbtree_next(cur_node);
+               }
+       }
+
+       ldns_rr_list_free(keys);
+       return result;
+}
+
+int
+main(int argc, char **argv)
+{
+       char *filename;
+       FILE *fp;
+       ldns_zone *z;
+       int line_nr = 0;
+       int c;
+       bool canonicalize = false;
+       bool sort = false;
+       bool strip = false;
+       ldns_status s;
+       size_t i;
+       ldns_rr_list *stripped_list;
+       ldns_rr *cur_rr;
+       ldns_dnssec_zone *dnssec_zone;
+       ldns_status result = LDNS_STATUS_ERR;
+       ldns_rr_list *glue_rrs;
+       
+       while ((c = getopt(argc, argv, "chsvz")) != -1) {
+               switch(c) {
+               case 'c':
+                       canonicalize = true;
+                       break;
+               case 'h':
+                       printf("Usage: %s [OPTIONS] <zonefile>\n", argv[0]);
+                       printf("\tReads the zonefile and checks for DNSSEC errors.\n");
+                       printf("\nIt checks whether NSEC(3)s are present,");
+                       printf(" and verifies all signatures\n");
+                       printf("It does NOT check the NSEC(3) chain itself\n");
+                       printf("\nOPTIONS:\n");
+                       printf("\t-h show this text\n");
+                       printf("\t-v shows the version and exits\n");
+                       printf("\nif no file is given standard input is read\n");
+                       exit(EXIT_SUCCESS);
+                       break;
+               case 's':
+                       strip = true;
+                       break;
+               case 'v':
+                       printf("read zone version %s (ldns version %s)\n", 
+                                 LDNS_VERSION, ldns_version());
+                       exit(EXIT_SUCCESS);
+                       break;
+               case 'z':
+                       canonicalize = true;
+                       sort = true;
+                       break;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       
+       if (argc == 0) {
+               fp = stdin;
+       } else {
+               filename = argv[0];
+
+               fp = fopen(filename, "r");
+               if (!fp) {
+                       fprintf(stderr, "Unable to open %s: %s\n", filename, strerror(errno));
+                       exit(EXIT_FAILURE);
+               }
+       }
+       
+       s = ldns_zone_new_frm_fp_l(&z, fp, NULL, 0, LDNS_RR_CLASS_IN, &line_nr);
+
+       if (strip) {
+               stripped_list = ldns_rr_list_new();
+               while ((cur_rr = ldns_rr_list_pop_rr(ldns_zone_rrs(z)))) {
+                       if (ldns_rr_get_type(cur_rr) == LDNS_RR_TYPE_RRSIG ||
+                           ldns_rr_get_type(cur_rr) == LDNS_RR_TYPE_NSEC
+                          ) {
+                               
+                               ldns_rr_free(cur_rr);
+                       } else {
+                               ldns_rr_list_push_rr(stripped_list, cur_rr);
+                       }
+               }
+               ldns_rr_list_free(ldns_zone_rrs(z));
+               ldns_zone_set_rrs(z, stripped_list);
+       }
+
+       glue_rrs = ldns_zone_glue_rr_list(z);
+       printf("GLUE RRS:\n");
+       ldns_rr_list_print(stdout, glue_rrs);
+
+       if (s == LDNS_STATUS_OK) {
+               if (canonicalize) {
+                       ldns_rr2canonical(ldns_zone_soa(z));
+                       for (i = 0; i < ldns_rr_list_rr_count(ldns_zone_rrs(z)); i++) {
+                               ldns_rr2canonical(ldns_rr_list_rr(ldns_zone_rrs(z), i));
+                       }
+               }
+               if (sort) {
+                       ldns_zone_sort(z);
+               }
+
+               /*ldns_zone_print(stdout, z);*/
+               dnssec_zone = create_dnssec_zone(z);
+
+               result = verify_dnssec_zone(dnssec_zone, 
+                                                          ldns_rr_owner(ldns_zone_soa(z)),
+                                                          glue_rrs);
+
+               if (result == LDNS_STATUS_OK) {
+                       printf("Zone is verified and complete\n");
+               } else {
+                       printf("There were errors in the zone\n");
+               }
+
+               ldns_zone_deep_free(z);
+       } else {
+               fprintf(stderr, "%s at %d\n", 
+                               ldns_get_errorstr_by_id(s),
+                               line_nr);
+                exit(EXIT_FAILURE);
+       }
+       fclose(fp);
+
+       exit(result);
+}