1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-02 22:46:30 +02:00

All standard keyserver commands are now using dirmngr.

This commit is contained in:
Werner Koch 2011-01-20 14:12:53 +01:00
parent 357f8d5398
commit 7f32d88ed1
20 changed files with 689 additions and 735 deletions

View file

@ -111,13 +111,11 @@ static struct parse_options keyserver_opts[]=
{NULL,0,NULL,NULL}
};
static int keyserver_work (ctrl_t ctrl, enum ks_action action,strlist_t list,
KEYDB_SEARCH_DESC *desc,int count,
unsigned char **fpr,size_t *fpr_len,
struct keyserver_spec *keyserver);
static gpg_error_t keyserver_get (ctrl_t ctrl,
KEYDB_SEARCH_DESC *desc, int ndesc,
struct keyserver_spec *keyserver);
static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
struct keyserver_spec *keyserver);
/* Reasonable guess */
@ -989,664 +987,6 @@ search_line_handler (void *opaque, char *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 if(strcmp(type,"hkps")==0)
return "hkp";
else
return type;
}
/* 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
direct_uri_map(const char *scheme,unsigned int is_direct)
{
if(is_direct && strcmp(scheme,"ldap")==0)
return 1;
return 0;
}
#if GNUPG_MAJOR_VERSION == 2
#define GPGKEYS_PREFIX "gpg2keys_"
#else
#define GPGKEYS_PREFIX "gpgkeys_"
#endif
#define GPGKEYS_CURL GPGKEYS_PREFIX "curl" EXEEXT
#define GPGKEYS_PREFIX_LEN (strlen(GPGKEYS_CURL))
#define KEYSERVER_ARGS_KEEP " -o \"%O\" \"%I\""
#define KEYSERVER_ARGS_NOKEEP " -o \"%o\" \"%i\""
static int
keyserver_spawn (ctrl_t ctrl,
enum ks_action action,strlist_t 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_t temp;
unsigned int maxlen,buflen;
char *command,*end,*searchstr=NULL;
byte *line=NULL;
struct exec_info *spawn;
const char *scheme;
const char *libexecdir = gnupg_libexecdir ();
assert(keyserver);
#ifdef EXEC_TEMPFILE_ONLY
opt.keyserver_options.options|=KEYSERVER_USE_TEMP_FILES;
#endif
/* Build the filename for the helper to execute */
scheme=keyserver_typemap(keyserver->scheme);
#ifdef DISABLE_KEYSERVER_PATH
/* 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 gnupg_libexecdir function has been
modified to return just this. Setting the exec-path is not
anymore required.
set_exec_path(libexecdir);
*/
#else
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);
}
end=command+strlen(command);
/* Build a path for the keyserver helper. If it is direct_uri
(i.e. an object fetch and not a keyserver), then add "_uri" to
the end to distinguish the keyserver helper from an object
fetcher that can speak that protocol (this is a problem for
LDAP). */
strcat(command,GPGKEYS_PREFIX);
strcat(command,scheme);
/* This "_uri" thing is in case we need to call a direct handler
instead of the keyserver handler. This lets us use gpgkeys_curl
or gpgkeys_ldap_uri (we don't provide it, but a user might)
instead of gpgkeys_ldap to fetch things like
ldap://keyserver.pgp.com/o=PGP%20keys?pgpkey?sub?pgpkeyid=99242560 */
if(direct_uri_map(scheme,keyserver->flags.direct_uri))
strcat(command,"_uri");
strcat(command,EXEEXT);
/* Can we execute it? If not, try curl as our catchall. */
if(path_access(command,X_OK)!=0)
strcpy(end,GPGKEYS_CURL);
if(opt.keyserver_options.options&KEYSERVER_USE_TEMP_FILES)
{
if(opt.keyserver_options.options&KEYSERVER_KEEP_TEMP_FILES)
{
command=xrealloc(command,strlen(command)+
strlen(KEYSERVER_ARGS_KEEP)+1);
strcat(command,KEYSERVER_ARGS_KEEP);
}
else
{
command=xrealloc(command,strlen(command)+
strlen(KEYSERVER_ARGS_NOKEEP)+1);
strcat(command,KEYSERVER_ARGS_NOKEEP);
}
ret=exec_write(&spawn,NULL,command,NULL,0,0);
}
else
ret=exec_write(&spawn,command,NULL,NULL,0,0);
xfree(command);
if(ret)
return ret;
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(keyserver->opaque)
fprintf(spawn->tochild,"OPAQUE %s\n",keyserver->opaque);
else
{
if(keyserver->auth)
fprintf(spawn->tochild,"AUTH %s\n",keyserver->auth);
if(keyserver->host)
fprintf(spawn->tochild,"HOST %s\n",keyserver->host);
if(keyserver->port)
fprintf(spawn->tochild,"PORT %s\n",keyserver->port);
if(keyserver->path)
fprintf(spawn->tochild,"PATH %s\n",keyserver->path);
}
/* Write global options */
for(temp=opt.keyserver_options.other;temp;temp=temp->next)
fprintf(spawn->tochild,"OPTION %s\n",temp->d);
/* Write per-keyserver options */
for(temp=keyserver->options;temp;temp=temp->next)
fprintf(spawn->tochild,"OPTION %s\n",temp->d);
switch(action)
{
case KS_GET:
{
fprintf(spawn->tochild,"COMMAND GET\n\n");
/* Which keys do we want? */
for(i=0;i<count;i++)
{
int quiet=0;
if(desc[i].mode==KEYDB_SEARCH_MODE_FPR20)
{
int f;
fprintf(spawn->tochild,"0x");
for(f=0;f<MAX_FINGERPRINT_LEN;f++)
fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]);
fprintf(spawn->tochild,"\n");
}
else if(desc[i].mode==KEYDB_SEARCH_MODE_FPR16)
{
int f;
fprintf(spawn->tochild,"0x");
for(f=0;f<16;f++)
fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]);
fprintf(spawn->tochild,"\n");
}
else if(desc[i].mode==KEYDB_SEARCH_MODE_LONG_KID)
fprintf(spawn->tochild,"0x%08lX%08lX\n",
(ulong)desc[i].u.kid[0],
(ulong)desc[i].u.kid[1]);
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");
break;
}
case KS_GETNAME:
{
strlist_t 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_t key;
/* Note the extra \n here to send an empty keylist block */
fprintf(spawn->tochild,"COMMAND SEND\n\n\n");
for(key=list;key!=NULL;key=key->next)
{
armor_filter_context_t *afx;
IOBUF buffer = iobuf_temp ();
KBNODE block;
temp=NULL;
add_to_strlist(&temp,key->d);
afx = new_armor_context ();
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';
push_armor_filter (afx, buffer);
release_armor_context (afx);
/* TODO: Remove Comment: lines from keys exported this
way? */
if(export_pubkeys_stream (ctrl, buffer,temp,&block,
opt.keyserver_options.export_options)==-1)
iobuf_close(buffer);
else
{
KBNODE node;
iobuf_flush_temp(buffer);
merge_keys_and_selfsig(block);
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)
{
switch(node->pkt->pkttype)
{
default:
continue;
case PKT_PUBLIC_KEY:
case PKT_PUBLIC_SUBKEY:
{
PKT_public_key *pk=node->pkt->pkt.public_key;
keyid_from_pk(pk,NULL);
fprintf(spawn->tochild,"%sb:%08lX%08lX:%u:%u:%u:%u:",
node->pkt->pkttype==PKT_PUBLIC_KEY?"pu":"su",
(ulong)pk->keyid[0],(ulong)pk->keyid[1],
pk->pubkey_algo,
nbits_from_pk(pk),
pk->timestamp,
pk->expiredate);
if(pk->flags.revoked)
fprintf(spawn->tochild,"r");
if(pk->has_expired)
fprintf(spawn->tochild,"e");
fprintf(spawn->tochild,"\n");
}
break;
case PKT_USER_ID:
{
PKT_user_id *uid=node->pkt->pkt.user_id;
int r;
if(uid->attrib_data)
continue;
fprintf(spawn->tochild,"uid:");
/* Quote ':', '%', and any 8-bit
characters */
for(r=0;r<uid->len;r++)
{
if(uid->name[r]==':' || uid->name[r]=='%'
|| uid->name[r]&0x80)
fprintf(spawn->tochild,"%%%02X",
(byte)uid->name[r]);
else
fprintf(spawn->tochild,"%c",uid->name[r]);
}
fprintf(spawn->tochild,":%u:%u:",
uid->created,uid->expiredate);
if(uid->is_revoked)
fprintf(spawn->tochild,"r");
if(uid->is_expired)
fprintf(spawn->tochild,"e");
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 %08lX%08lX END\n",
(ulong)block->pkt->pkt.public_key->keyid[0],
(ulong)block->pkt->pkt.public_key->keyid[1]);
fprintf(spawn->tochild,"KEY %08lX%08lX BEGIN\n",
(ulong)block->pkt->pkt.public_key->keyid[0],
(ulong)block->pkt->pkt.public_key->keyid[1]);
fwrite(iobuf_get_temp_buffer(buffer),
iobuf_get_temp_length(buffer),1,spawn->tochild);
fprintf(spawn->tochild,"KEY %08lX%08lX END\n",
(ulong)block->pkt->pkt.public_key->keyid[0],
(ulong)block->pkt->pkt.public_key->keyid[1]);
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);
}
free_strlist(temp);
}
break;
}
case KS_SEARCH:
{
strlist_t key;
fprintf(spawn->tochild,"COMMAND SEARCH\n\n");
/* Which keys do we want? Remember that the gpgkeys_ program
is going to lump these together into a search string. */
for(key=list;key!=NULL;key=key->next)
{
fprintf(spawn->tochild,"%s\n",key->d);
if(key!=list)
{
searchstr=xrealloc(searchstr,
strlen(searchstr)+strlen(key->d)+2);
strcat(searchstr," ");
}
else
{
searchstr=xmalloc(strlen(key->d)+1);
searchstr[0]='\0';
}
strcat(searchstr,key->d);
}
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;
}
default:
log_fatal(_("no keyserver action!\n"));
break;
}
/* Done sending, so start reading. */
ret=exec_read(spawn);
if(ret)
goto fail;
/* Now handle the response */
for(;;)
{
int plen;
char *ptr;
maxlen=1024;
if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0)
{
ret = gpg_error_from_syserror ();
goto fail; /* i.e. EOF */
}
ptr=line;
/* remove trailing whitespace */
plen=strlen(ptr);
while(plen>0 && ascii_isspace(ptr[plen-1]))
plen--;
plen[ptr]='\0';
if(*ptr=='\0')
break;
if(ascii_strncasecmp(ptr,"VERSION ",8)==0)
{
gotversion=1;
if(atoi(&ptr[8])!=KEYSERVER_PROTO_VERSION)
{
log_error(_("invalid keyserver protocol (us %d!=handler %d)\n"),
KEYSERVER_PROTO_VERSION,atoi(&ptr[8]));
goto fail;
}
}
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]);
}
else if(ascii_strncasecmp(ptr,"OPTION OUTOFBAND",16)==0)
outofband=1; /* Currently the only OPTION */
}
if(!gotversion)
{
log_error(_("keyserver did not send VERSION\n"));
goto fail;
}
if(!outofband)
switch(action)
{
case KS_GET:
case KS_GETNAME:
{
void *stats_handle;
stats_handle=import_new_stats_handle();
/* 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. */
/* FIXME: Pass CTRL. */
import_keys_stream (NULL, spawn->fromchild,stats_handle,fpr,fpr_len,
opt.keyserver_options.import_options);
import_print_stats(stats_handle);
import_release_stats_handle(stats_handle);
break;
}
/* Nothing to do here */
case KS_SEND:
break;
case KS_SEARCH:
//keyserver_search_prompt (ctrl, spawn->fromchild,searchstr);
break;
default:
log_fatal(_("no keyserver action!\n"));
break;
}
fail:
xfree(line);
xfree(searchstr);
*prog=exec_finish(spawn);
return ret;
}
static int
keyserver_work (ctrl_t ctrl,
enum ks_action action,strlist_t list,KEYDB_SEARCH_DESC *desc,
int count,unsigned char **fpr,size_t *fpr_len,
struct keyserver_spec *keyserver)
{
int rc=0,ret=0;
if (!keyserver)
{
log_error (_("no keyserver known (use option --keyserver)\n"));
return gpg_error (GPG_ERR_BAD_URI);
}
rc = keyserver_spawn (ctrl, 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"),
keyserver->scheme);
break;
case KEYSERVER_NOT_SUPPORTED:
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_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:
default:
log_error(_("keyserver internal error\n"));
break;
}
return G10ERR_KEYSERVER;
}
if (rc)
{
log_error (_("keyserver communications error: %s\n"),g10_errstr(rc));
return rc;
}
return 0;
}
int
keyserver_export (ctrl_t ctrl, strlist_t users)
@ -1674,7 +1014,7 @@ keyserver_export (ctrl_t ctrl, strlist_t users)
if(sl)
{
rc = keyserver_work (ctrl, KS_SEND,sl,NULL,0,NULL,NULL,opt.keyserver);
rc = keyserver_put (ctrl, sl, opt.keyserver);
free_strlist(sl);
}
@ -2113,26 +1453,6 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens)
/* Called using:
show_prompt:
import:
import_foo:
refresh:
rc=keyserver_work (ctrl, KS_GET, NULL, desc, count,
NULL, NULL, opt.keyserver);
fetch:
rc = keyserver_work (ctrl, 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));
export:
rc = keyserver_work (ctrl, KS_SEND,sl,NULL,0,NULL,NULL,opt.keyserver);
import_name:
rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
0, fpr, fpr_len, keyserver);
@ -2267,6 +1587,58 @@ keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
}
/* Send all keys specified by KEYSPECS to the KEYSERVERS. */
static gpg_error_t
keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
struct keyserver_spec *keyserver)
{
gpg_error_t err;
strlist_t kspec;
if (!keyspecs)
return 0; /* Return success if the list is empty. */
if (!opt.keyserver)
{
log_error (_("no keyserver known (use option --keyserver)\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,
&keyblock, &data, &datalen);
if (err)
log_error (_("skipped \"%s\": %s\n"), kspec->d, gpg_strerror (err));
else
{
if (keyserver->host)
log_info (_("sending key %s to %s server %s\n"),
keystr (keyblock->pkt->pkt.public_key->keyid),
keyserver->scheme, keyserver->host);
else
log_info (_("sending key %s to %s\n"),
keystr (keyblock->pkt->pkt.public_key->keyid),
keyserver->uri);
err = gpg_dirmngr_ks_put (ctrl, data, datalen, keyblock);
release_kbnode (keyblock);
xfree (data);
if (err)
log_error (_("keyserver send failed: %s\n"), gpg_strerror (err));
}
}
return err;
}
@ -2439,8 +1811,9 @@ keyserver_import_name (ctrl_t ctrl, const char *name,
append_to_strlist(&list,name);
rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
0, fpr, fpr_len, keyserver);
rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */
/* keyserver_work (ctrl, KS_GETNAME, list, NULL, */
/* 0, fpr, fpr_len, keyserver); */
free_strlist(list);
@ -2513,8 +1886,9 @@ keyserver_import_ldap (ctrl_t ctrl,
append_to_strlist(&list,name);
rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
0, fpr, fpr_len, keyserver);
rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*FIXME*/
/* keyserver_work (ctrl, KS_GETNAME, list, NULL, */
/* 0, fpr, fpr_len, keyserver); */
free_strlist(list);