gpg,sm: Simplify keyserver spec parsing.

* common/keyserver.h: Remove.
* sm/gpgsm.h (struct keyserver_spec): Remove.
(opt): Change keyserver to a strlist_t.
* sm/gpgsm.c (keyserver_list_free): Remove.
(parse_keyserver_line): Remove.
(main): Store keyserver in an strlist.
* sm/call-dirmngr.c (prepare_dirmngr): Adjust for the strlist.  Avoid
an ambiguity in dirmngr by adding a prefix if needed.

* g10/options.h (struct keyserver_spec): Move definition from
keyserver.h to here.  Remove most fields.
* g10/keyserver.c (free_keyserver_spec): Adjust.
(cmp_keyserver_spec): Adjust.
(parse_keyserver_uri): Simplify.
(keyidlist): Remove fakev3 arg which does not make any sense because
we don't even support v3 keys.
--

We now rely on the dirmngr to parse the keyserver specs.  Thus a bad
specification will not be caught immediately.  However, even before
that dirmngr had stricter tests.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2021-05-26 14:28:26 +02:00
parent 72124fadaf
commit 9f586700ec
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
10 changed files with 36 additions and 480 deletions

View File

@ -60,7 +60,6 @@ common_sources = \
shareddefs.h \
openpgpdefs.h \
gc-opt-flags.h \
keyserver.h \
sexp-parse.h \
tlv.c tlv.h \
init.c init.h \

View File

@ -1,73 +0,0 @@
/* keyserver.h - Public definitions for gpg keyserver helpers.
* Copyright (C) 2001, 2002, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#ifndef GNUPG_COMMON_KEYSERVER_H
#define GNUPG_COMMON_KEYSERVER_H
#define KEYSERVER_PROTO_VERSION 1
/* These are usable for return codes for the gpgkeys_ process, and
also KEY FAILED codes. */
#define KEYSERVER_OK 0 /* not an error */
#define KEYSERVER_INTERNAL_ERROR 1 /* gpgkeys_ internal error */
#define KEYSERVER_NOT_SUPPORTED 2 /* operation not supported */
#define KEYSERVER_VERSION_ERROR 3 /* VERSION mismatch */
#define KEYSERVER_GENERAL_ERROR 4 /* keyserver internal error */
#define KEYSERVER_NO_MEMORY 5 /* out of memory */
#define KEYSERVER_KEY_NOT_FOUND 6 /* key not found */
#define KEYSERVER_KEY_EXISTS 7 /* key already exists */
#define KEYSERVER_KEY_INCOMPLETE 8 /* key incomplete (EOF) */
#define KEYSERVER_UNREACHABLE 9 /* unable to contact keyserver */
/* Must be 127 due to shell internal magic. */
#define KEYSERVER_SCHEME_NOT_FOUND 127
/* Object to hold information pertaining to a keyserver; it also
allows building a list of keyservers. Note that g10/options.h has
a typedef for this. FIXME: We should make use of the
parse_uri_t. */
struct keyserver_spec
{
struct keyserver_spec *next;
char *uri;
char *scheme;
char *auth;
char *host;
char *port;
char *path;
char *opaque;
strlist_t options;
struct
{
unsigned int direct_uri:1;
} flags;
};
#endif /*GNUPG_COMMON_KEYSERVER_H*/

View File

@ -1929,10 +1929,7 @@ keys on. The format of the @var{name} is a URI:
"hkp"/"hkps" for the HTTP (or compatible) keyservers or "ldap"/"ldaps"
for the LDAP keyservers. Note that your particular installation of
GnuPG may have other keyserver types available as well. Keyserver
schemes are case-insensitive. After the keyserver name, optional
keyserver configuration options may be provided. These are the same as
the global @option{--keyserver-options} from below, but apply only to
this particular keyserver.
schemes are case-insensitive.
Most keyservers synchronize with each other, so there is generally no
need to send keys to more than one server. The keyserver

View File

@ -36,7 +36,6 @@
#include "options.h"
#include "../common/i18n.h"
#include "../common/asshelp.h"
#include "../common/keyserver.h"
#include "../common/status.h"
#include "keyserver-internal.h"
#include "call-dirmngr.h"

View File

@ -21,7 +21,6 @@
#define _KEYSERVER_INTERNAL_H_
#include <time.h>
#include "../common/keyserver.h"
#include "../common/iobuf.h"
#include "../common/types.h"

View File

@ -186,37 +186,17 @@ 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)
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;
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 *
@ -231,40 +211,24 @@ keyserver_match(struct keyserver_spec *spec)
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. */
/* 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)
parse_keyserver_uri (const char *string, int require_scheme)
{
int assume_hkp=0;
struct keyserver_spec *keyserver;
const char *idx;
int count;
char *uri, *duped_uri, *options;
log_assert (string);
keyserver=xmalloc_clear(sizeof(struct keyserver_spec));
duped_uri = uri = xstrdup (string);
options=strchr(uri,' ');
if(options)
{
char *tok;
*options='\0';
options++;
while((tok=optsep(&options)))
warn_kshelper_option (tok, 0);
}
keyserver = xcalloc (1, sizeof *keyserver);
/* Get the scheme */
for(idx=uri,count=0;*idx && *idx!=':';idx++)
for(idx=string, count=0; *idx && *idx!=':';idx++)
{
count++;
@ -290,162 +254,21 @@ parse_keyserver_uri (const char *string,int require_scheme)
return NULL;
/* Assume HKP if there is no scheme */
assume_hkp=1;
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);
keyserver->uri = xstrconcat ("hkp://", string, NULL);
}
else
{
int i;
keyserver->uri=xstrdup(uri);
keyserver->scheme=xmalloc(count+1);
/* Force to lowercase */
for(i=0;i<count;i++)
keyserver->scheme[i]=ascii_tolower(uri[i]);
keyserver->scheme[i]='\0';
/* Skip past the scheme and colon */
uri+=count+1;
keyserver->uri = xstrdup (string);
}
if(ascii_strcasecmp(keyserver->scheme,"x-broken-hkp")==0)
{
log_info ("keyserver option '%s' is obsolete\n",
"x-broken-hkp");
}
else if(ascii_strcasecmp(keyserver->scheme,"x-hkp")==0)
{
/* Canonicalize this to "hkp" so it works with both the internal
and external keyserver interface. */
xfree(keyserver->scheme);
keyserver->scheme=xstrdup("hkp");
}
if (uri[0]=='/' && uri[1]=='/' && uri[2] == '/')
{
/* Three slashes means network path with a default host name.
This is a hack because it does not crok all possible
combinations. We should better replace all code by the parser
from http.c. */
keyserver->path = xstrdup (uri+2);
}
else if(assume_hkp || (uri[0]=='/' && uri[1]=='/'))
{
/* Two slashes means network path. */
/* Skip over the "//", if any */
if(!assume_hkp)
uri+=2;
/* Do we have userinfo auth data present? */
for(idx=uri,count=0;*idx && *idx!='@' && *idx!='/';idx++)
count++;
/* We found a @ before the slash, so that means everything
before the @ is auth data. */
if(*idx=='@')
{
if(count==0)
goto fail;
keyserver->auth=xmalloc(count+1);
strncpy(keyserver->auth,uri,count);
keyserver->auth[count]='\0';
uri+=count+1;
}
/* 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++;
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;
}
/* Everything else is the path */
if(*uri)
keyserver->path=xstrdup(uri);
else
keyserver->path=xstrdup("/");
if(keyserver->path[1])
keyserver->flags.direct_uri=1;
}
else if(uri[0]!='/')
{
/* No slash means opaque. Just record the opaque blob and get
out. */
keyserver->opaque=xstrdup(uri);
}
else
{
/* One slash means absolute path. We don't need to support that
yet. */
goto fail;
}
xfree (duped_uri);
return keyserver;
fail:
free_keyserver_spec(keyserver);
xfree (duped_uri);
return NULL;
}
struct keyserver_spec *
parse_preferred_keyserver(PKT_signature *sig)
{
@ -1225,7 +1048,7 @@ keyserver_import_keyid (ctrl_t ctrl,
/* code mostly stolen from do_export_stream */
static int
keyidlist (ctrl_t ctrl, strlist_t users, KEYDB_SEARCH_DESC **klist,
int *count, int fakev3)
int *count)
{
int rc = 0;
int num = 100;
@ -1290,28 +1113,6 @@ keyidlist (ctrl_t ctrl, strlist_t users, KEYDB_SEARCH_DESC **klist,
if((node=find_kbnode(keyblock,PKT_PUBLIC_KEY)))
{
/* This is to work around a bug in some keyservers (pksd and
OKS) that calculate v4 RSA keyids as if they were v3 RSA.
The answer is to refresh both the correct v4 keyid
(e.g. 99242560) and the fake v3 keyid (e.g. 68FDDBC7).
This only happens for key refresh using the HKP scheme
and if the refresh-add-fake-v3-keyids keyserver option is
set. */
if(fakev3 && is_RSA(node->pkt->pkt.public_key->pubkey_algo) &&
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);
(*count)++;
if(*count==num)
{
num+=100;
*klist=xrealloc(*klist,sizeof(KEYDB_SEARCH_DESC)*num);
}
}
/* 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. */
@ -1402,7 +1203,6 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users)
{
gpg_error_t err;
int count, numdesc;
int fakev3 = 0;
KEYDB_SEARCH_DESC *desc;
unsigned int options=opt.keyserver_options.import_options;
@ -1416,19 +1216,8 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users)
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. Note that this
works only with a keyserver configured. gpg.conf
(i.e. opt.keyserver); however that method of configuring a
keyserver is deprecated and in any case it is questionable
whether we should keep on supporting these ancient and broken
keyservers. */
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;
err = keyidlist (ctrl, users, &desc, &numdesc, fakev3);
err = keyidlist (ctrl, users, &desc, &numdesc);
if (err)
return err;
@ -1515,16 +1304,6 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens)
if (!tokens)
return 0; /* Return success if no patterns are given. */
/* Write global options */
/* for(temp=opt.keyserver_options.other;temp;temp=temp->next) */
/* es_fprintf(spawn->tochild,"OPTION %s\n",temp->d); */
/* Write per-keyserver options */
/* for(temp=keyserver->options;temp;temp=temp->next) */
/* es_fprintf(spawn->tochild,"OPTION %s\n",temp->d); */
{
membuf_t mb;
strlist_t item;
@ -1544,8 +1323,6 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens)
goto leave;
}
}
/* FIXME: Enable the next line */
/* log_info (_("searching for \"%s\" from %s\n"), searchstr, keyserver->uri); */
parm.ctrl = ctrl;
if (searchstr)
@ -1567,31 +1344,6 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens)
else if (err)
log_error ("error searching keyserver: %s\n", gpg_strerror (err));
/* switch(ret) */
/* { */
/* case KEYSERVER_SCHEME_NOT_FOUND: */
/* log_error(_("no handler for keyserver scheme '%s'\n"), */
/* opt.keyserver->scheme); */
/* break; */
/* case KEYSERVER_NOT_SUPPORTED: */
/* log_error(_("action '%s' not supported with keyserver " */
/* "scheme '%s'\n"), "search", opt.keyserver->scheme); */
/* break; */
/* case KEYSERVER_TIMEOUT: */
/* log_error(_("keyserver timed out\n")); */
/* break; */
/* case KEYSERVER_INTERNAL_ERROR: */
/* default: */
/* log_error(_("keyserver internal error\n")); */
/* break; */
/* } */
/* return gpg_error (GPG_ERR_KEYSERVER); */
leave:
xfree (parm.desc);
xfree (parm.searchstr_disp);
@ -1749,13 +1501,8 @@ keyserver_get_chunk (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
if (!quiet && override_keyserver)
{
if (override_keyserver->host)
log_info (_("requesting key %s from %s server %s\n"),
keystr_from_desc (&desc[idx]),
override_keyserver->scheme, override_keyserver->host);
else
log_info (_("requesting key %s from %s\n"),
keystr_from_desc (&desc[idx]), override_keyserver->uri);
log_info (_("requesting key %s from %s\n"),
keystr_from_desc (&desc[idx]), override_keyserver->uri);
}
}

View File

@ -31,9 +31,14 @@
#include "../common/compliance.h"
/* Declaration of a keyserver spec type. The definition is found in
../common/keyserver.h. */
struct keyserver_spec;
/* Object to hold information pertaining to a keyserver; it also
allows building a list of keyservers. For historic reasons this is
not a strlist_t. */
struct keyserver_spec
{
struct keyserver_spec *next;
char *uri;
};
typedef struct keyserver_spec *keyserver_spec_t;

View File

@ -198,7 +198,7 @@ warn_version_mismatch (ctrl_t ctrl, assuan_context_t ctx,
static void
prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err)
{
struct keyserver_spec *server;
strlist_t server;
if (!err)
err = warn_version_mismatch (ctrl, ctx, DIRMNGR_NAME, 0);
@ -219,12 +219,13 @@ prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err)
while (server)
{
char line[ASSUAN_LINELENGTH];
char *user = server->user ? server->user : "";
char *pass = server->pass ? server->pass : "";
char *base = server->base ? server->base : "";
snprintf (line, DIM (line), "LDAPSERVER %s:%i:%s:%s:%s",
server->host, server->port, user, pass, base);
/* If the host is "ldap" we prefix the entire line with "ldap:"
* to avoid an ambiguity on the server due to the introduction
* of this optional prefix. */
snprintf (line, DIM (line), "LDAPSERVER %s%s",
!strncmp (server->d, "ldap:", 5)? "ldap:":"",
server->d);
assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
/* The code below is not required because we don't return an error. */

View File

@ -796,99 +796,6 @@ parse_validation_model (const char *model)
}
/* Release the list of SERVERS. As usual it is okay to call this
function with SERVERS passed as NULL. */
void
keyserver_list_free (struct keyserver_spec *servers)
{
while (servers)
{
struct keyserver_spec *tmp = servers->next;
xfree (servers->host);
xfree (servers->user);
if (servers->pass)
memset (servers->pass, 0, strlen (servers->pass));
xfree (servers->pass);
xfree (servers->base);
xfree (servers);
servers = tmp;
}
}
/* See also dirmngr ldapserver_parse_one(). */
struct keyserver_spec *
parse_keyserver_line (char *line,
const char *filename, unsigned int lineno)
{
char *p;
char *endp;
struct keyserver_spec *server;
int fieldno;
int fail = 0;
/* Parse the colon separated fields. */
server = xcalloc (1, sizeof *server);
for (fieldno = 1, p = line; p; p = endp, fieldno++ )
{
endp = strchr (p, ':');
if (endp)
*endp++ = '\0';
trim_spaces (p);
switch (fieldno)
{
case 1:
if (*p)
server->host = xstrdup (p);
else
{
log_error (_("%s:%u: no hostname given\n"),
filename, lineno);
fail = 1;
}
break;
case 2:
if (*p)
server->port = atoi (p);
break;
case 3:
if (*p)
server->user = xstrdup (p);
break;
case 4:
if (*p && !server->user)
{
log_error (_("%s:%u: password given without user\n"),
filename, lineno);
fail = 1;
}
else if (*p)
server->pass = xstrdup (p);
break;
case 5:
if (*p)
server->base = xstrdup (p);
break;
default:
/* (We silently ignore extra fields.) */
break;
}
}
if (fail)
{
log_info (_("%s:%u: skipping this line\n"), filename, lineno);
keyserver_list_free (server);
server = NULL;
}
return server;
}
int
main ( int argc, char **argv)
@ -1446,21 +1353,7 @@ main ( int argc, char **argv)
case oValidationModel: parse_validation_model (pargs.r.ret_str); break;
case oKeyServer:
{
struct keyserver_spec *keyserver;
keyserver = parse_keyserver_line (pargs.r.ret_str,
configname, pargs.lineno);
if (! keyserver)
log_error (_("could not parse keyserver\n"));
else
{
/* FIXME: Keep last next pointer. */
struct keyserver_spec **next_p = &opt.keyserver;
while (*next_p)
next_p = &(*next_p)->next;
*next_p = keyserver;
}
}
append_to_strlist (&opt.keyserver, pargs.r.ret_str);
break;
case oIgnoreCertExtension:
@ -2142,7 +2035,7 @@ main ( int argc, char **argv)
}
/* cleanup */
keyserver_list_free (opt.keyserver);
free_strlist (opt.keyserver);
opt.keyserver = NULL;
gpgsm_release_certlist (recplist);
gpgsm_release_certlist (signerlist);

View File

@ -39,17 +39,6 @@
#define MAX_DIGEST_LEN 64
struct keyserver_spec
{
struct keyserver_spec *next;
char *host;
int port;
char *user;
char *pass;
char *base;
};
/* A large struct named "opt" to keep global flags. */
EXTERN_UNLESS_MAIN_MODULE
@ -141,7 +130,7 @@ struct
the integrity of the software at
runtime. */
struct keyserver_spec *keyserver;
strlist_t keyserver;
/* A list of certificate extension OIDs which are ignored so that
one can claim that a critical extension has been handled. One