common: Minor change of hex2str to allow for embedded nul.
[gnupg.git] / common / dns-cert.c
index 56af13a..405ca29 100644 (file)
@@ -3,12 +3,22 @@
  *
  * This file is part of GNUPG.
  *
- * GNUPG is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of either
  *
- * GNUPG is distributed in the hope that it will be useful,
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - the GNU General Public License as published by the Free
+ *     Software Foundation; either version 2 of the License, or (at
+ *     your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
@@ -21,6 +31,9 @@
 #include <sys/types.h>
 #ifdef USE_DNS_CERT
 # ifdef HAVE_W32_SYSTEM
+#  ifdef HAVE_WINSOCK2_H
+#   include <winsock2.h>
+#  endif
 #  include <windows.h>
 # else
 #  include <netinet/in.h>
 #endif
 #ifdef USE_ADNS
 # include <adns.h>
-# ifndef HAVE_ADNS_FREE
-#  define adns_free free
-# endif
 #endif
 
 #include "util.h"
+#include "host2net.h"
 #include "dns-cert.h"
 
 /* Not every installation has gotten around to supporting CERTs
 #define my_adns_r_cert 37
 
 
-/* Certificate types according to RFC-4398.  */
-#define CERTTYPE_PKIX      1 /* X.509 as per PKIX. */
-#define CERTTYPE_SPKI      2 /* SPKI certificate.  */
-#define CERTTYPE_PGP       3 /* OpenPGP packet.  */
-#define CERTTYPE_IPKIX     4 /* The URL of an X.509 data object. */
-#define CERTTYPE_ISPKI     5 /* The URL of an SPKI certificate.  */
-#define CERTTYPE_IPGP      6 /* The fingerprint and URL of an OpenPGP packet.*/
-#define CERTTYPE_ACPKIX    7 /* Attribute Certificate.  */
-#define CERTTYPE_IACPKIX   8 /* The URL of an Attribute Certificate.  */
-#define CERTTYPE_URI     253 /* URI private.  */
-#define CERTTYPE_OID     254 /* OID private.  */
-
 
 /* Returns 0 on success or an error code.  If a PGP CERT record was
    found, a new estream with that key will be returned at R_KEY and
    the other return parameters are set to NULL/0.  If an IPGP CERT
    record was found the fingerprint is stored as an allocated block at
    R_FPR and its length at R_FPRLEN; an URL is is allocated as a
-   string and returned at R_URL.  Note that this function returns the
-   first CERT found with a supported type; it is expected that only
-   one CERT record is used. */
+   string and returned at R_URL.  If WANT_CERTTYPE is 0 this function
+   returns the first CERT found with a supported type; it is expected
+   that only one CERT record is used.  If WANT_CERTTYPE is one of the
+   supported certtypes only records wih this certtype are considered
+   and the first found is returned.  R_KEY is optional. */
 gpg_error_t
-_get_dns_cert (const char *name, estream_t *r_key,
-               unsigned char **r_fpr, size_t *r_fprlen, char **r_url,
-               gpg_err_source_t errsource)
+get_dns_cert (const char *name, int want_certtype,
+              estream_t *r_key,
+              unsigned char **r_fpr, size_t *r_fprlen, char **r_url)
 {
 #ifdef USE_DNS_CERT
 #ifdef USE_ADNS
@@ -83,14 +84,15 @@ _get_dns_cert (const char *name, estream_t *r_key,
   unsigned int ctype;
   int count;
 
-  *r_key = NULL;
+  if (r_key)
+    *r_key = NULL;
   *r_fpr = NULL;
   *r_fprlen = 0;
   *r_url = NULL;
 
   if (adns_init (&state, adns_if_noerrprint, NULL))
     {
-      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
       log_error ("error initializing adns: %s\n", strerror (errno));
       return err;
     }
@@ -98,7 +100,7 @@ _get_dns_cert (const char *name, estream_t *r_key,
   if (adns_synchronous (state, name, (adns_r_unknown | my_adns_r_cert),
                         adns_qf_quoteok_query, &answer))
     {
-      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
       /* log_error ("DNS query failed: %s\n", strerror (errno)); */
       adns_finish (state);
       return err;
@@ -108,11 +110,11 @@ _get_dns_cert (const char *name, estream_t *r_key,
       /* log_error ("DNS query returned an error: %s (%s)\n", */
       /*            adns_strerror (answer->status), */
       /*            adns_errabbrev (answer->status)); */
-      err = gpg_err_make (errsource, GPG_ERR_NOT_FOUND);
+      err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND);
       goto leave;
     }
 
-  err = gpg_err_make (errsource, GPG_ERR_NOT_FOUND);
+  err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND);
   for (count = 0; count < answer->nrrs; count++)
     {
       int datalen = answer->rrs.byteblock[count].len;
@@ -121,24 +123,27 @@ _get_dns_cert (const char *name, estream_t *r_key,
       if (datalen < 5)
         continue;  /* Truncated CERT record - skip.  */
 
-      ctype = ((data[0] << 8) | data[1]);
+      ctype = buf16_to_uint (data);
       /* (key tag and algorithm fields are not required.) */
       data += 5;
       datalen -= 5;
 
-      if (ctype == CERTTYPE_PGP && datalen >= 11)
+      if (want_certtype && want_certtype != ctype)
+        ; /* Not of the requested certtype.  */
+      else if (ctype == DNS_CERTTYPE_PGP && datalen >= 11 && r_key)
         {
           /* CERT type is PGP.  Gpg checks for a minimum length of 11,
              thus we do the same.  */
           *r_key = es_fopenmem_init (0, "rwb", data, datalen);
           if (!*r_key)
-            err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+            err = gpg_err_make (default_errsource,
+                                gpg_err_code_from_syserror ());
           else
             err = 0;
           goto leave;
         }
-      else if (ctype == CERTTYPE_IPGP && datalen && datalen < 1023
-               && datalen >= data[0] + 1 && fpr && fpr_len && url)
+      else if (ctype == DNS_CERTTYPE_IPGP && datalen && datalen < 1023
+               && datalen >= data[0] + 1 && r_fpr && r_fprlen && r_url)
         {
           /* CERT type is IPGP.  We made sure that the data is
              plausible and that the caller requested this
@@ -149,7 +154,8 @@ _get_dns_cert (const char *name, estream_t *r_key,
               *r_fpr = xtrymalloc (*r_fprlen);
               if (!*r_fpr)
                 {
-                  err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+                  err = gpg_err_make (default_errsource,
+                                      gpg_err_code_from_syserror ());
                   goto leave;
                 }
               memcpy (*r_fpr, data + 1, *r_fprlen);
@@ -159,16 +165,18 @@ _get_dns_cert (const char *name, estream_t *r_key,
 
           if (datalen > *r_fprlen + 1)
             {
-              *url = xtrymalloc (datalen - (*r_fprlen + 1) + 1);
+              *r_url = xtrymalloc (datalen - (*r_fprlen + 1) + 1);
               if (!*r_url)
                 {
-                  err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+                  err = gpg_err_make (default_errsource,
+                                      gpg_err_code_from_syserror ());
                   xfree (*r_fpr);
                   *r_fpr = NULL;
                   goto leave;
                 }
-              memcpy (*url, data + (*r_fprlen + 1), datalen - (*r_fprlen + 1));
-              (*url)[datalen - (*r_fprlen + 1)] = '\0';
+              memcpy (*r_url,
+                      data + (*r_fprlen + 1), datalen - (*r_fprlen + 1));
+              (*r_url)[datalen - (*r_fprlen + 1)] = '\0';
             }
           else
             *r_url = NULL;
@@ -190,7 +198,8 @@ _get_dns_cert (const char *name, estream_t *r_key,
   int r;
   u16 count;
 
-  *r_key = NULL;
+  if (r_key)
+    *r_key = NULL;
   *r_fpr = NULL;
   *r_fprlen = 0;
   *r_url = NULL;
@@ -198,9 +207,9 @@ _get_dns_cert (const char *name, estream_t *r_key,
   /* Allocate a 64k buffer which is the limit for an DNS response.  */
   answer = xtrymalloc (65536);
   if (!answer)
-    return gpg_err_make (errsource, gpg_err_code_from_syserror ());
+    return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
 
-  err = gpg_err_make (errsource, GPG_ERR_NOT_FOUND);
+  err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND);
 
   r = res_query (name, C_IN, T_CERT, answer, 65536);
   /* Not too big, not too small, no errors and at least 1 answer. */
@@ -220,7 +229,7 @@ _get_dns_cert (const char *name, estream_t *r_key,
       rc = dn_skipname (pt, emsg);
       if (rc == -1)
         {
-          err = gpg_err_make (errsource, GPG_ERR_INV_OBJ);
+          err = gpg_err_make (default_errsource, GPG_ERR_INV_OBJ);
           goto leave;
         }
       pt += rc + QFIXEDSZ;
@@ -238,7 +247,7 @@ _get_dns_cert (const char *name, estream_t *r_key,
           rc = dn_skipname (pt, emsg);  /* the name we just queried for */
           if (rc == -1)
             {
-              err = gpg_err_make (errsource, GPG_ERR_INV_OBJ);
+              err = gpg_err_make (default_errsource, GPG_ERR_INV_OBJ);
               goto leave;
             }
 
@@ -249,12 +258,12 @@ _get_dns_cert (const char *name, estream_t *r_key,
           if ((emsg - pt) < 15)
             break;
 
-          type = *pt++ << 8;
-          type |= *pt++;
+          type = buf16_to_u16 (pt);
+          pt += 2;
+
+          class = buf16_to_u16 (pt);
+          pt += 2;
 
-          class = *pt++ << 8;
-          class |= *pt++;
-          /* We asked for IN and got something else !? */
           if (class != C_IN)
             break;
 
@@ -262,8 +271,8 @@ _get_dns_cert (const char *name, estream_t *r_key,
           pt += 4;
 
           /* data length */
-          dlen = *pt++ << 8;
-          dlen |= *pt++;
+          dlen = buf16_to_u16 (pt);
+          pt += 2;
 
           /* We asked for CERT and got something else - might be a
              CNAME, so loop around again. */
@@ -274,8 +283,8 @@ _get_dns_cert (const char *name, estream_t *r_key,
             }
 
           /* The CERT type */
-          ctype = *pt++ << 8;
-          ctype |= *pt++;
+          ctype = buf16_to_u16 (pt);
+          pt += 2;
 
           /* Skip the CERT key tag and algo which we don't need. */
           pt += 3;
@@ -283,18 +292,20 @@ _get_dns_cert (const char *name, estream_t *r_key,
           dlen -= 5;
 
           /* 15 bytes takes us to here */
-
-          if (ctype == CERTTYPE_PGP && dlen)
+          if (want_certtype && want_certtype != ctype)
+            ; /* Not of the requested certtype.  */
+          else if (ctype == DNS_CERTTYPE_PGP && dlen && r_key)
             {
               /* PGP type */
               *r_key = es_fopenmem_init (0, "rwb", pt, dlen);
               if (!*r_key)
-                err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+                err = gpg_err_make (default_errsource,
+                                    gpg_err_code_from_syserror ());
               else
                 err = 0;
               goto leave;
             }
-          else if (ctype == CERTTYPE_IPGP
+          else if (ctype == DNS_CERTTYPE_IPGP
                    && dlen && dlen < 1023 && dlen >= pt[0] + 1)
             {
               /* IPGP type */
@@ -304,7 +315,7 @@ _get_dns_cert (const char *name, estream_t *r_key,
                   *r_fpr = xtrymalloc (*r_fprlen);
                   if (!*r_fpr)
                     {
-                      err = gpg_err_make (errsource,
+                      err = gpg_err_make (default_errsource,
                                           gpg_err_code_from_syserror ());
                       goto leave;
                     }
@@ -318,7 +329,7 @@ _get_dns_cert (const char *name, estream_t *r_key,
                   *r_url = xtrymalloc (dlen - (*r_fprlen + 1) + 1);
                   if (!*r_fpr)
                     {
-                      err = gpg_err_make (errsource,
+                      err = gpg_err_make (default_errsource,
                                           gpg_err_code_from_syserror ());
                       xfree (*r_fpr);
                       *r_fpr = NULL;
@@ -346,11 +357,12 @@ _get_dns_cert (const char *name, estream_t *r_key,
 #endif /*!USE_ADNS */
 #else /* !USE_DNS_CERT */
   (void)name;
-  (void)r_key;
-  (void)r_fpr;
-  (void)r_fprlen;
-  (void)r_url;
+  if (r_key)
+    *r_key = NULL;
+  *r_fpr = NULL;
+  *r_fprlen = 0;
+  *r_url = NULL;
 
-  return gpg_err_make (errsource, GPG_ERR_NOT_SUPPORTED);
+  return gpg_err_make (default_errsource, GPG_ERR_NOT_SUPPORTED);
 #endif
 }