diff options
Diffstat (limited to '')
-rw-r--r-- | g10/keyserver.c | 2001 |
1 files changed, 2001 insertions, 0 deletions
diff --git a/g10/keyserver.c b/g10/keyserver.c new file mode 100644 index 0000000..1fbe728 --- /dev/null +++ b/g10/keyserver.c @@ -0,0 +1,2001 @@ +/* keyserver.c - generic keyserver code + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, + * 2009, 2011, 2012 Free Software Foundation, Inc. + * Copyright (C) 2014 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include "gpg.h" +#include "../common/iobuf.h" +#include "filter.h" +#include "keydb.h" +#include "../common/status.h" +#include "exec.h" +#include "main.h" +#include "../common/i18n.h" +#include "../common/ttyio.h" +#include "options.h" +#include "packet.h" +#include "trustdb.h" +#include "keyserver-internal.h" +#include "../common/util.h" +#include "../common/membuf.h" +#include "../common/mbox-util.h" +#include "call-dirmngr.h" + +#ifdef HAVE_W32_SYSTEM +/* It seems Vista doesn't grok X_OK and so fails access() tests. + Previous versions interpreted X_OK as F_OK anyway, so we'll just + use F_OK directly. */ +#undef X_OK +#define X_OK F_OK +#endif /* HAVE_W32_SYSTEM */ + +struct keyrec +{ + KEYDB_SEARCH_DESC desc; + u32 createtime,expiretime; + int size,flags; + byte type; + IOBUF uidbuf; + unsigned int lines; +}; + +/* Parameters for the search line handler. */ +struct search_line_handler_parm_s +{ + ctrl_t ctrl; /* The session control structure. */ + char *searchstr_disp; /* Native encoded search string or NULL. */ + KEYDB_SEARCH_DESC *desc; /* Array with search descriptions. */ + int count; /* Number of keys we are currently prepared to + handle. This is the size of the DESC array. If + it is too small, it will grow safely. */ + int validcount; /* Enable the "Key x-y of z" messages. */ + int nkeys; /* Number of processed records. */ + int any_lines; /* At least one line has been processed. */ + unsigned int numlines; /* Counter for displayed lines. */ + int eof_seen; /* EOF encountered. */ + int not_found; /* Set if no keys have been found. */ +}; + + +enum ks_action {KS_UNKNOWN=0,KS_GET,KS_GETNAME,KS_SEND,KS_SEARCH}; + +static struct parse_options keyserver_opts[]= + { + /* some of these options are not real - just for the help + message */ + {"max-cert-size",0,NULL,NULL}, /* MUST be the first in this array! */ + {"http-proxy", KEYSERVER_HTTP_PROXY, NULL, /* MUST be the second! */ + N_("override proxy options set for dirmngr")}, + + {"include-revoked",0,NULL,N_("include revoked keys in search results")}, + {"include-subkeys",0,NULL,N_("include subkeys when searching by key ID")}, + {"timeout", KEYSERVER_TIMEOUT, NULL, + N_("override timeout options set for dirmngr")}, + {"refresh-add-fake-v3-keyids",KEYSERVER_ADD_FAKE_V3,NULL, + NULL}, + {"auto-key-retrieve",KEYSERVER_AUTO_KEY_RETRIEVE,NULL, + N_("automatically retrieve keys when verifying signatures")}, + {"honor-keyserver-url",KEYSERVER_HONOR_KEYSERVER_URL,NULL, + N_("honor the preferred keyserver URL set on the key")}, + {"honor-pka-record",KEYSERVER_HONOR_PKA_RECORD,NULL, + N_("honor the PKA record set on a key when retrieving keys")}, + {NULL,0,NULL,NULL} + }; + +static gpg_error_t keyserver_get (ctrl_t ctrl, + KEYDB_SEARCH_DESC *desc, int ndesc, + struct keyserver_spec *override_keyserver, + unsigned int flags, + unsigned char **r_fpr, size_t *r_fprlen); +static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs); + + +/* Reasonable guess. The commonly used test key simon.josefsson.org + is larger than 32k, thus we need at least this value. */ +#define DEFAULT_MAX_CERT_SIZE 65536 + +static size_t max_cert_size=DEFAULT_MAX_CERT_SIZE; + + +static void +warn_kshelper_option(char *option, int noisy) +{ + char *p; + + if ((p=strchr (option, '='))) + *p = 0; + + if (!strcmp (option, "ca-cert-file")) + log_info ("keyserver option '%s' is obsolete; please use " + "'%s' in dirmngr.conf\n", + "ca-cert-file", "hkp-cacert"); + else if (!strcmp (option, "check-cert") + || !strcmp (option, "broken-http-proxy")) + log_info ("keyserver option '%s' is obsolete\n", option); + else if (noisy || opt.verbose) + log_info ("keyserver option '%s' is unknown\n", option); +} + + +/* Called from main to parse the args for --keyserver-options. */ +int +parse_keyserver_options(char *options) +{ + int ret=1; + char *tok; + char *max_cert=NULL; + + keyserver_opts[0].value=&max_cert; + keyserver_opts[1].value=&opt.keyserver_options.http_proxy; + + while((tok=optsep(&options))) + { + if(tok[0]=='\0') + continue; + + /* We accept quite a few possible options here - some options to + handle specially, the keyserver_options list, and import and + export options that pertain to keyserver operations. */ + + if (!parse_options (tok,&opt.keyserver_options.options, keyserver_opts,0) + && !parse_import_options(tok,&opt.keyserver_options.import_options,0) + && !parse_export_options(tok,&opt.keyserver_options.export_options,0)) + { + /* All of the standard options have failed, so the option was + destined for a keyserver plugin as used by GnuPG < 2.1 */ + warn_kshelper_option (tok, 1); + } + } + + if(max_cert) + { + max_cert_size=strtoul(max_cert,(char **)NULL,10); + + if(max_cert_size==0) + max_cert_size=DEFAULT_MAX_CERT_SIZE; + } + + return ret; +} + + +void +free_keyserver_spec(struct keyserver_spec *keyserver) +{ + xfree(keyserver->uri); + xfree(keyserver); +} + +/* Return 0 for match */ +static int +cmp_keyserver_spec(struct keyserver_spec *one, struct keyserver_spec *two) +{ + return !!ascii_strcasecmp(one->uri, two->uri); +} + + +/* Try and match one of our keyservers. If we can, return that. If + we can't, return our input. */ +struct keyserver_spec * +keyserver_match(struct keyserver_spec *spec) +{ + struct keyserver_spec *ks; + + for(ks=opt.keyserver;ks;ks=ks->next) + if(cmp_keyserver_spec(spec,ks)==0) + return ks; + + return spec; +} + + +/* Create a new keyserver object from STRING. Unless REQUIRE_SCHEME + * is set a missing scheme is replaced by "hkp://". The data structure + * could be much easier but in the past we parsed the URI here for the + * old 2.0 keyserver helpers - which is not anymore needed. */ +keyserver_spec_t +parse_keyserver_uri (const char *string, int require_scheme) +{ + struct keyserver_spec *keyserver; + const char *idx; + int count; + + log_assert (string); + + keyserver = xcalloc (1, sizeof *keyserver); + + /* Get the scheme */ + for(idx=string, count=0; *idx && *idx!=':';idx++) + { + count++; + + /* Do we see the start of an RFC-2732 ipv6 address here? If so, + there clearly isn't a scheme so get out early. */ + if(*idx=='[') + { + /* Was the '[' the first thing in the string? If not, we + have a mangled scheme with a [ in it so fail. */ + if(count==1) + break; + else + goto fail; + } + } + + if(count==0) + goto fail; + + if(*idx=='\0' || *idx=='[') + { + if(require_scheme) + return NULL; + + /* Assume HKP if there is no scheme */ + keyserver->uri = xstrconcat ("hkp://", string, NULL); + } + else + { + keyserver->uri = xstrdup (string); + } + + return keyserver; + + fail: + free_keyserver_spec(keyserver); + return NULL; +} + + +struct keyserver_spec * +parse_preferred_keyserver(PKT_signature *sig) +{ + struct keyserver_spec *spec=NULL; + const byte *p; + size_t plen; + + p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&plen); + if(p && plen) + { + byte *dupe=xmalloc(plen+1); + + memcpy(dupe,p,plen); + dupe[plen]='\0'; + spec = parse_keyserver_uri (dupe, 1); + xfree(dupe); + } + + return spec; +} + +static void +print_keyrec (ctrl_t ctrl, int number,struct keyrec *keyrec) +{ + int i; + + iobuf_writebyte(keyrec->uidbuf,0); + iobuf_flush_temp(keyrec->uidbuf); + es_printf ("(%d)\t%s ", number, iobuf_get_temp_buffer (keyrec->uidbuf)); + + if (keyrec->size>0) + es_printf ("%d bit ", keyrec->size); + + if(keyrec->type) + { + const char *str; + + str = openpgp_pk_algo_name (keyrec->type); + + if (str && strcmp (str, "?")) + es_printf ("%s ",str); + else + es_printf ("unknown "); + } + + switch(keyrec->desc.mode) + { + /* If the keyserver helper gave us a short keyid, we have no + choice but to use it. Do check --keyid-format to add a 0x if + needed. */ + case KEYDB_SEARCH_MODE_SHORT_KID: + es_printf ("key %s%08lX", + (opt.keyid_format==KF_0xSHORT + || opt.keyid_format==KF_0xLONG)?"0x":"", + (ulong)keyrec->desc.u.kid[1]); + break; + + /* However, if it gave us a long keyid, we can honor + --keyid-format via keystr(). */ + case KEYDB_SEARCH_MODE_LONG_KID: + es_printf ("key %s",keystr(keyrec->desc.u.kid)); + break; + + /* If it gave us a PGP 2.x fingerprint, not much we can do + beyond displaying it. */ + case KEYDB_SEARCH_MODE_FPR16: + es_printf ("key "); + for(i=0;i<16;i++) + es_printf ("%02X",keyrec->desc.u.fpr[i]); + break; + + /* If we get a modern fingerprint, we have the most + flexibility. */ + case KEYDB_SEARCH_MODE_FPR20: + { + u32 kid[2]; + keyid_from_fingerprint (ctrl, keyrec->desc.u.fpr,20,kid); + es_printf("key %s",keystr(kid)); + } + break; + + default: + BUG(); + break; + } + + if(keyrec->createtime>0) + { + es_printf (", "); + es_printf (_("created: %s"), strtimestamp(keyrec->createtime)); + } + + if(keyrec->expiretime>0) + { + es_printf (", "); + es_printf (_("expires: %s"), strtimestamp(keyrec->expiretime)); + } + + if (keyrec->flags&1) + es_printf (" (%s)", _("revoked")); + if(keyrec->flags&2) + es_printf (" (%s)", _("disabled")); + if(keyrec->flags&4) + es_printf (" (%s)", _("expired")); + + es_printf ("\n"); +} + +/* Returns a keyrec (which must be freed) once a key is complete, and + NULL otherwise. Call with a NULL keystring once key parsing is + complete to return any unfinished keys. */ +static struct keyrec * +parse_keyrec(char *keystring) +{ + /* FIXME: Remove the static and put the data into the parms we use + for the caller anyway. */ + static struct keyrec *work=NULL; + struct keyrec *ret=NULL; + char *record; + int i; + + if(keystring==NULL) + { + if(work==NULL) + return NULL; + else if(work->desc.mode==KEYDB_SEARCH_MODE_NONE) + { + xfree(work); + return NULL; + } + else + { + ret=work; + work=NULL; + return ret; + } + } + + if(work==NULL) + { + work=xmalloc_clear(sizeof(struct keyrec)); + work->uidbuf=iobuf_temp(); + } + + trim_trailing_ws (keystring, strlen (keystring)); + + if((record=strsep(&keystring,":"))==NULL) + return ret; + + if(ascii_strcasecmp("pub",record)==0) + { + char *tok; + gpg_error_t err; + + if(work->desc.mode) + { + ret=work; + work=xmalloc_clear(sizeof(struct keyrec)); + work->uidbuf=iobuf_temp(); + } + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + err = classify_user_id (tok, &work->desc, 1); + if (err || (work->desc.mode != KEYDB_SEARCH_MODE_SHORT_KID + && work->desc.mode != KEYDB_SEARCH_MODE_LONG_KID + && work->desc.mode != KEYDB_SEARCH_MODE_FPR16 + && work->desc.mode != KEYDB_SEARCH_MODE_FPR20)) + { + work->desc.mode=KEYDB_SEARCH_MODE_NONE; + return ret; + } + + /* Note all items after this are optional. This allows us to + have a pub line as simple as pub:keyid and nothing else. */ + + work->lines++; + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + work->type=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + work->size=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + if(atoi(tok)<=0) + work->createtime=0; + else + work->createtime=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + if(atoi(tok)<=0) + work->expiretime=0; + else + { + work->expiretime=atoi(tok); + /* Force the 'e' flag on if this key is expired. */ + if(work->expiretime<=make_timestamp()) + work->flags|=4; + } + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + while(*tok) + switch(*tok++) + { + case 'r': + case 'R': + work->flags|=1; + break; + + case 'd': + case 'D': + work->flags|=2; + break; + + case 'e': + case 'E': + work->flags|=4; + break; + } + } + else if(ascii_strcasecmp("uid",record)==0 && work->desc.mode) + { + char *userid,*tok,*decoded; + + if((tok=strsep(&keystring,":"))==NULL) + return ret; + + if(strlen(tok)==0) + return ret; + + userid=tok; + + /* By definition, de-%-encoding is always smaller than the + original string so we can decode in place. */ + + i=0; + + while(*tok) + if(tok[0]=='%' && tok[1] && tok[2]) + { + int c; + + userid[i] = (c=hextobyte(&tok[1])) == -1 ? '?' : c; + i++; + tok+=3; + } + else + userid[i++]=*tok++; + + /* We don't care about the other info provided in the uid: line + since no keyserver supports marking userids with timestamps + or revoked/expired/disabled yet. */ + + /* No need to check for control characters, as utf8_to_native + does this for us. */ + + decoded=utf8_to_native(userid,i,0); + if(strlen(decoded)>opt.screen_columns-10) + decoded[opt.screen_columns-10]='\0'; + iobuf_writestr(work->uidbuf,decoded); + xfree(decoded); + iobuf_writestr(work->uidbuf,"\n\t"); + work->lines++; + } + + /* Ignore any records other than "pri" and "uid" for easy future + growth. */ + + return ret; +} + +/* Show a prompt and allow the user to select keys for retrieval. */ +static gpg_error_t +show_prompt (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int numdesc, + int count, const char *search) +{ + gpg_error_t err; + char *answer = NULL; + + es_fflush (es_stdout); + + if (count && opt.command_fd == -1) + { + static int from = 1; + tty_printf ("Keys %d-%d of %d for \"%s\". ", + from, numdesc, count, search); + from = numdesc + 1; + } + + again: + err = 0; + xfree (answer); + answer = cpr_get_no_help ("keysearch.prompt", + _("Enter number(s), N)ext, or Q)uit > ")); + /* control-d */ + if (answer[0]=='\x04') + { + tty_printf ("Q\n"); + answer[0] = 'q'; + } + + if (answer[0]=='q' || answer[0]=='Q') + err = gpg_error (GPG_ERR_CANCELED); + else if (atoi (answer) >= 1 && atoi (answer) <= numdesc) + { + char *split = answer; + char *num; + int numarray[50]; + int numidx = 0; + int idx; + + while ((num = strsep (&split, " ,"))) + if (atoi (num) >= 1 && atoi (num) <= numdesc) + { + if (numidx >= DIM (numarray)) + { + tty_printf ("Too many keys selected\n"); + goto again; + } + numarray[numidx++] = atoi (num); + } + + if (!numidx) + goto again; + + { + KEYDB_SEARCH_DESC *selarray; + + selarray = xtrymalloc (numidx * sizeof *selarray); + if (!selarray) + { + err = gpg_error_from_syserror (); + goto leave; + } + for (idx = 0; idx < numidx; idx++) + selarray[idx] = desc[numarray[idx]-1]; + err = keyserver_get (ctrl, selarray, numidx, NULL, 0, NULL, NULL); + xfree (selarray); + } + } + + leave: + xfree (answer); + return err; +} + + +/* This is a callback used by call-dirmngr.c to process the result of + KS_SEARCH command. If SPECIAL is 0, LINE is the actual data line + received with all escaping removed and guaranteed to be exactly one + line with stripped LF; an EOF is indicated by LINE passed as NULL. + If special is 1, the line contains the source of the information + (usually an URL). LINE may be modified after return. */ +static gpg_error_t +search_line_handler (void *opaque, int special, char *line) +{ + struct search_line_handler_parm_s *parm = opaque; + gpg_error_t err = 0; + struct keyrec *keyrec; + + if (special == 1) + { + log_info ("data source: %s\n", line); + return 0; + } + else if (special) + { + log_debug ("unknown value %d for special search callback", special); + return 0; + } + + if (parm->eof_seen && line) + { + log_debug ("ooops: unexpected data after EOF\n"); + line = NULL; + } + + /* Print the received line. */ + if (opt.with_colons && line) + { + es_printf ("%s\n", line); + } + + /* Look for an info: line. The only current info: values defined + are the version and key count. */ + if (line && !parm->any_lines && !ascii_strncasecmp ("info:", line, 5)) + { + char *str = line + 5; + char *tok; + + if ((tok = strsep (&str, ":"))) + { + int version; + + if (sscanf (tok, "%d", &version) !=1 ) + version = 1; + + if (version !=1 ) + { + log_error (_("invalid keyserver protocol " + "(us %d!=handler %d)\n"), 1, version); + return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + } + } + + if ((tok = strsep (&str, ":")) + && sscanf (tok, "%d", &parm->count) == 1) + { + if (!parm->count) + parm->not_found = 1;/* Server indicated that no items follow. */ + else if (parm->count < 0) + parm->count = 10; /* Bad value - assume something reasonable. */ + else + parm->validcount = 1; /* COUNT seems to be okay. */ + } + + parm->any_lines = 1; + return 0; /* Line processing finished. */ + } + + again: + if (line) + keyrec = parse_keyrec (line); + else + { + /* Received EOF - flush data */ + parm->eof_seen = 1; + keyrec = parse_keyrec (NULL); + if (!keyrec) + { + if (!parm->nkeys) + parm->not_found = 1; /* No keys at all. */ + else + { + if (parm->nkeys != parm->count) + parm->validcount = 0; + + if (!(opt.with_colons && opt.batch)) + { + err = show_prompt (parm->ctrl, parm->desc, parm->nkeys, + parm->validcount? parm->count : 0, + parm->searchstr_disp); + return err; + } + } + } + } + + /* Save the key in the key array. */ + if (keyrec) + { + /* Allocate or enlarge the key array if needed. */ + if (!parm->desc) + { + if (parm->count < 1) + { + parm->count = 10; + parm->validcount = 0; + } + parm->desc = xtrymalloc (parm->count * sizeof *parm->desc); + if (!parm->desc) + { + err = gpg_error_from_syserror (); + iobuf_close (keyrec->uidbuf); + xfree (keyrec); + return err; + } + } + else if (parm->nkeys == parm->count) + { + /* Keyserver sent more keys than claimed in the info: line. */ + KEYDB_SEARCH_DESC *tmp; + int newcount = parm->count + 10; + + tmp = xtryrealloc (parm->desc, newcount * sizeof *parm->desc); + if (!tmp) + { + err = gpg_error_from_syserror (); + iobuf_close (keyrec->uidbuf); + xfree (keyrec); + return err; + } + parm->count = newcount; + parm->desc = tmp; + parm->validcount = 0; + } + + parm->desc[parm->nkeys] = keyrec->desc; + + if (!opt.with_colons) + { + /* SCREEN_LINES - 1 for the prompt. */ + if (parm->numlines + keyrec->lines > opt.screen_lines - 1) + { + err = show_prompt (parm->ctrl, parm->desc, parm->nkeys, + parm->validcount ? parm->count:0, + parm->searchstr_disp); + if (err) + return err; + parm->numlines = 0; + } + + print_keyrec (parm->ctrl, parm->nkeys+1, keyrec); + } + + parm->numlines += keyrec->lines; + iobuf_close (keyrec->uidbuf); + xfree (keyrec); + + parm->any_lines = 1; + parm->nkeys++; + + /* If we are here due to a flush after the EOF, run again for + the last prompt. Fixme: Make this code better readable. */ + if (parm->eof_seen) + goto again; + } + + return 0; +} + + + +int +keyserver_export (ctrl_t ctrl, strlist_t users) +{ + gpg_error_t err; + strlist_t sl=NULL; + KEYDB_SEARCH_DESC desc; + int rc=0; + + /* Weed out descriptors that we don't support sending */ + for(;users;users=users->next) + { + err = classify_user_id (users->d, &desc, 1); + if (err || (desc.mode != KEYDB_SEARCH_MODE_SHORT_KID + && desc.mode != KEYDB_SEARCH_MODE_LONG_KID + && desc.mode != KEYDB_SEARCH_MODE_FPR16 + && desc.mode != KEYDB_SEARCH_MODE_FPR20)) + { + log_error(_("\"%s\" not a key ID: skipping\n"),users->d); + continue; + } + else + append_to_strlist(&sl,users->d); + } + + if(sl) + { + rc = keyserver_put (ctrl, sl); + free_strlist(sl); + } + + return rc; +} + + +/* Structure to convey the arg to keyserver_retrieval_screener. */ +struct ks_retrieval_screener_arg_s +{ + KEYDB_SEARCH_DESC *desc; + int ndesc; +}; + + +/* Check whether a key matches the search description. The function + returns 0 if the key shall be imported. */ +static gpg_error_t +keyserver_retrieval_screener (kbnode_t keyblock, void *opaque) +{ + struct ks_retrieval_screener_arg_s *arg = opaque; + KEYDB_SEARCH_DESC *desc = arg->desc; + int ndesc = arg->ndesc; + kbnode_t node; + PKT_public_key *pk; + int n; + u32 keyid[2]; + byte fpr[MAX_FINGERPRINT_LEN]; + size_t fpr_len = 0; + + /* Secret keys are not expected from a keyserver. We do not + care about secret subkeys because the import code takes care + of skipping them. Not allowing an import of a public key + with a secret subkey would make it too easy to inhibit the + downloading of a public key. Recall that keyservers do only + limited checks. */ + node = find_kbnode (keyblock, PKT_SECRET_KEY); + if (node) + return gpg_error (GPG_ERR_GENERAL); /* Do not import. */ + + if (!ndesc) + return 0; /* Okay if no description given. */ + + /* Loop over all key packets. */ + for (node = keyblock; node; node = node->next) + { + if (node->pkt->pkttype != PKT_PUBLIC_KEY + && node->pkt->pkttype != PKT_PUBLIC_SUBKEY) + continue; + + pk = node->pkt->pkt.public_key; + fingerprint_from_pk (pk, fpr, &fpr_len); + keyid_from_pk (pk, keyid); + + /* Compare requested and returned fingerprints if available. */ + for (n = 0; n < ndesc; n++) + { + if (desc[n].mode == KEYDB_SEARCH_MODE_FPR20) + { + if (fpr_len == 20 && !memcmp (fpr, desc[n].u.fpr, 20)) + return 0; + } + else if (desc[n].mode == KEYDB_SEARCH_MODE_FPR16) + { + if (fpr_len == 16 && !memcmp (fpr, desc[n].u.fpr, 16)) + return 0; + } + else if (desc[n].mode == KEYDB_SEARCH_MODE_LONG_KID) + { + if (keyid[0] == desc[n].u.kid[0] && keyid[1] == desc[n].u.kid[1]) + return 0; + } + else if (desc[n].mode == KEYDB_SEARCH_MODE_SHORT_KID) + { + if (keyid[1] == desc[n].u.kid[1]) + return 0; + } + else /* No keyid or fingerprint - can't check. */ + return 0; /* allow import. */ + } + } + + return gpg_error (GPG_ERR_GENERAL); +} + + +int +keyserver_import (ctrl_t ctrl, strlist_t users) +{ + gpg_error_t err; + KEYDB_SEARCH_DESC *desc; + int num=100,count=0; + int rc=0; + + /* Build a list of key ids */ + desc=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num); + + for(;users;users=users->next) + { + err = classify_user_id (users->d, &desc[count], 1); + if (err || (desc[count].mode != KEYDB_SEARCH_MODE_SHORT_KID + && desc[count].mode != KEYDB_SEARCH_MODE_LONG_KID + && desc[count].mode != KEYDB_SEARCH_MODE_FPR16 + && desc[count].mode != KEYDB_SEARCH_MODE_FPR20)) + { + log_error (_("\"%s\" not a key ID: skipping\n"), users->d); + continue; + } + + count++; + if(count==num) + { + num+=100; + desc=xrealloc(desc,sizeof(KEYDB_SEARCH_DESC)*num); + } + } + + if(count>0) + rc = keyserver_get (ctrl, desc, count, NULL, 0, NULL, NULL); + + xfree(desc); + + return rc; +} + + +/* Return true if any keyserver has been configured. */ +int +keyserver_any_configured (ctrl_t ctrl) +{ + return !gpg_dirmngr_ks_list (ctrl, NULL); +} + + +/* Import all keys that exactly match MBOX */ +int +keyserver_import_mbox (ctrl_t ctrl, const char *mbox, + unsigned char **fpr, size_t *fprlen, + struct keyserver_spec *keyserver) +{ + KEYDB_SEARCH_DESC desc = { 0 }; + + desc.mode = KEYDB_SEARCH_MODE_MAIL; + desc.u.name = mbox; + + return keyserver_get (ctrl, &desc, 1, keyserver, 0, fpr, fprlen); +} + + +/* Import the keys that match exactly MBOX */ +int +keyserver_import_ntds (ctrl_t ctrl, const char *mbox, + unsigned char **fpr, size_t *fprlen) +{ + KEYDB_SEARCH_DESC desc = { 0 }; + struct keyserver_spec keyserver = { NULL, "ldap:///" }; + + desc.mode = KEYDB_SEARCH_MODE_MAIL; + desc.u.name = mbox; + + return keyserver_get (ctrl, &desc, 1, &keyserver, 0, fpr, fprlen); +} + + +int +keyserver_import_fprint (ctrl_t ctrl, const byte *fprint, size_t fprint_len, + struct keyserver_spec *keyserver, + unsigned int flags) +{ + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof(desc)); + + if(fprint_len==16) + desc.mode=KEYDB_SEARCH_MODE_FPR16; + else if(fprint_len==20) + desc.mode=KEYDB_SEARCH_MODE_FPR20; + else + return gpg_error (GPG_ERR_INV_ARG); + + memcpy (desc.u.fpr, fprint, fprint_len); + + return keyserver_get (ctrl, &desc, 1, keyserver, flags, NULL, NULL); +} + + +int +keyserver_import_fprint_ntds (ctrl_t ctrl, + const byte *fprint, size_t fprint_len) +{ + struct keyserver_spec keyserver = { NULL, "ldap:///" }; + + return keyserver_import_fprint (ctrl, fprint, fprint_len, + &keyserver, KEYSERVER_IMPORT_FLAG_LDAP); +} + + +int +keyserver_import_keyid (ctrl_t ctrl, + u32 *keyid,struct keyserver_spec *keyserver, + unsigned int flags) +{ + KEYDB_SEARCH_DESC desc; + + memset(&desc,0,sizeof(desc)); + + desc.mode=KEYDB_SEARCH_MODE_LONG_KID; + desc.u.kid[0]=keyid[0]; + desc.u.kid[1]=keyid[1]; + + return keyserver_get (ctrl, &desc, 1, keyserver, flags, NULL, NULL); +} + + +/* code mostly stolen from do_export_stream */ +static int +keyidlist (ctrl_t ctrl, strlist_t users, KEYDB_SEARCH_DESC **klist, + int *count) +{ + int rc = 0; + int num = 100; + kbnode_t keyblock = NULL; + kbnode_t node; + KEYDB_HANDLE kdbhd; + int ndesc; + KEYDB_SEARCH_DESC *desc = NULL; + strlist_t sl; + + *count=0; + + *klist=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num); + + kdbhd = keydb_new (); + if (!kdbhd) + { + rc = gpg_error_from_syserror (); + goto leave; + } + keydb_disable_caching (kdbhd); /* We are looping the search. */ + + if(!users) + { + ndesc = 1; + desc = xmalloc_clear ( ndesc * sizeof *desc); + desc[0].mode = KEYDB_SEARCH_MODE_FIRST; + } + else + { + for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++) + ; + desc = xmalloc ( ndesc * sizeof *desc); + + for (ndesc=0, sl=users; sl; sl = sl->next) + { + gpg_error_t err; + if (!(err = classify_user_id (sl->d, desc+ndesc, 1))) + ndesc++; + else + log_error (_("key \"%s\" not found: %s\n"), + sl->d, gpg_strerror (err)); + } + } + + for (;;) + { + rc = keydb_search (kdbhd, desc, ndesc, NULL); + if (rc) + break; /* ready. */ + + if (!users) + desc[0].mode = KEYDB_SEARCH_MODE_NEXT; + + /* read the keyblock */ + rc = keydb_get_keyblock (kdbhd, &keyblock ); + if( rc ) + { + log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); + goto leave; + } + + if((node=find_kbnode(keyblock,PKT_PUBLIC_KEY))) + { + /* v4 keys get full fingerprints. v3 keys get long keyids. + This is because it's easy to calculate any sort of keyid + from a v4 fingerprint, but not a v3 fingerprint. */ + + if(node->pkt->pkt.public_key->version<4) + { + (*klist)[*count].mode=KEYDB_SEARCH_MODE_LONG_KID; + keyid_from_pk(node->pkt->pkt.public_key, + (*klist)[*count].u.kid); + } + else + { + size_t dummy; + + (*klist)[*count].mode=KEYDB_SEARCH_MODE_FPR20; + fingerprint_from_pk(node->pkt->pkt.public_key, + (*klist)[*count].u.fpr,&dummy); + } + + /* This is a little hackish, using the skipfncvalue as a + void* pointer to the keyserver spec, but we don't need + the skipfnc here, and it saves having an additional field + for this (which would be wasted space most of the + time). */ + + (*klist)[*count].skipfncvalue=NULL; + + /* Are we honoring preferred keyservers? */ + if(opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL) + { + PKT_user_id *uid=NULL; + PKT_signature *sig=NULL; + + merge_keys_and_selfsig (ctrl, keyblock); + + for(node=node->next;node;node=node->next) + { + if(node->pkt->pkttype==PKT_USER_ID + && node->pkt->pkt.user_id->flags.primary) + uid=node->pkt->pkt.user_id; + else if(node->pkt->pkttype==PKT_SIGNATURE + && node->pkt->pkt.signature-> + flags.chosen_selfsig && uid) + { + sig=node->pkt->pkt.signature; + break; + } + } + + /* Try and parse the keyserver URL. If it doesn't work, + then we end up writing NULL which indicates we are + the same as any other key. */ + if(sig) + (*klist)[*count].skipfncvalue=parse_preferred_keyserver(sig); + } + + (*count)++; + + if(*count==num) + { + num+=100; + *klist=xrealloc(*klist,sizeof(KEYDB_SEARCH_DESC)*num); + } + } + } + + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) + rc = 0; + + leave: + if(rc) + { + xfree(*klist); + *klist = NULL; + } + xfree(desc); + keydb_release(kdbhd); + release_kbnode(keyblock); + + return rc; +} + +/* Note this is different than the original HKP refresh. It allows + usernames to refresh only part of the keyring. */ + +gpg_error_t +keyserver_refresh (ctrl_t ctrl, strlist_t users) +{ + gpg_error_t err; + int count, numdesc; + KEYDB_SEARCH_DESC *desc; + unsigned int options=opt.keyserver_options.import_options; + + /* We switch merge-only on during a refresh, as 'refresh' should + never import new keys, even if their keyids match. */ + opt.keyserver_options.import_options|=IMPORT_MERGE_ONLY; + + /* Similarly, we switch on fast-import, since refresh may make + multiple import sets (due to preferred keyserver URLs). We don't + want each set to rebuild the trustdb. Instead we do it once at + the end here. */ + opt.keyserver_options.import_options|=IMPORT_FAST; + + + err = keyidlist (ctrl, users, &desc, &numdesc); + if (err) + return err; + + count=numdesc; + if(count>0) + { + int i; + + /* Try to handle preferred keyserver keys first */ + for(i=0;i<numdesc;i++) + { + if(desc[i].skipfncvalue) + { + struct keyserver_spec *keyserver=desc[i].skipfncvalue; + + if (!opt.quiet) + log_info (_("refreshing %d key from %s\n"), 1, keyserver->uri); + + /* We use the keyserver structure we parsed out before. + Note that a preferred keyserver without a scheme:// + will be interpreted as hkp:// */ + err = keyserver_get (ctrl, &desc[i], 1, keyserver, 0, NULL, NULL); + if (err) + log_info(_("WARNING: unable to refresh key %s" + " via %s: %s\n"),keystr_from_desc(&desc[i]), + keyserver->uri,gpg_strerror (err)); + else + { + /* We got it, so mark it as NONE so we don't try and + get it again from the regular keyserver. */ + + desc[i].mode=KEYDB_SEARCH_MODE_NONE; + count--; + } + + free_keyserver_spec(keyserver); + } + } + } + + if(count>0) + { + char *tmpuri; + + err = gpg_dirmngr_ks_list (ctrl, &tmpuri); + if (!err) + { + if (!opt.quiet) + { + log_info (ngettext("refreshing %d key from %s\n", + "refreshing %d keys from %s\n", + count), count, tmpuri); + } + xfree (tmpuri); + + err = keyserver_get (ctrl, desc, numdesc, NULL, 0, NULL, NULL); + } + } + + xfree(desc); + + opt.keyserver_options.import_options=options; + + /* If the original options didn't have fast import, and the trustdb + is dirty, rebuild. */ + if(!(opt.keyserver_options.import_options&IMPORT_FAST)) + check_or_update_trustdb (ctrl); + + return err; +} + + +/* Search for keys on the keyservers. The patterns are given in the + string list TOKENS. */ +gpg_error_t +keyserver_search (ctrl_t ctrl, strlist_t tokens) +{ + gpg_error_t err; + char *searchstr; + struct search_line_handler_parm_s parm; + + memset (&parm, 0, sizeof parm); + + if (!tokens) + return 0; /* Return success if no patterns are given. */ + + { + membuf_t mb; + strlist_t item; + + init_membuf (&mb, 1024); + for (item = tokens; item; item = item->next) + { + if (item != tokens) + put_membuf (&mb, " ", 1); + put_membuf_str (&mb, item->d); + } + put_membuf (&mb, "", 1); /* Append Nul. */ + searchstr = get_membuf (&mb, NULL); + if (!searchstr) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + parm.ctrl = ctrl; + if (searchstr) + parm.searchstr_disp = utf8_to_native (searchstr, strlen (searchstr), 0); + + err = gpg_dirmngr_ks_search (ctrl, searchstr, search_line_handler, &parm); + + if (parm.not_found || gpg_err_code (err) == GPG_ERR_NO_DATA) + { + if (parm.searchstr_disp) + log_info (_("key \"%s\" not found on keyserver\n"), + parm.searchstr_disp); + else + log_info (_("key not found on keyserver\n")); + } + + if (gpg_err_code (err) == GPG_ERR_NO_DATA) + err = gpg_error (GPG_ERR_NOT_FOUND); + else if (err) + log_error ("error searching keyserver: %s\n", gpg_strerror (err)); + + leave: + xfree (parm.desc); + xfree (parm.searchstr_disp); + xfree(searchstr); + + return err; +} + +/* Helper for keyserver_get. Here we only receive a chunk of the + description to be processed in one batch. This is required due to + the limited number of patterns the dirmngr interface (KS_GET) can + grok and to limit the amount of temporary required memory. */ +static gpg_error_t +keyserver_get_chunk (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc, + int *r_ndesc_used, + import_stats_t stats_handle, + struct keyserver_spec *override_keyserver, + unsigned int flags, + unsigned char **r_fpr, size_t *r_fprlen) + +{ + gpg_error_t err = 0; + char **pattern; + int idx, npat, npat_fpr; + estream_t datastream; + char *source = NULL; + size_t linelen; /* Estimated linelen for KS_GET. */ + size_t n; + int only_fprs; + +#define MAX_KS_GET_LINELEN 950 /* Somewhat lower than the real limit. */ + + *r_ndesc_used = 0; + + /* Create an array filled with a search pattern for each key. The + array is delimited by a NULL entry. */ + pattern = xtrycalloc (ndesc+1, sizeof *pattern); + if (!pattern) + return gpg_error_from_syserror (); + + /* Note that we break the loop as soon as our estimation of the to + be used line length reaches the limit. But we do this only if we + have processed at least one search requests so that an overlong + single request will be rejected only later by gpg_dirmngr_ks_get + but we are sure that R_NDESC_USED has been updated. This avoids + a possible indefinite loop. */ + linelen = 24; /* "KS_GET --quick --ldap --" */ + for (npat=npat_fpr=0, idx=0; idx < ndesc; idx++) + { + int quiet = 0; + + if (desc[idx].mode == KEYDB_SEARCH_MODE_FPR20 + || desc[idx].mode == KEYDB_SEARCH_MODE_FPR16) + { + n = 1+2+2*20; + if (idx && linelen + n > MAX_KS_GET_LINELEN) + break; /* Declare end of this chunk. */ + linelen += n; + + pattern[npat] = xtrymalloc (n); + if (!pattern[npat]) + err = gpg_error_from_syserror (); + else + { + strcpy (pattern[npat], "0x"); + bin2hex (desc[idx].u.fpr, + desc[idx].mode == KEYDB_SEARCH_MODE_FPR20? 20 : 16, + pattern[npat]+2); + npat++; + if (desc[idx].mode == KEYDB_SEARCH_MODE_FPR20) + npat_fpr++; + } + } + else if(desc[idx].mode == KEYDB_SEARCH_MODE_LONG_KID) + { + n = 1+2+16; + if (idx && linelen + n > MAX_KS_GET_LINELEN) + break; /* Declare end of this chunk. */ + linelen += n; + + pattern[npat] = xtryasprintf ("0x%08lX%08lX", + (ulong)desc[idx].u.kid[0], + (ulong)desc[idx].u.kid[1]); + if (!pattern[npat]) + err = gpg_error_from_syserror (); + else + npat++; + } + else if(desc[idx].mode == KEYDB_SEARCH_MODE_SHORT_KID) + { + n = 1+2+8; + if (idx && linelen + n > MAX_KS_GET_LINELEN) + break; /* Declare end of this chunk. */ + linelen += n; + + pattern[npat] = xtryasprintf ("0x%08lX", (ulong)desc[idx].u.kid[1]); + if (!pattern[npat]) + err = gpg_error_from_syserror (); + else + npat++; + } + else if(desc[idx].mode == KEYDB_SEARCH_MODE_EXACT) + { + /* The Dirmngr also uses classify_user_id to detect the type + of the search string. By adding the '=' prefix we force + Dirmngr's KS_GET to consider this an exact search string. + (In gpg 1.4 and gpg 2.0 the keyserver helpers used the + KS_GETNAME command to indicate this.) */ + + n = 1+1+strlen (desc[idx].u.name); + if (idx && linelen + n > MAX_KS_GET_LINELEN) + break; /* Declare end of this chunk. */ + linelen += n; + + pattern[npat] = strconcat ("=", desc[idx].u.name, NULL); + if (!pattern[npat]) + err = gpg_error_from_syserror (); + else + { + npat++; + quiet = 1; + } + } + else if(desc[idx].mode == KEYDB_SEARCH_MODE_MAIL) + { + n = 1 + strlen (desc[idx].u.name) + 1 + 1; + if (idx && linelen + n > MAX_KS_GET_LINELEN) + break; /* Declare end of this chunk. */ + linelen += n; + + if (desc[idx].u.name[0] == '<') + pattern[npat] = xtrystrdup (desc[idx].u.name); + else + pattern[npat] = strconcat ("<", desc[idx].u.name, ">", NULL); + if (!pattern[npat]) + err = gpg_error_from_syserror (); + else + { + npat++; + quiet = 1; + } + } + else if (desc[idx].mode == KEYDB_SEARCH_MODE_NONE) + continue; + else + BUG(); + + if (err) + { + for (idx=0; idx < npat; idx++) + xfree (pattern[idx]); + xfree (pattern); + return err; + } + + if (!quiet && override_keyserver) + { + log_info (_("requesting key %s from %s\n"), + keystr_from_desc (&desc[idx]), override_keyserver->uri); + } + } + + /* Remember now many of search items were considered. Note that + this is different from NPAT. */ + *r_ndesc_used = idx; + + only_fprs = (npat && npat == npat_fpr); + + err = gpg_dirmngr_ks_get (ctrl, pattern, override_keyserver, flags, + &datastream, &source); + for (idx=0; idx < npat; idx++) + xfree (pattern[idx]); + xfree (pattern); + if (opt.verbose && source) + log_info ("data source: %s\n", source); + + + + if (!err) + { + struct ks_retrieval_screener_arg_s screenerarg; + unsigned int options; + + /* FIXME: Check whether this comment should be moved to dirmngr. + + Slurp up all the key data. In the future, it might be nice + to look for KEY foo OUTOFBAND and FAILED indicators. It's + harmless to ignore them, but ignoring them does make gpg + complain about "no valid OpenPGP data found". One way to do + this could be to continue parsing this line-by-line and make + a temp iobuf for each key. Note that we don't allow the + import of secret keys from a keyserver. Keyservers should + never accept or send them but we better protect against rogue + keyservers. */ + + /* For LDAP servers we reset IMPORT_SELF_SIGS_ONLY unless it has + * been set explicitly. */ + options = (opt.keyserver_options.import_options | IMPORT_NO_SECKEY); + if (source && (!strncmp (source, "ldap:", 5) + || !strncmp (source, "ldaps:", 6)) + && !opt.flags.expl_import_self_sigs_only) + options &= ~IMPORT_SELF_SIGS_ONLY; + + screenerarg.desc = desc; + screenerarg.ndesc = *r_ndesc_used; + import_keys_es_stream (ctrl, datastream, stats_handle, + r_fpr, r_fprlen, options, + keyserver_retrieval_screener, &screenerarg, + only_fprs? KEYORG_KS : 0, + source); + } + es_fclose (datastream); + xfree (source); + + return err; +} + + +/* Retrieve a key from a keyserver. The search pattern are in + (DESC,NDESC). Allowed search modes are keyid, fingerprint, and + exact searches. OVERRIDE_KEYSERVER gives an optional override + keyserver. If (R_FPR,R_FPRLEN) are not NULL, they may return the + fingerprint of a single imported key. If the FLAG bit + KEYSERVER_IMPORT_FLAG_QUICK is set, dirmngr is advised to use a + shorter timeout. */ +static gpg_error_t +keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc, + struct keyserver_spec *override_keyserver, unsigned int flags, + unsigned char **r_fpr, size_t *r_fprlen) +{ + gpg_error_t err; + import_stats_t stats_handle; + int ndesc_used; + int any_good = 0; + + stats_handle = import_new_stats_handle(); + + for (;;) + { + err = keyserver_get_chunk (ctrl, desc, ndesc, &ndesc_used, stats_handle, + override_keyserver, flags, r_fpr, r_fprlen); + if (!err) + any_good = 1; + if (err || ndesc_used >= ndesc) + break; /* Error or all processed. */ + /* Prepare for the next chunk. */ + desc += ndesc_used; + ndesc -= ndesc_used; + } + + if (any_good) + import_print_stats (stats_handle); + + import_release_stats_handle (stats_handle); + return err; +} + + +/* Send all keys specified by KEYSPECS to the configured keyserver. */ +static gpg_error_t +keyserver_put (ctrl_t ctrl, strlist_t keyspecs) + +{ + gpg_error_t err; + strlist_t kspec; + char *ksurl; + + if (!keyspecs) + return 0; /* Return success if the list is empty. */ + + if (gpg_dirmngr_ks_list (ctrl, &ksurl)) + { + log_error (_("no keyserver known\n")); + return gpg_error (GPG_ERR_NO_KEYSERVER); + } + + for (kspec = keyspecs; kspec; kspec = kspec->next) + { + void *data; + size_t datalen; + kbnode_t keyblock; + + err = export_pubkey_buffer (ctrl, kspec->d, + opt.keyserver_options.export_options, + NULL, 0, NULL, + &keyblock, &data, &datalen); + if (err) + log_error (_("skipped \"%s\": %s\n"), kspec->d, gpg_strerror (err)); + else + { + if (!opt.quiet) + log_info (_("sending key %s to %s\n"), + keystr (keyblock->pkt->pkt.public_key->keyid), + ksurl?ksurl:"[?]"); + + err = gpg_dirmngr_ks_put (ctrl, data, datalen, keyblock); + release_kbnode (keyblock); + xfree (data); + if (err) + { + write_status_error ("keyserver_send", err); + log_error (_("keyserver send failed: %s\n"), gpg_strerror (err)); + } + } + } + + xfree (ksurl); + + return err; + +} + + +/* Loop over all URLs in STRLIST and fetch the key at that URL. Note + that the fetch operation ignores the configured keyservers and + instead directly retrieves the keys. */ +int +keyserver_fetch (ctrl_t ctrl, strlist_t urilist, int origin) +{ + gpg_error_t err; + strlist_t sl; + estream_t datastream; + unsigned int save_options = opt.keyserver_options.import_options; + int any_success = 0; + gpg_error_t firsterr = 0; + + /* Switch on fast-import, since fetch can handle more than one + import and we don't want each set to rebuild the trustdb. + Instead we do it once at the end. */ + opt.keyserver_options.import_options |= IMPORT_FAST; + + for (sl=urilist; sl; sl=sl->next) + { + if (!opt.quiet) + log_info (_("requesting key from '%s'\n"), sl->d); + + err = gpg_dirmngr_ks_fetch (ctrl, sl->d, &datastream); + if (!err) + { + import_stats_t stats_handle; + + stats_handle = import_new_stats_handle(); + import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL, + opt.keyserver_options.import_options, + NULL, NULL, origin, sl->d); + + import_print_stats (stats_handle); + import_release_stats_handle (stats_handle); + any_success = 1; + } + else + { + log_info (_("WARNING: unable to fetch URI %s: %s\n"), + sl->d, gpg_strerror (err)); + if (!firsterr) + firsterr = err; + } + es_fclose (datastream); + } + + if (!urilist) + err = gpg_error (GPG_ERR_NO_NAME); + else if (any_success) + err = 0; + else + err = firsterr; + + opt.keyserver_options.import_options = save_options; + + /* If the original options didn't have fast import, and the trustdb + is dirty, rebuild. */ + if (!(opt.keyserver_options.import_options&IMPORT_FAST)) + check_or_update_trustdb (ctrl); + + return err; +} + + +/* Import key in a CERT or pointed to by a CERT. In DANE_MODE fetch + the certificate using the DANE method. */ +int +keyserver_import_cert (ctrl_t ctrl, const char *name, int dane_mode, + unsigned char **fpr,size_t *fpr_len) +{ + gpg_error_t err; + char *look,*url; + estream_t key; + + look = xstrdup(name); + + if (!dane_mode) + { + char *domain = strrchr (look,'@'); + if (domain) + *domain='.'; + } + + err = gpg_dirmngr_dns_cert (ctrl, look, dane_mode? NULL : "*", + &key, fpr, fpr_len, &url); + if (err) + ; + else if (key) + { + int armor_status=opt.no_armor; + import_filter_t save_filt; + + /* CERTs and DANE records are always in binary format */ + opt.no_armor=1; + if (dane_mode) + { + save_filt = save_and_clear_import_filter (); + if (!save_filt) + err = gpg_error_from_syserror (); + else + { + char *filtstr = es_bsprintf ("keep-uid=mbox = %s", look); + err = filtstr? 0 : gpg_error_from_syserror (); + if (!err) + err = parse_and_set_import_filter (filtstr); + xfree (filtstr); + if (!err) + err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len, + IMPORT_NO_SECKEY, + NULL, NULL, KEYORG_DANE, NULL); + restore_import_filter (save_filt); + } + } + else + { + err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len, + (opt.keyserver_options.import_options + | IMPORT_NO_SECKEY), + NULL, NULL, 0, NULL); + } + + opt.no_armor=armor_status; + + es_fclose (key); + key = NULL; + } + else if (*fpr) + { + /* We only consider the IPGP type if a fingerprint was provided. + This lets us select the right key regardless of what a URL + points to, or get the key from a keyserver. */ + if(url) + { + struct keyserver_spec *spec; + + spec = parse_keyserver_uri (url, 1); + if(spec) + { + err = keyserver_import_fprint (ctrl, *fpr, *fpr_len, spec, 0); + free_keyserver_spec(spec); + } + } + else if (keyserver_any_configured (ctrl)) + { + /* If only a fingerprint is provided, try and fetch it from + the configured keyserver. */ + + err = keyserver_import_fprint (ctrl, + *fpr, *fpr_len, opt.keyserver, 0); + } + else + log_info(_("no keyserver known\n")); + + /* Give a better string here? "CERT fingerprint for \"%s\" + found, but no keyserver" " known (use option + --keyserver)\n" ? */ + + } + + xfree(url); + xfree(look); + + return err; +} + +/* Import key pointed to by a PKA record. Return the requested + fingerprint in fpr. */ +gpg_error_t +keyserver_import_pka (ctrl_t ctrl, const char *name, + unsigned char **fpr, size_t *fpr_len) +{ + gpg_error_t err; + char *url; + + err = gpg_dirmngr_get_pka (ctrl, name, fpr, fpr_len, &url); + if (url && *url && fpr && fpr_len) + { + /* An URL is available. Lookup the key. */ + struct keyserver_spec *spec; + spec = parse_keyserver_uri (url, 1); + if (spec) + { + err = keyserver_import_fprint (ctrl, *fpr, *fpr_len, spec, 0); + free_keyserver_spec (spec); + } + } + xfree (url); + + if (err) + { + xfree(*fpr); + *fpr = NULL; + *fpr_len = 0; + } + + return err; +} + + +/* Import a key using the Web Key Directory protocol. */ +gpg_error_t +keyserver_import_wkd (ctrl_t ctrl, const char *name, unsigned int flags, + unsigned char **fpr, size_t *fpr_len) +{ + gpg_error_t err; + char *mbox; + estream_t key; + char *url = NULL; + + /* We want to work on the mbox. That is what dirmngr will do anyway + * and we need the mbox for the import filter anyway. */ + mbox = mailbox_from_userid (name); + if (!mbox) + { + err = gpg_error_from_syserror (); + if (gpg_err_code (err) == GPG_ERR_EINVAL) + err = gpg_error (GPG_ERR_INV_USER_ID); + return err; + } + + err = gpg_dirmngr_wkd_get (ctrl, mbox, flags, &key, &url); + if (err) + ; + else if (key) + { + int armor_status = opt.no_armor; + import_filter_t save_filt; + + /* Keys returned via WKD are in binary format. However, we + * relax that requirement and allow also for armored data. */ + opt.no_armor = 0; + save_filt = save_and_clear_import_filter (); + if (!save_filt) + err = gpg_error_from_syserror (); + else + { + char *filtstr = es_bsprintf ("keep-uid=mbox = %s", mbox); + err = filtstr? 0 : gpg_error_from_syserror (); + if (!err) + err = parse_and_set_import_filter (filtstr); + xfree (filtstr); + if (!err) + err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len, + IMPORT_NO_SECKEY, + NULL, NULL, KEYORG_WKD, url); + + } + + restore_import_filter (save_filt); + opt.no_armor = armor_status; + + es_fclose (key); + key = NULL; + } + + xfree (url); + xfree (mbox); + return err; +} + + +/* Import a key by name using LDAP */ +int +keyserver_import_ldap (ctrl_t ctrl, + const char *name, unsigned char **fpr, size_t *fprlen) +{ + (void)ctrl; + (void)name; + (void)fpr; + (void)fprlen; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*FIXME*/ +#if 0 + char *domain; + struct keyserver_spec *keyserver; + strlist_t list=NULL; + int rc,hostlen=1; + struct srventry *srvlist=NULL; + int srvcount,i; + char srvname[MAXDNAME]; + + /* Parse out the domain */ + domain=strrchr(name,'@'); + if(!domain) + return GPG_ERR_GENERAL; + + domain++; + + keyserver=xmalloc_clear(sizeof(struct keyserver_spec)); + keyserver->scheme=xstrdup("ldap"); + keyserver->host=xmalloc(1); + keyserver->host[0]='\0'; + + snprintf(srvname,MAXDNAME,"_pgpkey-ldap._tcp.%s",domain); + + FIXME("network related - move to dirmngr or drop the code"); + srvcount=getsrv(srvname,&srvlist); + + for(i=0;i<srvcount;i++) + { + hostlen+=strlen(srvlist[i].target)+1; + keyserver->host=xrealloc(keyserver->host,hostlen); + + strcat(keyserver->host,srvlist[i].target); + + if(srvlist[i].port!=389) + { + char port[7]; + + hostlen+=6; /* a colon, plus 5 digits (unsigned 16-bit value) */ + keyserver->host=xrealloc(keyserver->host,hostlen); + + snprintf(port,7,":%u",srvlist[i].port); + strcat(keyserver->host,port); + } + + strcat(keyserver->host," "); + } + + free(srvlist); + + /* If all else fails, do the PGP Universal trick of + ldap://keys.(domain) */ + + hostlen+=5+strlen(domain); + keyserver->host=xrealloc(keyserver->host,hostlen); + strcat(keyserver->host,"keys."); + strcat(keyserver->host,domain); + + append_to_strlist(&list,name); + + rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*FIXME*/ + /* keyserver_work (ctrl, KS_GETNAME, list, NULL, */ + /* 0, fpr, fpr_len, keyserver); */ + + free_strlist(list); + + free_keyserver_spec(keyserver); + + return rc; +#endif +} |