diff options
author | Werner Koch <[email protected]> | 2005-07-28 18:59:36 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2005-07-28 18:59:36 +0000 |
commit | 986a137c58cae8dc7210ccf51cdea38ec6e8828f (patch) | |
tree | 4f847f04509fc74a1fa6db4839abd5d41c80eb9c | |
parent | Converted all m_free to xfree etc. (diff) | |
download | gnupg-986a137c58cae8dc7210ccf51cdea38ec6e8828f.tar.gz gnupg-986a137c58cae8dc7210ccf51cdea38ec6e8828f.zip |
Implemented PKA trust model
Diffstat (limited to '')
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | g10/ChangeLog | 20 | ||||
-rw-r--r-- | g10/Makefile.am | 2 | ||||
-rw-r--r-- | g10/free-packet.c | 29 | ||||
-rw-r--r-- | g10/gpgv.c | 7 | ||||
-rw-r--r-- | g10/keygen.c | 21 | ||||
-rw-r--r-- | g10/main.h | 1 | ||||
-rw-r--r-- | g10/mainproc.c | 115 | ||||
-rw-r--r-- | g10/misc.c | 25 | ||||
-rw-r--r-- | g10/packet.h | 80 | ||||
-rw-r--r-- | g10/parse-packet.c | 2 | ||||
-rw-r--r-- | g10/pkclist.c | 42 | ||||
-rw-r--r-- | include/util.h | 4 | ||||
-rw-r--r-- | util/ChangeLog | 5 | ||||
-rw-r--r-- | util/Makefile.am | 11 | ||||
-rw-r--r-- | util/pka.c | 254 | ||||
-rw-r--r-- | util/srv.c | 6 |
19 files changed, 576 insertions, 62 deletions
@@ -1,3 +1,7 @@ +2005-07-28 Werner Koch <[email protected]> + + * configure.ac (USE_DNS_PKA): Define in addition to USE_DNS_SRV. + 2005-07-27 Werner Koch <[email protected]> Replaced in all directories all calls to m_free, m_alloc, @@ -10,6 +10,12 @@ Noteworthy changes in version 1.4.3 --enable-old-keyserver-helpers. Note that none of this affects finger or LDAP support, which are unchanged. + * Implemented Public Key Association (PKA) trust model option. + This is an optional trust model on top of the standard ones. It + make use of of special DNS records and notation data to + associate a mail address with an OpenPGP key. See: XXXX for a + description. + Noteworthy changes in version 1.4.2 (2005-07-26) ------------------------------------------------ diff --git a/configure.ac b/configure.ac index 107983826..33a9e09f1 100644 --- a/configure.ac +++ b/configure.ac @@ -563,7 +563,8 @@ AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, gethostbyname, AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt, [NETLIBS="-lsocket $NETLIBS"])) -dnl Now try for the resolver functions so we can use DNS SRV +dnl Now try for the resolver functions so we can use DNS SRV and our +dnl PKA feature. AC_ARG_ENABLE(dns-srv, AC_HELP_STRING([--disable-dns-srv], @@ -597,6 +598,7 @@ if (test x"$try_hkp" = xyes || test x"$try_http" = xyes) && test x"$use_dns_srv" if test x"$use_dns_srv" = xyes ; then AC_DEFINE(USE_DNS_SRV,1,[define to use DNS SRV]) SRVLIBS=$LIBS + AC_DEFINE(USE_DNS_PKA,1,[define to use our experimental DNS PKA]) fi LIBS=$_srv_save_libs diff --git a/g10/ChangeLog b/g10/ChangeLog index 9c1acbca6..f9fab2bea 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,23 @@ +2005-07-28 Werner Koch <[email protected]> + + * Makefile.am (other_libs): Add SRVLIBS. + + * parse-packet.c (can_handle_critical_notation): We know about + * packet.h (PKT_signature): New fields PKA_INFO and PKA_TRIED. + (pka_info_t): New. + * free-packet.c (cp_pka_info): New. + (free_seckey_enc, copy_signature): Support new fields. + * mainproc.c (get_pka_address, pka_uri_from_sig): New. + (check_sig_and_print): Try to get the keyserver from the PKA + record. + * pkclist.c (check_signatures_trust): Adjust the trust based on + the PKA. + * gpgv.c (parse_keyserver_uri): New stub. + + * keygen.c (has_invalid_email_chars): Moved to .. + * misc.c (has_invalid_email_chars): .. here and made global. + 2005-07-27 Werner Koch <[email protected]> * export.c (do_export_stream): Make two strings translatable. diff --git a/g10/Makefile.am b/g10/Makefile.am index 9e50eeb9a..3c1ba7a76 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -28,7 +28,7 @@ if ! HAVE_DOSISH_SYSTEM AM_CFLAGS = -DGNUPG_LIBEXECDIR="\"$(libexecdir)/@PACKAGE@\"" endif needed_libs = ../cipher/libcipher.a ../mpi/libmpi.a ../util/libutil.a -other_libs = $(LIBICONV) $(LIBINTL) $(CAPLIBS) +other_libs = $(LIBICONV) $(SRVLIBS) $(LIBINTL) $(CAPLIBS) bin_PROGRAMS = gpg gpgv diff --git a/g10/free-packet.c b/g10/free-packet.c index 3ede4db9f..01ab543dd 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -1,6 +1,6 @@ /* free-packet.c - cleanup stuff for packets - * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 - * Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -62,10 +62,17 @@ free_seckey_enc( PKT_signature *sig ) mpi_free(sig->data[0]); for(i=0; i < n; i++ ) mpi_free( sig->data[i] ); - + xfree(sig->revkey); xfree(sig->hashed); xfree(sig->unhashed); + + if (sig->pka_info) + { + xfree (sig->pka_info->uri); + xfree (sig->pka_info); + } + xfree(sig); } @@ -195,6 +202,21 @@ copy_public_parts_to_secret_key( PKT_public_key *pk, PKT_secret_key *sk ) sk->keyid[1] = pk->keyid[1]; } + +static pka_info_t * +cp_pka_info (const pka_info_t *s) +{ + pka_info_t *d = xmalloc (sizeof *s + strlen (s->email)); + + d->valid = s->valid; + d->checked = s->checked; + d->uri = s->uri? xstrdup (s->uri):NULL; + memcpy (d->fpr, s->fpr, sizeof s->fpr); + strcpy (d->email, s->email); + return d; +} + + PKT_signature * copy_signature( PKT_signature *d, PKT_signature *s ) { @@ -210,6 +232,7 @@ copy_signature( PKT_signature *d, PKT_signature *s ) for(i=0; i < n; i++ ) d->data[i] = mpi_copy( s->data[i] ); } + d->pka_info = s->pka_info? cp_pka_info (s->pka_info) : NULL; d->hashed = cp_subpktarea (s->hashed); d->unhashed = cp_subpktarea (s->unhashed); if(s->numrevkeys) diff --git a/g10/gpgv.c b/g10/gpgv.c index 29211075f..1f2687605 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -333,6 +333,13 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, } struct keyserver_spec *parse_preferred_keyserver(PKT_signature *sig) {return NULL;} +struct keyserver_spec *parse_keyserver_uri(const char *uri,int require_scheme, + const char *configname, + unsigned int configlineno) +{ + return NULL; +} + void free_keyserver_spec(struct keyserver_spec *keyserver) {} /* Stubs to avoid linking to photoid.c */ diff --git a/g10/keygen.c b/g10/keygen.c index ca20ed8e8..a657c13c9 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1609,27 +1609,6 @@ ask_expiredate() return x? make_timestamp() + x : 0; } -static int -has_invalid_email_chars( const char *s ) -{ - int at_seen=0; - static char valid_chars[] = "01234567890_-." - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - for( ; *s; s++ ) { - if( *s & 0x80 ) - return 1; - if( *s == '@' ) - at_seen=1; - else if( !at_seen && !( !!strchr( valid_chars, *s ) || *s == '+' ) ) - return 1; - else if( at_seen && !strchr( valid_chars, *s ) ) - return 1; - } - return 0; -} - static char * ask_user_id( int mode ) diff --git a/g10/main.h b/g10/main.h index 8ffefbb5b..787b58add 100644 --- a/g10/main.h +++ b/g10/main.h @@ -125,6 +125,7 @@ char *argsplit(char *string); int parse_options(char *str,unsigned int *options, struct parse_options *opts,int noisy); char *unescape_percent_string (const unsigned char *s); +int has_invalid_email_chars (const char *s); char *default_homedir (void); const char *get_libexecdir (void); diff --git a/g10/mainproc.c b/g10/mainproc.c index 5913f9304..afd347cfc 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -1296,6 +1296,86 @@ do_proc_packets( CTX c, IOBUF a ) } +/* Helper for pka_uri_from_sig to parse the to-be-verified address out + of the notation data. */ +static pka_info_t * +get_pka_address (PKT_signature *sig) +{ + const unsigned char *p; + size_t len, n1, n2; + int seq = 0; + pka_info_t *pka = NULL; + + while ((p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_NOTATION, + &len, &seq, NULL))) + { + if (len < 8) + continue; /* Notation packet is too short. */ + n1 = (p[4]<<8)|p[5]; + n2 = (p[6]<<8)|p[7]; + if (8 + n1 + n2 != len) + continue; /* Length fields of notation packet are inconsistent. */ + p += 8; + if (n1 != 21 || memcmp (p, "[email protected]", 21)) + continue; /* Not the notation we want. */ + p += n1; + if (n2 < 3) + continue; /* Impossible email address. */ + + if (pka) + break; /* For now we only use the first valid PKA notation. In + future we might want to keep additional PKA + notations in a linked list. */ + + pka = xmalloc (sizeof *pka + n2); + pka->valid = 0; + pka->checked = 0; + pka->uri = NULL; + memcpy (pka->email, p, n2); + pka->email[n2] = 0; + + if (has_invalid_email_chars (pka->email)) + { + /* We don't accept invalid mail addresses. */ + xfree (pka); + pka = NULL; + } + } + + return pka; +} + + +/* Return the URI from a DNS PKA record. If this record has already + 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) +{ + if (!sig->flags.pka_tried) + { + assert (!sig->pka_info); + sig->flags.pka_tried = 1; + sig->pka_info = get_pka_address (sig); + if (sig->pka_info) + { + char *uri; + + uri = get_pka_info (sig->pka_info->email, sig->pka_info->fpr); + if (uri) + { + sig->pka_info->valid = 1; + if (!*uri) + xfree (uri); + else + sig->pka_info->uri = uri; + } + } + } + return sig->pka_info? sig->pka_info->uri : NULL; +} + + static int check_sig_and_print( CTX c, KBNODE node ) { @@ -1419,8 +1499,34 @@ check_sig_and_print( CTX c, KBNODE node ) } } - /* If the preferred keyserver thing above didn't work, this is a - second try. */ + + /* If the preferred keyserver thing above didn't work, our second + try is to use the URI from a DNS PKA record. */ + if ( rc == G10ERR_NO_PUBKEY ) + { + const char *uri = pka_uri_from_sig (sig); + + if (uri) + { + int res; + struct keyserver_spec *spec; + + spec = parse_keyserver_uri (uri, 0, NULL, 0); + if (spec) + { + glo_ctrl.in_auto_key_retrieve++; + res = keyserver_import_keyid (sig->keyid, spec); + glo_ctrl.in_auto_key_retrieve--; + free_keyserver_spec (spec); + if (!res) + rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey ); + } + } + } + + + /* If the preferred keyserver thing above didn't work and we got + no information from the DNS PKA, this is a third try. */ if( rc == G10ERR_NO_PUBKEY && opt.keyserver && (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE)) @@ -1673,8 +1779,11 @@ check_sig_and_print( CTX c, KBNODE node ) free_public_key( vpk ); } - if( !rc ) + if (!rc) + { + pka_uri_from_sig (sig); /* Make sure PKA info is available. */ rc = check_signatures_trust( sig ); + } if(sig->flags.expired) { diff --git a/g10/misc.c b/g10/misc.c index 380172f09..88b7b2bb1 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1065,6 +1065,31 @@ unescape_percent_string (const unsigned char *s) +int +has_invalid_email_chars (const char *s) +{ + int at_seen=0; + static char valid_chars[] = ("01234567890_-." + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + + for ( ; *s; s++ ) + { + if ( *s & 0x80 ) + return 1; + if ( *s == '@' ) + at_seen=1; + else if ( !at_seen && !( !!strchr( valid_chars, *s ) || *s == '+' ) ) + return 1; + else if ( at_seen && !strchr( valid_chars, *s ) ) + return 1; + } + return 0; +} + + + + /* This is a helper function to load a Windows function from either of one DLLs. */ #ifdef HAVE_W32_SYSTEM diff --git a/g10/packet.h b/g10/packet.h index cbbc0d7b6..0fe87f7ce 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -122,36 +122,56 @@ struct revocation_key { byte fpr[MAX_FINGERPRINT_LEN]; }; -typedef struct { - struct { - unsigned checked:1; /* signature has been checked */ - unsigned valid:1; /* signature is good (if checked is set) */ - unsigned chosen_selfsig:1; /* a selfsig that is the chosen one */ - unsigned unknown_critical:1; - unsigned exportable:1; - unsigned revocable:1; - unsigned policy_url:1; /* At least one policy URL is present */ - unsigned notation:1; /* At least one notation is present */ - unsigned pref_ks:1; /* At least one preferred keyserver is present */ - unsigned expired:1; - } flags; - u32 keyid[2]; /* 64 bit keyid */ - u32 timestamp; /* signature made */ - u32 expiredate; /* expires at this date or 0 if not at all */ - byte version; - byte sig_class; /* sig classification, append for MD calculation*/ - byte pubkey_algo; /* algorithm used for public key scheme */ - /* (PUBKEY_ALGO_xxx) */ - byte digest_algo; /* algorithm used for digest (DIGEST_ALGO_xxxx) */ - byte trust_depth; - byte trust_value; - const byte *trust_regexp; - struct revocation_key **revkey; - int numrevkeys; - subpktarea_t *hashed; /* all subpackets with hashed data (v4 only) */ - subpktarea_t *unhashed; /* ditto for unhashed data */ - byte digest_start[2]; /* first 2 bytes of the digest */ - MPI data[PUBKEY_MAX_NSIG]; + +/* Object to keep information about a PKA DNS record. */ +typedef struct +{ + int valid; /* An actual PKA record exists for EMAIL. */ + int checked; /* Set to true if the FPR has been checked against the + actual key. */ + char *uri; /* Malloced string with the URI. NULL if the URI is + not available.*/ + unsigned char fpr[20]; /* The fingerprint as stored in the PKA RR. */ + char email[1];/* The email address from the notation data. */ +} pka_info_t; + + +/* Object to keep information pertaining to a signature. */ +typedef struct +{ + struct + { + unsigned checked:1; /* Signature has been checked. */ + unsigned valid:1; /* Signature is good (if checked is set). */ + unsigned chosen_selfsig:1; /* A selfsig that is the chosen one. */ + unsigned unknown_critical:1; + unsigned exportable:1; + unsigned revocable:1; + unsigned policy_url:1; /* At least one policy URL is present */ + unsigned notation:1; /* At least one notation is present */ + unsigned pref_ks:1; /* At least one preferred keyserver is present */ + unsigned expired:1; + unsigned pka_tried:1; /* Set if we tried to retrieve the PKA record. */ + } flags; + u32 keyid[2]; /* 64 bit keyid */ + u32 timestamp; /* Signature made (seconds since Epoch). */ + u32 expiredate; /* Expires at this date or 0 if not at all. */ + byte version; + byte sig_class; /* Sig classification, append for MD calculation. */ + byte pubkey_algo; /* Algorithm used for public key scheme */ + /* (PUBKEY_ALGO_xxx) */ + byte digest_algo; /* Algorithm used for digest (DIGEST_ALGO_xxxx). */ + byte trust_depth; + byte trust_value; + const byte *trust_regexp; + struct revocation_key **revkey; + int numrevkeys; + pka_info_t *pka_info; /* Malloced PKA data or NULL if not + available. See also flags.pka_tried. */ + subpktarea_t *hashed; /* All subpackets with hashed data (v4 only). */ + subpktarea_t *unhashed; /* Ditto for unhashed data. */ + byte digest_start[2]; /* First 2 bytes of the digest. */ + MPI data[PUBKEY_MAX_NSIG]; } PKT_signature; #define ATTRIB_IMAGE 1 diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 068ffbffe..80ebd0d5d 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -1042,6 +1042,8 @@ can_handle_critical_notation(const byte *name,size_t len) { if(len==32 && memcmp(name,"[email protected]",32)==0) return 1; + if(len==21 && memcmp(name,"[email protected]",21)==0) + return 1; return 0; } diff --git a/g10/pkclist.c b/g10/pkclist.c index 3967b59ec..1b3238926 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -532,6 +532,48 @@ check_signatures_trust( PKT_signature *sig ) if ((trustlevel & TRUST_FLAG_DISABLED)) log_info (_("Note: This key has been disabled.\n")); + /* If we have PKA information adjust the trustlevel. */ + if (sig->pka_info && sig->pka_info->valid) + { + unsigned char fpr[MAX_FINGERPRINT_LEN]; + PKT_public_key *primary_pk; + size_t fprlen; + int okay; + + log_info (_("Note: Verified address is `%s'\n"), sig->pka_info->email); + + primary_pk = xmalloc_clear (sizeof *primary_pk); + get_pubkey (primary_pk, pk->main_keyid); + fingerprint_from_pk (primary_pk, fpr, &fprlen); + free_public_key (primary_pk); + + if ( fprlen == 20 && !memcmp (sig->pka_info->fpr, fpr, 20) ) + okay = 1; + else + okay = 0; + + switch ( (trustlevel & TRUST_MASK) ) + { + case TRUST_UNKNOWN: + case TRUST_UNDEFINED: + case TRUST_MARGINAL: + if (okay) + { + trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_FULLY); + log_info ("trustlevel adjusted to FULL due to valid PKA info\n"); + } + /* (fall through) */ + case TRUST_FULLY: + if (!okay) + { + trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_NEVER); + log_info ("trustlevel adjusted to NEVER due to bad PKA info\n"); + } + break; + } + } + + /* Now let the user know what up with the trustlevel. */ switch ( (trustlevel & TRUST_MASK) ) { case TRUST_EXPIRED: diff --git a/include/util.h b/include/util.h index 31e18879d..77b2b66f2 100644 --- a/include/util.h +++ b/include/util.h @@ -253,6 +253,10 @@ int vasprintf (char **result, const char *format, va_list args); int asprintf (char **buf, const char *fmt, ...); #endif /*_WIN32*/ +/*-- pka.c --*/ +char *get_pka_info (const char *address, unsigned char *fpr); + + /**** other missing stuff ****/ #ifndef HAVE_ATEXIT /* For SunOS */ diff --git a/util/ChangeLog b/util/ChangeLog index b2c243c8d..fd181172c 100644 --- a/util/ChangeLog +++ b/util/ChangeLog @@ -1,3 +1,8 @@ +2005-07-28 Werner Koch <[email protected]> + + * pka.c: New. + * Makefile.am (pka-test): new. + 2005-07-27 Werner Koch <[email protected]> * memory.c (FNAMEX, FNAMEXM): New macros to cope with the now used diff --git a/util/Makefile.am b/util/Makefile.am index a143199af..cb2019be4 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -38,7 +38,7 @@ endif #libutil_a_LDFLAGS = libutil_a_SOURCES = logger.c fileutil.c miscutil.c strgutil.c \ ttyio.c argparse.c memory.c secmem.c errors.c iobuf.c \ - dotlock.c http.c srv.h srv.c simple-gettext.c \ + dotlock.c http.c srv.h srv.c pka.c simple-gettext.c \ membuf.c w32reg.c $(assuan_source) libutil_a_DEPENDENCIES = @LIBOBJS@ @REGEX_O@ @@ -46,9 +46,14 @@ libutil_a_DEPENDENCIES = @LIBOBJS@ @REGEX_O@ libutil_a_LIBADD = @LIBOBJS@ @REGEX_O@ http-test: http.c - gcc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \ + cc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \ -DTEST -o http-test http.c libutil.a @LIBINTL@ @SRVLIBS@ @CAPLIBS@ srv-test: srv.c - gcc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \ + cc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \ -DTEST -o srv-test srv.c libutil.a @LIBINTL@ @SRVLIBS@ @CAPLIBS@ + +pka-test: pka.c + cc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \ + -DTEST -o pka-test pka.c libutil.a @LIBINTL@ @SRVLIBS@ @CAPLIBS@ + diff --git a/util/pka.c b/util/pka.c new file mode 100644 index 000000000..95689e02b --- /dev/null +++ b/util/pka.c @@ -0,0 +1,254 @@ +/* pka.c - DNS Public Key Association RR access + * Copyright (C) 2005 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef USE_DNS_PKA +#include <sys/types.h> +#ifdef _WIN32 +#include <windows.h> +#else +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> +#endif +#endif /* USE_DNS_PKA */ + +#include "memory.h" +#include "types.h" +#include "util.h" + + +#ifdef USE_DNS_PKA +/* Parse the TXT resource record. Format is: + + v=1;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=1")) + 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 optiobnal. + Caller must xfree the returned string. + + On error NULL is returned and the 20 bytes at FPR are not + defined. */ +char * +get_pka_info (const char *address, unsigned char *fpr) +{ + unsigned char answer[PACKETSZ]; + int anslen; + int qdcount, ancount, nscount, arcount; + int rc; + unsigned char *p, *pend; + const char *domain; + char *name; + + + domain = strrchr (address, '@'); + if (!domain || domain == address || !domain[1]) + return NULL; /* invalid mail address given. */ + + name = malloc (strlen (address) + 5 + 1); + memcpy (name, address, domain - address); + strcpy (stpcpy (name + (domain-address), "._pka."), domain+1); + + anslen = res_query (name, C_IN, T_TXT, answer, PACKETSZ); + xfree (name); + if (anslen < sizeof(HEADER)) + return NULL; /* DNS resolver returned a too short answer. */ + if ( (rc=((HEADER*)answer)->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*)answer)->qdcount); + ancount = ntohs (((HEADER*)answer)->ancount); + nscount = ntohs (((HEADER*)answer)->nscount); + arcount = ntohs (((HEADER*)answer)->arcount); + + 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 = *p++ << 8; + type |= *p++; + class = *p++ << 8; + class |= *p++; + p += 4; + txtlen = *p++ << 8; + txtlen |= *p++; + 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; +} +#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) +{ + 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 libutil.a" +End: +*/ diff --git a/util/srv.c b/util/srv.c index a00119dde..e146237b7 100644 --- a/util/srv.c +++ b/util/srv.c @@ -251,3 +251,9 @@ main(int argc,char *argv[]) return 0; } #endif /* TEST */ + +/* +Local Variables: +compile-command: "cc -DTEST -I.. -I../include -Wall -g -o srv srv.c -lresolv libutil.a" +End: +*/ |