[PATCH] Add support for ldns

Simon Vallet svallet at genoscope.cns.fr
Mon May 21 23:55:07 EST 2007


Hi,

as discussed before, we're trying to make use of SSHFP records (RFC
4255) to publish host key fingerprints in the DNS.

However, some non-OpenBSD platforms don't support DNSSEC in the native
resolver (e.g. glibc), which renders the whole thing quite useless,
since openssh correctly requires the RRs to be signed and validated.

The following patch adds support for ldns, an external resolver
library, with the following functionality:
- Set DO on the SSHFP query
- Support AD if the answer comes from a validating resolver 
- Support autonomous validation using a configured trust anchor in case
the answer is not marked as authentic.

It depends on the SVN version of ldns (revision 2345), which is available 
there: http://www.nlnetlabs.nl/ldns/

Simon

Index: configure.ac
===================================================================
RCS file: /cvs/openssh/configure.ac,v
retrieving revision 1.380
diff -u -r1.380 configure.ac
--- configure.ac	9 May 2007 22:57:43 -0000	1.380
+++ configure.ac	21 May 2007 13:46:58 -0000
@@ -1145,6 +1145,40 @@
 	]
 )
 
+# Check whether user wants to use ldns
+LDNS_MSG="no"
+AC_ARG_WITH(ldns,
+	[  --with-ldns[[=PATH]]      Use ldns for DNSSEC support (optionally in PATH)],
+    [
+        if test "x$withval" != "xno" ; then
+
+			if test "x$withval" != "xyes" ; then
+				CPPFLAGS="$CPPFLAGS -I${withval}/include"
+				LDFLAGS="$LDFLAGS -L${withval}/lib"
+			fi
+
+            AC_DEFINE(HAVE_LDNS, 1, [Define if you want ldns support])
+            LIBS="-lldns $LIBS"
+            LDNS_MSG="yes"
+
+            AC_MSG_CHECKING([for ldns support])
+            AC_LINK_IFELSE(
+                [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <ldns/ldns.h>
+int main() { ldns_status status = ldns_verify_trusted(NULL, NULL, NULL, NULL); status=LDNS_STATUS_OK; exit(0); }
+				]])],
+				[AC_MSG_RESULT(yes)],
+				[
+					AC_MSG_RESULT(no)
+					AC_MSG_ERROR([** Incomplete or missing ldns libraries.])
+				])
+        fi
+    ]
+)
+
 # Check whether user wants libedit support
 LIBEDIT_MSG="no"
 AC_ARG_WITH(libedit,
Index: openbsd-compat/getrrsetbyname.c
===================================================================
RCS file: /cvs/openssh/openbsd-compat/getrrsetbyname.c,v
retrieving revision 1.24
diff -u -r1.24 getrrsetbyname.c
--- openbsd-compat/getrrsetbyname.c	29 Apr 2007 03:58:07 -0000	1.24
+++ openbsd-compat/getrrsetbyname.c	21 May 2007 13:46:58 -0000
@@ -2,6 +2,7 @@
 
 /*
  * Copyright (c) 2001 Jakob Schlyter. All rights reserved.
+ * Copyright (c) 2007 Simon Vallet / Genoscope <svallet at genoscope.cns.fr>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -55,7 +56,12 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
+#ifdef HAVE_LDNS
+#include <ldns/ldns.h>
+#endif
+
 #include "getrrsetbyname.h"
+#include "log.h"
 
 #if defined(HAVE_DECL_H_ERRNO) && !HAVE_DECL_H_ERRNO
 extern int h_errno;
@@ -170,6 +176,9 @@
 	struct dns_rr		*additional;
 };
 
+
+#ifndef HAVE_LDNS
+
 static struct dns_response *parse_dns_response(const u_char *, int);
 static struct dns_query *parse_dns_qsection(const u_char *, int,
     const u_char **, int);
@@ -345,37 +354,6 @@
 	return (result);
 }
 
-void
-freerrset(struct rrsetinfo *rrset)
-{
-	u_int16_t i;
-
-	if (rrset == NULL)
-		return;
-
-	if (rrset->rri_rdatas) {
-		for (i = 0; i < rrset->rri_nrdatas; i++) {
-			if (rrset->rri_rdatas[i].rdi_data == NULL)
-				break;
-			free(rrset->rri_rdatas[i].rdi_data);
-		}
-		free(rrset->rri_rdatas);
-	}
-
-	if (rrset->rri_sigs) {
-		for (i = 0; i < rrset->rri_nsigs; i++) {
-			if (rrset->rri_sigs[i].rdi_data == NULL)
-				break;
-			free(rrset->rri_sigs[i].rdi_data);
-		}
-		free(rrset->rri_sigs);
-	}
-
-	if (rrset->rri_name)
-		free(rrset->rri_name);
-	free(rrset);
-}
-
 /*
  * DNS response parsing routines
  */
@@ -606,5 +584,220 @@
 
 	return (n);
 }
+
+#else
+
+int
+getrrsetbyname(const char *hostname, unsigned int rdclass,
+               unsigned int rdtype, unsigned int flags,
+               struct rrsetinfo **res)
+{
+  int result; unsigned int i; unsigned int j;
+  struct rrsetinfo *rrset = NULL;
+  
+  unsigned int index_ans; unsigned int  index_sig;
+  struct rdatainfo *rdata;
+  
+  ldns_resolver * ldns_res;
+  ldns_rdf * domain = NULL;  ldns_pkt * pkt;
+  ldns_rr_list * rrsigs = NULL;
+  ldns_rr_list * rrdata = NULL;
+  ldns_status err;
+  ldns_rr * rr;
+  
+  /* check for invalid class and type */
+  if (rdclass > 0xffff || rdtype > 0xffff) {
+    result = ERRSET_INVAL;
+    goto fail;
+  }
+  
+  /* don't allow queries of class or type ANY */
+  if (rdclass == 0xff || rdtype == 0xff) {
+    result = ERRSET_INVAL;
+    goto fail;
+  }
+  
+  /* don't allow flags yet, unimplemented */
+  if (flags) {
+    result = ERRSET_INVAL;
+    goto fail;
+  }
+  
+  /* initialize resolver */
+  domain = ldns_dname_new_frm_str(hostname);
+  if ((err = ldns_resolver_new_frm_file(&ldns_res, NULL)) != LDNS_STATUS_OK) { /* Initialize resolver from resolv.conf */    
+    result = ERRSET_FAIL;
+    goto fail;
+  }
+
+#ifdef DEBUG
+  ldns_resolver_set_debug(ldns_res, true);
+#endif /* DEBUG */
+
+  ldns_resolver_set_dnssec(ldns_res, true); /* Use DNSSEC, since ldns supports it */
+
+  /* make query */
+  pkt = ldns_resolver_query(ldns_res, domain, rdtype, rdclass, LDNS_RD);
+    
+  /*** TODO: finer errcodes -- see original **/
+  if (!pkt || ldns_pkt_ancount(pkt) < 1) {
+    result = ERRSET_FAIL;
+    goto fail;
+  }
+  
+  /* initialize rrset */
+  rrset = calloc(1, sizeof(struct rrsetinfo));
+  if (rrset == NULL) {
+    result = ERRSET_NOMEMORY;
+    goto fail;
+  }
+  
+  rrdata = ldns_pkt_rr_list_by_type(pkt, rdtype, LDNS_SECTION_ANSWER);
+  rrset->rri_nrdatas = ldns_rr_list_rr_count(rrdata);
+  if (!rrset->rri_nrdatas) {
+    result = ERRSET_NODATA;
+    goto fail;
+  }
+
+  /* copy name from answer section */
+  rrset->rri_name = strndup(ldns_rdf_data(ldns_rr_owner(ldns_rr_list_rr(rrdata, 0))),
+                            ldns_rdf_size(ldns_rr_owner(ldns_rr_list_rr(rrdata, 0))));
+  if (rrset->rri_name == NULL) {
+    result = ERRSET_NOMEMORY;
+    goto fail;
+  }
+
+  rrset->rri_rdclass = ldns_rr_get_class(ldns_rr_list_rr(rrdata, 0));
+  rrset->rri_rdtype = ldns_rr_get_type(ldns_rr_list_rr(rrdata, 0));
+  rrset->rri_ttl = ldns_rr_ttl(ldns_rr_list_rr(rrdata, 0));
+  
+  debug2("ldns: Got %u answers from DNS", rrset->rri_nrdatas);
+  
+  /* Check for authenticated data */
+  if (ldns_pkt_ad(pkt)) {
+    rrset->rri_flags |= RRSET_VALIDATED;
+  } else { /* AD is not set, try autonomous validation */
+
+    ldns_rr_list * trusted_keys = ldns_rr_list_new();
+
+    debug2("ldns: trying to validate RRset");
+    /* Get eventual sigs */
+    rrsigs = ldns_pkt_rr_list_by_type(pkt, LDNS_RR_TYPE_RRSIG, LDNS_SECTION_ANSWER);
+    rrset->rri_nsigs =  ldns_rr_list_rr_count(rrsigs);
+    debug2("ldns: Got %u sigs (RRTYPE %u) from DNS", rrset->rri_nsigs, LDNS_RR_TYPE_RRSIG);
+
+    if ((err = ldns_verify_trusted(ldns_res, rrdata, rrsigs, trusted_keys)) == LDNS_STATUS_OK) {
+      rrset->rri_flags |= RRSET_VALIDATED;
+      debug2("ldns: RRset is signed with a valid key");
+    } else {
+      debug2("ldns: RRset validation failed: %s", ldns_get_errorstr_by_id(err));
+    }
+
+    ldns_rr_list_deep_free(trusted_keys);
+  }
+
+  /* allocate memory for answers */
+  rrset->rri_rdatas = calloc(rrset->rri_nrdatas,
+                             sizeof(struct rdatainfo));
+  if (rrset->rri_rdatas == NULL) {
+    result = ERRSET_NOMEMORY;
+    goto fail;
+  }
+
+  /* allocate memory for signatures */
+  if (rrset->rri_nsigs > 0) {
+    rrset->rri_sigs = calloc(rrset->rri_nsigs, sizeof(struct rdatainfo));
+    if (rrset->rri_sigs == NULL) {
+      result = ERRSET_NOMEMORY;
+      goto fail;
+    }
+  }
+
+  /* copy answers & signatures */
+  for (i=0, index_ans=0, index_sig=0; i< pkt->_header->_ancount; i++) {
+    
+    rdata = NULL;
+    rr = ldns_rr_list_rr(ldns_pkt_answer(pkt), i);
+    
+    if (ldns_rr_get_class(rr) == rrset->rri_rdclass && ldns_rr_get_type(rr)  == rrset->rri_rdtype) {
+      rdata = &rrset->rri_rdatas[index_ans++];
+    }
+    
+    if (rr->_rr_class == rrset->rri_rdclass && rr->_rr_type  == LDNS_RR_TYPE_RRSIG) {
+      rdata = &rrset->rri_sigs[index_sig++];
+    }
+    
+    if (rdata) {
+      size_t rdata_offset = 0;
+      
+      rdata->rdi_length = 0;
+      for (j=0; j< rr->_rd_count; j++) {
+        rdata->rdi_length += ldns_rdf_size(ldns_rr_rdf(rr, j));
+      }
+      
+      rdata->rdi_data = malloc(rdata->rdi_length);
+      if (rdata->rdi_data == NULL) {
+        result = ERRSET_NOMEMORY;
+        goto fail;
+      }
+      
+      /* Re-create the raw DNS RDATA */
+      for (j=0; j< rr->_rd_count; j++) {
+        memcpy(rdata->rdi_data + rdata_offset, ldns_rdf_data(ldns_rr_rdf(rr, j)), ldns_rdf_size(ldns_rr_rdf(rr, j)));
+        rdata_offset += ldns_rdf_size(ldns_rr_rdf(rr, j));
+      }
+    }
+    
+  }
+  
+  *res = rrset;
+  result = ERRSET_SUCCESS;
+  /* return (ERRSET_SUCCESS); */
+
+fail:
+  /* freerrset(rrset); */
+  ldns_rdf_deep_free(domain);
+  ldns_pkt_free(pkt);
+  ldns_rr_list_deep_free(rrsigs);
+  ldns_rr_list_deep_free(rrdata);
+  ldns_resolver_deep_free(ldns_res);
+
+  return result;
+}
+
+
+#endif /* defined(HAVE_LDNS) */
+
+void
+freerrset(struct rrsetinfo *rrset)
+{
+	u_int16_t i;
+
+	if (rrset == NULL)
+		return;
+
+	if (rrset->rri_rdatas) {
+		for (i = 0; i < rrset->rri_nrdatas; i++) {
+			if (rrset->rri_rdatas[i].rdi_data == NULL)
+				break;
+			free(rrset->rri_rdatas[i].rdi_data);
+		}
+		free(rrset->rri_rdatas);
+	}
+
+	if (rrset->rri_sigs) {
+		for (i = 0; i < rrset->rri_nsigs; i++) {
+			if (rrset->rri_sigs[i].rdi_data == NULL)
+				break;
+			free(rrset->rri_sigs[i].rdi_data);
+		}
+		free(rrset->rri_sigs);
+	}
+
+	if (rrset->rri_name)
+		free(rrset->rri_name);
+	free(rrset);
+}
+
 
 #endif /* !defined(HAVE_GETRRSETBYNAME) */


-- 
Simon Vallet
Ingénieur Systèmes/Réseaux
Genoscope / CNRG
Tél. : 01 60 87 36 06
E-mail : svallet at genoscope.cns.fr


More information about the openssh-unix-dev mailing list