aboutsummaryrefslogtreecommitdiffstats
path: root/g10/keyserver.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--g10/keyserver.c1302
1 files changed, 1028 insertions, 274 deletions
diff --git a/g10/keyserver.c b/g10/keyserver.c
index 445c07620..af1e5f773 100644
--- a/g10/keyserver.c
+++ b/g10/keyserver.c
@@ -1,5 +1,6 @@
/* keyserver.c - generic keyserver code
- * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -15,7 +16,8 @@
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
*/
#include <config.h>
@@ -24,6 +26,9 @@
#include <string.h>
#include <stdlib.h>
#include <assert.h>
+#ifdef HAVE_LIBCURL
+#include <curl/curl.h>
+#endif
#include "gpg.h"
#include "filter.h"
@@ -33,149 +38,302 @@
#include "main.h"
#include "i18n.h"
#include "iobuf.h"
-#include "memory.h"
#include "ttyio.h"
#include "options.h"
#include "packet.h"
+#include "trustdb.h"
#include "keyserver-internal.h"
#include "util.h"
-#define GET 0
-#define SEND 1
-#define SEARCH 2
+#define GPGKEYS_PREFIX "gpgkeys_"
+
+#if defined(HAVE_LIBCURL) || defined(FAKE_CURL)
+#define GPGKEYS_CURL "gpgkeys_curl"
+#endif
+
+#ifdef GPGKEYS_CURL
+#define GPGKEYS_PREFIX_LEN (strlen(GPGKEYS_PREFIX)+strlen(GPGKEYS_CURL))
+#else
+#define GPGKEYS_PREFIX_LEN (strlen(GPGKEYS_PREFIX))
+#endif
struct keyrec
{
KEYDB_SEARCH_DESC desc;
- time_t createtime,expiretime;
+ u32 createtime,expiretime;
int size,flags;
byte type;
- iobuf_t uidbuf;
- int lines;
+ IOBUF uidbuf;
+ unsigned int lines;
};
-struct kopts
-{
- char *name;
- int tell; /* tell remote process about this one */
- int *flag;
-} keyserver_opts[]=
+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},
+ {"include-revoked",0,NULL,N_("include revoked keys in search results")},
+ {"include-subkeys",0,NULL,N_("include subkeys when searching by key ID")},
+ {"use-temp-files",0,NULL,
+ N_("use temporary files to pass data to keyserver helpers")},
+ {"keep-temp-files",KEYSERVER_KEEP_TEMP_FILES,NULL,
+ N_("do not delete temporary files after using them")},
+ {"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 int keyserver_work(enum ks_action action,STRLIST list,
+ KEYDB_SEARCH_DESC *desc,int count,
+ unsigned char **fpr,size_t *fpr_len,
+ struct keyserver_spec *keyserver);
+
+/* Reasonable guess */
+#define DEFAULT_MAX_CERT_SIZE 16384
+
+static size_t max_cert_size=DEFAULT_MAX_CERT_SIZE;
+
+static void
+add_canonical_option(char *option,STRLIST *list)
{
- {"include-revoked",1,&opt.keyserver_options.include_revoked},
- {"include-disabled",1,&opt.keyserver_options.include_disabled},
- {"include-subkeys",1,&opt.keyserver_options.include_subkeys},
- {"keep-temp-files",0,&opt.keyserver_options.keep_temp_files},
- {"honor-http-proxy",1,&opt.keyserver_options.honor_http_proxy},
- {"broken-http-proxy",1,&opt.keyserver_options.broken_http_proxy},
- {"refresh-add-fake-v3-keyids",0,&opt.keyserver_options.fake_v3_keyids},
- {"auto-key-retrieve",0,&opt.keyserver_options.auto_key_retrieve},
- {"try-dns-srv",1,&opt.keyserver_options.try_dns_srv},
- {NULL}
-};
+ char *arg=argsplit(option);
-static int keyserver_work(int action,STRLIST list,
- KEYDB_SEARCH_DESC *desc,int count);
+ if(arg)
+ {
+ char *joined;
+
+ joined=xmalloc(strlen(option)+1+strlen(arg)+1);
+ /* Make a canonical name=value form with no spaces */
+ strcpy(joined,option);
+ strcat(joined,"=");
+ strcat(joined,arg);
+ append_to_strlist(list,joined);
+ xfree(joined);
+ }
+ else
+ append_to_strlist(list,option);
+}
-void
+int
parse_keyserver_options(char *options)
{
+ int ret=1;
char *tok;
+ char *max_cert=NULL;
- while((tok=strsep(&options," ,")))
- {
- int i,hit=0;
+ keyserver_opts[0].value=&max_cert;
+ while((tok=optsep(&options)))
+ {
if(tok[0]=='\0')
continue;
- for(i=0;keyserver_opts[i].name;i++)
- {
- if(ascii_strcasecmp(tok,keyserver_opts[i].name)==0)
- {
- *(keyserver_opts[i].flag)=1;
- hit=1;
- break;
- }
- else if(ascii_strncasecmp("no-",tok,3)==0 &&
- ascii_strcasecmp(&tok[3],keyserver_opts[i].name)==0)
- {
- *(keyserver_opts[i].flag)=0;
- hit=1;
- break;
- }
- }
+ /* For backwards compatibility. 1.2.x used honor-http-proxy and
+ there are a good number of documents published that recommend
+ it. */
+ if(ascii_strcasecmp(tok,"honor-http-proxy")==0)
+ tok="http-proxy";
+ else if(ascii_strcasecmp(tok,"no-honor-http-proxy")==0)
+ tok="no-http-proxy";
+
+ /* 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. Note
+ that you must use strncasecmp here as there might be an
+ =argument attached which will foil the use of strcasecmp. */
- /* These options need more than just a flag */
- if(!hit)
- {
- if(ascii_strcasecmp(tok,"verbose")==0)
- opt.keyserver_options.verbose++;
- else if(ascii_strcasecmp(tok,"no-verbose")==0)
- opt.keyserver_options.verbose--;
#ifdef EXEC_TEMPFILE_ONLY
- else if(ascii_strcasecmp(tok,"use-temp-files")==0 ||
- ascii_strcasecmp(tok,"no-use-temp-files")==0)
- log_info(_("WARNING: keyserver option \"%s\" is not used "
- "on this platform\n"),tok);
+ if(ascii_strncasecmp(tok,"use-temp-files",14)==0 ||
+ ascii_strncasecmp(tok,"no-use-temp-files",17)==0)
+ log_info(_("WARNING: keyserver option `%s' is not used"
+ " on this platform\n"),tok);
#else
- else if(ascii_strcasecmp(tok,"use-temp-files")==0)
- opt.keyserver_options.use_temp_files=1;
- else if(ascii_strcasecmp(tok,"no-use-temp-files")==0)
- opt.keyserver_options.use_temp_files=0;
+ if(ascii_strncasecmp(tok,"use-temp-files",14)==0)
+ opt.keyserver_options.options|=KEYSERVER_USE_TEMP_FILES;
+ else if(ascii_strncasecmp(tok,"no-use-temp-files",17)==0)
+ opt.keyserver_options.options&=~KEYSERVER_USE_TEMP_FILES;
#endif
- else
- if(!parse_import_options(tok,
- &opt.keyserver_options.import_options) &&
- !parse_export_options(tok,
- &opt.keyserver_options.export_options))
- add_to_strlist(&opt.keyserver_options.other,tok);
+ else 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 is
+ destined for a keyserver plugin. */
+ add_canonical_option(tok,&opt.keyserver_options.other);
}
}
+
+ 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;
}
-int
-parse_keyserver_uri(char *uri,const char *configname,unsigned int configlineno)
+void
+free_keyserver_spec(struct keyserver_spec *keyserver)
+{
+ xfree(keyserver->uri);
+ xfree(keyserver->scheme);
+ xfree(keyserver->auth);
+ xfree(keyserver->host);
+ xfree(keyserver->port);
+ xfree(keyserver->path);
+ xfree(keyserver->opaque);
+ free_strlist(keyserver->options);
+ xfree(keyserver);
+}
+
+/* Return 0 for match */
+static int
+cmp_keyserver_spec(struct keyserver_spec *one,struct keyserver_spec *two)
+{
+ if(ascii_strcasecmp(one->scheme,two->scheme)==0)
+ {
+ if(one->host && two->host && ascii_strcasecmp(one->host,two->host)==0)
+ {
+ if((one->port && two->port
+ && ascii_strcasecmp(one->port,two->port)==0)
+ || (!one->port && !two->port))
+ return 0;
+ }
+ else if(one->opaque && two->opaque
+ && ascii_strcasecmp(one->opaque,two->opaque)==0)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* 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;
+}
+
+/* TODO: once we cut over to an all-curl world, we don't need this
+ parser any longer so it can be removed, or at least moved to
+ keyserver/ksutil.c for limited use in gpgkeys_ldap or the like. */
+
+struct keyserver_spec *
+parse_keyserver_uri(const char *string,int require_scheme,
+ const char *configname,unsigned int configlineno)
{
int assume_hkp=0;
+ struct keyserver_spec *keyserver;
+ const char *idx;
+ int count;
+ char *uri,*options;
+
+ assert(string!=NULL);
+
+ keyserver=xmalloc_clear(sizeof(struct keyserver_spec));
+
+ uri=xstrdup(string);
+
+ options=strchr(uri,' ');
+ if(options)
+ {
+ char *tok;
- assert(uri!=NULL);
+ *options='\0';
+ options++;
- opt.keyserver_host=NULL;
- opt.keyserver_port=NULL;
- opt.keyserver_opaque=NULL;
+ while((tok=optsep(&options)))
+ add_canonical_option(tok,&keyserver->options);
+ }
/* Get the scheme */
- opt.keyserver_scheme=strsep(&uri,":");
- if(uri==NULL)
+ for(idx=uri,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 */
assume_hkp=1;
- uri=opt.keyserver_scheme;
- opt.keyserver_scheme="hkp";
+ keyserver->scheme=xstrdup("hkp");
+
+ keyserver->uri=xmalloc(strlen(keyserver->scheme)+3+strlen(uri)+1);
+ strcpy(keyserver->uri,keyserver->scheme);
+ strcat(keyserver->uri,"://");
+ strcat(keyserver->uri,uri);
}
else
{
+ int i;
+
+ keyserver->uri=xstrdup(uri);
+
+ keyserver->scheme=xmalloc(count+1);
+
/* Force to lowercase */
- char *i;
+ for(i=0;i<count;i++)
+ keyserver->scheme[i]=ascii_tolower(uri[i]);
+
+ keyserver->scheme[i]='\0';
- for(i=opt.keyserver_scheme;*i!='\0';i++)
- *i=ascii_tolower(*i);
+ /* Skip past the scheme and colon */
+ uri+=count+1;
}
- if(ascii_strcasecmp(opt.keyserver_scheme,"x-broken-hkp")==0)
+ if(ascii_strcasecmp(keyserver->scheme,"x-broken-hkp")==0)
{
deprecated_warning(configname,configlineno,"x-broken-hkp",
"--keyserver-options ","broken-http-proxy");
- opt.keyserver_scheme="hkp";
- opt.keyserver_options.broken_http_proxy=1;
+ xfree(keyserver->scheme);
+ keyserver->scheme=xstrdup("hkp");
+ append_to_strlist(&opt.keyserver_options.other,"broken-http-proxy");
}
- else if(ascii_strcasecmp(opt.keyserver_scheme,"x-hkp")==0
- || ascii_strcasecmp(opt.keyserver_scheme,"http")==0)
+ else if(ascii_strcasecmp(keyserver->scheme,"x-hkp")==0)
{
/* Canonicalize this to "hkp" so it works with both the internal
and external keyserver interface. */
- opt.keyserver_scheme="hkp";
+ xfree(keyserver->scheme);
+ keyserver->scheme=xstrdup("hkp");
}
if(assume_hkp || (uri[0]=='/' && uri[1]=='/'))
@@ -186,57 +344,124 @@ parse_keyserver_uri(char *uri,const char *configname,unsigned int configlineno)
if(!assume_hkp)
uri+=2;
- /* Get the host */
- opt.keyserver_host=strsep(&uri,":/");
- if(opt.keyserver_host[0]=='\0')
- return GPG_ERR_BAD_URI;
+ /* Do we have userinfo auth data present? */
+ for(idx=uri,count=0;*idx && *idx!='@' && *idx!='/';idx++)
+ count++;
- if(uri==NULL || uri[0]=='\0')
- opt.keyserver_port=NULL;
- else
+ /* We found a @ before the slash, so that means everything
+ before the @ is auth data. */
+ if(*idx=='@')
{
- char *ch;
+ if(count==0)
+ goto fail;
- /* Get the port */
- opt.keyserver_port=strsep(&uri,"/");
+ keyserver->auth=xmalloc(count+1);
+ strncpy(keyserver->auth,uri,count);
+ keyserver->auth[count]='\0';
+ uri+=count+1;
+ }
- /* Ports are digits only */
- ch=opt.keyserver_port;
- while(*ch!='\0')
- {
- if(!digitp(ch))
- return GPG_ERR_BAD_URI;
+ /* Is it an RFC-2732 ipv6 [literal address] ? */
+ if(*uri=='[')
+ {
+ for(idx=uri+1,count=1;*idx
+ && ((isascii (*idx) && isxdigit(*idx))
+ || *idx==':' || *idx=='.');idx++)
+ count++;
+
+ /* Is the ipv6 literal address terminated? */
+ if(*idx==']')
+ count++;
+ else
+ goto fail;
+ }
+ else
+ for(idx=uri,count=0;*idx && *idx!=':' && *idx!='/';idx++)
+ count++;
- ch++;
- }
+ if(count==0)
+ goto fail;
+
+ keyserver->host=xmalloc(count+1);
+ strncpy(keyserver->host,uri,count);
+ keyserver->host[count]='\0';
+ /* Skip past the host */
+ uri+=count;
+
+ if(*uri==':')
+ {
/* It would seem to be reasonable to limit the range of the
ports to values between 1-65535, but RFC 1738 and 1808
imply there is no limit. Of course, the real world has
limits. */
+
+ for(idx=uri+1,count=0;*idx && *idx!='/';idx++)
+ {
+ count++;
+
+ /* Ports are digits only */
+ if(!digitp(idx))
+ goto fail;
+ }
+
+ keyserver->port=xmalloc(count+1);
+ strncpy(keyserver->port,uri+1,count);
+ keyserver->port[count]='\0';
+
+ /* Skip past the colon and port number */
+ uri+=1+count;
}
- /* (any path part of the URI is discarded for now as no keyserver
- uses it yet) */
+ /* Everything else is the path */
+ if(*uri)
+ keyserver->path=xstrdup(uri);
+ else
+ keyserver->path=xstrdup("/");
+
+ if(keyserver->path[1]!='\0')
+ keyserver->flags.direct_uri=1;
}
else if(uri[0]!='/')
{
/* No slash means opaque. Just record the opaque blob and get
out. */
- opt.keyserver_opaque=uri;
- return 0;
+ keyserver->opaque=xstrdup(uri);
}
else
{
/* One slash means absolute path. We don't need to support that
yet. */
- return GPG_ERR_BAD_URI;
+ goto fail;
}
- if(opt.keyserver_scheme[0]=='\0')
- return GPG_ERR_BAD_URI;
+ return keyserver;
- return 0;
+ 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,NULL,0);
+ xfree(dupe);
+ }
+
+ return spec;
}
static void
@@ -253,7 +478,7 @@ print_keyrec(int number,struct keyrec *keyrec)
if(keyrec->type)
{
- const char *str = gcry_pk_algo_name (keyrec->type);
+ const char *str=pubkey_algo_to_string(keyrec->type);
if(str)
printf("%s ",str);
@@ -263,25 +488,32 @@ print_keyrec(int number,struct keyrec *keyrec)
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:
- printf("key %08lX",(ulong)keyrec->desc.u.kid[1]);
+ 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 */
case KEYDB_SEARCH_MODE_LONG_KID:
- printf("key %08lX%08lX",(ulong)keyrec->desc.u.kid[0],
- (ulong)keyrec->desc.u.kid[1]);
+ printf("key %s",keystr(keyrec->desc.u.kid));
break;
case KEYDB_SEARCH_MODE_FPR16:
printf("key ");
for(i=0;i<16;i++)
- printf("%02X",(unsigned char)keyrec->desc.u.fpr[i]);
+ printf("%02X",keyrec->desc.u.fpr[i]);
break;
case KEYDB_SEARCH_MODE_FPR20:
printf("key ");
for(i=0;i<20;i++)
- printf("%02X",(unsigned char)keyrec->desc.u.fpr[i]);
+ printf("%02X",keyrec->desc.u.fpr[i]);
break;
default:
@@ -290,17 +522,23 @@ print_keyrec(int number,struct keyrec *keyrec)
}
if(keyrec->createtime>0)
- printf(", created %s",strtimestamp(keyrec->createtime));
+ {
+ printf(", ");
+ printf(_("created: %s"),strtimestamp(keyrec->createtime));
+ }
if(keyrec->expiretime>0)
- printf(", expires %s",strtimestamp(keyrec->expiretime));
+ {
+ printf(", ");
+ printf(_("expires: %s"),strtimestamp(keyrec->expiretime));
+ }
if(keyrec->flags&1)
- printf(" (%s)",("revoked"));
+ printf(" (%s)",_("revoked"));
if(keyrec->flags&2)
- printf(" (%s)",("disabled"));
+ printf(" (%s)",_("disabled"));
if(keyrec->flags&4)
- printf(" (%s)",("expired"));
+ printf(" (%s)",_("expired"));
printf("\n");
}
@@ -322,7 +560,7 @@ parse_keyrec(char *keystring)
return NULL;
else if(work->desc.mode==KEYDB_SEARCH_MODE_NONE)
{
- xfree (work);
+ xfree(work);
return NULL;
}
else
@@ -335,7 +573,7 @@ parse_keyrec(char *keystring)
if(work==NULL)
{
- work=xcalloc (1,sizeof(struct keyrec));
+ work=xmalloc_clear(sizeof(struct keyrec));
work->uidbuf=iobuf_temp();
}
@@ -356,7 +594,7 @@ parse_keyrec(char *keystring)
if(work->desc.mode)
{
ret=work;
- work=xcalloc (1,sizeof(struct keyrec));
+ work=xmalloc_clear(sizeof(struct keyrec));
work->uidbuf=iobuf_temp();
}
@@ -391,12 +629,23 @@ parse_keyrec(char *keystring)
if((tok=strsep(&keystring,":"))==NULL)
return ret;
- work->createtime=atoi(tok);
+ if(atoi(tok)<=0)
+ work->createtime=0;
+ else
+ work->createtime=atoi(tok);
if((tok=strsep(&keystring,":"))==NULL)
return ret;
- work->expiretime=atoi(tok);
+ 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;
@@ -419,9 +668,6 @@ parse_keyrec(char *keystring)
work->flags|=4;
break;
}
-
- if(work->expiretime && work->expiretime<=make_timestamp())
- work->flags|=4;
}
else if(ascii_strcasecmp("uid",record)==0 && work->desc.mode)
{
@@ -460,12 +706,14 @@ parse_keyrec(char *keystring)
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);
+ xfree(decoded);
iobuf_writestr(work->uidbuf,"\n\t");
work->lines++;
}
-
+
/* Ignore any records other than "pri" and "uid" for easy future
growth. */
@@ -499,7 +747,7 @@ show_prompt(KEYDB_SEARCH_DESC *desc,int numdesc,int count,const char *search)
if(answer[0]=='q' || answer[0]=='Q')
{
- xfree (answer);
+ xfree(answer);
return 1;
}
else if(atoi(answer)>=1 && atoi(answer)<=numdesc)
@@ -508,9 +756,10 @@ show_prompt(KEYDB_SEARCH_DESC *desc,int numdesc,int count,const char *search)
while((num=strsep(&split," ,"))!=NULL)
if(atoi(num)>=1 && atoi(num)<=numdesc)
- keyserver_work(GET,NULL,&desc[atoi(num)-1],1);
+ keyserver_work(KS_GET,NULL,&desc[atoi(num)-1],1,
+ NULL,NULL,opt.keyserver);
- xfree (answer);
+ xfree(answer);
return 1;
}
@@ -519,18 +768,20 @@ show_prompt(KEYDB_SEARCH_DESC *desc,int numdesc,int count,const char *search)
/* Count and searchstr are just for cosmetics. If the count is too
small, it will grow safely. If negative it disables the "Key x-y
- of z" messages. */
+ of z" messages. searchstr should be UTF-8 (rather than native). */
static void
-keyserver_search_prompt(iobuf_t buffer,const char *searchstr)
+keyserver_search_prompt(IOBUF buffer,const char *searchstr)
{
int i=0,validcount=0,started=0,header=0,count=1;
- unsigned int maxlen,buflen;
+ unsigned int maxlen,buflen,numlines=0;
KEYDB_SEARCH_DESC *desc;
byte *line=NULL;
- /* TODO: Something other than 23? That's 24-1 (the prompt). */
- int maxlines=23,numlines=0;
+ char *localstr=NULL;
+
+ if(searchstr)
+ localstr=utf8_to_native(searchstr,strlen(searchstr),0);
- desc=xmalloc (count*sizeof(KEYDB_SEARCH_DESC));
+ desc=xmalloc(count*sizeof(KEYDB_SEARCH_DESC));
for(;;)
{
@@ -609,7 +860,7 @@ keyserver_search_prompt(iobuf_t buffer,const char *searchstr)
for(;;)
{
- if(show_prompt(desc,i,validcount?count:0,searchstr))
+ if(show_prompt(desc,i,validcount?count:0,localstr))
break;
validcount=0;
}
@@ -635,9 +886,10 @@ keyserver_search_prompt(iobuf_t buffer,const char *searchstr)
if(!opt.with_colons)
{
- if(numlines+keyrec->lines>maxlines)
+ /* screen_lines - 1 for the prompt. */
+ if(numlines+keyrec->lines>opt.screen_lines-1)
{
- if(show_prompt(desc,i,validcount?count:0,searchstr))
+ if(show_prompt(desc,i,validcount?count:0,localstr))
break;
else
numlines=0;
@@ -648,62 +900,144 @@ keyserver_search_prompt(iobuf_t buffer,const char *searchstr)
numlines+=keyrec->lines;
iobuf_close(keyrec->uidbuf);
- xfree (keyrec);
+ xfree(keyrec);
started=1;
i++;
}
}
- xfree (desc);
- xfree (line);
-
notfound:
+ /* Leave this commented out or now, and perhaps for a very long
+ time. All HKPish servers return HTML error messages for
+ no-key-found. */
+ /*
+ if(!started)
+ log_info(_("keyserver does not support searching\n"));
+ else
+ */
if(count==0)
{
- if(searchstr)
- log_info(_("key \"%s\" not found on keyserver\n"),searchstr);
+ if(localstr)
+ log_info(_("key \"%s\" not found on keyserver\n"),localstr);
else
log_info(_("key not found on keyserver\n"));
- return;
}
+
+ xfree(localstr);
+ xfree(desc);
+ xfree(line);
}
+/* We sometimes want to use a different gpgkeys_xxx for a given
+ protocol (for example, ldaps is handled by gpgkeys_ldap). Map
+ these here. */
+static const char *
+keyserver_typemap(const char *type)
+{
+ if(strcmp(type,"ldaps")==0)
+ return "ldap";
+ else
+ return type;
+}
+
+#ifdef GPGKEYS_CURL
+/* The PGP LDAP and the curl fetch-a-LDAP-object methodologies are
+ sufficiently different that we can't use curl to do LDAP. */
+static int
+curl_cant_handle(const char *scheme,unsigned int direct_uri)
+{
+ if(!direct_uri && (strcmp(scheme,"ldap")==0 || strcmp(scheme,"ldaps")==0))
+ return 1;
+
+ return 0;
+}
+#endif
+
#define KEYSERVER_ARGS_KEEP " -o \"%O\" \"%I\""
#define KEYSERVER_ARGS_NOKEEP " -o \"%o\" \"%i\""
static int
-keyserver_spawn(int action,STRLIST list,
- KEYDB_SEARCH_DESC *desc,int count,int *prog)
+keyserver_spawn(enum ks_action action,STRLIST list,KEYDB_SEARCH_DESC *desc,
+ int count,int *prog,unsigned char **fpr,size_t *fpr_len,
+ struct keyserver_spec *keyserver)
{
int ret=0,i,gotversion=0,outofband=0;
STRLIST temp;
unsigned int maxlen,buflen;
- char *command=NULL,*searchstr=NULL;
+ char *command,*end,*searchstr=NULL;
byte *line=NULL;
- struct kopts *kopts;
struct exec_info *spawn;
+ const char *scheme;
+ const char *libexecdir = get_libexecdir ();
+
+ assert(keyserver);
#ifdef EXEC_TEMPFILE_ONLY
- opt.keyserver_options.use_temp_files=1;
+ opt.keyserver_options.options|=KEYSERVER_USE_TEMP_FILES;
#endif
- /* Push the libexecdir into path. If DISABLE_KEYSERVER_PATH is set,
- use the 0 arg to replace the path. */
+ /* Build the filename for the helper to execute */
+ scheme=keyserver_typemap(keyserver->scheme);
+
#ifdef DISABLE_KEYSERVER_PATH
- set_exec_path(GNUPG_LIBEXECDIR,0);
+ /* Destroy any path we might have. This is a little tricky,
+ portability-wise. It's not correct to delete the PATH
+ environment variable, as that may fall back to a system built-in
+ PATH. Similarly, it is not correct to set PATH to the null
+ string (PATH="") since this actually deletes the PATH environment
+ variable under MinGW. The safest thing to do here is to force
+ PATH to be GNUPG_LIBEXECDIR. All this is not that meaningful on
+ Unix-like systems (since we're going to give a full path to
+ gpgkeys_foo), but on W32 it prevents loading any DLLs from
+ directories in %PATH%.
+
+ After some more thinking about this we came to the conclusion
+ that it is better to load the helpers from the directory where
+ the program of this process lives. Fortunately Windows provides
+ a way to retrieve this and our get_libexecdir function has been
+ modified to return just this. Setting the exec-path is not
+ anymore required.
+ set_exec_path(libexecdir);
+ */
#else
- set_exec_path(GNUPG_LIBEXECDIR,opt.exec_path_set);
+ if(opt.exec_path_set)
+ {
+ /* If exec-path was set, and DISABLE_KEYSERVER_PATH is
+ undefined, then don't specify a full path to gpgkeys_foo, so
+ that the PATH can work. */
+ command=xmalloc(GPGKEYS_PREFIX_LEN+strlen(scheme)+3+strlen(EXEEXT)+1);
+ command[0]='\0';
+ }
+ else
#endif
+ {
+ /* Specify a full path to gpgkeys_foo. */
+ command=xmalloc(strlen(libexecdir)+strlen(DIRSEP_S)+
+ GPGKEYS_PREFIX_LEN+strlen(scheme)+3+strlen(EXEEXT)+1);
+ strcpy(command,libexecdir);
+ strcat(command,DIRSEP_S);
+ }
- /* Build the filename for the helper to execute */
- command=xmalloc (strlen("gpgkeys_")+strlen(opt.keyserver_scheme)+1);
- strcpy(command,"gpgkeys_");
- strcat(command,opt.keyserver_scheme);
+ end=command+strlen(command);
+
+ strcat(command,GPGKEYS_PREFIX);
+ strcat(command,scheme);
+
+ if(keyserver->flags.direct_uri)
+ strcat(command,"uri");
- if(opt.keyserver_options.use_temp_files)
+ strcat(command,EXEEXT);
+
+#ifdef GPGKEYS_CURL
+ if(!curl_cant_handle(scheme,keyserver->flags.direct_uri)
+ && path_access(command,X_OK)!=0)
+ strcpy(end,GPGKEYS_CURL);
+#endif
+
+ if(opt.keyserver_options.options&KEYSERVER_USE_TEMP_FILES)
{
- if(opt.keyserver_options.keep_temp_files)
+ if(opt.keyserver_options.options&KEYSERVER_KEEP_TEMP_FILES)
{
command=xrealloc(command,strlen(command)+
strlen(KEYSERVER_ARGS_KEEP)+1);
@@ -721,41 +1055,47 @@ keyserver_spawn(int action,STRLIST list,
else
ret=exec_write(&spawn,command,NULL,NULL,0,0);
+ xfree(command);
+
if(ret)
return ret;
- fprintf(spawn->tochild,"# This is a gpg keyserver communications file\n");
+ fprintf(spawn->tochild,
+ "# This is a GnuPG %s keyserver communications file\n",VERSION);
fprintf(spawn->tochild,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
fprintf(spawn->tochild,"PROGRAM %s\n",VERSION);
+ fprintf(spawn->tochild,"SCHEME %s\n",keyserver->scheme);
- if(opt.keyserver_opaque)
- fprintf(spawn->tochild,"OPAQUE %s\n",opt.keyserver_opaque);
+ if(keyserver->opaque)
+ fprintf(spawn->tochild,"OPAQUE %s\n",keyserver->opaque);
else
{
- if(opt.keyserver_host)
- fprintf(spawn->tochild,"HOST %s\n",opt.keyserver_host);
+ if(keyserver->auth)
+ fprintf(spawn->tochild,"AUTH %s\n",keyserver->auth);
- if(opt.keyserver_port)
- fprintf(spawn->tochild,"PORT %s\n",opt.keyserver_port);
- }
+ if(keyserver->host)
+ fprintf(spawn->tochild,"HOST %s\n",keyserver->host);
- /* Write options */
+ if(keyserver->port)
+ fprintf(spawn->tochild,"PORT %s\n",keyserver->port);
- for(i=0,kopts=keyserver_opts;kopts[i].name;i++)
- if(*(kopts[i].flag) && kopts[i].tell)
- fprintf(spawn->tochild,"OPTION %s\n",kopts[i].name);
+ if(keyserver->path)
+ fprintf(spawn->tochild,"PATH %s\n",keyserver->path);
+ }
+
+ /* Write global options */
- for(i=0;i<opt.keyserver_options.verbose;i++)
- fprintf(spawn->tochild,"OPTION verbose\n");
+ for(temp=opt.keyserver_options.other;temp;temp=temp->next)
+ fprintf(spawn->tochild,"OPTION %s\n",temp->d);
- temp=opt.keyserver_options.other;
+ /* Write per-keyserver options */
- for(;temp;temp=temp->next)
+ for(temp=keyserver->options;temp;temp=temp->next)
fprintf(spawn->tochild,"OPTION %s\n",temp->d);
switch(action)
{
- case GET:
+ case KS_GET:
{
fprintf(spawn->tochild,"COMMAND GET\n\n");
@@ -763,6 +1103,8 @@ keyserver_spawn(int action,STRLIST list,
for(i=0;i<count;i++)
{
+ int quiet=0;
+
if(desc[i].mode==KEYDB_SEARCH_MODE_FPR20)
{
int f;
@@ -770,7 +1112,7 @@ keyserver_spawn(int action,STRLIST list,
fprintf(spawn->tochild,"0x");
for(f=0;f<MAX_FINGERPRINT_LEN;f++)
- fprintf(spawn->tochild,"%02X",(byte)desc[i].u.fpr[f]);
+ fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]);
fprintf(spawn->tochild,"\n");
}
@@ -781,7 +1123,7 @@ keyserver_spawn(int action,STRLIST list,
fprintf(spawn->tochild,"0x");
for(f=0;f<16;f++)
- fprintf(spawn->tochild,"%02X",(byte)desc[i].u.fpr[f]);
+ fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]);
fprintf(spawn->tochild,"\n");
}
@@ -789,9 +1131,29 @@ keyserver_spawn(int action,STRLIST list,
fprintf(spawn->tochild,"0x%08lX%08lX\n",
(ulong)desc[i].u.kid[0],
(ulong)desc[i].u.kid[1]);
- else
+ else if(desc[i].mode==KEYDB_SEARCH_MODE_SHORT_KID)
fprintf(spawn->tochild,"0x%08lX\n",
(ulong)desc[i].u.kid[1]);
+ else if(desc[i].mode==KEYDB_SEARCH_MODE_EXACT)
+ {
+ fprintf(spawn->tochild,"0x0000000000000000\n");
+ quiet=1;
+ }
+ else if(desc[i].mode==KEYDB_SEARCH_MODE_NONE)
+ continue;
+ else
+ BUG();
+
+ if(!quiet)
+ {
+ if(keyserver->host)
+ log_info(_("requesting key %s from %s server %s\n"),
+ keystr_from_desc(&desc[i]),
+ keyserver->scheme,keyserver->host);
+ else
+ log_info(_("requesting key %s from %s\n"),
+ keystr_from_desc(&desc[i]),keyserver->uri);
+ }
}
fprintf(spawn->tochild,"\n");
@@ -799,7 +1161,29 @@ keyserver_spawn(int action,STRLIST list,
break;
}
- case SEND:
+ case KS_GETNAME:
+ {
+ STRLIST key;
+
+ fprintf(spawn->tochild,"COMMAND GETNAME\n\n");
+
+ /* Which names do we want? */
+
+ for(key=list;key!=NULL;key=key->next)
+ fprintf(spawn->tochild,"%s\n",key->d);
+
+ fprintf(spawn->tochild,"\n");
+
+ if(keyserver->host)
+ log_info(_("searching for names from %s server %s\n"),
+ keyserver->scheme,keyserver->host);
+ else
+ log_info(_("searching for names from %s\n"),keyserver->uri);
+
+ break;
+ }
+
+ case KS_SEND:
{
STRLIST key;
@@ -809,7 +1193,7 @@ keyserver_spawn(int action,STRLIST list,
for(key=list;key!=NULL;key=key->next)
{
armor_filter_context_t afx;
- iobuf_t buffer=iobuf_temp();
+ IOBUF buffer=iobuf_temp();
KBNODE block;
temp=NULL;
@@ -817,11 +1201,17 @@ keyserver_spawn(int action,STRLIST list,
memset(&afx,0,sizeof(afx));
afx.what=1;
+ /* Tell the armor filter to use Unix-style \n line
+ endings, since we're going to fprintf this to a file
+ that (on Win32) is open in text mode. The win32 stdio
+ will transform the \n to \r\n and we'll end up with the
+ proper line endings on win32. This is a no-op on
+ Unix. */
+ afx.eol[0]='\n';
iobuf_push_filter(buffer,armor_filter,&afx);
- /* TODO: Don't use the keyblock hack here - instead,
- output each key as a different ascii armored blob with
- its own INFO section. */
+ /* TODO: Remove Comment: lines from keys exported this
+ way? */
if(export_pubkeys_stream(buffer,temp,&block,
opt.keyserver_options.export_options)==-1)
@@ -834,7 +1224,9 @@ keyserver_spawn(int action,STRLIST list,
merge_keys_and_selfsig(block);
- fprintf(spawn->tochild,"INFO %s BEGIN\n",key->d);
+ fprintf(spawn->tochild,"INFO %08lX%08lX BEGIN\n",
+ (ulong)block->pkt->pkt.public_key->keyid[0],
+ (ulong)block->pkt->pkt.public_key->keyid[1]);
for(node=block;node;node=node->next)
{
@@ -864,9 +1256,8 @@ keyserver_spawn(int action,STRLIST list,
fprintf(spawn->tochild,"e");
fprintf(spawn->tochild,"\n");
-
- break;
}
+ break;
case PKT_USER_ID:
{
@@ -884,7 +1275,8 @@ keyserver_spawn(int action,STRLIST list,
{
if(uid->name[r]==':' || uid->name[r]=='%'
|| uid->name[r]&0x80)
- fprintf(spawn->tochild,"%%%02X",uid->name[r]);
+ fprintf(spawn->tochild,"%%%02X",
+ (byte)uid->name[r]);
else
fprintf(spawn->tochild,"%c",uid->name[r]);
}
@@ -899,10 +1291,32 @@ keyserver_spawn(int action,STRLIST list,
fprintf(spawn->tochild,"\n");
}
+ break;
+
+ /* This bit is really for the benefit of
+ people who store their keys in LDAP
+ servers. It makes it easy to do queries
+ for things like "all keys signed by
+ Isabella". */
+ case PKT_SIGNATURE:
+ {
+ PKT_signature *sig=node->pkt->pkt.signature;
+
+ if(!IS_UID_SIG(sig))
+ continue;
+
+ fprintf(spawn->tochild,"sig:%08lX%08lX:%X:%u:%u\n",
+ (ulong)sig->keyid[0],(ulong)sig->keyid[1],
+ sig->sig_class,sig->timestamp,
+ sig->expiredate);
+ }
+ break;
}
}
- fprintf(spawn->tochild,"INFO %s END\n",key->d);
+ fprintf(spawn->tochild,"INFO %08lX%08lX END\n",
+ (ulong)block->pkt->pkt.public_key->keyid[0],
+ (ulong)block->pkt->pkt.public_key->keyid[1]);
fprintf(spawn->tochild,"KEY %s BEGIN\n",key->d);
fwrite(iobuf_get_temp_buffer(buffer),
@@ -910,6 +1324,16 @@ keyserver_spawn(int action,STRLIST list,
fprintf(spawn->tochild,"KEY %s END\n",key->d);
iobuf_close(buffer);
+
+ if(keyserver->host)
+ log_info(_("sending key %s to %s server %s\n"),
+ keystr(block->pkt->pkt.public_key->keyid),
+ keyserver->scheme,keyserver->host);
+ else
+ log_info(_("sending key %s to %s\n"),
+ keystr(block->pkt->pkt.public_key->keyid),
+ keyserver->uri);
+
release_kbnode(block);
}
@@ -919,7 +1343,7 @@ keyserver_spawn(int action,STRLIST list,
break;
}
- case SEARCH:
+ case KS_SEARCH:
{
STRLIST key;
@@ -939,7 +1363,7 @@ keyserver_spawn(int action,STRLIST list,
}
else
{
- searchstr=xmalloc (strlen(key->d)+1);
+ searchstr=xmalloc(strlen(key->d)+1);
searchstr[0]='\0';
}
@@ -948,6 +1372,13 @@ keyserver_spawn(int action,STRLIST list,
fprintf(spawn->tochild,"\n");
+ if(keyserver->host)
+ log_info(_("searching for \"%s\" from %s server %s\n"),
+ searchstr,keyserver->scheme,keyserver->host);
+ else
+ log_info(_("searching for \"%s\" from %s\n"),
+ searchstr,keyserver->uri);
+
break;
}
@@ -971,7 +1402,7 @@ keyserver_spawn(int action,STRLIST list,
maxlen=1024;
if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0)
{
- ret = iobuf_error (spawn->fromchild);
+ ret=G10ERR_READ_FILE;
goto fail; /* i.e. EOF */
}
@@ -1000,8 +1431,8 @@ keyserver_spawn(int action,STRLIST list,
else if(ascii_strncasecmp(ptr,"PROGRAM ",8)==0)
{
if(ascii_strncasecmp(&ptr[8],VERSION,strlen(VERSION))!=0)
- log_info(_("WARNING: keyserver handler from a different "
- "version of GnuPG (%s)\n"),&ptr[8]);
+ log_info(_("WARNING: keyserver handler from a different"
+ " version of GnuPG (%s)\n"),&ptr[8]);
}
else if(ascii_strncasecmp(ptr,"OPTION OUTOFBAND",16)==0)
outofband=1; /* Currently the only OPTION */
@@ -1016,7 +1447,8 @@ keyserver_spawn(int action,STRLIST list,
if(!outofband)
switch(action)
{
- case GET:
+ case KS_GET:
+ case KS_GETNAME:
{
void *stats_handle;
@@ -1029,7 +1461,7 @@ keyserver_spawn(int action,STRLIST list,
way to do this could be to continue parsing this
line-by-line and make a temp iobuf for each key. */
- import_keys_stream(spawn->fromchild,stats_handle,
+ import_keys_stream(spawn->fromchild,stats_handle,fpr,fpr_len,
opt.keyserver_options.import_options);
import_print_stats(stats_handle);
@@ -1039,15 +1471,12 @@ keyserver_spawn(int action,STRLIST list,
}
/* Nothing to do here */
- case SEND:
+ case KS_SEND:
break;
- case SEARCH:
- {
- keyserver_search_prompt(spawn->fromchild,searchstr);
-
- break;
- }
+ case KS_SEARCH:
+ keyserver_search_prompt(spawn->fromchild,searchstr);
+ break;
default:
log_fatal(_("no keyserver action!\n"));
@@ -1055,7 +1484,9 @@ keyserver_spawn(int action,STRLIST list,
}
fail:
- xfree (line);
+ xfree(line);
+ xfree(searchstr);
+
*prog=exec_finish(spawn);
@@ -1063,45 +1494,53 @@ keyserver_spawn(int action,STRLIST list,
}
static int
-keyserver_work(int action,STRLIST list,KEYDB_SEARCH_DESC *desc,int count)
+keyserver_work(enum ks_action action,STRLIST list,KEYDB_SEARCH_DESC *desc,
+ int count,unsigned char **fpr,size_t *fpr_len,
+ struct keyserver_spec *keyserver)
{
int rc=0,ret=0;
- if(opt.keyserver_scheme==NULL)
+ if(!keyserver)
{
log_error(_("no keyserver known (use option --keyserver)\n"));
- return GPG_ERR_BAD_URI;
+ return G10ERR_BAD_URI;
}
#ifdef DISABLE_KEYSERVER_HELPERS
log_error(_("external keyserver calls are not supported in this build\n"));
- return GPG_ERR_KEYSERVER;
+ return G10ERR_KEYSERVER;
#else
/* Spawn a handler */
- rc=keyserver_spawn(action,list,desc,count,&ret);
+ rc=keyserver_spawn(action,list,desc,count,&ret,fpr,fpr_len,keyserver);
if(ret)
{
switch(ret)
{
case KEYSERVER_SCHEME_NOT_FOUND:
- log_error(_("no handler for keyserver scheme \"%s\"\n"),
- opt.keyserver_scheme);
+ log_error(_("no handler for keyserver scheme `%s'\n"),
+ keyserver->scheme);
break;
case KEYSERVER_NOT_SUPPORTED:
- log_error(_("action \"%s\" not supported with keyserver "
- "scheme \"%s\"\n"),
- action==GET?"get":action==SEND?"send":
- action==SEARCH?"search":"unknown",
- opt.keyserver_scheme);
+ log_error(_("action `%s' not supported with keyserver "
+ "scheme `%s'\n"),
+ action==KS_GET?"get":action==KS_SEND?"send":
+ action==KS_SEARCH?"search":"unknown",
+ keyserver->scheme);
break;
case KEYSERVER_VERSION_ERROR:
- log_error(_("gpgkeys_%s does not support handler version %d\n"),
- opt.keyserver_scheme,KEYSERVER_PROTO_VERSION);
+ log_error(_(GPGKEYS_PREFIX "%s does not support"
+ " handler version %d\n"),
+ keyserver_typemap(keyserver->scheme),
+ KEYSERVER_PROTO_VERSION);
+ break;
+
+ case KEYSERVER_TIMEOUT:
+ log_error(_("keyserver timed out\n"));
break;
case KEYSERVER_INTERNAL_ERROR:
@@ -1110,12 +1549,12 @@ keyserver_work(int action,STRLIST list,KEYDB_SEARCH_DESC *desc,int count)
break;
}
- return GPG_ERR_KEYSERVER;
+ return G10ERR_KEYSERVER;
}
if(rc)
{
- log_error(_("keyserver communications error: %s\n"),gpg_strerror (rc));
+ log_error(_("keyserver communications error: %s\n"),g10_errstr(rc));
return rc;
}
@@ -1127,18 +1566,33 @@ keyserver_work(int action,STRLIST list,KEYDB_SEARCH_DESC *desc,int count)
int
keyserver_export(STRLIST users)
{
- /* We better ask for confirmation when the user entered --send-keys
- without arguments. Sending all keys might not be the thing he
- intended to do */
- if (users || opt.batch || opt.answer_yes)
- ;
- else if ( !cpr_get_answer_is_yes
- ("keyserver_export.send_all",
- _("Do you really want to send all your "
- "public keys to the keyserver? (y/N) ")))
- return -1;
+ STRLIST sl=NULL;
+ KEYDB_SEARCH_DESC desc;
+ int rc=0;
+
+ /* Weed out descriptors that we don't support sending */
+ for(;users;users=users->next)
+ {
+ classify_user_id (users->d, &desc);
+ if(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_work(KS_SEND,sl,NULL,0,NULL,NULL,opt.keyserver);
+ free_strlist(sl);
+ }
- return keyserver_work(SEND,users,NULL,0);
+ return rc;
}
int
@@ -1149,7 +1603,7 @@ keyserver_import(STRLIST users)
int rc=0;
/* Build a list of key ids */
- desc=xmalloc (sizeof(KEYDB_SEARCH_DESC)*num);
+ desc=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num);
for(;users;users=users->next)
{
@@ -1159,7 +1613,7 @@ keyserver_import(STRLIST users)
desc[count].mode!=KEYDB_SEARCH_MODE_FPR16 &&
desc[count].mode!=KEYDB_SEARCH_MODE_FPR20)
{
- log_error(_("skipping invalid key ID \"%s\"\n"),users->d);
+ log_error(_("\"%s\" not a key ID: skipping\n"),users->d);
continue;
}
@@ -1172,15 +1626,16 @@ keyserver_import(STRLIST users)
}
if(count>0)
- rc=keyserver_work(GET,NULL,desc,count);
+ rc=keyserver_work(KS_GET,NULL,desc,count,NULL,NULL,opt.keyserver);
- xfree (desc);
+ xfree(desc);
return rc;
}
int
-keyserver_import_fprint(const byte *fprint,size_t fprint_len)
+keyserver_import_fprint(const byte *fprint,size_t fprint_len,
+ struct keyserver_spec *keyserver)
{
KEYDB_SEARCH_DESC desc;
@@ -1195,11 +1650,13 @@ keyserver_import_fprint(const byte *fprint,size_t fprint_len)
memcpy(desc.u.fpr,fprint,fprint_len);
- return keyserver_work(GET,NULL,&desc,1);
+ /* TODO: Warn here if the fingerprint we got doesn't match the one
+ we asked for? */
+ return keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,keyserver);
}
int
-keyserver_import_keyid(u32 *keyid)
+keyserver_import_keyid(u32 *keyid,struct keyserver_spec *keyserver)
{
KEYDB_SEARCH_DESC desc;
@@ -1209,7 +1666,7 @@ keyserver_import_keyid(u32 *keyid)
desc.u.kid[0]=keyid[0];
desc.u.kid[1]=keyid[1];
- return keyserver_work(GET,NULL,&desc,1);
+ return keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,keyserver);
}
/* code mostly stolen from do_export_stream */
@@ -1224,14 +1681,14 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
*count=0;
- *klist=xmalloc (sizeof(KEYDB_SEARCH_DESC)*num);
+ *klist=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num);
kdbhd=keydb_new(0);
if(!users)
{
ndesc = 1;
- desc = xcalloc (1, ndesc * sizeof *desc);
+ desc = xmalloc_clear ( ndesc * sizeof *desc);
desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
}
else
@@ -1245,8 +1702,8 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
if(classify_user_id (sl->d, desc+ndesc))
ndesc++;
else
- log_error (_("key `%s' not found: %s\n"),
- sl->d, gpg_strerror (GPG_ERR_INV_USER_ID));
+ log_error (_("key \"%s\" not found: %s\n"),
+ sl->d, g10_errstr (G10ERR_INV_USER_ID));
}
}
@@ -1259,7 +1716,7 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
rc = keydb_get_keyblock (kdbhd, &keyblock );
if( rc )
{
- log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) );
+ log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
goto leave;
}
@@ -1276,8 +1733,8 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
node->pkt->pkt.public_key->version>=4)
{
(*klist)[*count].mode=KEYDB_SEARCH_MODE_LONG_KID;
- v3_keyid (node->pkt->pkt.public_key->pkey[0],
- (*klist)[*count].u.kid);
+ mpi_get_keyid(node->pkt->pkt.public_key->pkey[0],
+ (*klist)[*count].u.kid);
(*count)++;
if(*count==num)
@@ -1288,7 +1745,7 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
}
/* v4 keys get full fingerprints. v3 keys get long keyids.
- This is because it's easy to calculate any sort of key id
+ 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)
@@ -1306,6 +1763,43 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
(*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(keyblock);
+
+ for(node=node->next;node;node=node->next)
+ {
+ if(node->pkt->pkttype==PKT_USER_ID
+ && node->pkt->pkt.user_id->is_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)
@@ -1320,7 +1814,9 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
rc=0;
leave:
- xfree (desc);
+ if(rc)
+ xfree(*klist);
+ xfree(desc);
keydb_release(kdbhd);
release_kbnode(keyblock);
@@ -1330,43 +1826,91 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
/* Note this is different than the original HKP refresh. It allows
usernames to refresh only part of the keyring. */
-int
+int
keyserver_refresh(STRLIST users)
{
- int rc,count,fakev3=0;
+ int rc,count,numdesc,fakev3=0;
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. Is it worth
- preserving the old merge_only value here? */
- opt.merge_only=1;
+ /* 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;
/* If refresh_add_fake_v3_keyids is on and it's a HKP or MAILTO
scheme, then enable fake v3 keyid generation. */
- if(opt.keyserver_options.fake_v3_keyids && opt.keyserver_scheme &&
- (ascii_strcasecmp(opt.keyserver_scheme,"hkp")==0 ||
- ascii_strcasecmp(opt.keyserver_scheme,"mailto")==0))
+ if((opt.keyserver_options.options&KEYSERVER_ADD_FAKE_V3) && opt.keyserver
+ && (ascii_strcasecmp(opt.keyserver->scheme,"hkp")==0 ||
+ ascii_strcasecmp(opt.keyserver->scheme,"mailto")==0))
fakev3=1;
- rc=keyidlist(users,&desc,&count,fakev3);
+ rc=keyidlist(users,&desc,&numdesc,fakev3);
if(rc)
return rc;
+ count=numdesc;
if(count>0)
{
- if(opt.keyserver_uri)
+ 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;
+
+ /* We use the keyserver structure we parsed out before.
+ Note that a preferred keyserver without a scheme://
+ will be interpreted as hkp:// */
+
+ rc=keyserver_work(KS_GET,NULL,&desc[i],1,NULL,NULL,keyserver);
+ if(rc)
+ log_info(_("WARNING: unable to refresh key %s"
+ " via %s: %s\n"),keystr_from_desc(&desc[i]),
+ keyserver->uri,g10_errstr(rc));
+ 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)
+ {
+ if(opt.keyserver)
{
if(count==1)
- log_info(_("refreshing 1 key from %s\n"),opt.keyserver_uri);
+ log_info(_("refreshing 1 key from %s\n"),opt.keyserver->uri);
else
log_info(_("refreshing %d keys from %s\n"),
- count,opt.keyserver_uri);
+ count,opt.keyserver->uri);
}
- rc=keyserver_work(GET,NULL,desc,count);
+ rc=keyserver_work(KS_GET,NULL,desc,numdesc,NULL,NULL,opt.keyserver);
}
- xfree (desc);
+ 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))
+ trustdb_check_or_update();
return rc;
}
@@ -1375,7 +1919,217 @@ int
keyserver_search(STRLIST tokens)
{
if(tokens)
- return keyserver_work(SEARCH,tokens,NULL,0);
+ return keyserver_work(KS_SEARCH,tokens,NULL,0,NULL,NULL,opt.keyserver);
else
return 0;
}
+
+int
+keyserver_fetch(STRLIST urilist)
+{
+ KEYDB_SEARCH_DESC desc;
+ STRLIST sl;
+ unsigned int options=opt.keyserver_options.import_options;
+
+ /* 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;
+
+ /* A dummy desc since we're not actually fetching a particular key
+ ID */
+ memset(&desc,0,sizeof(desc));
+ desc.mode=KEYDB_SEARCH_MODE_EXACT;
+
+ for(sl=urilist;sl;sl=sl->next)
+ {
+ struct keyserver_spec *spec;
+
+ spec=parse_keyserver_uri(sl->d,1,NULL,0);
+ if(spec)
+ {
+ int rc;
+
+ /*
+ Set the direct_uri flag so we know later to call a direct
+ handler instead of the keyserver style. This lets us use
+ gpgkeys_curl or gpgkeys_ldapuri instead of gpgkeys_ldap to
+ fetch things like
+ ldap://keyserver.pgp.com/o=PGP%20keys?pgpkey?sub?pgpkeyid=99242560
+ */
+ spec->flags.direct_uri=1;
+
+ rc=keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,spec);
+ if(rc)
+ log_info (_("WARNING: unable to fetch URI %s: %s\n"),
+ sl->d,g10_errstr(rc));
+
+ free_keyserver_spec(spec);
+ }
+ else
+ log_info (_("WARNING: unable to parse URI %s\n"),sl->d);
+ }
+
+ 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))
+ trustdb_check_or_update();
+
+ return 0;
+}
+
+/* Import key in a CERT or pointed to by a CERT */
+int
+keyserver_import_cert(const char *name,unsigned char **fpr,size_t *fpr_len)
+{
+ char *domain,*look,*url;
+ IOBUF key;
+ int type,rc=G10ERR_GENERAL;
+
+ look=xstrdup(name);
+
+ domain=strrchr(look,'@');
+ if(domain)
+ *domain='.';
+
+ type=get_cert(look,max_cert_size,&key,fpr,fpr_len,&url);
+ if(type==1)
+ {
+ int armor_status=opt.no_armor;
+
+ /* CERTs are always in binary format */
+ opt.no_armor=1;
+
+ rc=import_keys_stream(key,NULL,fpr,fpr_len,
+ opt.keyserver_options.import_options);
+
+ opt.no_armor=armor_status;
+
+ iobuf_close(key);
+ }
+ else if(type==2 && *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,NULL,0);
+ if(spec)
+ {
+ STRLIST list=NULL;
+
+ add_to_strlist(&list,url);
+
+ rc=keyserver_fetch(list);
+
+ free_strlist(list);
+ free_keyserver_spec(spec);
+ }
+ }
+ else if(opt.keyserver)
+ {
+ /* If only a fingerprint is provided, try and fetch it from
+ our --keyserver */
+
+ rc=keyserver_import_fprint(*fpr,*fpr_len,opt.keyserver);
+ }
+
+ xfree(url);
+ }
+
+ xfree(look);
+
+ return rc;
+}
+
+/* Import key pointed to by a PKA record. Return the requested
+ fingerprint in fpr. */
+int
+keyserver_import_pka(const char *name,unsigned char **fpr,size_t *fpr_len)
+{
+ char *uri;
+ int rc=-1;
+
+ *fpr=xmalloc(20);
+ *fpr_len=20;
+
+ uri = get_pka_info (name, *fpr);
+ if (uri)
+ {
+ struct keyserver_spec *spec;
+ spec = parse_keyserver_uri (uri, 1, NULL, 0);
+ if (spec)
+ {
+ rc=keyserver_import_fprint (*fpr, 20, spec);
+ free_keyserver_spec (spec);
+ }
+ xfree (uri);
+ }
+
+ if(rc!=0)
+ xfree(*fpr);
+
+ return rc;
+}
+
+/* Import all keys that match name */
+int
+keyserver_import_name(const char *name,unsigned char **fpr,size_t *fpr_len,
+ struct keyserver_spec *keyserver)
+{
+ STRLIST list=NULL;
+ int rc;
+
+ append_to_strlist(&list,name);
+
+ rc=keyserver_work(KS_GETNAME,list,NULL,0,fpr,fpr_len,keyserver);
+
+ free_strlist(list);
+
+ return rc;
+}
+
+/* Use the PGP Universal trick of asking ldap://keys.(maildomain) for
+ the key. */
+int
+keyserver_import_ldap(const char *name,unsigned char **fpr,size_t *fpr_len)
+{
+ char *domain;
+ struct keyserver_spec *keyserver;
+ STRLIST list=NULL;
+ int rc;
+
+ append_to_strlist(&list,name);
+
+ /* Parse out the domain */
+ domain=strrchr(name,'@');
+ if(!domain)
+ return G10ERR_GENERAL;
+
+ domain++;
+
+ keyserver=xmalloc_clear(sizeof(struct keyserver_spec));
+
+ keyserver->scheme=xstrdup("ldap");
+ keyserver->host=xmalloc(5+strlen(domain)+1);
+ strcpy(keyserver->host,"keys.");
+ strcat(keyserver->host,domain);
+ keyserver->uri=xmalloc(strlen(keyserver->scheme)+
+ 3+strlen(keyserver->host)+1);
+ strcpy(keyserver->uri,keyserver->scheme);
+ strcat(keyserver->uri,"://");
+ strcat(keyserver->uri,keyserver->host);
+
+ rc=keyserver_work(KS_GETNAME,list,NULL,0,fpr,fpr_len,keyserver);
+
+ free_strlist(list);
+
+ free_keyserver_spec(keyserver);
+
+ return rc;
+}