[ldns-users] [PATCH 3/3] Add ldns_verify_trusted and associated

Simon Vallet svallet at genoscope.cns.fr
Mon May 14 10:22:58 UTC 2007


this one adds an ldns_verify_trusted() method and associated utility
functions to build a trust path and verify the sig using the apex RRset.

It also adds status messages in case there is no valid DS along the path.

Simon

Index: ldns/dnssec.h
===================================================================
--- ldns/dnssec.h	(revision 2344)
+++ ldns/dnssec.h	(working copy)
@@ -29,6 +29,7 @@
 #include <ldns/packet.h>
 #include <ldns/keys.h>
 #include <ldns/zone.h>
+#include <ldns/resolver.h>
 
 #define LDNS_MAX_KEYLEN		2048
 #define LDNS_DNSSEC_KEYPROTO	3
@@ -52,7 +53,7 @@
  * \param[out] good_keys  if this is a (initialized) list, the keys from keys that validate one of the signatures are added to it
  * \return status LDNS_STATUS_OK if there is at least one correct key
  */
-ldns_status ldns_verify(ldns_rr_list *rrset, ldns_rr_list *rrsig, ldns_rr_list *keys, ldns_rr_list *good_keys);	
+ldns_status ldns_verify(ldns_rr_list *rrset, ldns_rr_list *rrsig, const ldns_rr_list *keys, ldns_rr_list *good_keys);	
 
 /**
  * Verifies the already processed data in the buffers
@@ -74,7 +75,7 @@
  * \param[out] good_keys  if this is a (initialized) list, the keys from keys that validate one of the signatures are added to it
  * \return a list of keys which validate the rrsig + rrset. Return NULL when none of the keys validate.
  */
-ldns_status ldns_verify_rrsig_keylist(ldns_rr_list *rrset, ldns_rr *rrsig, ldns_rr_list *keys, ldns_rr_list *good_keys);
+ldns_status ldns_verify_rrsig_keylist(ldns_rr_list *rrset, ldns_rr *rrsig, const ldns_rr_list *keys, ldns_rr_list *good_keys);
 
 /**
  * verify an rrsig with 1 key
@@ -237,4 +238,52 @@
  */
 ldns_status ldns_init_random(FILE *fd, uint16_t bytes);
 
+/**
+ * Tries to build an authentication chain from the given keys down to the queried domain.
+ *
+ * If we find a valid trust path, return the valid keys for the domain.
+ * 
+ * \param[in] res the current resolver
+ * \param[in] domain the domain we want valid keys for
+ * \param[in] keys the current set of trusted keys
+ * \return the set of trusted keys for the domain, or NULL if no trust path could be built.
+ */
+ldns_rr_list *
+ldns_fetch_valid_domain_keys(const ldns_resolver * res, const ldns_rdf * domain, const ldns_rr_list * keys);
+
+/**
+ * Validates the DNSKEY RRset for the given domain using the provided trusted keys.
+ *
+ * \param[in] res the current resolver
+ * \param[in] domain the domain we want valid keys for
+ * \param[in] keys the current set of trusted keys
+ * \return the set of trusted keys for the domain, or NULL if the RRSET could not be validated
+ */
+ldns_rr_list *
+ldns_validate_domain_dnskey (const ldns_resolver * res, const ldns_rdf * domain, const ldns_rr_list * keys);
+
+/**
+ * Validates the DS RRset for the given domain using the provided trusted keys.
+ *
+ * \param[in] res the current resolver
+ * \param[in] domain the domain we want valid keys for
+ * \param[in] keys the current set of trusted keys
+ * \return the set of trusted keys for the domain, or NULL if the RRSET could not be validated
+ */
+ldns_rr_list *
+ldns_validate_domain_ds (const ldns_resolver * res, const ldns_rdf * domain, const ldns_rr_list * keys);
+
+/**
+ * Verifies a list of signatures for one RRset using a valid trust path.
+ *
+ * \param[in] res the current resolver
+ * \param[in] rrset the rrset to verify
+ * \param[in] rrsigs a list of signatures to check
+ * \param[out] validating_keys  if this is a (initialized) list, the keys from keys that validate one of the signatures are added to it
+ * \return status LDNS_STATUS_OK if there is at least one correct key
+ */
+ldns_status
+ldns_verify_trusted(ldns_resolver * res, ldns_rr_list * rrset, ldns_rr_list * rrsigs, ldns_rr_list * validating_keys);
+
+
 #endif /* LDNS_DNSSEC_H */
Index: dnssec.c
===================================================================
--- dnssec.c	(revision 2344)
+++ dnssec.c	(working copy)
@@ -76,7 +76,7 @@
 }
 
 ldns_status
-ldns_verify(ldns_rr_list *rrset, ldns_rr_list *rrsig, ldns_rr_list *keys, 
+ldns_verify(ldns_rr_list *rrset, ldns_rr_list *rrsig, const ldns_rr_list *keys, 
 		ldns_rr_list *good_keys)
 {
 	uint16_t i;
@@ -112,7 +112,198 @@
 	return verify_result;
 }
 
+ldns_rr_list *
+ldns_fetch_valid_domain_keys(const ldns_resolver * res, const ldns_rdf * domain, const ldns_rr_list * keys)
+{
+  ldns_status status;
+  ldns_rr_list * trusted_keys = NULL;
+  ldns_rr_list * ds_keys = NULL;
+
+  if (res && domain && keys) {
+
+    if ((trusted_keys = ldns_validate_domain_dnskey(res, domain, keys))) {
+      status = LDNS_STATUS_OK;
+    } else {
+      
+      /* No trusted keys in this domain, we'll have to find some in the parent domain */
+      status = LDNS_STATUS_CRYPTO_NO_TRUSTED_DNSKEY;
+      
+      if (ldns_rdf_size(domain) > 1) { /* Fail if we are at the root */
+        ldns_rr_list * parent_keys;
+        ldns_rdf * parent_domain = ldns_dname_left_chop(domain);
+	
+        if ((parent_keys = ldns_fetch_valid_domain_keys(res, parent_domain, keys))) {
+	  
+          /* Check DS records */
+          if ((ds_keys = ldns_validate_domain_ds(res, domain, parent_keys))) {
+            trusted_keys = ldns_fetch_valid_domain_keys(res, domain, ds_keys);
+            ldns_rr_list_deep_free(ds_keys);
+          } else {
+            /* No valid DS at the parent -- fail */
+            status = LDNS_STATUS_CRYPTO_NO_TRUSTED_DS ;
+          }
+          ldns_rr_list_deep_free(parent_keys);
+        }
+        ldns_rdf_free(parent_domain);
+      }
+    }
+  }
+
+  return trusted_keys;
+}
+
+ldns_rr_list *
+ldns_validate_domain_dnskey (const ldns_resolver * res, const ldns_rdf * domain, const ldns_rr_list * keys)
+{
+  ldns_status status;
+  ldns_pkt * keypkt;
+  ldns_rr * cur_key;
+  uint16_t key_i; uint16_t key_j; uint16_t key_k;
+  uint16_t sig_i; ldns_rr * cur_sig;
+
+  ldns_rr_list * domain_keys = NULL;
+  ldns_rr_list * domain_sigs = NULL;
+  ldns_rr_list * trusted_keys = NULL;
+
+  /* Fetch keys for the domain */
+  if ((keypkt = ldns_resolver_query(res, domain, LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN, LDNS_RD))) {
+
+    domain_keys = ldns_pkt_rr_list_by_type(keypkt, LDNS_RR_TYPE_DNSKEY, LDNS_SECTION_ANSWER);
+    domain_sigs = ldns_pkt_rr_list_by_type(keypkt, LDNS_RR_TYPE_RRSIG, LDNS_SECTION_ANSWER);
+
+    /* Try to validate the record using our keys */
+    for (key_i=0; key_i< ldns_rr_list_rr_count(domain_keys); key_i++) {
+      
+      cur_key = ldns_rr_list_rr(domain_keys, key_i);
+      for (key_j=0; key_j<ldns_rr_list_rr_count(keys); key_j++) {
+        if (ldns_rr_compare_ds(ldns_rr_list_rr(keys, key_j), cur_key)) {
+          
+          /* Current key is trusted -- validate */
+          trusted_keys = ldns_rr_list_new();
+          
+          for (sig_i=0; sig_i<ldns_rr_list_rr_count(domain_sigs); sig_i++) {
+            cur_sig = ldns_rr_list_rr(domain_sigs, sig_i);
+            /* Avoid non-matching sigs */
+            if (ldns_rdf2native_int16(ldns_rr_rrsig_keytag(cur_sig)) == ldns_calc_keytag(cur_key)) {
+              if ((status=ldns_verify_rrsig(domain_keys, cur_sig, cur_key)) == LDNS_STATUS_OK) {
+                
+                /* Push the whole rrset -- we can't do much more */
+                for (key_k=0; key_k<ldns_rr_list_rr_count(domain_keys); key_k++) {
+                  ldns_rr_list_push_rr(trusted_keys, ldns_rr_clone(ldns_rr_list_rr(domain_keys, key_k)));
+                }
+                
+                ldns_rr_list_deep_free(domain_keys);
+                ldns_rr_list_deep_free(domain_sigs);
+                ldns_pkt_free(keypkt);
+                return trusted_keys;
+              } /* else {
+                fprintf(stderr, "# Signature verification failed: %s\n", ldns_get_errorstr_by_id(status));
+              }
+            } else {
+              fprintf(stderr, "# Non-matching keytag for sig %u. Skipping.\n", sig_i);
+                */
+            }
+          }
+	  
+          /* Only push our trusted key */
+          ldns_rr_list_push_rr(trusted_keys, ldns_rr_clone(cur_key));
+        }
+      }
+    }
+
+    ldns_rr_list_deep_free(domain_keys);
+    ldns_rr_list_deep_free(domain_sigs);
+    ldns_pkt_free(keypkt);
+
+  } else {
+    status = LDNS_STATUS_CRYPTO_NO_DNSKEY;
+  }
+    
+  return trusted_keys;
+}
+
+ldns_rr_list *
+ldns_validate_domain_ds (const ldns_resolver * res, const ldns_rdf * domain, const ldns_rr_list * keys)
+{
+  ldns_status status;
+  ldns_pkt * dspkt;
+  uint16_t key_i;
+  ldns_rr_list * rrset = NULL;
+  ldns_rr_list * sigs = NULL;
+  ldns_rr_list * trusted_keys = NULL;
+
+  /* Fetch DS for the domain */
+  if ((dspkt = ldns_resolver_query(res, domain, LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN, LDNS_RD))) {
+
+    rrset = ldns_pkt_rr_list_by_type(dspkt, LDNS_RR_TYPE_DS, LDNS_SECTION_ANSWER);
+    sigs = ldns_pkt_rr_list_by_type(dspkt, LDNS_RR_TYPE_RRSIG, LDNS_SECTION_ANSWER);
+
+    /* Validate sigs */
+    if ((status = ldns_verify(rrset, sigs, keys, NULL)) == LDNS_STATUS_OK) {
+      trusted_keys = ldns_rr_list_new();
+      for (key_i=0; key_i<ldns_rr_list_rr_count(rrset); key_i++) {
+	ldns_rr_list_push_rr(trusted_keys, ldns_rr_clone(ldns_rr_list_rr(rrset, key_i)));
+      }
+    }
+
+    ldns_rr_list_deep_free(rrset);
+    ldns_rr_list_deep_free(sigs);
+    ldns_pkt_free(dspkt);
+
+  } else {
+    status = LDNS_STATUS_CRYPTO_NO_DS;
+  }
+
+  return trusted_keys;
+}
+
 ldns_status
+ldns_verify_trusted(ldns_resolver * res, ldns_rr_list * rrset, ldns_rr_list * rrsigs, ldns_rr_list * validating_keys)
+{
+  /* */
+  uint16_t sig_i; uint16_t key_i;
+  ldns_rr * cur_sig; ldns_rr * cur_key;
+  ldns_rr_list * trusted_keys = NULL;
+  ldns_status result = LDNS_STATUS_ERR;
+
+  if (!res || !rrset || !rrsigs) {
+    return LDNS_STATUS_ERR;
+  }
+
+  if (ldns_rr_list_rr_count(rrset) < 1) {
+    return LDNS_STATUS_ERR;
+  }
+
+  if (ldns_rr_list_rr_count(rrsigs) < 1) {
+    return LDNS_STATUS_CRYPTO_NO_RRSIG;
+  }
+  
+  /* Look at each sig */
+  for (sig_i=0; sig_i < ldns_rr_list_rr_count(rrsigs); sig_i++) {
+
+    cur_sig = ldns_rr_list_rr(rrsigs, sig_i);
+    /* Get a valid signer key and validate the sig */
+    if ((trusted_keys = ldns_fetch_valid_domain_keys(res, ldns_rr_rrsig_signame(cur_sig), ldns_resolver_dnssec_anchors(res)))) {
+
+      for (key_i=0; key_i<ldns_rr_list_rr_count(trusted_keys); key_i++) {
+        cur_key = ldns_rr_list_rr(trusted_keys, key_i);
+
+        if ((result = ldns_verify_rrsig(rrset, cur_sig, cur_key)) == LDNS_STATUS_OK) {
+          
+          if (validating_keys) { ldns_rr_list_push_rr(validating_keys, ldns_rr_clone(cur_key)); }
+          ldns_rr_list_deep_free(trusted_keys);
+          return LDNS_STATUS_OK;
+        }
+      }
+    }
+  }
+
+  ldns_rr_list_deep_free(trusted_keys);
+  return result;
+}
+
+
+ldns_status
 ldns_verify_rrsig_buffers(ldns_buffer *rawsig_buf, ldns_buffer *verify_buf, 
 		ldns_buffer *key_buf, uint8_t algo)
 {
@@ -144,7 +335,7 @@
  * - verify the rrset+sig, with the b64 data and the b64 key data
  */
 ldns_status
-ldns_verify_rrsig_keylist(ldns_rr_list *rrset, ldns_rr *rrsig, ldns_rr_list *keys, 
+ldns_verify_rrsig_keylist(ldns_rr_list *rrset, ldns_rr *rrsig, const ldns_rr_list *keys, 
 		ldns_rr_list *good_keys)
 {
 	ldns_buffer *rawsig_buf;
Index: ldns/error.h
===================================================================
--- ldns/error.h	(revision 2344)
+++ ldns/error.h	(working copy)
@@ -49,6 +49,8 @@
 	LDNS_STATUS_CRYPTO_NO_RRSIG,
 	LDNS_STATUS_CRYPTO_NO_DNSKEY,
 	LDNS_STATUS_CRYPTO_NO_TRUSTED_DNSKEY,
+	LDNS_STATUS_CRYPTO_NO_DS,
+	LDNS_STATUS_CRYPTO_NO_TRUSTED_DS,
 	LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY,
 	LDNS_STATUS_CRYPTO_VALIDATED,
 	LDNS_STATUS_CRYPTO_BOGUS,
Index: error.c
===================================================================
--- error.c	(revision 2344)
+++ error.c	(working copy)
@@ -43,6 +43,8 @@
         { LDNS_STATUS_CRYPTO_NO_DNSKEY, "No DNSSEC public key(s)" },
         { LDNS_STATUS_CRYPTO_TYPE_COVERED_ERR, "The signature does not cover this RRset" },
         { LDNS_STATUS_CRYPTO_NO_TRUSTED_DNSKEY, "No signatures found for trusted DNSSEC public key(s)" },
+        { LDNS_STATUS_CRYPTO_NO_DS, "No DS record(s)" },
+        { LDNS_STATUS_CRYPTO_NO_TRUSTED_DS, "Could not validate DS record(s)" },
         { LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY, "No keys with the keytag from the RRSIG found" },
         { LDNS_STATUS_CRYPTO_VALIDATED, "Valid DNSSEC signature" },
         { LDNS_STATUS_CRYPTO_BOGUS, "Bogus DNSSEC signature" },



More information about the ldns-users mailing list