From 154f3ed2bf64de801ae0f9796338a2767ec6357b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 23 Apr 2015 15:42:56 +0200 Subject: gpg: Move all DNS access to Dirmngr. * common/dns-cert.h: Move to ../dirmngr/. * common/dns-cert.c: Move to ../dirmngr/. Change args to return the key as a buffer. * common/t-dns-cert.c: Move to ../dirmngr/. * common/pka.c, common/pka.h, common/t-pka.c: Remove. * dirmngr/server.c (data_line_cookie_write): Factor code out to data_line_write and make it a wrapper for that. (data_line_write): New. (cmd_dns_cert): New. (register_commands): Register new command. * g10/Makefile.am (LDADD): Remove DNSLIBS. * g10/call-dirmngr.c (dns_cert_parm_s): New. (dns_cert_data_cb, dns_cert_status_cb): New. (gpg_dirmngr_dns_cert): New. (gpg_dirmngr_get_pka): New. * g10/gpgv.c (gpg_dirmngr_get_pka): New dummy function. * g10/keyserver.c (keyserver_import_cert): Replace get_dns_cert by gpg_dirmngr_dns_cert. (keyserver_import_pka): Replace get_pka_info by gpg_dirmngr_get_pka. * g10/mainproc.c: Include call-dirmngr.h. (pka_uri_from_sig): Add CTX arg. Replace get_pka_info by gpg_dirmngr_get_pka. -- With this patch gpg does not do any network access itself but uses dirmngr for that. Note that we need to keep linking to NETLIBS due to the logging code and because we need TCP for our socket emulation under Windows. Probably also required for Solaris etc. Signed-off-by: Werner Koch --- common/Makefile.am | 8 +- common/dns-cert.c | 368 --------------------------------------------- common/dns-cert.h | 55 ------- common/pka.c | 107 ------------- common/pka.h | 35 ----- common/t-dns-cert.c | 95 ------------ common/t-pka.c | 72 --------- dirmngr/Makefile.am | 6 +- dirmngr/dns-cert.c | 382 +++++++++++++++++++++++++++++++++++++++++++++++ dirmngr/dns-cert.h | 55 +++++++ dirmngr/server.c | 189 +++++++++++++++++++++-- dirmngr/t-dns-cert.c | 93 ++++++++++++ g10/Makefile.am | 3 +- g10/call-dirmngr.c | 235 +++++++++++++++++++++++++++++ g10/call-dirmngr.h | 8 + g10/gpgv.c | 16 ++ g10/keyserver-internal.h | 4 +- g10/keyserver.c | 34 ++--- g10/mainproc.c | 38 +++-- 19 files changed, 1014 insertions(+), 789 deletions(-) delete mode 100644 common/dns-cert.c delete mode 100644 common/dns-cert.h delete mode 100644 common/pka.c delete mode 100644 common/pka.h delete mode 100644 common/t-dns-cert.c delete mode 100644 common/t-pka.c create mode 100644 dirmngr/dns-cert.c create mode 100644 dirmngr/dns-cert.h create mode 100644 dirmngr/t-dns-cert.c diff --git a/common/Makefile.am b/common/Makefile.am index 4493ae7c5..d137df871 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -87,8 +87,6 @@ common_sources = \ signal.c \ audit.c audit.h \ srv.h \ - dns-cert.c dns-cert.h \ - pka.c pka.h \ localename.c \ session-env.c session-env.h \ userids.c userids.h \ @@ -177,8 +175,8 @@ if HAVE_W32_SYSTEM jnlib_tests += t-w32-reg endif module_tests = t-convert t-percent t-gettime t-sysutils t-sexputil \ - t-session-env t-openpgp-oid t-ssh-utils t-dns-cert \ - t-pka t-mapstrings t-zb32 t-mbox-util + t-session-env t-openpgp-oid t-ssh-utils \ + t-mapstrings t-zb32 t-mbox-util if !HAVE_W32CE_SYSTEM module_tests += t-exechelp endif @@ -221,8 +219,6 @@ t_exechelp_LDADD = $(t_common_ldadd) t_session_env_LDADD = $(t_common_ldadd) t_openpgp_oid_LDADD = $(t_common_ldadd) t_ssh_utils_LDADD = $(t_common_ldadd) -t_dns_cert_LDADD = $(t_common_ldadd) $(DNSLIBS) -t_pka_LDADD = $(t_common_ldadd) $(DNSLIBS) t_mapstrings_LDADD = $(t_common_ldadd) t_zb32_LDADD = $(t_common_ldadd) t_mbox_util_LDADD = $(t_common_ldadd) diff --git a/common/dns-cert.c b/common/dns-cert.c deleted file mode 100644 index 405ca293e..000000000 --- a/common/dns-cert.c +++ /dev/null @@ -1,368 +0,0 @@ -/* dns-cert.c - DNS CERT code (rfc-4398) - * Copyright (C) 2005, 2006, 2009 Free Software Foundation, Inc. - * - * This file is part of GNUPG. - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of either - * - * - 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include -#include -#ifdef USE_DNS_CERT -# ifdef HAVE_W32_SYSTEM -# ifdef HAVE_WINSOCK2_H -# include -# endif -# include -# else -# include -# include -# include -# endif -# include -#endif -#ifdef USE_ADNS -# include -#endif - -#include "util.h" -#include "host2net.h" -#include "dns-cert.h" - -/* Not every installation has gotten around to supporting CERTs - yet... */ -#ifndef T_CERT -#define T_CERT 37 -#endif - -/* ADNS has no support for CERT yet. */ -#define my_adns_r_cert 37 - - - -/* 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. 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, 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 - gpg_error_t err; - adns_state state; - adns_answer *answer = NULL; - unsigned int ctype; - int count; - - 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 (default_errsource, gpg_err_code_from_syserror ()); - log_error ("error initializing adns: %s\n", strerror (errno)); - return err; - } - - if (adns_synchronous (state, name, (adns_r_unknown | my_adns_r_cert), - adns_qf_quoteok_query, &answer)) - { - 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; - } - if (answer->status != adns_s_ok) - { - /* log_error ("DNS query returned an error: %s (%s)\n", */ - /* adns_strerror (answer->status), */ - /* adns_errabbrev (answer->status)); */ - err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND); - goto leave; - } - - err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND); - for (count = 0; count < answer->nrrs; count++) - { - int datalen = answer->rrs.byteblock[count].len; - const unsigned char *data = answer->rrs.byteblock[count].data; - - if (datalen < 5) - continue; /* Truncated CERT record - skip. */ - - ctype = buf16_to_uint (data); - /* (key tag and algorithm fields are not required.) */ - data += 5; - datalen -= 5; - - 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 (default_errsource, - gpg_err_code_from_syserror ()); - else - err = 0; - goto leave; - } - 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 - information. */ - *r_fprlen = data[0]; - if (*r_fprlen) - { - *r_fpr = xtrymalloc (*r_fprlen); - if (!*r_fpr) - { - err = gpg_err_make (default_errsource, - gpg_err_code_from_syserror ()); - goto leave; - } - memcpy (*r_fpr, data + 1, *r_fprlen); - } - else - *r_fpr = NULL; - - if (datalen > *r_fprlen + 1) - { - *r_url = xtrymalloc (datalen - (*r_fprlen + 1) + 1); - if (!*r_url) - { - err = gpg_err_make (default_errsource, - gpg_err_code_from_syserror ()); - xfree (*r_fpr); - *r_fpr = NULL; - goto leave; - } - memcpy (*r_url, - data + (*r_fprlen + 1), datalen - (*r_fprlen + 1)); - (*r_url)[datalen - (*r_fprlen + 1)] = '\0'; - } - else - *r_url = NULL; - - err = 0; - goto leave; - } - } - - leave: - adns_free (answer); - adns_finish (state); - return err; - -#else /*!USE_ADNS*/ - - gpg_error_t err; - unsigned char *answer; - int r; - u16 count; - - if (r_key) - *r_key = NULL; - *r_fpr = NULL; - *r_fprlen = 0; - *r_url = NULL; - - /* Allocate a 64k buffer which is the limit for an DNS response. */ - answer = xtrymalloc (65536); - if (!answer) - return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); - - 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. */ - if (r >= sizeof (HEADER) && r <= 65536 - && (((HEADER *) answer)->rcode) == NOERROR - && (count = ntohs (((HEADER *) answer)->ancount))) - { - int rc; - unsigned char *pt, *emsg; - - emsg = &answer[r]; - - pt = &answer[sizeof (HEADER)]; - - /* Skip over the query */ - - rc = dn_skipname (pt, emsg); - if (rc == -1) - { - err = gpg_err_make (default_errsource, GPG_ERR_INV_OBJ); - goto leave; - } - pt += rc + QFIXEDSZ; - - /* There are several possible response types for a CERT request. - We're interested in the PGP (a key) and IPGP (a URI) types. - Skip all others. TODO: A key is better than a URI since - we've gone through all this bother to fetch it, so favor that - if we have both PGP and IPGP? */ - - while (count-- > 0 && pt < emsg) - { - u16 type, class, dlen, ctype; - - rc = dn_skipname (pt, emsg); /* the name we just queried for */ - if (rc == -1) - { - err = gpg_err_make (default_errsource, GPG_ERR_INV_OBJ); - goto leave; - } - - pt += rc; - - /* Truncated message? 15 bytes takes us to the point where - we start looking at the ctype. */ - if ((emsg - pt) < 15) - break; - - type = buf16_to_u16 (pt); - pt += 2; - - class = buf16_to_u16 (pt); - pt += 2; - - if (class != C_IN) - break; - - /* ttl */ - pt += 4; - - /* data length */ - dlen = buf16_to_u16 (pt); - pt += 2; - - /* We asked for CERT and got something else - might be a - CNAME, so loop around again. */ - if (type != T_CERT) - { - pt += dlen; - continue; - } - - /* The CERT type */ - ctype = buf16_to_u16 (pt); - pt += 2; - - /* Skip the CERT key tag and algo which we don't need. */ - pt += 3; - - dlen -= 5; - - /* 15 bytes takes us to here */ - 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 (default_errsource, - gpg_err_code_from_syserror ()); - else - err = 0; - goto leave; - } - else if (ctype == DNS_CERTTYPE_IPGP - && dlen && dlen < 1023 && dlen >= pt[0] + 1) - { - /* IPGP type */ - *r_fprlen = pt[0]; - if (*r_fprlen) - { - *r_fpr = xtrymalloc (*r_fprlen); - if (!*r_fpr) - { - err = gpg_err_make (default_errsource, - gpg_err_code_from_syserror ()); - goto leave; - } - memcpy (*r_fpr, &pt[1], *r_fprlen); - } - else - *r_fpr = NULL; - - if (dlen > *r_fprlen + 1) - { - *r_url = xtrymalloc (dlen - (*r_fprlen + 1) + 1); - if (!*r_fpr) - { - err = gpg_err_make (default_errsource, - gpg_err_code_from_syserror ()); - xfree (*r_fpr); - *r_fpr = NULL; - goto leave; - } - memcpy (*r_url, &pt[*r_fprlen + 1], dlen - (*r_fprlen + 1)); - (*r_url)[dlen - (*r_fprlen + 1)] = '\0'; - } - else - *r_url = NULL; - - err = 0; - goto leave; - } - - /* Neither type matches, so go around to the next answer. */ - pt += dlen; - } - } - - leave: - xfree (answer); - return err; - -#endif /*!USE_ADNS */ -#else /* !USE_DNS_CERT */ - (void)name; - if (r_key) - *r_key = NULL; - *r_fpr = NULL; - *r_fprlen = 0; - *r_url = NULL; - - return gpg_err_make (default_errsource, GPG_ERR_NOT_SUPPORTED); -#endif -} diff --git a/common/dns-cert.h b/common/dns-cert.h deleted file mode 100644 index 4b49efc1c..000000000 --- a/common/dns-cert.h +++ /dev/null @@ -1,55 +0,0 @@ -/* dns-cert.h - DNS CERT definition - * Copyright (C) 2006 Free Software Foundation, Inc. - * - * This file is part of GnuPG. - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of either - * - * - 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ -#ifndef GNUPG_COMMON_DNS_CERT_H -#define GNUPG_COMMON_DNS_CERT_H - - -#define DNS_CERTTYPE_ANY 0 /* Internal catch all type. */ -/* Certificate types according to RFC-4398: */ -#define DNS_CERTTYPE_PKIX 1 /* X.509 as per PKIX. */ -#define DNS_CERTTYPE_SPKI 2 /* SPKI certificate. */ -#define DNS_CERTTYPE_PGP 3 /* OpenPGP packet. */ -#define DNS_CERTTYPE_IPKIX 4 /* The URL of an X.509 data object. */ -#define DNS_CERTTYPE_ISPKI 5 /* The URL of an SPKI certificate. */ -#define DNS_CERTTYPE_IPGP 6 /* The fingerprint - and URL of an OpenPGP packet. */ -#define DNS_CERTTYPE_ACPKIX 7 /* Attribute Certificate. */ -#define DNS_CERTTYPE_IACPKIX 8 /* The URL of an Attribute Certificate. */ -#define DNS_CERTTYPE_URI 253 /* URI private. */ -#define DNS_CERTTYPE_OID 254 /* OID private. */ - - -gpg_error_t get_dns_cert (const char *name, int want_certtype, - estream_t *r_key, - unsigned char **r_fpr, size_t *r_fprlen, - char **r_url); - - - -#endif /*GNUPG_COMMON_DNS_CERT_H*/ diff --git a/common/pka.c b/common/pka.c deleted file mode 100644 index 1aa5b3343..000000000 --- a/common/pka.c +++ /dev/null @@ -1,107 +0,0 @@ -/* pka.c - DNS Public Key Association RR access - * Copyright (C) 2005, 2009 Free Software Foundation, Inc. - * - * This file is part of GnuPG. - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of either - * - * - 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include - -#include -#include -#include - -#include "util.h" -#include "mbox-util.h" -#include "dns-cert.h" -#include "pka.h" - - -/* For the given email ADDRESS lookup the PKA information in the DNS. - - 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 FPRBUF is not defined. */ -char * -get_pka_info (const char *address, void *fprbuf, size_t fprbuflen) -{ - 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; - - 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) - goto leave; - - if (get_dns_cert (name, DNS_CERTTYPE_IPGP, NULL, &fpr, &fpr_len, &url)) - goto leave; - if (!fpr) - goto leave; - - /* Return the fingerprint. */ - if (fpr_len != fprbuflen) - { - /* fprintf (stderr, "get_dns_cert failed: fprlen (%zu/%zu)\n", */ - /* fpr_len, fprbuflen); */ - goto leave; - } - memcpy (fprbuf, fpr, fpr_len); - - /* We return the URL or an empty string. */ - if (!url) - url = xtrycalloc (1, 1); - result = url; - url = NULL; - - leave: - xfree (fpr); - xfree (url); - xfree (name); - xfree (hash); - xfree (mbox); - return result; -} diff --git a/common/pka.h b/common/pka.h deleted file mode 100644 index 93a4eb3ee..000000000 --- a/common/pka.h +++ /dev/null @@ -1,35 +0,0 @@ -/* pka.h - DNS Public Key Association RR access definitions - * Copyright (C) 2006 Free Software Foundation, Inc. - * - * This file is part of GnuPG. - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of either - * - * - 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ -#ifndef GNUPG_COMMON_PKA_H -#define GNUPG_COMMON_PKA_H - -char *get_pka_info (const char *address, void *fprbuf, size_t fprbuflen); - - -#endif /*GNUPG_COMMON_PKA_H*/ diff --git a/common/t-dns-cert.c b/common/t-dns-cert.c deleted file mode 100644 index a170ffb2d..000000000 --- a/common/t-dns-cert.c +++ /dev/null @@ -1,95 +0,0 @@ -/* t-dns-cert.c - Module test for dns-cert.c - * Copyright (C) 2011 Free Software Foundation, Inc. - * - * 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. - * - * GnuPG 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include -#include -#include -#include - -#include "util.h" -#include "dns-cert.h" - - -int -main (int argc, char **argv) -{ - gpg_error_t err; - unsigned char *fpr; - size_t fpr_len; - char *url; - estream_t key; - char const *name; - - if (argc) - { - argc--; - argv++; - } - - if (!argc) - name = "simon.josefsson.org"; - else if (argc == 1) - name = *argv; - else - { - fputs ("usage: t-dns-cert [name]\n", stderr); - return 1; - } - - printf ("CERT lookup on '%s'\n", name); - - err = get_dns_cert (name, DNS_CERTTYPE_ANY, &key, &fpr, &fpr_len, &url); - if (err) - printf ("get_dns_cert failed: %s <%s>\n", - gpg_strerror (err), gpg_strsource (err)); - else if (key) - { - int count = 0; - - while (es_getc (key) != EOF) - count++; - printf ("Key found (%d bytes)\n", count); - } - else - { - if (fpr) - { - int i; - - printf ("Fingerprint found (%d bytes): ", (int)fpr_len); - for (i = 0; i < fpr_len; i++) - printf ("%02X", fpr[i]); - putchar ('\n'); - } - else - printf ("No fingerprint found\n"); - - if (url) - printf ("URL found: %s\n", url); - else - printf ("No URL found\n"); - - } - - es_fclose (key); - xfree (fpr); - xfree (url); - - return 0; -} diff --git a/common/t-pka.c b/common/t-pka.c deleted file mode 100644 index 7c4d7c306..000000000 --- a/common/t-pka.c +++ /dev/null @@ -1,72 +0,0 @@ -/* t-pak.c - Module test for pka.c - * Copyright (C) 2015 Werner Koch - * - * 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. - * - * GnuPG 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include -#include -#include -#include - -#include "util.h" -#include "pka.h" - - -int -main (int argc, char **argv) -{ - unsigned char fpr[20]; - char *url; - char const *name; - int i; - - if (argc) - { - argc--; - argv++; - } - - if (!argc) - name = "wk@gnupg.org"; - else if (argc == 1) - name = *argv; - else - { - fputs ("usage: t-pka [userid]\n", stderr); - return 1; - } - - printf ("User id ...: %s\n", name); - - url = get_pka_info (name, fpr, sizeof fpr); - printf ("Fingerprint: "); - if (url) - { - for (i = 0; i < sizeof fpr; i++) - printf ("%02X", fpr[i]); - } - else - printf ("[not found]"); - - putchar ('\n'); - - printf ("URL .......: %s\n", (url && *url)? url : "[none]"); - - xfree (url); - - return 0; -} diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am index 906fe37eb..cee777a6a 100644 --- a/dirmngr/Makefile.am +++ b/dirmngr/Makefile.am @@ -61,6 +61,7 @@ dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \ certcache.c certcache.h \ cdb.h cdblib.c misc.c dirmngr-err.h \ ocsp.c ocsp.h validate.c validate.h \ + dns-cert.c dns-cert.h \ ks-action.c ks-action.h ks-engine.h \ ks-engine-hkp.c ks-engine-http.c ks-engine-finger.c ks-engine-kdns.c @@ -113,7 +114,7 @@ t_common_ldadd = $(libcommontls) $(libcommon) no-libgcrypt.o \ $(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) \ $(DNSLIBS) $(LIBINTL) $(LIBICONV) -module_tests = +module_tests = t-dns-cert if USE_LDAP module_tests += t-ldap-parse-uri @@ -124,4 +125,7 @@ t_ldap_parse_uri_SOURCES = \ $(ldap_url) $(t_common_src) t_ldap_parse_uri_LDADD = $(ldaplibs) $(t_common_ldadd) +t_dns_cert_SOURCES = t-dns-cert.c dns-cert.c +t_dns_cert_LDADD = $(t_common_ldadd) + $(PROGRAMS) : $(libcommon) $(libcommonpth) $(libcommontls) $(libcommontlsnpth) diff --git a/dirmngr/dns-cert.c b/dirmngr/dns-cert.c new file mode 100644 index 000000000..de523b5f2 --- /dev/null +++ b/dirmngr/dns-cert.c @@ -0,0 +1,382 @@ +/* dns-cert.c - DNS CERT code (rfc-4398) + * Copyright (C) 2005, 2006, 2009 Free Software Foundation, Inc. + * + * This file is part of GNUPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#ifdef USE_DNS_CERT +# ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +# else +# include +# include +# include +# endif +# include +#endif +#ifdef USE_ADNS +# include +#endif + +#include "util.h" +#include "host2net.h" +#include "dns-cert.h" + +/* Not every installation has gotten around to supporting CERTs + yet... */ +#ifndef T_CERT +#define T_CERT 37 +#endif + +/* ADNS has no support for CERT yet. */ +#define my_adns_r_cert 37 + + + +/* Returns 0 on success or an error code. If a PGP CERT record was + found, the malloced data is returned at (R_KEY, R_KEYLEN) 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. 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,R_KEYLEN) are optional. */ +gpg_error_t +get_dns_cert (const char *name, int want_certtype, + void **r_key, size_t *r_keylen, + unsigned char **r_fpr, size_t *r_fprlen, char **r_url) +{ +#ifdef USE_DNS_CERT +#ifdef USE_ADNS + gpg_error_t err; + adns_state state; + adns_answer *answer = NULL; + unsigned int ctype; + int count; + + if (r_key) + *r_key = NULL; + if (r_keylen) + *r_keylen = 0; + *r_fpr = NULL; + *r_fprlen = 0; + *r_url = NULL; + + if (adns_init (&state, adns_if_noerrprint, NULL)) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + log_error ("error initializing adns: %s\n", strerror (errno)); + return err; + } + + if (adns_synchronous (state, name, (adns_r_unknown | my_adns_r_cert), + adns_qf_quoteok_query, &answer)) + { + 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; + } + if (answer->status != adns_s_ok) + { + /* log_error ("DNS query returned an error: %s (%s)\n", */ + /* adns_strerror (answer->status), */ + /* adns_errabbrev (answer->status)); */ + err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND); + goto leave; + } + + err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND); + for (count = 0; count < answer->nrrs; count++) + { + int datalen = answer->rrs.byteblock[count].len; + const unsigned char *data = answer->rrs.byteblock[count].data; + + if (datalen < 5) + continue; /* Truncated CERT record - skip. */ + + ctype = buf16_to_uint (data); + /* (key tag and algorithm fields are not required.) */ + data += 5; + datalen -= 5; + + if (want_certtype && want_certtype != ctype) + ; /* Not of the requested certtype. */ + else if (ctype == DNS_CERTTYPE_PGP && datalen >= 11 && r_key && r_keylen) + { + /* CERT type is PGP. Gpg checks for a minimum length of 11, + thus we do the same. */ + *r_key = xtrymalloc (datalen); + if (!*r_key) + err = gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + else + { + memcpy (*r_key, data, datalen); + *r_keylen = datalen; + err = 0; + } + goto leave; + } + 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 + information. */ + *r_fprlen = data[0]; + if (*r_fprlen) + { + *r_fpr = xtrymalloc (*r_fprlen); + if (!*r_fpr) + { + err = gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + goto leave; + } + memcpy (*r_fpr, data + 1, *r_fprlen); + } + else + *r_fpr = NULL; + + if (datalen > *r_fprlen + 1) + { + *r_url = xtrymalloc (datalen - (*r_fprlen + 1) + 1); + if (!*r_url) + { + err = gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + xfree (*r_fpr); + *r_fpr = NULL; + goto leave; + } + memcpy (*r_url, + data + (*r_fprlen + 1), datalen - (*r_fprlen + 1)); + (*r_url)[datalen - (*r_fprlen + 1)] = '\0'; + } + else + *r_url = NULL; + + err = 0; + goto leave; + } + } + + leave: + adns_free (answer); + adns_finish (state); + return err; + +#else /*!USE_ADNS*/ + + gpg_error_t err; + unsigned char *answer; + int r; + u16 count; + + if (r_key) + *r_key = NULL; + if (r_keylen) + *r_keylen = 0; + *r_fpr = NULL; + *r_fprlen = 0; + *r_url = NULL; + + /* Allocate a 64k buffer which is the limit for an DNS response. */ + answer = xtrymalloc (65536); + if (!answer) + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + + 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. */ + if (r >= sizeof (HEADER) && r <= 65536 + && (((HEADER *) answer)->rcode) == NOERROR + && (count = ntohs (((HEADER *) answer)->ancount))) + { + int rc; + unsigned char *pt, *emsg; + + emsg = &answer[r]; + + pt = &answer[sizeof (HEADER)]; + + /* Skip over the query */ + + rc = dn_skipname (pt, emsg); + if (rc == -1) + { + err = gpg_err_make (default_errsource, GPG_ERR_INV_OBJ); + goto leave; + } + pt += rc + QFIXEDSZ; + + /* There are several possible response types for a CERT request. + We're interested in the PGP (a key) and IPGP (a URI) types. + Skip all others. TODO: A key is better than a URI since + we've gone through all this bother to fetch it, so favor that + if we have both PGP and IPGP? */ + + while (count-- > 0 && pt < emsg) + { + u16 type, class, dlen, ctype; + + rc = dn_skipname (pt, emsg); /* the name we just queried for */ + if (rc == -1) + { + err = gpg_err_make (default_errsource, GPG_ERR_INV_OBJ); + goto leave; + } + + pt += rc; + + /* Truncated message? 15 bytes takes us to the point where + we start looking at the ctype. */ + if ((emsg - pt) < 15) + break; + + type = buf16_to_u16 (pt); + pt += 2; + + class = buf16_to_u16 (pt); + pt += 2; + + if (class != C_IN) + break; + + /* ttl */ + pt += 4; + + /* data length */ + dlen = buf16_to_u16 (pt); + pt += 2; + + /* We asked for CERT and got something else - might be a + CNAME, so loop around again. */ + if (type != T_CERT) + { + pt += dlen; + continue; + } + + /* The CERT type */ + ctype = buf16_to_u16 (pt); + pt += 2; + + /* Skip the CERT key tag and algo which we don't need. */ + pt += 3; + + dlen -= 5; + + /* 15 bytes takes us to here */ + if (want_certtype && want_certtype != ctype) + ; /* Not of the requested certtype. */ + else if (ctype == DNS_CERTTYPE_PGP && dlen && r_key && r_keylen) + { + /* PGP type */ + *r_key = xtrymalloc (dlen); + if (!*r_key) + err = gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + else + { + memcpy (*r_key, pt, dlen); + *r_keylen = dlen; + err = 0; + } + goto leave; + } + else if (ctype == DNS_CERTTYPE_IPGP + && dlen && dlen < 1023 && dlen >= pt[0] + 1) + { + /* IPGP type */ + *r_fprlen = pt[0]; + if (*r_fprlen) + { + *r_fpr = xtrymalloc (*r_fprlen); + if (!*r_fpr) + { + err = gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + goto leave; + } + memcpy (*r_fpr, &pt[1], *r_fprlen); + } + else + *r_fpr = NULL; + + if (dlen > *r_fprlen + 1) + { + *r_url = xtrymalloc (dlen - (*r_fprlen + 1) + 1); + if (!*r_fpr) + { + err = gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + xfree (*r_fpr); + *r_fpr = NULL; + goto leave; + } + memcpy (*r_url, &pt[*r_fprlen + 1], dlen - (*r_fprlen + 1)); + (*r_url)[dlen - (*r_fprlen + 1)] = '\0'; + } + else + *r_url = NULL; + + err = 0; + goto leave; + } + + /* Neither type matches, so go around to the next answer. */ + pt += dlen; + } + } + + leave: + xfree (answer); + return err; + +#endif /*!USE_ADNS */ +#else /* !USE_DNS_CERT */ + (void)name; + if (r_key) + *r_key = NULL; + if (r_keylen) + *r_keylen = NULL; + *r_fpr = NULL; + *r_fprlen = 0; + *r_url = NULL; + + return gpg_err_make (default_errsource, GPG_ERR_NOT_SUPPORTED); +#endif +} diff --git a/dirmngr/dns-cert.h b/dirmngr/dns-cert.h new file mode 100644 index 000000000..5a579ec1f --- /dev/null +++ b/dirmngr/dns-cert.h @@ -0,0 +1,55 @@ +/* dns-cert.h - DNS CERT definition + * Copyright (C) 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#ifndef GNUPG_DIRMNGR_DNS_CERT_H +#define GNUPG_DIRMNGR_DNS_CERT_H + + +#define DNS_CERTTYPE_ANY 0 /* Internal catch all type. */ +/* Certificate types according to RFC-4398: */ +#define DNS_CERTTYPE_PKIX 1 /* X.509 as per PKIX. */ +#define DNS_CERTTYPE_SPKI 2 /* SPKI certificate. */ +#define DNS_CERTTYPE_PGP 3 /* OpenPGP packet. */ +#define DNS_CERTTYPE_IPKIX 4 /* The URL of an X.509 data object. */ +#define DNS_CERTTYPE_ISPKI 5 /* The URL of an SPKI certificate. */ +#define DNS_CERTTYPE_IPGP 6 /* The fingerprint + and URL of an OpenPGP packet. */ +#define DNS_CERTTYPE_ACPKIX 7 /* Attribute Certificate. */ +#define DNS_CERTTYPE_IACPKIX 8 /* The URL of an Attribute Certificate. */ +#define DNS_CERTTYPE_URI 253 /* URI private. */ +#define DNS_CERTTYPE_OID 254 /* OID private. */ + + +gpg_error_t get_dns_cert (const char *name, int want_certtype, + void **r_key, size_t *r_keylen, + unsigned char **r_fpr, size_t *r_fprlen, + char **r_url); + + + +#endif /*GNUPG_DIRMNGR_DNS_CERT_H*/ diff --git a/dirmngr/server.c b/dirmngr/server.c index c0f63ac7d..df6c66fcc 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -51,6 +51,8 @@ #if USE_LDAP # include "ldap-parse-uri.h" #endif +#include "dns-cert.h" +#include "mbox-util.h" /* To avoid DoS attacks we limit the size of a certificate to something reasonable. */ @@ -150,13 +152,14 @@ leave_cmd (assuan_context_t ctx, gpg_error_t err) return err; } -/* A write handler used by es_fopencookie to write assuan data - lines. */ -static ssize_t -data_line_cookie_write (void *cookie, const void *buffer_arg, size_t size) + +/* This is a wrapper around assuan_send_data which makes debugging the + output in verbose mode easier. */ +static gpg_error_t +data_line_write (assuan_context_t ctx, const void *buffer_arg, size_t size) { - assuan_context_t ctx = cookie; const char *buffer = buffer_arg; + gpg_error_t err; if (opt.verbose && buffer && size) { @@ -169,33 +172,49 @@ data_line_cookie_write (void *cookie, const void *buffer_arg, size_t size) { p = memchr (buffer, '\n', nbytes); n = p ? (p - buffer) + 1 : nbytes; - if (assuan_send_data (ctx, buffer, n)) + err = assuan_send_data (ctx, buffer, n); + if (err) { gpg_err_set_errno (EIO); - return -1; + return err; } buffer += n; nbytes -= n; - if (nbytes && assuan_send_data (ctx, NULL, 0)) /* Flush line. */ + if (nbytes && (err=assuan_send_data (ctx, NULL, 0))) /* Flush line. */ { gpg_err_set_errno (EIO); - return -1; + return err; } } while (nbytes); } else { - if (assuan_send_data (ctx, buffer, size)) + err = assuan_send_data (ctx, buffer, size); + if (err) { - gpg_err_set_errno (EIO); - return -1; + gpg_err_set_errno (EIO); /* For use by data_line_cookie_write. */ + return err; } } - return size; + return 0; } + +/* A write handler used by es_fopencookie to write assuan data + lines. */ +static ssize_t +data_line_cookie_write (void *cookie, const void *buffer, size_t size) +{ + assuan_context_t ctx = cookie; + + if (data_line_write (ctx, buffer, size)) + return -1; + return (ssize_t)size; +} + + static int data_line_cookie_close (void *cookie) { @@ -609,6 +628,149 @@ option_handler (assuan_context_t ctx, const char *key, const char *value) } + +static const char hlp_dns_cert[] = + "DNS_CERT \n" + "DNS_CERT --pka \n" + "\n" + "Return the CERT record for . is one of\n" + " * Return the first record of any supported subtype\n" + " PGP Return the first record of subtype PGP (3)\n" + " IPGP Return the first record of subtype IPGP (6)\n" + "If the content of a certifciate is available (PGP) it is returned\n" + "by data lines. Fingerprints and URLs are returned via status lines.\n" + "In --pka mode the fingerprint and if available an URL is returned."; +static gpg_error_t +cmd_dns_cert (assuan_context_t ctx, char *line) +{ + /* ctrl_t ctrl = assuan_get_pointer (ctx); */ + gpg_error_t err = 0; + int pka_mode; + char *mbox = NULL; + char *namebuf = NULL; + char *encodedhash = NULL; + const char *name; + int certtype; + char *p; + void *key = NULL; + size_t keylen; + unsigned char *fpr = NULL; + size_t fprlen; + char *url = NULL; + + pka_mode = has_option (line, "--pka"); + line = skip_options (line); + if (pka_mode) + ; /* No need to parse here - we do this later. */ + else + { + p = strchr (line, ' '); + if (!p) + { + err = PARM_ERROR ("missing arguments"); + goto leave; + } + *p++ = 0; + if (!strcmp (line, "*")) + certtype = DNS_CERTTYPE_ANY; + else if (!strcmp (line, "IPGP")) + certtype = DNS_CERTTYPE_IPGP; + else if (!strcmp (line, "PGP")) + certtype = DNS_CERTTYPE_PGP; + else + { + err = PARM_ERROR ("unknown subtype"); + goto leave; + } + while (spacep (p)) + p++; + line = p; + if (!*line) + { + err = PARM_ERROR ("name missing"); + goto leave; + } + } + + if (pka_mode) + { + char *domain; /* Points to mbox. */ + char hashbuf[20]; + + mbox = mailbox_from_userid (line); + if (!mbox || !(domain = strchr (mbox, '@'))) + { + err = set_error (GPG_ERR_INV_USER_ID, "no mailbox in user id"); + goto leave; + } + *domain++ = 0; + + gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, mbox, strlen (mbox)); + encodedhash = zb32_encode (hashbuf, 8*20); + if (!encodedhash) + { + err = gpg_error_from_syserror (); + goto leave; + } + namebuf = strconcat (encodedhash, "._pka.", domain, NULL); + if (!namebuf) + { + err = gpg_error_from_syserror (); + goto leave; + } + name = namebuf; + certtype = DNS_CERTTYPE_IPGP; + } + else + name = line; + + err = get_dns_cert (name, certtype, &key, &keylen, &fpr, &fprlen, &url); + if (err) + goto leave; + + if (key) + { + err = data_line_write (ctx, key, keylen); + if (err) + goto leave; + } + + if (fpr) + { + char *tmpstr; + + tmpstr = bin2hex (fpr, fprlen, NULL); + if (!tmpstr) + err = gpg_error_from_syserror (); + else + { + err = assuan_write_status (ctx, "FPR", tmpstr); + xfree (tmpstr); + } + if (err) + goto leave; + } + + if (url) + { + err = assuan_write_status (ctx, "URL", url); + if (err) + goto leave; + } + + + leave: + xfree (key); + xfree (fpr); + xfree (url); + xfree (mbox); + xfree (namebuf); + xfree (encodedhash); + return leave_cmd (ctx, err); +} + + + static const char hlp_ldapserver[] = "LDAPSERVER \n" "\n" @@ -1919,6 +2081,7 @@ register_commands (assuan_context_t ctx) assuan_handler_t handler; const char * const help; } table[] = { + { "DNS_CERT", cmd_dns_cert, hlp_dns_cert }, { "LDAPSERVER", cmd_ldapserver, hlp_ldapserver }, { "ISVALID", cmd_isvalid, hlp_isvalid }, { "CHECKCRL", cmd_checkcrl, hlp_checkcrl }, diff --git a/dirmngr/t-dns-cert.c b/dirmngr/t-dns-cert.c new file mode 100644 index 000000000..61536c566 --- /dev/null +++ b/dirmngr/t-dns-cert.c @@ -0,0 +1,93 @@ +/* t-dns-cert.c - Module test for dns-cert.c + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * 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. + * + * GnuPG 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include + +#include "util.h" +#include "dns-cert.h" + + +int +main (int argc, char **argv) +{ + gpg_error_t err; + unsigned char *fpr; + size_t fpr_len; + char *url; + void *key; + size_t keylen; + char const *name; + + if (argc) + { + argc--; + argv++; + } + + if (!argc) + name = "simon.josefsson.org"; + else if (argc == 1) + name = *argv; + else + { + fputs ("usage: t-dns-cert [name]\n", stderr); + return 1; + } + + printf ("CERT lookup on '%s'\n", name); + + err = get_dns_cert (name, DNS_CERTTYPE_ANY, &key, &keylen, + &fpr, &fpr_len, &url); + if (err) + printf ("get_dns_cert failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + else if (key) + { + printf ("Key found (%u bytes)\n", (unsigned int)keylen); + } + else + { + if (fpr) + { + int i; + + printf ("Fingerprint found (%d bytes): ", (int)fpr_len); + for (i = 0; i < fpr_len; i++) + printf ("%02X", fpr[i]); + putchar ('\n'); + } + else + printf ("No fingerprint found\n"); + + if (url) + printf ("URL found: %s\n", url); + else + printf ("No URL found\n"); + + } + + xfree (key); + xfree (fpr); + xfree (url); + + return 0; +} diff --git a/g10/Makefile.am b/g10/Makefile.am index b66abb84c..ca99314b7 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -141,8 +141,7 @@ gpgv2_SOURCES = gpgv.c \ # here, even that it is not used by gpg. A proper solution would # either to split up libkeybox.a or to use a separate keybox daemon. LDADD = $(needed_libs) ../common/libgpgrl.a \ - $(ZLIBS) $(DNSLIBS) \ - $(LIBINTL) $(CAPLIBS) $(NETLIBS) + $(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS) gpg2_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \ $(KSBA_LIBS) $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ $(LIBICONV) $(resource_objs) $(extra_sys_libs) diff --git a/g10/call-dirmngr.c b/g10/call-dirmngr.c index bb571b2e9..e452c971e 100644 --- a/g10/call-dirmngr.c +++ b/g10/call-dirmngr.c @@ -78,6 +78,16 @@ struct ks_put_parm_s }; +/* Parameter structure used with the DNS_CERT command. */ +struct dns_cert_parm_s +{ + estream_t memfp; + unsigned char *fpr; + size_t fprlen; + char *url; +}; + + /* Data used to associate an session with dirmngr contexts. We can't use a simple one to one mapping because we sometimes need two connections to the dirmngr; for example while doing a listing and @@ -957,3 +967,228 @@ gpg_dirmngr_ks_put (ctrl_t ctrl, void *data, size_t datalen, kbnode_t keyblock) close_context (ctrl, ctx); return err; } + + + +/* Data callback for the DNS_CERT command. */ +static gpg_error_t +dns_cert_data_cb (void *opaque, const void *data, size_t datalen) +{ + struct dns_cert_parm_s *parm = opaque; + gpg_error_t err = 0; + size_t nwritten; + + if (!data) + return 0; /* Ignore END commands. */ + if (!parm->memfp) + return 0; /* Data is not required. */ + + if (es_write (parm->memfp, data, datalen, &nwritten)) + err = gpg_error_from_syserror (); + + return err; +} + + +/* Status callback for the DNS_CERT command. */ +static gpg_error_t +dns_cert_status_cb (void *opaque, const char *line) +{ + struct dns_cert_parm_s *parm = opaque; + gpg_error_t err = 0; + const char *s; + size_t nbytes; + + if ((s = has_leading_keyword (line, "FPR"))) + { + char *buf; + + if (!(buf = xtrystrdup (s))) + err = gpg_error_from_syserror (); + else if (parm->fpr) + err = gpg_error (GPG_ERR_DUP_KEY); + else if (!hex2str (buf, buf, strlen (buf)+1, &nbytes)) + err = gpg_error_from_syserror (); + else if (nbytes < 20) + err = gpg_error (GPG_ERR_TOO_SHORT); + else + { + parm->fpr = xtrymalloc (nbytes); + if (!parm->fpr) + err = gpg_error_from_syserror (); + else + memcpy (parm->fpr, buf, (parm->fprlen = nbytes)); + } + xfree (buf); + } + else if ((s = has_leading_keyword (line, "URL")) && *s) + { + if (parm->url) + err = gpg_error (GPG_ERR_DUP_KEY); + else if (!(parm->fpr = xtrymalloc (nbytes))) + err = gpg_error_from_syserror (); + else + memcpy (parm->fpr, line, (parm->fprlen = nbytes)); + } + + return err; +} + +/* Ask the dirmngr for a DNS CERT record. Depending on the found + subtypes different return values are set: + + - For a PGP subtype a new estream with that key will be returned at + R_KEY and the other return parameters are set to NULL/0. + + - For an IPGP subtype the fingerprint is stored as a malloced block + at (R_FPR,R_FPRLEN). If an URL is available it is stored as a + malloced string at R_URL; NULL is stored if there is no URL. + + If CERTTYPE is DNS_CERTTYPE_ANY this function returns the first + CERT record found with a supported type; it is expected that only + one CERT record is used. If CERTTYPE is one of the supported + certtypes, only records with this certtype are considered and the + first one found is returned. All R_* args are optional. */ +gpg_error_t +gpg_dirmngr_dns_cert (ctrl_t ctrl, const char *name, const char *certtype, + estream_t *r_key, + unsigned char **r_fpr, size_t *r_fprlen, + char **r_url) +{ + gpg_error_t err; + assuan_context_t ctx; + struct dns_cert_parm_s parm; + char *line = NULL; + + memset (&parm, 0, sizeof parm); + if (r_key) + *r_key = NULL; + if (r_fpr) + *r_fpr = NULL; + if (r_fprlen) + *r_fprlen = 0; + if (r_url) + *r_url = NULL; + + err = open_context (ctrl, &ctx); + if (err) + return err; + + line = es_bsprintf ("DNS_CERT %s %s", certtype, name); + if (!line) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (strlen (line) + 2 >= ASSUAN_LINELENGTH) + { + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + } + + parm.memfp = es_fopenmem (0, "rwb"); + if (!parm.memfp) + { + err = gpg_error_from_syserror (); + goto leave; + } + err = assuan_transact (ctx, line, dns_cert_data_cb, &parm, + NULL, NULL, dns_cert_status_cb, &parm); + if (err) + goto leave; + + if (r_key) + { + es_rewind (parm.memfp); + *r_key = parm.memfp; + parm.memfp = NULL; + } + + if (r_fpr && parm.fpr) + { + *r_fpr = parm.fpr; + parm.fpr = NULL; + } + if (r_fprlen) + *r_fprlen = parm.fprlen; + + if (r_url && parm.url) + { + *r_url = parm.url; + parm.url = NULL; + } + + leave: + xfree (parm.fpr); + xfree (parm.url); + es_fclose (parm.memfp); + xfree (line); + close_context (ctrl, ctx); + return err; +} + + +/* Ask the dirmngr for PKA info. On success the retrieved fingerprint + is returned in a malloced buffer at R_FPR and its length is stored + at R_FPRLEN. If an URL is available it is stored as a malloced + string at R_URL. On error all return values are set to NULL/0. */ +gpg_error_t +gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid, + unsigned char **r_fpr, size_t *r_fprlen, + char **r_url) +{ + gpg_error_t err; + assuan_context_t ctx; + struct dns_cert_parm_s parm; + char *line = NULL; + + memset (&parm, 0, sizeof parm); + if (r_fpr) + *r_fpr = NULL; + if (r_fprlen) + *r_fprlen = 0; + if (r_url) + *r_url = NULL; + + err = open_context (ctrl, &ctx); + if (err) + return err; + + line = es_bsprintf ("DNS_CERT --pka -- %s", userid); + if (!line) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (strlen (line) + 2 >= ASSUAN_LINELENGTH) + { + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + } + + err = assuan_transact (ctx, line, dns_cert_data_cb, &parm, + NULL, NULL, dns_cert_status_cb, &parm); + if (err) + goto leave; + + if (r_fpr && parm.fpr) + { + *r_fpr = parm.fpr; + parm.fpr = NULL; + } + if (r_fprlen) + *r_fprlen = parm.fprlen; + + if (r_url && parm.url) + { + *r_url = parm.url; + parm.url = NULL; + } + + leave: + xfree (parm.fpr); + xfree (parm.url); + xfree (line); + close_context (ctrl, ctx); + return err; +} diff --git a/g10/call-dirmngr.h b/g10/call-dirmngr.h index bae11238c..b9b8e21a3 100644 --- a/g10/call-dirmngr.h +++ b/g10/call-dirmngr.h @@ -31,6 +31,14 @@ gpg_error_t gpg_dirmngr_ks_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp); gpg_error_t gpg_dirmngr_ks_put (ctrl_t ctrl, void *data, size_t datalen, kbnode_t keyblock); +gpg_error_t gpg_dirmngr_dns_cert (ctrl_t ctrl, + const char *name, const char *certtype, + estream_t *r_key, + unsigned char **r_fpr, size_t *r_fprlen, + char **r_url); +gpg_error_t gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid, + unsigned char **r_fpr, size_t *r_fprlen, + char **r_url); #endif /*GNUPG_G10_CALL_DIRMNGR_H*/ diff --git a/g10/gpgv.c b/g10/gpgv.c index 157fdea45..479bb9599 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -575,3 +575,19 @@ agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno) *r_serialno = NULL; return gpg_error (GPG_ERR_NO_SECKEY); } + +gpg_error_t +gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid, + unsigned char **r_fpr, size_t *r_fprlen, + char **r_url) +{ + (void)ctrl; + (void)userid; + if (r_fpr) + *r_fpr = NULL; + if (r_fprlen) + *r_fprlen = 0; + if (r_url) + *r_url = NULL; + return gpg_error (GPG_ERR_NOT_FOUND); +} diff --git a/g10/keyserver-internal.h b/g10/keyserver-internal.h index a955fc7da..fc1c3435d 100644 --- a/g10/keyserver-internal.h +++ b/g10/keyserver-internal.h @@ -42,8 +42,8 @@ gpg_error_t keyserver_search (ctrl_t ctrl, strlist_t tokens); int keyserver_fetch (ctrl_t ctrl, strlist_t urilist); int keyserver_import_cert (ctrl_t ctrl, const char *name, unsigned char **fpr,size_t *fpr_len); -int keyserver_import_pka (ctrl_t ctrl, - const char *name,unsigned char **fpr,size_t *fpr_len); +gpg_error_t keyserver_import_pka (ctrl_t ctrl, const char *name, + unsigned char **fpr,size_t *fpr_len); int keyserver_import_name (ctrl_t ctrl, const char *name,unsigned char **fpr,size_t *fpr_len, struct keyserver_spec *keyserver); diff --git a/g10/keyserver.c b/g10/keyserver.c index abe4bdebf..40ba49a61 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -41,8 +41,6 @@ #include "trustdb.h" #include "keyserver-internal.h" #include "util.h" -#include "dns-cert.h" -#include "pka.h" #ifdef USE_DNS_SRV #include "srv.h" #endif @@ -1897,7 +1895,7 @@ keyserver_import_cert (ctrl_t ctrl, if(domain) *domain='.'; - err = get_dns_cert (look, DNS_CERTTYPE_ANY, &key, fpr, fpr_len, &url); + err = gpg_dirmngr_dns_cert (ctrl, look, "*", &key, fpr, fpr_len, &url); if (err) ; else if (key) @@ -1957,37 +1955,35 @@ keyserver_import_cert (ctrl_t ctrl, /* Import key pointed to by a PKA record. Return the requested fingerprint in fpr. */ -int -keyserver_import_pka (ctrl_t ctrl, - const char *name,unsigned char **fpr,size_t *fpr_len) +gpg_error_t +keyserver_import_pka (ctrl_t ctrl, const char *name, + unsigned char **fpr, size_t *fpr_len) { - char *uri; - int rc = GPG_ERR_NO_PUBKEY; - - *fpr = xmalloc (20); - *fpr_len = 20; + gpg_error_t err; + char *url; - uri = get_pka_info (name, *fpr, 20); - if (uri && *uri) + err = gpg_dirmngr_get_pka (ctrl, name, fpr, fpr_len, &url); + if (url && *url && fpr && fpr_len) { - /* An URI is available. Lookup the key. */ + /* An URL is available. Lookup the key. */ struct keyserver_spec *spec; - spec = parse_keyserver_uri (uri, 1); + spec = parse_keyserver_uri (url, 1); if (spec) { - rc = keyserver_import_fprint (ctrl, *fpr, 20, spec); + err = keyserver_import_fprint (ctrl, *fpr, *fpr_len, spec); free_keyserver_spec (spec); } } - xfree (uri); + xfree (url); - if (rc) + if (err) { xfree(*fpr); *fpr = NULL; + *fpr_len = 0; } - return rc; + return err; } diff --git a/g10/mainproc.c b/g10/mainproc.c index 0f6ba2b32..e72d07640 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -38,9 +38,8 @@ #include "trustdb.h" #include "keyserver-internal.h" #include "photoid.h" -#include "pka.h" #include "mbox-util.h" - +#include "call-dirmngr.h" /* Put an upper limit on nested packets. The 32 is an arbitrary value, a much lower should actually be sufficient. */ @@ -1487,7 +1486,7 @@ get_pka_address (PKT_signature *sig) be retrieved for the signature we merely return it; if not we go out and try to get that DNS record. */ static const char * -pka_uri_from_sig (PKT_signature *sig) +pka_uri_from_sig (CTX c, PKT_signature *sig) { if (!sig->flags.pka_tried) { @@ -1496,17 +1495,28 @@ pka_uri_from_sig (PKT_signature *sig) sig->pka_info = get_pka_address (sig); if (sig->pka_info) { - char *uri; + char *url; + unsigned char *fpr; + size_t fprlen; - uri = get_pka_info (sig->pka_info->email, - sig->pka_info->fpr, sizeof sig->pka_info->fpr); - if (uri) + if (!gpg_dirmngr_get_pka (c->ctrl, sig->pka_info->email, + &fpr, &fprlen, &url)) { - sig->pka_info->valid = 1; - if (!*uri) - xfree (uri); - else - sig->pka_info->uri = uri; + if (fpr && fprlen == sizeof sig->pka_info->fpr) + { + memcpy (sig->pka_info->fpr, fpr, fprlen); + if (url) + { + sig->pka_info->valid = 1; + if (!*url) + xfree (url); + else + sig->pka_info->uri = url; + url = NULL; + } + } + xfree (fpr); + xfree (url); } } } @@ -1734,7 +1744,7 @@ check_sig_and_print (CTX c, kbnode_t node) && (opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE) && (opt.keyserver_options.options & KEYSERVER_HONOR_PKA_RECORD)) { - const char *uri = pka_uri_from_sig (sig); + const char *uri = pka_uri_from_sig (c, sig); if (uri) { @@ -1997,7 +2007,7 @@ check_sig_and_print (CTX c, kbnode_t node) if (!rc) { if ((opt.verify_options & VERIFY_PKA_LOOKUPS)) - pka_uri_from_sig (sig); /* Make sure PKA info is available. */ + pka_uri_from_sig (c, sig); /* Make sure PKA info is available. */ rc = check_signatures_trust (sig); } -- cgit v1.2.3