diff options
Diffstat (limited to 'common/pka.c')
-rw-r--r-- | common/pka.c | 341 |
1 files changed, 55 insertions, 286 deletions
diff --git a/common/pka.c b/common/pka.c index 4ead97f63..c3c68b538 100644 --- a/common/pka.c +++ b/common/pka.c @@ -33,307 +33,76 @@ #include <stdlib.h> #include <string.h> -#ifdef USE_DNS_PKA -#include <sys/types.h> -#ifdef _WIN32 -# ifdef HAVE_WINSOCK2_H -# include <winsock2.h> -# endif -# include <windows.h> -#else -#include <netinet/in.h> -#include <arpa/nameser.h> -#include <resolv.h> -#endif -#endif /* USE_DNS_PKA */ -#ifdef USE_ADNS -# include <adns.h> -#endif - #include "util.h" -#include "host2net.h" +#include "mbox-util.h" +#include "dns-cert.h" #include "pka.h" -#ifdef USE_DNS_PKA -/* Parse the TXT resource record. Format is: - - v=pka1;fpr=a4d94e92b0986ab5ee9dcd755de249965b0358a2;uri=string - - For simplicity white spaces are not allowed. Because we expect to - use a new RRTYPE for this in the future we define the TXT really - strict for simplicity: No white spaces, case sensitivity of the - names, order must be as given above. Only URI is optional. - - This function modifies BUFFER. On success 0 is returned, the 20 - byte fingerprint stored at FPR and BUFFER contains the URI or an - empty string. -*/ -static int -parse_txt_record (char *buffer, unsigned char *fpr) -{ - char *p, *pend; - int i; - - p = buffer; - pend = strchr (p, ';'); - if (!pend) - return -1; - *pend++ = 0; - if (strcmp (p, "v=pka1")) - return -1; /* Wrong or missing version. */ - - p = pend; - pend = strchr (p, ';'); - if (pend) - *pend++ = 0; - if (strncmp (p, "fpr=", 4)) - return -1; /* Missing fingerprint part. */ - p += 4; - for (i=0; i < 20 && hexdigitp (p) && hexdigitp (p+1); i++, p += 2) - fpr[i] = xtoi_2 (p); - if (i != 20) - return -1; /* Fingerprint consists not of exactly 40 hexbytes. */ - - p = pend; - if (!p || !*p) - { - *buffer = 0; - return 0; /* Success (no URI given). */ - } - if (strncmp (p, "uri=", 4)) - return -1; /* Unknown part. */ - p += 4; - /* There is an URI, copy it to the start of the buffer. */ - while (*p) - *buffer++ = *p++; - *buffer = 0; - return 0; -} - /* For the given email ADDRESS lookup the PKA information in the DNS. - On success the 20 byte SHA-1 fingerprint is stored at FPR and the - URI will be returned in an allocated buffer. Note that the URI - might be an zero length string as this information is optional. - Caller must xfree the returned string. + On success the fingerprint is stored at FPRBUF and the URI will be + returned in an allocated buffer. Note that the URI might be a zero + length string as this information is optional. Caller must xfree + the returned string. FPRBUFLEN gives the size of the expected + fingerprint (usually 20). - On error NULL is returned and the 20 bytes at FPR are not - defined. */ + On error NULL is returned and the FPRBUF is not defined. */ char * -get_pka_info (const char *address, unsigned char *fpr) +get_pka_info (const char *address, void *fprbuf, size_t fprbuflen) { -#ifdef USE_ADNS - int rc; - adns_state state; - const char *domain; - char *name; - adns_answer *answer = NULL; - char *buffer = NULL; - - domain = strrchr (address, '@'); - if (!domain || domain == address || !domain[1]) - return NULL; /* Invalid mail address given. */ - name = xtrymalloc (strlen (address) + 5 + 1); + char *result = NULL; + char *mbox; + char *domain; /* Points to mbox. */ + char hashbuf[20]; + char *hash = NULL; + char *name = NULL; + unsigned char *fpr = NULL; + size_t fpr_len; + char *url = NULL; + gpg_error_t err; + + mbox = mailbox_from_userid (address); + if (!mbox) + goto leave; + domain = strchr (mbox, '@'); + if (!domain) + goto leave; + *domain++ = 0; + + gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, mbox, strlen (mbox)); + hash = zb32_encode (hashbuf, 8*20); + if (!hash) + goto leave; + name = strconcat (hash, "._pka.", domain, NULL); if (!name) - return NULL; - memcpy (name, address, domain - address); - strcpy (stpcpy (name + (domain-address), "._pka."), domain+1); - - rc = adns_init (&state, adns_if_noerrprint, NULL); - if (rc) - { - log_error ("error initializing adns: %s\n", strerror (errno)); - xfree (name); - return NULL; - } + goto leave; - rc = adns_synchronous (state, name, adns_r_txt, adns_qf_quoteok_query, - &answer); - xfree (name); - if (rc) - { - log_error ("DNS query failed: %s\n", strerror (errno)); - adns_finish (state); - return NULL; - } - if (answer->status != adns_s_ok - || answer->type != adns_r_txt || !answer->nrrs) - { - log_error ("DNS query returned an error: %s (%s)\n", - adns_strerror (answer->status), - adns_errabbrev (answer->status)); - adns_free (answer); - adns_finish (state); - return NULL; - } + if (get_dns_cert (name, DNS_CERTTYPE_IPGP, NULL, &fpr, &fpr_len, &url)) + goto leave; + if (!fpr) + goto leave; - /* We use a PKA records iff there is exactly one record. */ - if (answer->nrrs == 1 && answer->rrs.manyistr[0]->i != -1) + /* Return the fingerprint. */ + if (fpr_len != fprbuflen) { - buffer = xtrystrdup (answer->rrs.manyistr[0]->str); - if (parse_txt_record (buffer, fpr)) - { - xfree (buffer); - buffer = NULL; /* Not a valid gpg trustdns RR. */ - } + /* fprintf (stderr, "get_dns_cert failed: fprlen (%zu/%zu)\n", */ + /* fpr_len, fprbuflen); */ + goto leave; } + memcpy (fprbuf, fpr, fpr_len); - adns_free (answer); - adns_finish (state); - return buffer; - -#else /*!USE_ADNS*/ - unsigned char answer[PACKETSZ]; - int anslen; - int qdcount, ancount; - int rc; - unsigned char *p, *pend; - const char *domain; - char *name; - HEADER header; - - domain = strrchr (address, '@'); - if (!domain || domain == address || !domain[1]) - return NULL; /* invalid mail address given. */ + /* We return the URL or an empty string. */ + if (!url) + url = xtrycalloc (1, 1); + result = url; + url = NULL; - name = xtrymalloc (strlen (address) + 5 + 1); - if (!name) - return NULL; - memcpy (name, address, domain - address); - strcpy (stpcpy (name + (domain-address), "._pka."), domain+1); - - anslen = res_query (name, C_IN, T_TXT, answer, PACKETSZ); + leave: + xfree (fpr); + xfree (url); xfree (name); - if (anslen < sizeof(HEADER)) - return NULL; /* DNS resolver returned a too short answer. */ - - /* Don't despair: A good compiler should optimize this away, as - header is just 32 byte and constant at compile time. It's - one way to comply with strict aliasing rules. */ - memcpy (&header, answer, sizeof (header)); - - if ( (rc=header.rcode) != NOERROR ) - return NULL; /* DNS resolver returned an error. */ - - /* We assume that PACKETSZ is large enough and don't do dynmically - expansion of the buffer. */ - if (anslen > PACKETSZ) - return NULL; /* DNS resolver returned a too long answer */ - - qdcount = ntohs (header.qdcount); - ancount = ntohs (header.ancount); - - if (!ancount) - return NULL; /* Got no answer. */ - - p = answer + sizeof (HEADER); - pend = answer + anslen; /* Actually points directly behind the buffer. */ - - while (qdcount-- && p < pend) - { - rc = dn_skipname (p, pend); - if (rc == -1) - return NULL; - p += rc + QFIXEDSZ; - } - - if (ancount > 1) - return NULL; /* more than one possible gpg trustdns record - none used. */ - - while (ancount-- && p <= pend) - { - unsigned int type, class, txtlen, n; - char *buffer, *bufp; - - rc = dn_skipname (p, pend); - if (rc == -1) - return NULL; - p += rc; - if (p >= pend - 10) - return NULL; /* RR too short. */ - - type = buf16_to_uint (p); - p += 2; - class = buf16_to_uint (p); - p += 2; - p += 4; - txtlen = buf16_to_uint (p); - p += 2; - - if (type != T_TXT || class != C_IN) - return NULL; /* Answer does not match the query. */ - - buffer = bufp = xmalloc (txtlen + 1); - while (txtlen && p < pend) - { - for (n = *p++, txtlen--; txtlen && n && p < pend; txtlen--, n--) - *bufp++ = *p++; - } - *bufp = 0; - if (parse_txt_record (buffer, fpr)) - { - xfree (buffer); - return NULL; /* Not a valid gpg trustdns RR. */ - } - return buffer; - } - - return NULL; -#endif /*!USE_ADNS*/ + xfree (hash); + xfree (mbox); + return result; } - -#else /* !USE_DNS_PKA */ - -/* Dummy version of the function if we can't use the resolver - functions. */ -char * -get_pka_info (const char *address, unsigned char *fpr) -{ - (void)address; - (void)fpr; - return NULL; -} -#endif /* !USE_DNS_PKA */ - - -#ifdef TEST -int -main(int argc,char *argv[]) -{ - unsigned char fpr[20]; - char *uri; - int i; - - if (argc < 2) - { - fprintf (stderr, "usage: pka mail-addresses\n"); - return 1; - } - argc--; - argv++; - - for (; argc; argc--, argv++) - { - uri = get_pka_info ( *argv, fpr ); - printf ("%s", *argv); - if (uri) - { - putchar (' '); - for (i=0; i < 20; i++) - printf ("%02X", fpr[i]); - if (*uri) - printf (" %s", uri); - xfree (uri); - } - putchar ('\n'); - } - return 0; -} -#endif /* TEST */ - -/* -Local Variables: -compile-command: "cc -DUSE_DNS_PKA -DTEST -I.. -I../include -Wall -g -o pka pka.c -lresolv ../tools/no-libgcrypt.o ../jnlib/libjnlib.a" -End: -*/ |