mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
Merge branch 'master' into ECC-INTEGRATION-2-1
This commit is contained in:
commit
c5e8a4c0fd
10
ChangeLog
10
ChangeLog
@ -1,3 +1,13 @@
|
||||
2011-01-20 Werner Koch <wk@g10code.com>
|
||||
|
||||
* configure.ac (AC_CONFIG_FILES): Remove keyserver/.
|
||||
|
||||
2011-01-19 Werner Koch <wk@g10code.com>
|
||||
|
||||
* configure.ac: Add new option --enable-gpg2-is-gpg.
|
||||
(NAME_OF_INSTALLED_GPG): New ac_define.
|
||||
* autogen.sh [--build-w32ce]: Use --enable-gpg2-is-gpg.
|
||||
|
||||
2011-01-21 Werner Koch <wk@g10code.com>
|
||||
|
||||
* configure.ac: Need Libgcrypt 1.4.6 due to AESWRAP.
|
||||
|
@ -35,7 +35,7 @@ endif
|
||||
if BUILD_GPG
|
||||
gpg = g10
|
||||
if !HAVE_W32CE_SYSTEM
|
||||
keyserver = keyserver
|
||||
keyserver =
|
||||
endif
|
||||
else
|
||||
gpg =
|
||||
|
3
NEWS
3
NEWS
@ -17,6 +17,9 @@ Noteworthy changes in version 2.1.0beta2 (unreleased)
|
||||
* Fixed TTY management for pinentries and session variable update
|
||||
problem.
|
||||
|
||||
* Dirmngr has taken over the function of the keyserver helpers. Thus
|
||||
we now have a specified direct interface to keyservers via Dirmngr.
|
||||
|
||||
|
||||
Noteworthy changes in version 2.1.0beta1 (2010-10-26)
|
||||
-----------------------------------------------------
|
||||
|
@ -1,3 +1,7 @@
|
||||
2011-01-19 Werner Koch <wk@g10code.com>
|
||||
|
||||
* trustlist.c (read_one_trustfile): Also chop an CR.
|
||||
|
||||
2011-01-21 Werner Koch <wk@g10code.com>
|
||||
|
||||
* pksign.c (do_encode_dsa): Compare MDLEN to bytes.
|
||||
|
@ -140,7 +140,8 @@ read_one_trustfile (const char *fname, int allow_include,
|
||||
{
|
||||
lnr++;
|
||||
|
||||
if (!*line || line[strlen(line)-1] != '\n')
|
||||
n = strlen (line);
|
||||
if (!n || line[n-1] != '\n')
|
||||
{
|
||||
/* Eat until end of line. */
|
||||
while ( (c=es_getc (fp)) != EOF && c != '\n')
|
||||
@ -151,7 +152,9 @@ read_one_trustfile (const char *fname, int allow_include,
|
||||
fname, lnr, gpg_strerror (err));
|
||||
continue;
|
||||
}
|
||||
line[strlen(line)-1] = 0; /* Chop the LF. */
|
||||
line[--n] = 0; /* Chop the LF. */
|
||||
if (n && line[n-1] == '\r')
|
||||
line[--n] = 0; /* Chop an optional CR. */
|
||||
|
||||
/* Allow for empty lines and spaces */
|
||||
for (p=line; spacep (p); p++)
|
||||
|
@ -104,7 +104,8 @@ if [ "$myhost" = "w32" ]; then
|
||||
[ -z "$w32root" ] && w32root="$HOME/w32ce_root"
|
||||
toolprefixes="$w32ce_toolprefixes arm-mingw32ce"
|
||||
extraoptions="--enable-dirmngr-auto-start --disable-scdaemon "
|
||||
extraoptions="$extraoptions --disable-zip $w32ce_extraoptions"
|
||||
extraoptions="$extraoptions --disable-zip --enable-gpg2-is-gpg"
|
||||
extraoptions="$extraoptions $w32ce_extraoptions"
|
||||
;;
|
||||
*)
|
||||
[ -z "$w32root" ] && w32root="$HOME/w32root"
|
||||
|
@ -1,3 +1,55 @@
|
||||
2011-01-20 Werner Koch <wk@g10code.com>
|
||||
|
||||
Fix bug#1313.
|
||||
|
||||
* http.c (my_select): New. Define to pth_select if building with Pth.
|
||||
(start_server, write_server, cookie_read, cookie_write): Use it.
|
||||
(my_connect): New. Define to pth_connect if building with Pth.
|
||||
(connect_server): Use it.
|
||||
(my_accept): New. Define to pth_accept if building with Pth.
|
||||
(start_server): Use it.
|
||||
|
||||
2011-01-20 Werner Koch <wk@g10code.com>
|
||||
|
||||
* util.h (struct b64state): Add field LASTERR.
|
||||
* b64enc.c (enc_start, b64enc_write, b64enc_finish): Handle
|
||||
LASTERR. This is to make sure that we don't leak strduped data.
|
||||
* b64dec.c (b64dec_start, b64dec_proc, b64dec_finish): Ditto.
|
||||
|
||||
* http.c (escape_data): New.
|
||||
(insert_escapes): Implement using escape_data.
|
||||
(http_escape_data): New.
|
||||
|
||||
2011-01-19 Werner Koch <wk@g10code.com>
|
||||
|
||||
* homedir.c (gnupg_module_name): Use NAME_OF_INSTALLED_GPG instead
|
||||
of "gpg2".
|
||||
|
||||
2011-01-18 Werner Koch <wk@g10code.com>
|
||||
|
||||
* iobuf.c (file_es_filter_ctx_t): New.
|
||||
(file_es_filter): New.
|
||||
(iobuf_esopen): New.
|
||||
|
||||
* membuf.c (clear_membuf, peek_membuf): New.
|
||||
|
||||
* util.h (GPG_ERR_NO_KEYSERVER): New.
|
||||
|
||||
* keyserver.h (keyserver_spec): Move from ../g10/options.h to here.
|
||||
|
||||
* http.c (do_parse_uri): Add arg NO_SCHEME_CHECK. Change all
|
||||
callers. Support HKP and HKPS.
|
||||
(_http_parse_uri): Do proper error management.
|
||||
* http.h (parsed_uri_s): Add field IS_HTTP.
|
||||
(http_parse_uri): Support NO_SCHEME_CHECK arg.
|
||||
|
||||
* estream.c (es_func_mem_write): Fix computation of NEWSIZE.
|
||||
|
||||
2011-01-10 Werner Koch <wk@g10code.com>
|
||||
|
||||
* session-env.c (update_var): Fix same value detection. Fixes
|
||||
bug#1311.
|
||||
|
||||
2011-01-10 Werner Koch <wk@g10code.com>
|
||||
|
||||
* session-env.c (update_var): Fix same value detection. Fixes
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* b64dec.c - Simple Base64 decoder.
|
||||
* Copyright (C) 2008 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2008, 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -72,16 +72,19 @@ b64dec_start (struct b64state *state, const char *title)
|
||||
if (title)
|
||||
{
|
||||
if (!strncmp (title, "PGP", 3) && (!title[3] || title[3] == ' '))
|
||||
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
|
||||
|
||||
state->lasterr = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
|
||||
else
|
||||
{
|
||||
state->title = xtrystrdup (title);
|
||||
if (!state->title)
|
||||
return gpg_error_from_syserror ();
|
||||
state->lasterr = gpg_error_from_syserror ();
|
||||
else
|
||||
state->idx = s_init;
|
||||
}
|
||||
}
|
||||
else
|
||||
state->idx = s_b64_0;
|
||||
return 0;
|
||||
return state->lasterr;
|
||||
}
|
||||
|
||||
|
||||
@ -96,10 +99,16 @@ b64dec_proc (struct b64state *state, void *buffer, size_t length,
|
||||
int pos = state->quad_count;
|
||||
char *d, *s;
|
||||
|
||||
if (state->lasterr)
|
||||
return state->lasterr;
|
||||
|
||||
if (state->stop_seen)
|
||||
{
|
||||
*r_nbytes = 0;
|
||||
return gpg_error (GPG_ERR_EOF);
|
||||
state->lasterr = gpg_error (GPG_ERR_EOF);
|
||||
xfree (state->title);
|
||||
state->title = NULL;
|
||||
return state->lasterr;
|
||||
}
|
||||
|
||||
for (s=d=buffer; length && !state->stop_seen; length--, s++)
|
||||
@ -210,6 +219,9 @@ b64dec_proc (struct b64state *state, void *buffer, size_t length,
|
||||
gpg_error_t
|
||||
b64dec_finish (struct b64state *state)
|
||||
{
|
||||
if (state->lasterr)
|
||||
return state->lasterr;
|
||||
|
||||
xfree (state->title);
|
||||
state->title = NULL;
|
||||
return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0;
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* b64enc.c - Simple Base64 encoder.
|
||||
* Copyright (C) 2001, 2003, 2004, 2008, 2010 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2001, 2003, 2004, 2008, 2010,
|
||||
* 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -143,6 +144,7 @@ enc_start (struct b64state *state, FILE *fp, estream_t stream,
|
||||
memset (state, 0, sizeof *state);
|
||||
state->fp = fp;
|
||||
state->stream = stream;
|
||||
state->lasterr = 0;
|
||||
if (title && !*title)
|
||||
state->flags |= B64ENC_NO_LINEFEEDS;
|
||||
else if (title)
|
||||
@ -154,9 +156,9 @@ enc_start (struct b64state *state, FILE *fp, estream_t stream,
|
||||
}
|
||||
state->title = xtrystrdup (title);
|
||||
if (!state->title)
|
||||
return gpg_error_from_syserror ();
|
||||
state->lasterr = gpg_error_from_syserror ();
|
||||
}
|
||||
return 0;
|
||||
return state->lasterr;
|
||||
}
|
||||
|
||||
|
||||
@ -203,6 +205,8 @@ b64enc_write (struct b64state *state, const void *buffer, size_t nbytes)
|
||||
int idx, quad_count;
|
||||
const unsigned char *p;
|
||||
|
||||
if (state->lasterr)
|
||||
return state->lasterr;
|
||||
|
||||
if (!nbytes)
|
||||
{
|
||||
@ -285,7 +289,13 @@ b64enc_write (struct b64state *state, const void *buffer, size_t nbytes)
|
||||
return 0;
|
||||
|
||||
write_error:
|
||||
return gpg_error_from_syserror ();
|
||||
state->lasterr = gpg_error_from_syserror ();
|
||||
if (state->title)
|
||||
{
|
||||
xfree (state->title);
|
||||
state->title = NULL;
|
||||
}
|
||||
return state->lasterr;
|
||||
}
|
||||
|
||||
|
||||
@ -297,6 +307,9 @@ b64enc_finish (struct b64state *state)
|
||||
int idx, quad_count;
|
||||
char tmp[4];
|
||||
|
||||
if (state->lasterr)
|
||||
return state->lasterr;
|
||||
|
||||
if (!(state->flags & B64ENC_DID_HEADER))
|
||||
goto cleanup;
|
||||
|
||||
@ -404,6 +417,7 @@ b64enc_finish (struct b64state *state)
|
||||
}
|
||||
state->fp = NULL;
|
||||
state->stream = NULL;
|
||||
state->lasterr = err;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -641,7 +641,7 @@ es_func_mem_write (void *cookie, const void *buffer, size_t size)
|
||||
if (!mem_cookie->memory_size)
|
||||
newsize = size; /* Not yet allocated. */
|
||||
else
|
||||
newsize = mem_cookie->memory_size + (nleft - size);
|
||||
newsize = mem_cookie->memory_size + (size - nleft);
|
||||
if (newsize < mem_cookie->offset)
|
||||
{
|
||||
_set_errno (EINVAL);
|
||||
|
@ -528,7 +528,7 @@ gnupg_module_name (int which)
|
||||
X(bindir, "gpgsm");
|
||||
|
||||
case GNUPG_MODULE_NAME_GPG:
|
||||
X(bindir, "gpg2");
|
||||
X(bindir, NAME_OF_INSTALLED_GPG);
|
||||
|
||||
case GNUPG_MODULE_NAME_CONNECT_AGENT:
|
||||
X(bindir, "gpg-connect-agent");
|
||||
|
115
common/http.c
115
common/http.c
@ -1,6 +1,6 @@
|
||||
/* http.c - HTTP protocol handler
|
||||
* Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006,
|
||||
* 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006, 2009, 2010,
|
||||
* 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -105,6 +105,16 @@ struct srventry
|
||||
#endif/*!USE_DNS_SRV*/
|
||||
|
||||
|
||||
#ifdef HAVE_PTH
|
||||
# define my_select(a,b,c,d,e) pth_select ((a), (b), (c), (d), (e))
|
||||
# define my_connect(a,b,c) pth_connect ((a), (b), (c))
|
||||
# define my_accept(a,b,c) pth_accept ((a), (b), (c))
|
||||
#else
|
||||
# define my_select(a,b,c,d,e) select ((a), (b), (c), (d), (e))
|
||||
# define my_connect(a,b,c) connect ((a), (b), (c))
|
||||
# define my_accept(a,b,c) accept ((a), (b), (c))
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
#define sock_close(a) closesocket(a)
|
||||
#else
|
||||
@ -138,7 +148,8 @@ typedef unsigned long longcounter_t;
|
||||
typedef void * gnutls_session_t;
|
||||
#endif
|
||||
|
||||
static gpg_err_code_t do_parse_uri (parsed_uri_t uri, int only_local_part);
|
||||
static gpg_err_code_t do_parse_uri (parsed_uri_t uri, int only_local_part,
|
||||
int no_scheme_check);
|
||||
static int remove_escapes (char *string);
|
||||
static int insert_escapes (char *buffer, const char *string,
|
||||
const char *special);
|
||||
@ -356,7 +367,7 @@ _http_open (http_t *r_hd, http_req_t reqtype, const char *url,
|
||||
hd->flags = flags;
|
||||
hd->tls_context = tls_context;
|
||||
|
||||
err = _http_parse_uri (&hd->uri, url, errsource);
|
||||
err = _http_parse_uri (&hd->uri, url, 0, errsource);
|
||||
if (!err)
|
||||
err = send_request (hd, auth, proxy, srvtag, headers, errsource);
|
||||
|
||||
@ -368,7 +379,6 @@ _http_open (http_t *r_hd, http_req_t reqtype, const char *url,
|
||||
es_fclose (hd->fp_read);
|
||||
if (hd->fp_write)
|
||||
es_fclose (hd->fp_write);
|
||||
http_release_parsed_uri (hd->uri);
|
||||
xfree (hd);
|
||||
}
|
||||
else
|
||||
@ -511,18 +521,27 @@ http_get_status_code (http_t hd)
|
||||
|
||||
/*
|
||||
* Parse an URI and put the result into the newly allocated RET_URI.
|
||||
* The caller must always use release_parsed_uri() to releases the
|
||||
* resources (even on error).
|
||||
* On success the caller must use release_parsed_uri() to releases the
|
||||
* resources. If NO_SCHEME_CHECK is set, the function tries to parse
|
||||
* the URL in the same way it would do for an HTTP style URI.
|
||||
*/
|
||||
gpg_error_t
|
||||
_http_parse_uri (parsed_uri_t *ret_uri, const char *uri,
|
||||
gpg_err_source_t errsource)
|
||||
int no_scheme_check, gpg_err_source_t errsource)
|
||||
{
|
||||
gpg_err_code_t ec;
|
||||
|
||||
*ret_uri = xtrycalloc (1, sizeof **ret_uri + strlen (uri));
|
||||
if (!*ret_uri)
|
||||
return gpg_err_make (errsource, gpg_err_code_from_syserror ());
|
||||
strcpy ((*ret_uri)->buffer, uri);
|
||||
return gpg_err_make (errsource, do_parse_uri (*ret_uri, 0));
|
||||
ec = do_parse_uri (*ret_uri, 0, no_scheme_check);
|
||||
if (ec)
|
||||
{
|
||||
xfree (*ret_uri);
|
||||
*ret_uri = NULL;
|
||||
}
|
||||
return gpg_err_make (errsource, ec);
|
||||
}
|
||||
|
||||
void
|
||||
@ -543,7 +562,7 @@ http_release_parsed_uri (parsed_uri_t uri)
|
||||
|
||||
|
||||
static gpg_err_code_t
|
||||
do_parse_uri (parsed_uri_t uri, int only_local_part)
|
||||
do_parse_uri (parsed_uri_t uri, int only_local_part, int no_scheme_check)
|
||||
{
|
||||
uri_tuple_t *tail;
|
||||
char *p, *p2, *p3, *pp;
|
||||
@ -557,6 +576,7 @@ do_parse_uri (parsed_uri_t uri, int only_local_part)
|
||||
uri->port = 0;
|
||||
uri->params = uri->query = NULL;
|
||||
uri->use_tls = 0;
|
||||
uri->is_http = 0;
|
||||
|
||||
/* A quick validity check. */
|
||||
if (strspn (p, VALID_URI_CHARS) != n)
|
||||
@ -572,15 +592,24 @@ do_parse_uri (parsed_uri_t uri, int only_local_part)
|
||||
*pp = tolower (*(unsigned char*)pp);
|
||||
uri->scheme = p;
|
||||
if (!strcmp (uri->scheme, "http"))
|
||||
{
|
||||
uri->port = 80;
|
||||
uri->is_http = 1;
|
||||
}
|
||||
else if (!strcmp (uri->scheme, "hkp"))
|
||||
{
|
||||
uri->port = 11371;
|
||||
uri->is_http = 1;
|
||||
}
|
||||
#ifdef HTTP_USE_GNUTLS
|
||||
else if (!strcmp (uri->scheme, "https"))
|
||||
else if (!strcmp (uri->scheme, "https") || !strcmp (uri->scheme,"hkps"))
|
||||
{
|
||||
uri->port = 443;
|
||||
uri->is_http = 1;
|
||||
uri->use_tls = 1;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
else if (!no_scheme_check)
|
||||
return GPG_ERR_INV_URI; /* Unsupported scheme */
|
||||
|
||||
p = p2;
|
||||
@ -723,14 +752,14 @@ remove_escapes (char *string)
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
insert_escapes (char *buffer, const char *string,
|
||||
static size_t
|
||||
escape_data (char *buffer, const void *data, size_t datalen,
|
||||
const char *special)
|
||||
{
|
||||
const unsigned char *s = (const unsigned char*)string;
|
||||
int n = 0;
|
||||
const unsigned char *s;
|
||||
size_t n = 0;
|
||||
|
||||
for (; *s; s++)
|
||||
for (s = data; datalen; s++, datalen--)
|
||||
{
|
||||
if (strchr (VALID_URI_CHARS, *s) && !strchr (special, *s))
|
||||
{
|
||||
@ -752,6 +781,14 @@ insert_escapes (char *buffer, const char *string,
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
insert_escapes (char *buffer, const char *string,
|
||||
const char *special)
|
||||
{
|
||||
return escape_data (buffer, string, strlen (string), special);
|
||||
}
|
||||
|
||||
|
||||
/* Allocate a new string from STRING using standard HTTP escaping as
|
||||
well as escaping of characters given in SPECIALS. A common pattern
|
||||
for SPECIALS is "%;?&=". However it depends on the needs, for
|
||||
@ -773,6 +810,27 @@ http_escape_string (const char *string, const char *specials)
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Allocate a new string from {DATA,DATALEN} using standard HTTP
|
||||
escaping as well as escaping of characters given in SPECIALS. A
|
||||
common pattern for SPECIALS is "%;?&=". However it depends on the
|
||||
needs, for example "+" and "/: often needs to be escaped too.
|
||||
Returns NULL on failure and sets ERRNO. */
|
||||
char *
|
||||
http_escape_data (const void *data, size_t datalen, const char *specials)
|
||||
{
|
||||
int n;
|
||||
char *buf;
|
||||
|
||||
n = escape_data (NULL, data, datalen, specials);
|
||||
buf = xtrymalloc (n+1);
|
||||
if (buf)
|
||||
{
|
||||
escape_data (buf, data, datalen, specials);
|
||||
buf[n] = 0;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static uri_tuple_t
|
||||
@ -852,12 +910,11 @@ send_request (http_t hd, const char *auth,
|
||||
if (proxy)
|
||||
http_proxy = proxy;
|
||||
|
||||
err = _http_parse_uri (&uri, http_proxy, errsource);
|
||||
err = _http_parse_uri (&uri, http_proxy, 0, errsource);
|
||||
if (err)
|
||||
{
|
||||
log_error ("invalid HTTP proxy (%s): %s\n",
|
||||
http_proxy, gpg_strerror (err));
|
||||
http_release_parsed_uri (uri);
|
||||
return gpg_err_make (errsource, GPG_ERR_CONFIGURATION);
|
||||
}
|
||||
|
||||
@ -1374,14 +1431,14 @@ start_server ()
|
||||
FD_ZERO (&rfds);
|
||||
FD_SET (fd, &rfds);
|
||||
|
||||
if (select (fd + 1, &rfds, NULL, NULL, NULL) <= 0)
|
||||
if (my_select (fd + 1, &rfds, NULL, NULL, NULL) <= 0)
|
||||
continue; /* ignore any errors */
|
||||
|
||||
if (!FD_ISSET (fd, &rfds))
|
||||
continue;
|
||||
|
||||
addrlen = sizeof peer;
|
||||
client = accept (fd, (struct sockaddr *) &peer, &addrlen);
|
||||
client = my_accept (fd, (struct sockaddr *) &peer, &addrlen);
|
||||
if (client == -1)
|
||||
continue; /* oops */
|
||||
|
||||
@ -1451,7 +1508,7 @@ connect_server (const char *server, unsigned short port,
|
||||
addr.sin_port = htons(port);
|
||||
memcpy (&addr.sin_addr,&inaddr,sizeof(inaddr));
|
||||
|
||||
if (!connect (sock,(struct sockaddr *)&addr,sizeof(addr)) )
|
||||
if (!my_connect (sock,(struct sockaddr *)&addr,sizeof(addr)) )
|
||||
return sock;
|
||||
sock_close(sock);
|
||||
return -1;
|
||||
@ -1519,7 +1576,7 @@ connect_server (const char *server, unsigned short port,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (connect (sock, ai->ai_addr, ai->ai_addrlen))
|
||||
if (my_connect (sock, ai->ai_addr, ai->ai_addrlen))
|
||||
last_errno = errno;
|
||||
else
|
||||
connected = 1;
|
||||
@ -1573,7 +1630,7 @@ connect_server (const char *server, unsigned short port,
|
||||
for (i = 0; host->h_addr_list[i] && !connected; i++)
|
||||
{
|
||||
memcpy (&addr.sin_addr, host->h_addr_list[i], host->h_length);
|
||||
if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)))
|
||||
if (my_connect (sock, (struct sockaddr *) &addr, sizeof (addr)))
|
||||
last_errno = errno;
|
||||
else
|
||||
{
|
||||
@ -1613,7 +1670,6 @@ write_server (int sock, const char *data, size_t length)
|
||||
int nleft;
|
||||
int nwritten;
|
||||
|
||||
/* FIXME: We would better use pth I/O functions. */
|
||||
nleft = length;
|
||||
while (nleft > 0)
|
||||
{
|
||||
@ -1640,7 +1696,7 @@ write_server (int sock, const char *data, size_t length)
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 50000;
|
||||
select (0, NULL, NULL, NULL, &tv);
|
||||
my_select (0, NULL, NULL, NULL, &tv);
|
||||
continue;
|
||||
}
|
||||
log_info ("network write failed: %s\n", strerror (errno));
|
||||
@ -1686,7 +1742,7 @@ cookie_read (void *cookie, void *buffer, size_t size)
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 50000;
|
||||
select (0, NULL, NULL, NULL, &tv);
|
||||
my_select (0, NULL, NULL, NULL, &tv);
|
||||
goto again;
|
||||
}
|
||||
if (nread == GNUTLS_E_REHANDSHAKE)
|
||||
@ -1748,7 +1804,7 @@ cookie_write (void *cookie, const void *buffer, size_t size)
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 50000;
|
||||
select (0, NULL, NULL, NULL, &tv);
|
||||
my_select (0, NULL, NULL, NULL, &tv);
|
||||
continue;
|
||||
}
|
||||
log_info ("TLS network write failed: %s\n",
|
||||
@ -1882,11 +1938,10 @@ main (int argc, char **argv)
|
||||
http_register_tls_callback (verify_callback);
|
||||
#endif /*HTTP_USE_GNUTLS*/
|
||||
|
||||
rc = http_parse_uri (&uri, *argv);
|
||||
rc = http_parse_uri (&uri, *argv, 0);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("`%s': %s\n", *argv, gpg_strerror (rc));
|
||||
http_release_parsed_uri (uri);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,8 @@
|
||||
#include <gpg-error.h>
|
||||
#include "../common/estream.h"
|
||||
|
||||
struct uri_tuple_s {
|
||||
struct uri_tuple_s
|
||||
{
|
||||
struct uri_tuple_s *next;
|
||||
const char *name; /* A pointer into name. */
|
||||
char *value; /* A pointer to value (a Nul is always appended). */
|
||||
@ -36,8 +37,9 @@ typedef struct uri_tuple_s *uri_tuple_t;
|
||||
struct parsed_uri_s
|
||||
{
|
||||
/* All these pointers point into BUFFER; most stuff is not escaped. */
|
||||
char *scheme; /* Pointer to the scheme string (lowercase). */
|
||||
int use_tls; /* Whether TLS should be used. */
|
||||
char *scheme; /* Pointer to the scheme string (always lowercase). */
|
||||
unsigned int is_http:1; /* This is a HTTP style URI. */
|
||||
unsigned int use_tls:1; /* Whether TLS should be used. */
|
||||
char *auth; /* username/password for basic auth */
|
||||
char *host; /* Host (converted to lowercase). */
|
||||
unsigned short port; /* Port (always set if the host is set). */
|
||||
@ -71,9 +73,9 @@ typedef struct http_context_s *http_t;
|
||||
void http_register_tls_callback (gpg_error_t (*cb) (http_t, void *, int));
|
||||
|
||||
gpg_error_t _http_parse_uri (parsed_uri_t *ret_uri, const char *uri,
|
||||
gpg_err_source_t errsource);
|
||||
#define http_parse_uri(a,b) \
|
||||
_http_parse_uri ((a), (b), GPG_ERR_SOURCE_DEFAULT)
|
||||
int no_scheme_check, gpg_err_source_t errsource);
|
||||
#define http_parse_uri(a,b,c) \
|
||||
_http_parse_uri ((a), (b), (c), GPG_ERR_SOURCE_DEFAULT)
|
||||
|
||||
void http_release_parsed_uri (parsed_uri_t uri);
|
||||
|
||||
@ -115,6 +117,7 @@ unsigned int http_get_status_code (http_t hd);
|
||||
const char *http_get_header (http_t hd, const char *name);
|
||||
|
||||
char *http_escape_string (const char *string, const char *specials);
|
||||
char *http_escape_data (const void *data, size_t datalen, const char *specials);
|
||||
|
||||
|
||||
#endif /*GNUPG_COMMON_HTTP_H*/
|
||||
|
127
common/iobuf.c
127
common/iobuf.c
@ -1,6 +1,6 @@
|
||||
/* iobuf.c - File Handling for OpenPGP.
|
||||
* Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008,
|
||||
* 2009, 2010 Free Software Foundation, Inc.
|
||||
* 2009, 2010, 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -78,6 +78,17 @@ typedef struct
|
||||
char fname[1]; /* Name of the file. */
|
||||
} file_filter_ctx_t;
|
||||
|
||||
/* The context used by the estream filter. */
|
||||
typedef struct
|
||||
{
|
||||
estream_t fp; /* Open estream handle. */
|
||||
int keep_open;
|
||||
int no_cache;
|
||||
int eof_seen;
|
||||
int print_only_name; /* Flags indicating that fname is not a real file. */
|
||||
char fname[1]; /* Name of the file. */
|
||||
} file_es_filter_ctx_t;
|
||||
|
||||
|
||||
/* Object to control the "close cache". */
|
||||
struct close_cache_s
|
||||
@ -577,6 +588,96 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf,
|
||||
}
|
||||
|
||||
|
||||
/* Similar to file_filter but using the estream system. */
|
||||
static int
|
||||
file_es_filter (void *opaque, int control, iobuf_t chain, byte * buf,
|
||||
size_t * ret_len)
|
||||
{
|
||||
file_es_filter_ctx_t *a = opaque;
|
||||
estream_t f = a->fp;
|
||||
size_t size = *ret_len;
|
||||
size_t nbytes = 0;
|
||||
int rc = 0;
|
||||
|
||||
(void)chain; /* Not used. */
|
||||
|
||||
if (control == IOBUFCTRL_UNDERFLOW)
|
||||
{
|
||||
assert (size); /* We need a buffer. */
|
||||
if (a->eof_seen)
|
||||
{
|
||||
rc = -1;
|
||||
*ret_len = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nbytes = 0;
|
||||
rc = es_read (f, buf, size, &nbytes);
|
||||
if (rc == -1)
|
||||
{ /* error */
|
||||
rc = gpg_error_from_syserror ();
|
||||
log_error ("%s: read error: %s\n", a->fname, strerror (errno));
|
||||
}
|
||||
else if (!nbytes)
|
||||
{ /* eof */
|
||||
a->eof_seen = 1;
|
||||
rc = -1;
|
||||
}
|
||||
*ret_len = nbytes;
|
||||
}
|
||||
}
|
||||
else if (control == IOBUFCTRL_FLUSH)
|
||||
{
|
||||
if (size)
|
||||
{
|
||||
byte *p = buf;
|
||||
size_t nwritten;
|
||||
|
||||
nbytes = size;
|
||||
do
|
||||
{
|
||||
nwritten = 0;
|
||||
if (es_write (f, p, nbytes, &nwritten))
|
||||
{
|
||||
rc = gpg_error_from_syserror ();
|
||||
log_error ("%s: write error: %s\n",
|
||||
a->fname, strerror (errno));
|
||||
break;
|
||||
}
|
||||
p += nwritten;
|
||||
nbytes -= nwritten;
|
||||
}
|
||||
while (nbytes);
|
||||
nbytes = p - buf;
|
||||
}
|
||||
*ret_len = nbytes;
|
||||
}
|
||||
else if (control == IOBUFCTRL_INIT)
|
||||
{
|
||||
a->eof_seen = 0;
|
||||
a->no_cache = 0;
|
||||
}
|
||||
else if (control == IOBUFCTRL_DESC)
|
||||
{
|
||||
*(char **) buf = "estream_filter";
|
||||
}
|
||||
else if (control == IOBUFCTRL_FREE)
|
||||
{
|
||||
if (f != es_stdin && f != es_stdout)
|
||||
{
|
||||
if (DBG_IOBUF)
|
||||
log_debug ("%s: es_fclose %p\n", a->fname, f);
|
||||
if (!a->keep_open)
|
||||
es_fclose (f);
|
||||
}
|
||||
f = NULL;
|
||||
xfree (a); /* We can free our context now. */
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
/* Because network sockets are special objects under Lose32 we have to
|
||||
use a dedicated filter for them. */
|
||||
@ -1257,6 +1358,30 @@ iobuf_fdopen_nc (int fd, const char *mode)
|
||||
}
|
||||
|
||||
|
||||
iobuf_t
|
||||
iobuf_esopen (estream_t estream, const char *mode, int keep_open)
|
||||
{
|
||||
iobuf_t a;
|
||||
file_es_filter_ctx_t *fcx;
|
||||
size_t len;
|
||||
|
||||
a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, IOBUF_BUFFER_SIZE);
|
||||
fcx = xtrymalloc (sizeof *fcx + 30);
|
||||
fcx->fp = estream;
|
||||
fcx->print_only_name = 1;
|
||||
fcx->keep_open = keep_open;
|
||||
sprintf (fcx->fname, "[fd %p]", estream);
|
||||
a->filter = file_es_filter;
|
||||
a->filter_ov = fcx;
|
||||
file_es_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
|
||||
file_es_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
|
||||
if (DBG_IOBUF)
|
||||
log_debug ("iobuf-%d.%d: esopen%s `%s'\n",
|
||||
a->no, a->subno, keep_open? "_nc":"", fcx->fname);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
iobuf_t
|
||||
iobuf_sockopen (int fd, const char *mode)
|
||||
{
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "../include/types.h" /* fixme: should be moved elsewhere. */
|
||||
#include "../common/sysutils.h"
|
||||
#include "../common/estream.h"
|
||||
|
||||
#define DBG_IOBUF iobuf_debug_mode
|
||||
|
||||
@ -102,6 +103,7 @@ iobuf_t iobuf_open_fd_or_name (gnupg_fd_t fd, const char *fname,
|
||||
iobuf_t iobuf_open (const char *fname);
|
||||
iobuf_t iobuf_fdopen (int fd, const char *mode);
|
||||
iobuf_t iobuf_fdopen_nc (int fd, const char *mode);
|
||||
iobuf_t iobuf_esopen (estream_t estream, const char *mode, int keep_open);
|
||||
iobuf_t iobuf_sockopen (int fd, const char *mode);
|
||||
iobuf_t iobuf_create (const char *fname);
|
||||
iobuf_t iobuf_append (const char *fname);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* keyserver.h - Public definitions for gpg keyserver helpers.
|
||||
* Copyright (C) 2001, 2002 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2001, 2002, 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -39,4 +39,26 @@
|
||||
/* Must be 127 due to shell internal magic. */
|
||||
#define KEYSERVER_SCHEME_NOT_FOUND 127
|
||||
|
||||
/* Object to hold information pertaining to a keyserver; it further
|
||||
allows to build 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*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* membuf.c - A simple implementation of a dynamic buffer.
|
||||
* Copyright (C) 2001, 2003, 2009 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2001, 2003, 2009, 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -56,6 +56,26 @@ init_membuf_secure (membuf_t *mb, int initiallen)
|
||||
}
|
||||
|
||||
|
||||
/* Shift the the content of the membuf MB by AMOUNT bytes. The next
|
||||
operation will then behave as if AMOUNT bytes had not been put into
|
||||
the buffer. If AMOUNT is greater than the actual accumulated
|
||||
bytes, the membuf is basically reset to its initial state. */
|
||||
void
|
||||
clear_membuf (membuf_t *mb, size_t amount)
|
||||
{
|
||||
/* No need to clear if we are already out of core. */
|
||||
if (mb->out_of_core)
|
||||
return;
|
||||
if (amount >= mb->len)
|
||||
mb->len = 0;
|
||||
else
|
||||
{
|
||||
mb->len -= amount;
|
||||
memmove (mb->buf, mb->buf+amount, mb->len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
put_membuf (membuf_t *mb, const void *buf, size_t len)
|
||||
{
|
||||
@ -116,3 +136,26 @@ get_membuf (membuf_t *mb, size_t *len)
|
||||
mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/* Peek at the membuf MB. On success a pointer to the buffer is
|
||||
returned which is valid until the next operation on MB. If LEN is
|
||||
not NULL the current LEN of the buffer is stored there. On error
|
||||
NULL is returned and ERRNO is set. */
|
||||
const void *
|
||||
peek_membuf (membuf_t *mb, size_t *len)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
if (mb->out_of_core)
|
||||
{
|
||||
gpg_err_set_errno (mb->out_of_core);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = mb->buf;
|
||||
if (len)
|
||||
*len = mb->len;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -39,9 +39,10 @@ typedef struct private_membuf_s membuf_t;
|
||||
|
||||
void init_membuf (membuf_t *mb, int initiallen);
|
||||
void init_membuf_secure (membuf_t *mb, int initiallen);
|
||||
void clear_membuf (membuf_t *mb, size_t amount);
|
||||
void put_membuf (membuf_t *mb, const void *buf, size_t len);
|
||||
void put_membuf_str (membuf_t *mb, const char *string);
|
||||
void *get_membuf (membuf_t *mb, size_t *len);
|
||||
|
||||
const void *peek_membuf (membuf_t *mb, size_t *len);
|
||||
|
||||
#endif /*GNUPG_COMMON_MEMBUF_H*/
|
||||
|
@ -36,6 +36,9 @@
|
||||
#ifndef GPG_ERR_MISSING_ISSUER_CERT
|
||||
#define GPG_ERR_MISSING_ISSUER_CERT 185
|
||||
#endif
|
||||
#ifndef GPG_ERR_NO_KEYSERVER
|
||||
#define GPG_ERR_NO_KEYSERVER 186
|
||||
#endif
|
||||
#ifndef GPG_ERR_FULLY_CANCELED
|
||||
#define GPG_ERR_FULLY_CANCELED 198
|
||||
#endif
|
||||
@ -147,6 +150,7 @@ struct b64state
|
||||
u32 crc;
|
||||
int stop_seen:1;
|
||||
int invalid_encoding:1;
|
||||
gpg_error_t lasterr;
|
||||
};
|
||||
|
||||
gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title);
|
||||
|
26
configure.ac
26
configure.ac
@ -168,6 +168,24 @@ show_gnupg_dirmngr_ldap_pgm="(default)"
|
||||
test -n "$GNUPG_DIRMNGR_LDAP_PGM" \
|
||||
&& show_gnupg_dirmngr_ldap_pgm="$GNUPG_DIRMNGR_LDAP_PGM"
|
||||
|
||||
#
|
||||
# On some platforms gpg2 is usually installed as gpg without using a
|
||||
# symlink. For correct operation of gpgconf it needs to know the
|
||||
# installed name of gpg. This option sets "gpg2"'s installed name to
|
||||
# just "gpg". Note that it might be required to rename gpg2 to gpg
|
||||
# manually after the build process.
|
||||
#
|
||||
AC_ARG_ENABLE(gpg2-is-gpg,
|
||||
AC_HELP_STRING([--enable-gpg2-is-gpg],[Set installed name of gpg2 to gpg]),
|
||||
gpg2_is_gpg=$enableval)
|
||||
if test "$gpg2_is_gpg" = "yes"; then
|
||||
name_of_installed_gpg=gpg
|
||||
else
|
||||
name_of_installed_gpg=gpg2
|
||||
fi
|
||||
AC_DEFINE_UNQUOTED(NAME_OF_INSTALLED_GPG, "$name_of_installed_gpg",
|
||||
[The name of the installed GPG tool])
|
||||
|
||||
|
||||
# Some folks want to use only the agent from this packet. Make it
|
||||
# easier for them by providing the configure option
|
||||
@ -1679,9 +1697,6 @@ agent/Makefile
|
||||
scd/Makefile
|
||||
g13/Makefile
|
||||
dirmngr/Makefile
|
||||
keyserver/Makefile
|
||||
keyserver/gpg2keys_mailto
|
||||
keyserver/gpg2keys_test
|
||||
tools/gpg-zip
|
||||
tools/Makefile
|
||||
doc/Makefile
|
||||
@ -1689,6 +1704,11 @@ tests/Makefile
|
||||
tests/openpgp/Makefile
|
||||
tests/pkits/Makefile
|
||||
])
|
||||
#keyserver/Makefile
|
||||
#keyserver/gpg2keys_mailto
|
||||
#keyserver/gpg2keys_test
|
||||
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
|
||||
|
@ -1,3 +1,20 @@
|
||||
2011-01-20 Werner Koch <wk@g10code.com>
|
||||
|
||||
* server.c (release_ctrl_keyservers): New.
|
||||
(cmd_keyserver, cmd_ks_seach, cmd_ks_get, cmd_ks_put): New.
|
||||
* dirmngr.h (uri_item_t): New.
|
||||
(struct server_control_s): Add field KEYSERVERS.
|
||||
* ks-engine-hkp.c: New.
|
||||
* ks-engine.h: New.
|
||||
* ks-action.c, ks-action.h: New.
|
||||
* server.c: Include ks-action.h.
|
||||
(cmd_ks_search): New.
|
||||
* Makefile.am (dirmngr_SOURCES): Add new files.
|
||||
|
||||
2011-01-19 Werner Koch <wk@g10code.com>
|
||||
|
||||
* dirmngr.c (main): Use es_printf for --gpgconf-list.
|
||||
|
||||
2010-12-14 Werner Koch <wk@g10code.com>
|
||||
|
||||
* cdb.h (struct cdb) [W32]: Add field CDB_MAPPING.
|
||||
@ -1488,7 +1505,8 @@
|
||||
[Update after merge with GnuPG: see ./ChangeLog.1]
|
||||
|
||||
|
||||
Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010 g10 Code GmbH
|
||||
Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software; as a special exception the author gives
|
||||
unlimited permission to copy and/or distribute it, with or without
|
||||
|
@ -49,7 +49,8 @@ noinst_HEADERS = dirmngr.h crlcache.h crlfetch.h misc.h
|
||||
dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \
|
||||
ldapserver.h ldapserver.c certcache.c certcache.h \
|
||||
cdb.h cdblib.c ldap.c misc.c dirmngr-err.h w32-ldap-help.h \
|
||||
ocsp.c ocsp.h validate.c validate.h ldap-wrapper.h $(ldap_url)
|
||||
ocsp.c ocsp.h validate.c validate.h ldap-wrapper.h $(ldap_url) \
|
||||
ks-action.c ks-action.h ks-engine.h ks-engine-hkp.c
|
||||
|
||||
if USE_LDAPWRAPPER
|
||||
dirmngr_SOURCES += ldap-wrapper.c
|
||||
|
@ -160,7 +160,7 @@ crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader)
|
||||
*reader = NULL;
|
||||
|
||||
once_more:
|
||||
err = http_parse_uri (&uri, url);
|
||||
err = http_parse_uri (&uri, url, 0);
|
||||
http_release_parsed_uri (uri);
|
||||
if (err && url && !strncmp (url, "https:", 6))
|
||||
{
|
||||
@ -172,7 +172,7 @@ crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader)
|
||||
if (free_this)
|
||||
{
|
||||
strcpy (stpcpy (free_this,"http:"), url+6);
|
||||
err = http_parse_uri (&uri, free_this);
|
||||
err = http_parse_uri (&uri, free_this, 0);
|
||||
http_release_parsed_uri (uri);
|
||||
if (!err)
|
||||
{
|
||||
|
@ -1019,7 +1019,7 @@ main (int argc, char **argv)
|
||||
start of the dirmngr. */
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
pid = getpid ();
|
||||
printf ("set DIRMNGR_INFO=%s;%lu;1\n", socket_name, (ulong) pid);
|
||||
es_printf ("set DIRMNGR_INFO=%s;%lu;1\n", socket_name, (ulong) pid);
|
||||
#else
|
||||
pid = pth_fork ();
|
||||
if (pid == (pid_t)-1)
|
||||
@ -1051,11 +1051,11 @@ main (int argc, char **argv)
|
||||
if (csh_style)
|
||||
{
|
||||
*strchr (infostr, '=') = ' ';
|
||||
printf ( "setenv %s\n", infostr);
|
||||
es_printf ( "setenv %s\n", infostr);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ( "%s; export DIRMNGR_INFO;\n", infostr);
|
||||
es_printf ( "%s; export DIRMNGR_INFO;\n", infostr);
|
||||
}
|
||||
free (infostr);
|
||||
exit (0);
|
||||
@ -1220,15 +1220,15 @@ main (int argc, char **argv)
|
||||
"dirmngr.conf", NULL );
|
||||
|
||||
filename = percent_escape (opt.config_filename, NULL);
|
||||
printf ("gpgconf-dirmngr.conf:%lu:\"%s\n",
|
||||
es_printf ("gpgconf-dirmngr.conf:%lu:\"%s\n",
|
||||
GC_OPT_FLAG_DEFAULT, filename);
|
||||
xfree (filename);
|
||||
|
||||
printf ("verbose:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("quiet:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("debug-level:%lu:\"none\n", flags | GC_OPT_FLAG_DEFAULT);
|
||||
printf ("log-file:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("force:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("verbose:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("quiet:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("debug-level:%lu:\"none\n", flags | GC_OPT_FLAG_DEFAULT);
|
||||
es_printf ("log-file:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("force:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
|
||||
/* --csh and --sh are mutually exclusive, something we can not
|
||||
express in GPG Conf. --options is only usable from the
|
||||
@ -1241,34 +1241,34 @@ main (int argc, char **argv)
|
||||
"ldapservers.conf":"dirmngr_ldapservers.conf",
|
||||
NULL);
|
||||
filename_esc = percent_escape (filename, NULL);
|
||||
printf ("ldapserverlist-file:%lu:\"%s\n", flags | GC_OPT_FLAG_DEFAULT,
|
||||
es_printf ("ldapserverlist-file:%lu:\"%s\n", flags | GC_OPT_FLAG_DEFAULT,
|
||||
filename_esc);
|
||||
xfree (filename_esc);
|
||||
xfree (filename);
|
||||
|
||||
printf ("ldaptimeout:%lu:%u\n",
|
||||
es_printf ("ldaptimeout:%lu:%u\n",
|
||||
flags | GC_OPT_FLAG_DEFAULT, DEFAULT_LDAP_TIMEOUT);
|
||||
printf ("max-replies:%lu:%u\n",
|
||||
es_printf ("max-replies:%lu:%u\n",
|
||||
flags | GC_OPT_FLAG_DEFAULT, DEFAULT_MAX_REPLIES);
|
||||
printf ("allow-ocsp:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("ocsp-responder:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("ocsp-signer:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("allow-ocsp:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("ocsp-responder:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("ocsp-signer:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
|
||||
printf ("faked-system-time:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("no-greeting:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("faked-system-time:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("no-greeting:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
|
||||
printf ("disable-http:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("disable-ldap:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("honor-http-proxy:%lu\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("http-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("ldap-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("only-ldap-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("ignore-ldap-dp:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("ignore-http-dp:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
printf ("ignore-ocsp-service-url:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("disable-http:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("disable-ldap:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("honor-http-proxy:%lu\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("http-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("ldap-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("only-ldap-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("ignore-ldap-dp:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("ignore-http-dp:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("ignore-ocsp-service-url:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
/* Note: The next one is to fix a typo in gpgconf - should be
|
||||
removed eventually. */
|
||||
printf ("ignore-ocsp-servic-url:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
es_printf ("ignore-ocsp-servic-url:%lu:\n", flags | GC_OPT_FLAG_NONE);
|
||||
}
|
||||
cleanup ();
|
||||
return !!rc;
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include "../common/membuf.h"
|
||||
#include "../common/sysutils.h" /* (gnupg_fd_t) */
|
||||
#include "../common/i18n.h"
|
||||
|
||||
#include "../common/http.h" /* (parsed_uri_t) */
|
||||
|
||||
/* This objects keeps information about a particular LDAP server and
|
||||
is used as item of a single linked list of servers. */
|
||||
@ -49,6 +49,17 @@ struct ldap_server_s
|
||||
typedef struct ldap_server_s *ldap_server_t;
|
||||
|
||||
|
||||
/* This objects is used to build a list of URI consisting of the
|
||||
original and the parsed URI. */
|
||||
struct uri_item_s
|
||||
{
|
||||
struct uri_item_s *next;
|
||||
parsed_uri_t parsed_uri; /* The broken down URI. */
|
||||
char uri[1]; /* The original URI. */
|
||||
};
|
||||
typedef struct uri_item_s *uri_item_t;
|
||||
|
||||
|
||||
/* A list of fingerprints. */
|
||||
struct fingerprint_list_s;
|
||||
typedef struct fingerprint_list_s *fingerprint_list_t;
|
||||
@ -163,6 +174,7 @@ struct server_control_s
|
||||
response. */
|
||||
|
||||
int audit_events; /* Send audit events to client. */
|
||||
uri_item_t keyservers; /* List of keyservers. */
|
||||
};
|
||||
|
||||
|
||||
|
183
dirmngr/ks-action.c
Normal file
183
dirmngr/ks-action.c
Normal file
@ -0,0 +1,183 @@
|
||||
/* ks-action.c - OpenPGP keyserver actions
|
||||
* Copyright (C) 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "dirmngr.h"
|
||||
#include "misc.h"
|
||||
#include "ks-engine.h"
|
||||
#include "ks-action.h"
|
||||
|
||||
|
||||
/* Copy all data from IN to OUT. */
|
||||
static gpg_error_t
|
||||
copy_stream (estream_t in, estream_t out)
|
||||
{
|
||||
char buffer[512];
|
||||
size_t nread;
|
||||
|
||||
while (!es_read (in, buffer, sizeof buffer, &nread))
|
||||
{
|
||||
if (!nread)
|
||||
return 0; /* EOF */
|
||||
if (es_write (out, buffer, nread, NULL))
|
||||
break;
|
||||
|
||||
}
|
||||
return gpg_error_from_syserror ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Search all configured keyservers for keys matching PATTERNS and
|
||||
write the result to the provided output stream. */
|
||||
gpg_error_t
|
||||
ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
int any = 0;
|
||||
uri_item_t uri;
|
||||
estream_t infp;
|
||||
|
||||
if (!patterns)
|
||||
return gpg_error (GPG_ERR_NO_USER_ID);
|
||||
|
||||
/* FIXME: We only take care of the first pattern. To fully support
|
||||
multiple patterns we might either want to run several queries in
|
||||
parallel and merge them. We also need to decide what to do with
|
||||
errors - it might not be the best idea to ignore an error from
|
||||
one server and silently continue with another server. For now we
|
||||
stop at the first error. */
|
||||
for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
|
||||
{
|
||||
if (uri->parsed_uri->is_http)
|
||||
{
|
||||
any = 1;
|
||||
err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d, &infp);
|
||||
if (!err)
|
||||
{
|
||||
err = copy_stream (infp, outfp);
|
||||
es_fclose (infp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!any)
|
||||
err = gpg_error (GPG_ERR_NO_KEYSERVER);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Get the requested keys (matching PATTERNS) using all configured
|
||||
keyservers and write the result to the provided output stream. */
|
||||
gpg_error_t
|
||||
ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
gpg_error_t first_err = 0;
|
||||
int any = 0;
|
||||
strlist_t sl;
|
||||
uri_item_t uri;
|
||||
estream_t infp;
|
||||
|
||||
if (!patterns)
|
||||
return gpg_error (GPG_ERR_NO_USER_ID);
|
||||
|
||||
/* FIXME: We only take care of the first keyserver. To fully
|
||||
support multiple keyservers we need to track the result for each
|
||||
pattern and use the next keyserver if one key was not found. The
|
||||
keyservers might not all be fully synced thus it is not clear
|
||||
whether the first keyserver has the freshest copy of the key.
|
||||
Need to think about a better strategy. */
|
||||
for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
|
||||
{
|
||||
if (uri->parsed_uri->is_http)
|
||||
{
|
||||
any = 1;
|
||||
for (sl = patterns; !err && sl; sl = sl->next)
|
||||
{
|
||||
err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
|
||||
if (err)
|
||||
{
|
||||
/* It is possible that a server does not carry a
|
||||
key, thus we only save the error and continue
|
||||
with the next pattern. FIXME: It is an open
|
||||
question how to return such an error condition to
|
||||
the caller. */
|
||||
first_err = err;
|
||||
err = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = copy_stream (infp, outfp);
|
||||
/* Reading from the keyserver should nver fail, thus
|
||||
return this error. */
|
||||
es_fclose (infp);
|
||||
infp = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!any)
|
||||
err = gpg_error (GPG_ERR_NO_KEYSERVER);
|
||||
else if (!err && first_err)
|
||||
err = first_err; /* fixme: Do we really want to do that? */
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Send an OpenPGP key to all keyservers. The key in {DATA,DATALEN}
|
||||
is expected in OpenPGP binary transport format. */
|
||||
gpg_error_t
|
||||
ks_action_put (ctrl_t ctrl, const void *data, size_t datalen)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
gpg_error_t first_err = 0;
|
||||
int any = 0;
|
||||
uri_item_t uri;
|
||||
|
||||
for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
|
||||
{
|
||||
if (uri->parsed_uri->is_http)
|
||||
{
|
||||
any = 1;
|
||||
err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
|
||||
if (err)
|
||||
{
|
||||
first_err = err;
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!any)
|
||||
err = gpg_error (GPG_ERR_NO_KEYSERVER);
|
||||
else if (!err && first_err)
|
||||
err = first_err; /* fixme: Do we really want to do that? */
|
||||
return err;
|
||||
}
|
||||
|
28
dirmngr/ks-action.h
Normal file
28
dirmngr/ks-action.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* ks-action.h - OpenPGP keyserver actions definitions
|
||||
* Copyright (C) 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DIRMNGR_KS_ACTION_H
|
||||
#define DIRMNGR_KS_ACTION_H 1
|
||||
|
||||
gpg_error_t ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp);
|
||||
gpg_error_t ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp);
|
||||
gpg_error_t ks_action_put (ctrl_t ctrl, const void *data, size_t datalen);
|
||||
|
||||
|
||||
#endif /*DIRMNGR_KS_ACTION_H*/
|
558
dirmngr/ks-engine-hkp.c
Normal file
558
dirmngr/ks-engine-hkp.c
Normal file
@ -0,0 +1,558 @@
|
||||
/* ks-engine-hkp.c - HKP keyserver engine
|
||||
* Copyright (C) 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "dirmngr.h"
|
||||
#include "misc.h"
|
||||
#include "userids.h"
|
||||
#include "ks-engine.h"
|
||||
|
||||
/* To match the behaviour of our old gpgkeys helper code we escape
|
||||
more characters than actually needed. */
|
||||
#define EXTRA_ESCAPE_CHARS "@!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
|
||||
|
||||
/* How many redirections do we allow. */
|
||||
#define MAX_REDIRECTS 2
|
||||
|
||||
|
||||
/* Send an HTTP request. On success returns an estream object at
|
||||
R_FP. HOSTPORTSTR is only used for diagnostics. If POST_CB is not
|
||||
NULL a post request is used and that callback is called to allow
|
||||
writing the post data. */
|
||||
static gpg_error_t
|
||||
send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
|
||||
gpg_error_t (*post_cb)(void *, http_t), void *post_cb_value,
|
||||
estream_t *r_fp)
|
||||
{
|
||||
gpg_error_t err;
|
||||
http_t http = NULL;
|
||||
int redirects_left = MAX_REDIRECTS;
|
||||
estream_t fp = NULL;
|
||||
char *request_buffer = NULL;
|
||||
|
||||
*r_fp = NULL;
|
||||
once_more:
|
||||
err = http_open (&http,
|
||||
post_cb? HTTP_REQ_POST : HTTP_REQ_GET,
|
||||
request,
|
||||
/* fixme: AUTH */ NULL,
|
||||
0,
|
||||
/* fixme: proxy*/ NULL,
|
||||
NULL, NULL,
|
||||
/*FIXME curl->srvtag*/NULL);
|
||||
if (!err)
|
||||
{
|
||||
fp = http_get_write_ptr (http);
|
||||
/* Avoid caches to get the most recent copy of the key. We set
|
||||
both the Pragma and Cache-Control versions of the header, so
|
||||
we're good with both HTTP 1.0 and 1.1. */
|
||||
es_fputs ("Pragma: no-cache\r\n"
|
||||
"Cache-Control: no-cache\r\n", fp);
|
||||
if (post_cb)
|
||||
err = post_cb (post_cb_value, http);
|
||||
if (!err)
|
||||
{
|
||||
http_start_data (http);
|
||||
if (es_ferror (fp))
|
||||
err = gpg_error_from_syserror ();
|
||||
}
|
||||
}
|
||||
if (err)
|
||||
{
|
||||
/* Fixme: After a redirection we show the old host name. */
|
||||
log_error (_("error connecting to `%s': %s\n"),
|
||||
hostportstr, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Wait for the response. */
|
||||
dirmngr_tick (ctrl);
|
||||
err = http_wait_response (http);
|
||||
if (err)
|
||||
{
|
||||
log_error (_("error reading HTTP response for `%s': %s\n"),
|
||||
hostportstr, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
switch (http_get_status_code (http))
|
||||
{
|
||||
case 200:
|
||||
err = 0;
|
||||
break; /* Success. */
|
||||
|
||||
case 301:
|
||||
case 302:
|
||||
{
|
||||
const char *s = http_get_header (http, "Location");
|
||||
|
||||
log_info (_("URL `%s' redirected to `%s' (%u)\n"),
|
||||
request, s?s:"[none]", http_get_status_code (http));
|
||||
if (s && *s && redirects_left-- )
|
||||
{
|
||||
xfree (request_buffer);
|
||||
request_buffer = xtrystrdup (s);
|
||||
if (request_buffer)
|
||||
{
|
||||
request = request_buffer;
|
||||
http_close (http, 0);
|
||||
http = NULL;
|
||||
goto once_more;
|
||||
}
|
||||
err = gpg_error_from_syserror ();
|
||||
}
|
||||
else
|
||||
err = gpg_error (GPG_ERR_NO_DATA);
|
||||
log_error (_("too many redirections\n"));
|
||||
}
|
||||
goto leave;
|
||||
|
||||
default:
|
||||
log_error (_("error accessing `%s': http status %u\n"),
|
||||
request, http_get_status_code (http));
|
||||
err = gpg_error (GPG_ERR_NO_DATA);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
fp = http_get_read_ptr (http);
|
||||
if (!fp)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_BUG);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Return the read stream and close the HTTP context. */
|
||||
*r_fp = fp;
|
||||
http_close (http, 1);
|
||||
http = NULL;
|
||||
|
||||
leave:
|
||||
http_close (http, 0);
|
||||
xfree (request_buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static gpg_error_t
|
||||
armor_data (char **r_string, const void *data, size_t datalen)
|
||||
{
|
||||
gpg_error_t err;
|
||||
struct b64state b64state;
|
||||
estream_t fp;
|
||||
long length;
|
||||
char *buffer;
|
||||
size_t nread;
|
||||
|
||||
*r_string = NULL;
|
||||
|
||||
fp = es_fopenmem (0, "rw");
|
||||
if (!fp)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
if ((err=b64enc_start_es (&b64state, fp, "PGP PUBLIC KEY BLOCK"))
|
||||
|| (err=b64enc_write (&b64state, data, datalen))
|
||||
|| (err = b64enc_finish (&b64state)))
|
||||
{
|
||||
es_fclose (fp);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* FIXME: To avoid the extra buffer allocation estream should
|
||||
provide a function to snatch the internal allocated memory from
|
||||
such a memory stream. */
|
||||
length = es_ftell (fp);
|
||||
if (length < 0)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
es_fclose (fp);
|
||||
return err;
|
||||
}
|
||||
|
||||
buffer = xtrymalloc (length+1);
|
||||
if (!buffer)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
es_fclose (fp);
|
||||
return err;
|
||||
}
|
||||
|
||||
es_rewind (fp);
|
||||
if (es_read (fp, buffer, length, &nread))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
es_fclose (fp);
|
||||
return err;
|
||||
}
|
||||
buffer[nread] = 0;
|
||||
es_fclose (fp);
|
||||
|
||||
*r_string = buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Search the keyserver identified by URI for keys matching PATTERN.
|
||||
On success R_FP has an open stream to read the data. */
|
||||
gpg_error_t
|
||||
ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
|
||||
estream_t *r_fp)
|
||||
{
|
||||
gpg_error_t err;
|
||||
KEYDB_SEARCH_DESC desc;
|
||||
char fprbuf[2+40+1];
|
||||
const char *scheme;
|
||||
char portstr[10];
|
||||
char *hostport = NULL;
|
||||
char *request = NULL;
|
||||
estream_t fp = NULL;
|
||||
|
||||
*r_fp = NULL;
|
||||
|
||||
/* Remove search type indicator and adjust PATTERN accordingly.
|
||||
Note that HKP keyservers like the 0x to be present when searching
|
||||
by keyid. We need to re-format the fingerprint and keyids so to
|
||||
remove the gpg specific force-use-of-this-key flag ("!"). */
|
||||
err = classify_user_id (pattern, &desc);
|
||||
if (err)
|
||||
return err;
|
||||
switch (desc.mode)
|
||||
{
|
||||
case KEYDB_SEARCH_MODE_EXACT:
|
||||
case KEYDB_SEARCH_MODE_SUBSTR:
|
||||
case KEYDB_SEARCH_MODE_MAIL:
|
||||
case KEYDB_SEARCH_MODE_MAILSUB:
|
||||
pattern = desc.u.name;
|
||||
break;
|
||||
case KEYDB_SEARCH_MODE_SHORT_KID:
|
||||
snprintf (fprbuf, sizeof fprbuf, "0x%08lX", (ulong)desc.u.kid[1]);
|
||||
pattern = fprbuf;
|
||||
break;
|
||||
case KEYDB_SEARCH_MODE_LONG_KID:
|
||||
snprintf (fprbuf, sizeof fprbuf, "0x%08lX%08lX",
|
||||
(ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
|
||||
pattern = fprbuf;
|
||||
break;
|
||||
case KEYDB_SEARCH_MODE_FPR16:
|
||||
bin2hex (desc.u.fpr, 16, fprbuf);
|
||||
pattern = fprbuf;
|
||||
break;
|
||||
case KEYDB_SEARCH_MODE_FPR20:
|
||||
case KEYDB_SEARCH_MODE_FPR:
|
||||
bin2hex (desc.u.fpr, 20, fprbuf);
|
||||
pattern = fprbuf;
|
||||
break;
|
||||
default:
|
||||
return gpg_error (GPG_ERR_INV_USER_ID);
|
||||
}
|
||||
|
||||
/* Map scheme and port. */
|
||||
if (!strcmp (uri->scheme,"hkps") || !strcmp (uri->scheme,"https"))
|
||||
{
|
||||
scheme = "https";
|
||||
strcpy (portstr, "443");
|
||||
}
|
||||
else /* HKP or HTTP. */
|
||||
{
|
||||
scheme = "http";
|
||||
strcpy (portstr, "11371");
|
||||
}
|
||||
if (uri->port)
|
||||
snprintf (portstr, sizeof portstr, "%hu", uri->port);
|
||||
else
|
||||
{} /*fixme_do_srv_lookup ()*/
|
||||
|
||||
/* Build the request string. */
|
||||
{
|
||||
char *searchkey;
|
||||
|
||||
hostport = strconcat (scheme, "://",
|
||||
*uri->host? uri->host: "localhost",
|
||||
":", portstr, NULL);
|
||||
if (!hostport)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
searchkey = http_escape_string (pattern, EXTRA_ESCAPE_CHARS);
|
||||
if (!searchkey)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
request = strconcat (hostport,
|
||||
"/pks/lookup?op=index&options=mr&search=",
|
||||
searchkey,
|
||||
NULL);
|
||||
xfree (searchkey);
|
||||
if (!request)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the request. */
|
||||
err = send_request (ctrl, request, hostport, NULL, NULL, &fp);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
/* Start reading the response. */
|
||||
{
|
||||
int c = es_getc (fp);
|
||||
if (c == -1)
|
||||
{
|
||||
err = es_ferror (fp)?gpg_error_from_syserror ():gpg_error (GPG_ERR_EOF);
|
||||
log_error ("error reading response: %s\n", gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
if (c == '<')
|
||||
{
|
||||
/* The document begins with a '<', assume it's a HTML
|
||||
response, which we don't support. */
|
||||
err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
|
||||
goto leave;
|
||||
}
|
||||
es_ungetc (c, fp);
|
||||
}
|
||||
|
||||
/* Return the read stream. */
|
||||
*r_fp = fp;
|
||||
fp = NULL;
|
||||
|
||||
leave:
|
||||
es_fclose (fp);
|
||||
xfree (request);
|
||||
xfree (hostport);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Get the key described key the KEYSPEC string from the keyserver
|
||||
identified by URI. On success R_FP has an open stream to read the
|
||||
data. */
|
||||
gpg_error_t
|
||||
ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp)
|
||||
{
|
||||
gpg_error_t err;
|
||||
KEYDB_SEARCH_DESC desc;
|
||||
char kidbuf[8+1];
|
||||
const char *scheme;
|
||||
char portstr[10];
|
||||
char *hostport = NULL;
|
||||
char *request = NULL;
|
||||
estream_t fp = NULL;
|
||||
|
||||
*r_fp = NULL;
|
||||
|
||||
/* Remove search type indicator and adjust PATTERN accordingly.
|
||||
Note that HKP keyservers like the 0x to be present when searching
|
||||
by keyid. We need to re-format the fingerprint and keyids so to
|
||||
remove the gpg specific force-use-of-this-key flag ("!"). */
|
||||
err = classify_user_id (keyspec, &desc);
|
||||
if (err)
|
||||
return err;
|
||||
switch (desc.mode)
|
||||
{
|
||||
case KEYDB_SEARCH_MODE_SHORT_KID:
|
||||
case KEYDB_SEARCH_MODE_LONG_KID:
|
||||
snprintf (kidbuf, sizeof kidbuf, "%08lX", (ulong)desc.u.kid[1]);
|
||||
break;
|
||||
case KEYDB_SEARCH_MODE_FPR20:
|
||||
case KEYDB_SEARCH_MODE_FPR:
|
||||
/* This is a v4 fingerprint. Take the last 8 hex digits from
|
||||
the fingerprint which is the expected short keyid. */
|
||||
bin2hex (desc.u.fpr+16, 4, kidbuf);
|
||||
break;
|
||||
|
||||
case KEYDB_SEARCH_MODE_FPR16:
|
||||
log_error ("HKP keyserver do not support v3 fingerprints\n");
|
||||
default:
|
||||
return gpg_error (GPG_ERR_INV_USER_ID);
|
||||
}
|
||||
|
||||
/* Map scheme and port. */
|
||||
if (!strcmp (uri->scheme,"hkps") || !strcmp (uri->scheme,"https"))
|
||||
{
|
||||
scheme = "https";
|
||||
strcpy (portstr, "443");
|
||||
}
|
||||
else /* HKP or HTTP. */
|
||||
{
|
||||
scheme = "http";
|
||||
strcpy (portstr, "11371");
|
||||
}
|
||||
if (uri->port)
|
||||
snprintf (portstr, sizeof portstr, "%hu", uri->port);
|
||||
else
|
||||
{} /*fixme_do_srv_lookup ()*/
|
||||
|
||||
/* Build the request string. */
|
||||
{
|
||||
hostport = strconcat (scheme, "://",
|
||||
*uri->host? uri->host: "localhost",
|
||||
":", portstr, NULL);
|
||||
if (!hostport)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
request = strconcat (hostport,
|
||||
"/pks/lookup?op=get&options=mr&search=0x",
|
||||
kidbuf,
|
||||
NULL);
|
||||
if (!request)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the request. */
|
||||
err = send_request (ctrl, request, hostport, NULL, NULL, &fp);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
/* Return the read stream and close the HTTP context. */
|
||||
*r_fp = fp;
|
||||
fp = NULL;
|
||||
|
||||
leave:
|
||||
es_fclose (fp);
|
||||
xfree (request);
|
||||
xfree (hostport);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Callback parameters for put_post_cb. */
|
||||
struct put_post_parm_s
|
||||
{
|
||||
char *datastring;
|
||||
};
|
||||
|
||||
|
||||
/* Helper for ks_hkp_put. */
|
||||
static gpg_error_t
|
||||
put_post_cb (void *opaque, http_t http)
|
||||
{
|
||||
struct put_post_parm_s *parm = opaque;
|
||||
gpg_error_t err = 0;
|
||||
estream_t fp;
|
||||
size_t len;
|
||||
|
||||
fp = http_get_write_ptr (http);
|
||||
len = strlen (parm->datastring);
|
||||
|
||||
es_fprintf (fp,
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
"Content-Length: %zu\r\n", len+8 /* 8 is for "keytext" */);
|
||||
http_start_data (http);
|
||||
if (es_fputs ("keytext=", fp) || es_write (fp, parm->datastring, len, NULL))
|
||||
err = gpg_error_from_syserror ();
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Send the key in {DATA,DATALEN} to the keyserver identified by URI. */
|
||||
gpg_error_t
|
||||
ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen)
|
||||
{
|
||||
gpg_error_t err;
|
||||
const char *scheme;
|
||||
char portstr[10];
|
||||
char *hostport = NULL;
|
||||
char *request = NULL;
|
||||
estream_t fp = NULL;
|
||||
struct put_post_parm_s parm;
|
||||
char *armored = NULL;
|
||||
|
||||
parm.datastring = NULL;
|
||||
|
||||
/* Map scheme and port. */
|
||||
if (!strcmp (uri->scheme,"hkps") || !strcmp (uri->scheme,"https"))
|
||||
{
|
||||
scheme = "https";
|
||||
strcpy (portstr, "443");
|
||||
}
|
||||
else /* HKP or HTTP. */
|
||||
{
|
||||
scheme = "http";
|
||||
strcpy (portstr, "11371");
|
||||
}
|
||||
if (uri->port)
|
||||
snprintf (portstr, sizeof portstr, "%hu", uri->port);
|
||||
else
|
||||
{} /*fixme_do_srv_lookup ()*/
|
||||
|
||||
err = armor_data (&armored, data, datalen);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS);
|
||||
if (!parm.datastring)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
xfree (armored);
|
||||
armored = NULL;
|
||||
|
||||
/* Build the request string. */
|
||||
hostport = strconcat (scheme, "://",
|
||||
*uri->host? uri->host: "localhost",
|
||||
":", portstr, NULL);
|
||||
if (!hostport)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
request = strconcat (hostport, "/pks/add", NULL);
|
||||
if (!request)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Send the request. */
|
||||
err = send_request (ctrl, request, hostport, put_post_cb, &parm, &fp);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
leave:
|
||||
es_fclose (fp);
|
||||
xfree (parm.datastring);
|
||||
xfree (armored);
|
||||
xfree (request);
|
||||
xfree (hostport);
|
||||
return err;
|
||||
}
|
36
dirmngr/ks-engine.h
Normal file
36
dirmngr/ks-engine.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* ks-engine.h - Keyserver engines definitions
|
||||
* Copyright (C) 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DIRMNGR_KS_ENGINE_H
|
||||
#define DIRMNGR_KS_ENGINE_H 1
|
||||
|
||||
#include "../common/estream.h"
|
||||
#include "../common/http.h"
|
||||
|
||||
/*-- ks-engine-hkp.c --*/
|
||||
gpg_error_t ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
|
||||
estream_t *r_fp);
|
||||
gpg_error_t ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri,
|
||||
const char *keyspec, estream_t *r_fp);
|
||||
gpg_error_t ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri,
|
||||
const void *data, size_t datalen);
|
||||
|
||||
|
||||
|
||||
#endif /*DIRMNGR_KS_ENGINE_H*/
|
285
dirmngr/server.c
285
dirmngr/server.c
@ -1,6 +1,6 @@
|
||||
/* dirmngr.c - LDAP access
|
||||
* Copyright (C) 2002 Klarälvdalens Datakonsult AB
|
||||
* Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009 g10 Code GmbH
|
||||
* Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2011 g10 Code GmbH
|
||||
*
|
||||
* This file is part of DirMngr.
|
||||
*
|
||||
@ -41,11 +41,18 @@
|
||||
#include "validate.h"
|
||||
#include "misc.h"
|
||||
#include "ldap-wrapper.h"
|
||||
#include "ks-action.h"
|
||||
|
||||
/* To avoid DoS attacks we limit the size of a certificate to
|
||||
something reasonable. */
|
||||
#define MAX_CERT_LENGTH (8*1024)
|
||||
|
||||
/* The same goes for OpenPGP keyblocks, but here we need to allow for
|
||||
much longer blocks; a 200k keyblock is not too unusual for keys
|
||||
with a lot of signatures (e.g. 0x5b0358a2). */
|
||||
#define MAX_KEYBLOCK_LENGTH (512*1024)
|
||||
|
||||
|
||||
#define PARM_ERROR(t) assuan_set_error (ctx, \
|
||||
gpg_error (GPG_ERR_ASS_PARAMETER), (t))
|
||||
#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
|
||||
@ -58,7 +65,7 @@ struct server_local_s
|
||||
/* Data used to associate an Assuan context with local server data */
|
||||
assuan_context_t assuan_ctx;
|
||||
|
||||
/* Per-session LDAP serfver. */
|
||||
/* Per-session LDAP servers. */
|
||||
ldap_server_t ldapservers;
|
||||
|
||||
/* If this flag is set to true this dirmngr process will be
|
||||
@ -94,6 +101,21 @@ get_ldapservers_from_ctrl (ctrl_t ctrl)
|
||||
}
|
||||
|
||||
|
||||
/* Release all configured keyserver info from CTRL. */
|
||||
void
|
||||
release_ctrl_keyservers (ctrl_t ctrl)
|
||||
{
|
||||
while (ctrl->keyservers)
|
||||
{
|
||||
uri_item_t tmp = ctrl->keyservers->next;
|
||||
http_release_parsed_uri (ctrl->keyservers->parsed_uri);
|
||||
xfree (ctrl->keyservers);
|
||||
ctrl->keyservers = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Helper to print a message while leaving a command. */
|
||||
static gpg_error_t
|
||||
leave_cmd (assuan_context_t ctx, gpg_error_t err)
|
||||
@ -147,7 +169,7 @@ data_line_cookie_close (void *cookie)
|
||||
/* Copy the % and + escaped string S into the buffer D and replace the
|
||||
escape sequences. Note, that it is sufficient to allocate the
|
||||
target string D as long as the source string S, i.e.: strlen(s)+1.
|
||||
NOte further that If S contains an escaped binary nul the resulting
|
||||
Note further that if S contains an escaped binary Nul the resulting
|
||||
string D will contain the 0 as well as all other characters but it
|
||||
will be impossible to know whether this is the original EOS or a
|
||||
copied Nul. */
|
||||
@ -1335,6 +1357,254 @@ cmd_validate (assuan_context_t ctx, char *line)
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
static const char hlp_keyserver[] =
|
||||
"KEYSERVER [--clear] [<uri>]\n"
|
||||
"\n"
|
||||
"If called without arguments list all configured keyserver URLs.\n"
|
||||
"If called with option \"--clear\" remove all configured keyservers\n"
|
||||
"If called with an URI add this as keyserver. Note that keyservers\n"
|
||||
"are configured on a per-session base. A default keyserver may already be\n"
|
||||
"present, thus the \"--clear\" option must be used to get full control.\n"
|
||||
"If \"--clear\" and an URI are used together the clear command is\n"
|
||||
"obviously executed first. A RESET command does not change the list\n"
|
||||
"of configured keyservers.";
|
||||
static gpg_error_t
|
||||
cmd_keyserver (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err;
|
||||
int clear_flag, add_flag;
|
||||
uri_item_t item = NULL; /* gcc 4.4.5 is not able to detect that it
|
||||
is always initialized. */
|
||||
|
||||
clear_flag = has_option (line, "--clear");
|
||||
line = skip_options (line);
|
||||
add_flag = !!*line;
|
||||
|
||||
if (add_flag)
|
||||
{
|
||||
item = xtrymalloc (sizeof *item + strlen (line));
|
||||
if (!item)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
item->next = NULL;
|
||||
item->parsed_uri = NULL;
|
||||
strcpy (item->uri, line);
|
||||
|
||||
err = http_parse_uri (&item->parsed_uri, line, 1);
|
||||
if (err)
|
||||
{
|
||||
xfree (item);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
if (clear_flag)
|
||||
release_ctrl_keyservers (ctrl);
|
||||
if (add_flag)
|
||||
{
|
||||
item->next = ctrl->keyservers;
|
||||
ctrl->keyservers = item;
|
||||
}
|
||||
|
||||
if (!add_flag && !clear_flag) /* List configured keyservers. */
|
||||
{
|
||||
uri_item_t u;
|
||||
|
||||
for (u=ctrl->keyservers; u; u = u->next)
|
||||
dirmngr_status (ctrl, "KEYSERVER", u->uri, NULL);
|
||||
}
|
||||
err = 0;
|
||||
|
||||
leave:
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char hlp_ks_search[] =
|
||||
"KS_SEARCH {<pattern>}\n"
|
||||
"\n"
|
||||
"Search the configured OpenPGP keyservers (see command KEYSERVER)\n"
|
||||
"for keys matching PATTERN";
|
||||
static gpg_error_t
|
||||
cmd_ks_search (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err;
|
||||
strlist_t list, sl;
|
||||
char *p;
|
||||
estream_t outfp;
|
||||
|
||||
/* No options for now. */
|
||||
line = skip_options (line);
|
||||
|
||||
/* Break the line down into an strlist. Each pattern is
|
||||
percent-plus escaped. */
|
||||
list = NULL;
|
||||
for (p=line; *p; line = p)
|
||||
{
|
||||
while (*p && *p != ' ')
|
||||
p++;
|
||||
if (*p)
|
||||
*p++ = 0;
|
||||
if (*line)
|
||||
{
|
||||
sl = xtrymalloc (sizeof *sl + strlen (line));
|
||||
if (!sl)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
free_strlist (list);
|
||||
goto leave;
|
||||
}
|
||||
sl->flags = 0;
|
||||
strcpy_escaped_plus (sl->d, line);
|
||||
sl->next = list;
|
||||
list = sl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup an output stream and perform the search. */
|
||||
outfp = es_fopencookie (ctx, "w", data_line_cookie_functions);
|
||||
if (!outfp)
|
||||
err = set_error (GPG_ERR_ASS_GENERAL, "error setting up a data stream");
|
||||
else
|
||||
{
|
||||
err = ks_action_search (ctrl, list, outfp);
|
||||
es_fclose (outfp);
|
||||
}
|
||||
|
||||
leave:
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char hlp_ks_get[] =
|
||||
"KS_GET {<pattern>}\n"
|
||||
"\n"
|
||||
"Get the keys matching PATTERN from the configured OpenPGP keyservers\n"
|
||||
"(see command KEYSERVER). Each pattern should be a keyid or a fingerprint";
|
||||
static gpg_error_t
|
||||
cmd_ks_get (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err;
|
||||
strlist_t list, sl;
|
||||
char *p;
|
||||
estream_t outfp;
|
||||
|
||||
/* No options for now. */
|
||||
line = skip_options (line);
|
||||
|
||||
/* Break the line down into an strlist. Each pattern is by
|
||||
definition percent-plus escaped. However we only support keyids
|
||||
and fingerprints and thus the client has no need to apply the
|
||||
escaping. */
|
||||
list = NULL;
|
||||
for (p=line; *p; line = p)
|
||||
{
|
||||
while (*p && *p != ' ')
|
||||
p++;
|
||||
if (*p)
|
||||
*p++ = 0;
|
||||
if (*line)
|
||||
{
|
||||
sl = xtrymalloc (sizeof *sl + strlen (line));
|
||||
if (!sl)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
free_strlist (list);
|
||||
goto leave;
|
||||
}
|
||||
sl->flags = 0;
|
||||
strcpy_escaped_plus (sl->d, line);
|
||||
sl->next = list;
|
||||
list = sl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup an output stream and perform the get. */
|
||||
outfp = es_fopencookie (ctx, "w", data_line_cookie_functions);
|
||||
if (!outfp)
|
||||
err = set_error (GPG_ERR_ASS_GENERAL, "error setting up a data stream");
|
||||
else
|
||||
{
|
||||
err = ks_action_get (ctrl, list, outfp);
|
||||
es_fclose (outfp);
|
||||
}
|
||||
|
||||
leave:
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char hlp_ks_put[] =
|
||||
"KS_PUT\n"
|
||||
"\n"
|
||||
"Send a key to the configured OpenPGP keyservers. The actual key material\n"
|
||||
"is then requested by Dirmngr using\n"
|
||||
"\n"
|
||||
" INQUIRE KEYBLOCK\n"
|
||||
"\n"
|
||||
"The client shall respond with a binary version of the keyblock. For LDAP\n"
|
||||
"keyservers Dirmngr may ask for meta information of the provided keyblock\n"
|
||||
"using:\n"
|
||||
"\n"
|
||||
" INQUIRE KEYBLOCK_INFO\n"
|
||||
"\n"
|
||||
"The client shall respond with a colon delimited info lines";
|
||||
static gpg_error_t
|
||||
cmd_ks_put (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err;
|
||||
unsigned char *value = NULL;
|
||||
size_t valuelen;
|
||||
unsigned char *info = NULL;
|
||||
size_t infolen;
|
||||
|
||||
/* No options for now. */
|
||||
line = skip_options (line);
|
||||
|
||||
/* Ask for the key material. */
|
||||
err = assuan_inquire (ctx, "KEYBLOCK",
|
||||
&value, &valuelen, MAX_KEYBLOCK_LENGTH);
|
||||
if (err)
|
||||
{
|
||||
log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (!valuelen) /* No data returned; return a comprehensible error. */
|
||||
{
|
||||
err = gpg_error (GPG_ERR_MISSING_CERT);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Ask for the key meta data. Not actually needed for HKP servers
|
||||
but we do it anyway test the client implementaion. */
|
||||
err = assuan_inquire (ctx, "KEYBLOCK_INFO",
|
||||
&info, &infolen, MAX_KEYBLOCK_LENGTH);
|
||||
if (err)
|
||||
{
|
||||
log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Send the key. */
|
||||
err = ks_action_put (ctrl, value, valuelen);
|
||||
|
||||
leave:
|
||||
xfree (info);
|
||||
xfree (value);
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static const char hlp_getinfo[] =
|
||||
@ -1469,6 +1739,10 @@ register_commands (assuan_context_t ctx)
|
||||
{ "LISTCRLS", cmd_listcrls, hlp_listcrls },
|
||||
{ "CACHECERT", cmd_cachecert, hlp_cachecert },
|
||||
{ "VALIDATE", cmd_validate, hlp_validate },
|
||||
{ "KEYSERVER", cmd_keyserver, hlp_keyserver },
|
||||
{ "KS_SEARCH", cmd_ks_search, hlp_ks_search },
|
||||
{ "KS_GET", cmd_ks_get, hlp_ks_get },
|
||||
{ "KS_PUT", cmd_ks_put, hlp_ks_put },
|
||||
{ "GETINFO", cmd_getinfo, hlp_getinfo },
|
||||
{ "KILLDIRMNGR",cmd_killdirmngr,hlp_killdirmngr },
|
||||
{ "RELOADDIRMNGR",cmd_reloaddirmngr,hlp_reloaddirmngr },
|
||||
@ -1487,6 +1761,7 @@ register_commands (assuan_context_t ctx)
|
||||
}
|
||||
|
||||
|
||||
/* Note that we do not reset the list of configured keyservers. */
|
||||
static gpg_error_t
|
||||
reset_notify (assuan_context_t ctx, char *line)
|
||||
{
|
||||
@ -1681,8 +1956,8 @@ dirmngr_status (ctrl_t ctrl, const char *keyword, ...)
|
||||
}
|
||||
|
||||
|
||||
/* Note, that we ignore CTRL for now but use the first connection to
|
||||
send the progress info back. */
|
||||
/* Send a tick progress indicator back. Fixme: This is only does for
|
||||
the currently active channel. */
|
||||
gpg_error_t
|
||||
dirmngr_tick (ctrl_t ctrl)
|
||||
{
|
||||
|
@ -103,7 +103,7 @@ used. Using the keyserver debug option as in
|
||||
is thus often helpful. Note that the actual output depends on the
|
||||
backend and may change from release to release.
|
||||
|
||||
|
||||
@ifset gpgtwoone
|
||||
@item Logging on WindowsCE
|
||||
|
||||
For development, the best logging method on WindowsCE is the use of
|
||||
@ -113,7 +113,7 @@ on the given port. (@pxref{option watchgnupg --tcp}). For in the field
|
||||
tests it is better to make use of the logging facility provided by the
|
||||
@command{gpgcedev} driver (part of libassuan); this is enabled by using
|
||||
a log file name of @file{GPG2:}. (@pxref{option --log-file}).
|
||||
|
||||
@end ifset
|
||||
|
||||
@end itemize
|
||||
|
||||
|
@ -34,7 +34,7 @@ Published by the Free Software Foundation@*
|
||||
Boston, MA 02110-1301 USA
|
||||
@end iftex
|
||||
|
||||
Copyright @copyright{} 2002, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
|
||||
Copyright @copyright{} 2002, 2004, 2005, 2006, 2007, 2010 Free Software Foundation, Inc.
|
||||
|
||||
@quotation
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
@ -51,8 +51,10 @@ section entitled ``Copying''.
|
||||
* gpg2: (gnupg). OpenPGP encryption and signing tool.
|
||||
* gpgsm: (gnupg). S/MIME encryption and signing tool.
|
||||
* gpg-agent: (gnupg). The secret key daemon.
|
||||
@ifset gpgtwoone
|
||||
* dirmngr: (gnupg). X.509 CRL and OCSP server.
|
||||
* dirmngr-client: (gnupg). X.509 CRL and OCSP client.
|
||||
@end ifset
|
||||
@end direntry
|
||||
|
||||
|
||||
@ -124,7 +126,9 @@ the administration and the architecture.
|
||||
* Installation:: A short installation guide.
|
||||
|
||||
* Invoking GPG-AGENT:: How to launch the secret key daemon.
|
||||
@ifset gpgtwoone
|
||||
* Invoking DIRMNGR:: How to launch the CRL and OCSP daemon.
|
||||
@end ifset
|
||||
* Invoking GPG:: Using the OpenPGP protocol.
|
||||
* Invoking GPGSM:: Using the S/MIME protocol.
|
||||
* Invoking SCDAEMON:: How to handle Smartcards.
|
||||
@ -156,7 +160,9 @@ the administration and the architecture.
|
||||
@include instguide.texi
|
||||
|
||||
@include gpg-agent.texi
|
||||
@ifset gpgtwoone
|
||||
@include dirmngr.texi
|
||||
@end ifset
|
||||
@include gpg.texi
|
||||
@include gpgsm.texi
|
||||
@include scdaemon.texi
|
||||
|
@ -16,7 +16,9 @@ GnuPG comes with a couple of smaller tools:
|
||||
* gpgsm-gencert.sh:: Generate an X.509 certificate request.
|
||||
* gpg-preset-passphrase:: Put a passphrase into the cache.
|
||||
* gpg-connect-agent:: Communicate with a running agent.
|
||||
@ifset gpgtwoone
|
||||
* dirmngr-client:: How to use the Dirmngr client tool.
|
||||
@end ifset
|
||||
* gpgparsemail:: Parse a mail message into an annotated format
|
||||
* symcryptrun:: Call a simple symmetric encryption tool.
|
||||
* gpg-zip:: Encrypt or sign files into an archive.
|
||||
@ -1434,6 +1436,7 @@ Print a list of available control commands.
|
||||
@include see-also-note.texi
|
||||
@end ifset
|
||||
|
||||
@ifset gpgtwoone
|
||||
@c
|
||||
@c DIRMNGR-CLIENT
|
||||
@c
|
||||
@ -1594,7 +1597,7 @@ Squid's @option{external_acl_type} option.
|
||||
@command{gpgsm}(1)
|
||||
@include see-also-note.texi
|
||||
@end ifset
|
||||
|
||||
@end ifset
|
||||
|
||||
@c
|
||||
@c GPGPARSEMAIL
|
||||
|
@ -1,3 +1,25 @@
|
||||
2011-01-20 Werner Koch <wk@g10code.com>
|
||||
|
||||
* keyserver.c: Rewrite most stuff for use with dirmngr. Get rid
|
||||
of all spawn code. Work work pending.
|
||||
|
||||
* export.c (export_pubkeys_buffer): New.
|
||||
|
||||
* import.c (import_keys_es_stream): New.
|
||||
|
||||
* call-dirmngr.c, call-dirmngr.h: New.
|
||||
* gpg.h (server_control_s): Add DIRMNGR_LOCAL.
|
||||
* gpg.c: Include call-dirmngr.h.
|
||||
(gpg_deinit_default_ctrl): Call gpg_dirmngr_deinit_session_data.
|
||||
|
||||
2011-01-06 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpg.c (main): Use keyserver_spec_t.
|
||||
|
||||
* options.h (struct opt): Factor definition of struct keyserver
|
||||
out to ../common/keyserver.h.
|
||||
(keyserver_spec_t): New.
|
||||
|
||||
2011-01-21 Werner Koch <wk@g10code.com>
|
||||
|
||||
* seskey.c (encode_md_value): Truncate the DSA hash again.
|
||||
@ -11654,7 +11676,7 @@ Thu Feb 12 22:24:42 1998 Werner Koch (wk@frodo)
|
||||
|
||||
|
||||
Copyright 1998,1999,2000,2001,2002,2003,2004,2005,
|
||||
2006,2007,2008,2009,2010 Free Software Foundation, Inc.
|
||||
2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software; as a special exception the author gives
|
||||
unlimited permission to copy and/or distribute it, with or without
|
||||
|
@ -103,6 +103,7 @@ gpg2_SOURCES = gpg.c \
|
||||
helptext.c \
|
||||
keyserver.c \
|
||||
keyserver-internal.h \
|
||||
call-dirmngr.c call-dirmngr.h \
|
||||
photoid.c photoid.h \
|
||||
call-agent.c call-agent.h \
|
||||
card-util.c \
|
||||
|
611
g10/call-dirmngr.c
Normal file
611
g10/call-dirmngr.c
Normal file
@ -0,0 +1,611 @@
|
||||
/* call-dirmngr.c - GPG operations to the Dirmngr.
|
||||
* Copyright (C) 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#ifdef HAVE_LOCALE_H
|
||||
# include <locale.h>
|
||||
#endif
|
||||
|
||||
#include "gpg.h"
|
||||
#include <assuan.h>
|
||||
#include "util.h"
|
||||
#include "membuf.h"
|
||||
#include "options.h"
|
||||
#include "i18n.h"
|
||||
#include "asshelp.h"
|
||||
#include "keyserver.h"
|
||||
#include "call-dirmngr.h"
|
||||
|
||||
|
||||
/* Parameter structure used with the KS_SEARCH command. */
|
||||
struct ks_search_parm_s
|
||||
{
|
||||
gpg_error_t lasterr; /* Last error code. */
|
||||
membuf_t saveddata; /* Buffer to build complete lines. */
|
||||
char *helpbuf; /* NULL or malloced buffer. */
|
||||
size_t helpbufsize; /* Allocated size of HELPBUF. */
|
||||
gpg_error_t (*data_cb)(void*, char*); /* Callback. */
|
||||
void *data_cb_value; /* First argument for DATA_CB. */
|
||||
};
|
||||
|
||||
|
||||
/* Parameter structure used with the KS_GET command. */
|
||||
struct ks_get_parm_s
|
||||
{
|
||||
estream_t memfp;
|
||||
};
|
||||
|
||||
|
||||
/* Parameter structure used with the KS_PUT command. */
|
||||
struct ks_put_parm_s
|
||||
{
|
||||
assuan_context_t ctx;
|
||||
kbnode_t keyblock; /* The optional keyblock. */
|
||||
const void *data; /* The key in OpenPGP binary format. */
|
||||
size_t datalen; /* The length of DATA. */
|
||||
};
|
||||
|
||||
|
||||
/* Data used to associate an session with dirmngr contexts. We can't
|
||||
use a simple one to one mapping because we sometimes need two
|
||||
connection s to the dirmngr; for example while doing a listing and
|
||||
being in a data callback we may want to retrieve a key. The local
|
||||
dirmngr data takes care of this. At the end of the session the
|
||||
function dirmngr_deinit_session_data is called bu gpg.c to cleanup
|
||||
these resources. Note that gpg.h defines a typedef dirmngr_local_t
|
||||
for this structure. */
|
||||
struct dirmngr_local_s
|
||||
{
|
||||
/* Link to other contexts which are used simultaneously. */
|
||||
struct dirmngr_local_s *next;
|
||||
|
||||
/* The active Assuan context. */
|
||||
assuan_context_t ctx;
|
||||
|
||||
/* Flag set to true while an operation is running on CTX. */
|
||||
int is_active;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Deinitialize all session data of dirmngr pertaining to CTRL. */
|
||||
void
|
||||
gpg_dirmngr_deinit_session_data (ctrl_t ctrl)
|
||||
{
|
||||
dirmngr_local_t dml;
|
||||
|
||||
while ((dml = ctrl->dirmngr_local))
|
||||
{
|
||||
ctrl->dirmngr_local = dml->next;
|
||||
if (dml->is_active)
|
||||
log_error ("oops: trying to cleanup an active dirmngr context\n");
|
||||
else
|
||||
assuan_release (dml->ctx);
|
||||
xfree (dml);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Try to connect to the Dirmngr via a socket or fork it off if
|
||||
possible. Handle the server's initial greeting and set global
|
||||
options. */
|
||||
static gpg_error_t
|
||||
create_context (ctrl_t ctrl, assuan_context_t *r_ctx)
|
||||
{
|
||||
gpg_error_t err;
|
||||
assuan_context_t ctx;
|
||||
|
||||
*r_ctx = NULL;
|
||||
err = start_new_dirmngr (&ctx,
|
||||
GPG_ERR_SOURCE_DEFAULT,
|
||||
opt.homedir,
|
||||
NULL,
|
||||
opt.verbose, DBG_ASSUAN,
|
||||
NULL /*gpg_status2*/, ctrl);
|
||||
if (!err)
|
||||
{
|
||||
keyserver_spec_t ksi;
|
||||
|
||||
/* Tell the dirmngr that we want to collect audit event. */
|
||||
/* err = assuan_transact (agent_ctx, "OPTION audit-events=1", */
|
||||
/* NULL, NULL, NULL, NULL, NULL, NULL); */
|
||||
|
||||
/* Set all configured keyservers. We clear existing keyservers
|
||||
so that any keyserver configured in GPG overrides keyservers
|
||||
possibly configured in Dirmngr. */
|
||||
for (ksi = opt.keyserver; !err && ksi; ksi = ksi->next)
|
||||
{
|
||||
char *line;
|
||||
|
||||
line = xtryasprintf ("KEYSERVER%s %s",
|
||||
ksi == opt.keyserver? " --clear":"", ksi->uri);
|
||||
if (!line)
|
||||
err = gpg_error_from_syserror ();
|
||||
else
|
||||
{
|
||||
err = assuan_transact (ctx, line,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
xfree (line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
assuan_release (ctx);
|
||||
else
|
||||
{
|
||||
/* audit_log_ok (ctrl->audit, AUDIT_DIRMNGR_READY, err); */
|
||||
*r_ctx = ctx;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Get a context for accessing dirmngr. If no context is available a
|
||||
new one is created and - if requred - dirmngr started. On success
|
||||
an assuan context is stored at R_CTX. This Context may only be
|
||||
released by means of close_context. Note that NULL is stored at
|
||||
R_CTX on error. */
|
||||
static gpg_error_t
|
||||
open_context (ctrl_t ctrl, assuan_context_t *r_ctx)
|
||||
{
|
||||
gpg_error_t err;
|
||||
dirmngr_local_t dml;
|
||||
|
||||
*r_ctx = NULL;
|
||||
for (;;)
|
||||
{
|
||||
for (dml = ctrl->dirmngr_local; dml && dml->is_active; dml = dml->next)
|
||||
;
|
||||
if (dml)
|
||||
{
|
||||
/* Found an inactive local session - return that. */
|
||||
assert (!dml->is_active);
|
||||
dml->is_active = 1;
|
||||
*r_ctx = dml->ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
dml = xtrycalloc (1, sizeof *dml);
|
||||
if (!dml)
|
||||
return gpg_error_from_syserror ();
|
||||
err = create_context (ctrl, &dml->ctx);
|
||||
if (err)
|
||||
{
|
||||
xfree (dml);
|
||||
return err;
|
||||
}
|
||||
/* To be on the Pth thread safe site we need to add it to a
|
||||
list; this is far easier than to have a lock for this
|
||||
function. It should not happen anyway but the code is free
|
||||
because we need it for the is_active check above. */
|
||||
dml->next = ctrl->dirmngr_local;
|
||||
ctrl->dirmngr_local = dml;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Close the assuan context CTX or return it to a pool of unused
|
||||
contexts. If CTX is NULL, the function does nothing. */
|
||||
static void
|
||||
close_context (ctrl_t ctrl, assuan_context_t ctx)
|
||||
{
|
||||
dirmngr_local_t dml;
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
for (dml = ctrl->dirmngr_local; dml; dml = dml->next)
|
||||
{
|
||||
if (dml->ctx == ctx)
|
||||
{
|
||||
if (!dml->is_active)
|
||||
log_fatal ("closing inactive dirmngr context %p\n", ctx);
|
||||
dml->is_active = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
log_fatal ("closing unknown dirmngr ctx %p\n", ctx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Data callback for the KS_SEARCH command. */
|
||||
static gpg_error_t
|
||||
ks_search_data_cb (void *opaque, const void *data, size_t datalen)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
struct ks_search_parm_s *parm = opaque;
|
||||
const char *line, *s;
|
||||
size_t rawlen, linelen;
|
||||
char fixedbuf[256];
|
||||
|
||||
if (parm->lasterr)
|
||||
return 0;
|
||||
|
||||
if (!data)
|
||||
return 0; /* Ignore END commands. */
|
||||
|
||||
put_membuf (&parm->saveddata, data, datalen);
|
||||
|
||||
again:
|
||||
line = peek_membuf (&parm->saveddata, &rawlen);
|
||||
if (!line)
|
||||
{
|
||||
parm->lasterr = gpg_error_from_syserror ();
|
||||
return parm->lasterr; /* Tell the server about our problem. */
|
||||
}
|
||||
if ((s = memchr (line, '\n', rawlen)))
|
||||
{
|
||||
linelen = s - line; /* That is the length excluding the LF. */
|
||||
if (linelen + 1 < sizeof fixedbuf)
|
||||
{
|
||||
/* We can use the static buffer. */
|
||||
memcpy (fixedbuf, line, linelen);
|
||||
fixedbuf[linelen] = 0;
|
||||
if (linelen && fixedbuf[linelen-1] == '\r')
|
||||
fixedbuf[linelen-1] = 0;
|
||||
err = parm->data_cb (parm->data_cb_value, fixedbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (linelen + 1 >= parm->helpbufsize)
|
||||
{
|
||||
xfree (parm->helpbuf);
|
||||
parm->helpbufsize = linelen + 1 + 1024;
|
||||
parm->helpbuf = xtrymalloc (parm->helpbufsize);
|
||||
if (!parm->helpbuf)
|
||||
{
|
||||
parm->lasterr = gpg_error_from_syserror ();
|
||||
return parm->lasterr;
|
||||
}
|
||||
}
|
||||
memcpy (parm->helpbuf, line, linelen);
|
||||
parm->helpbuf[linelen] = 0;
|
||||
if (linelen && parm->helpbuf[linelen-1] == '\r')
|
||||
parm->helpbuf[linelen-1] = 0;
|
||||
err = parm->data_cb (parm->data_cb_value, parm->helpbuf);
|
||||
}
|
||||
if (err)
|
||||
parm->lasterr = err;
|
||||
else
|
||||
{
|
||||
clear_membuf (&parm->saveddata, linelen+1);
|
||||
goto again; /* There might be another complete line. */
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Run the KS_SEARCH command using the search string SEARCHSTR. All
|
||||
data lines are passed to the CB function. That function is called
|
||||
with CB_VALUE as its first argument and the decoded data line as
|
||||
second argument. The callback function may modify the data line
|
||||
and it is guaranteed that this data line is a complete line with a
|
||||
terminating 0 character but without the linefeed. NULL is passed
|
||||
to the callback to indicate EOF. */
|
||||
gpg_error_t
|
||||
gpg_dirmngr_ks_search (ctrl_t ctrl, const char *searchstr,
|
||||
gpg_error_t (*cb)(void*, char *), void *cb_value)
|
||||
{
|
||||
gpg_error_t err;
|
||||
assuan_context_t ctx;
|
||||
struct ks_search_parm_s parm;
|
||||
char line[ASSUAN_LINELENGTH];
|
||||
|
||||
err = open_context (ctrl, &ctx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
{
|
||||
char *escsearchstr = percent_plus_escape (searchstr);
|
||||
if (!escsearchstr)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
close_context (ctrl, ctx);
|
||||
return err;
|
||||
}
|
||||
snprintf (line, sizeof line, "KS_SEARCH -- %s", escsearchstr);
|
||||
xfree (escsearchstr);
|
||||
}
|
||||
|
||||
memset (&parm, 0, sizeof parm);
|
||||
init_membuf (&parm.saveddata, 1024);
|
||||
parm.data_cb = cb;
|
||||
parm.data_cb_value = cb_value;
|
||||
|
||||
err = assuan_transact (ctx, line, ks_search_data_cb, &parm,
|
||||
NULL, NULL, NULL, NULL);
|
||||
if (!err)
|
||||
err = cb (cb_value, NULL); /* Send EOF. */
|
||||
|
||||
xfree (get_membuf (&parm.saveddata, NULL));
|
||||
xfree (parm.helpbuf);
|
||||
|
||||
close_context (ctrl, ctx);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Data callback for the KS_GET command. */
|
||||
static gpg_error_t
|
||||
ks_get_data_cb (void *opaque, const void *data, size_t datalen)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
struct ks_get_parm_s *parm = opaque;
|
||||
size_t nwritten;
|
||||
|
||||
if (!data)
|
||||
return 0; /* Ignore END commands. */
|
||||
|
||||
if (es_write (parm->memfp, data, datalen, &nwritten))
|
||||
err = gpg_error_from_syserror ();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Run the KS_GET command using the patterns in the array PATTERN. On
|
||||
success an estream object is returned to retrieve the keys. On
|
||||
error an error code is returned and NULL stored at R_FP.
|
||||
|
||||
The pattern may only use search specification which a keyserver can
|
||||
use to retriev keys. Because we know the format of the pattern we
|
||||
don't need to escape the patterns before sending them to the
|
||||
server.
|
||||
|
||||
If there are too many patterns the function returns an error. That
|
||||
could be fixed by issuing several search commands or by
|
||||
implementing a different interface. However with long keyids we
|
||||
are able to ask for (1000-10-1)/(2+8+1) = 90 keys at once. */
|
||||
gpg_error_t
|
||||
gpg_dirmngr_ks_get (ctrl_t ctrl, char **pattern, estream_t *r_fp)
|
||||
{
|
||||
gpg_error_t err;
|
||||
assuan_context_t ctx;
|
||||
struct ks_get_parm_s parm;
|
||||
char *line = NULL;
|
||||
size_t linelen;
|
||||
membuf_t mb;
|
||||
int idx;
|
||||
|
||||
memset (&parm, 0, sizeof parm);
|
||||
|
||||
*r_fp = NULL;
|
||||
|
||||
err = open_context (ctrl, &ctx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Lump all patterns into one string. */
|
||||
init_membuf (&mb, 1024);
|
||||
put_membuf_str (&mb, "KS_GET --");
|
||||
for (idx=0; pattern[idx]; idx++)
|
||||
{
|
||||
put_membuf (&mb, " ", 1); /* Append Delimiter. */
|
||||
put_membuf_str (&mb, pattern[idx]);
|
||||
}
|
||||
put_membuf (&mb, "", 1); /* Append Nul. */
|
||||
line = get_membuf (&mb, &linelen);
|
||||
if (!line)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
if (linelen + 2 >= ASSUAN_LINELENGTH)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_TOO_MANY);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
parm.memfp = es_fopenmem (0, "rwb");
|
||||
if (!parm.memfp)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
err = assuan_transact (ctx, line, ks_get_data_cb, &parm,
|
||||
NULL, NULL, NULL, NULL);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
es_rewind (parm.memfp);
|
||||
*r_fp = parm.memfp;
|
||||
parm.memfp = NULL;
|
||||
|
||||
leave:
|
||||
es_fclose (parm.memfp);
|
||||
xfree (line);
|
||||
close_context (ctrl, ctx);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Handle the KS_PUT inquiries. */
|
||||
static gpg_error_t
|
||||
ks_put_inq_cb (void *opaque, const char *line)
|
||||
{
|
||||
struct ks_put_parm_s *parm = opaque;
|
||||
gpg_error_t err = 0;
|
||||
|
||||
if (!strncmp (line, "KEYBLOCK", 8) && (line[8] == ' ' || !line[8]))
|
||||
{
|
||||
if (parm->data)
|
||||
err = assuan_send_data (parm->ctx, parm->data, parm->datalen);
|
||||
}
|
||||
else if (!strncmp (line, "KEYBLOCK_INFO", 13) && (line[13]==' ' || !line[13]))
|
||||
{
|
||||
kbnode_t node;
|
||||
estream_t fp;
|
||||
|
||||
/* Parse the keyblock and send info lines back to the server. */
|
||||
fp = es_fopenmem (0, "rw");
|
||||
if (!fp)
|
||||
err = gpg_error_from_syserror ();
|
||||
|
||||
for (node = parm->keyblock; !err && node; node=node->next)
|
||||
{
|
||||
switch(node->pkt->pkttype)
|
||||
{
|
||||
case PKT_PUBLIC_KEY:
|
||||
case PKT_PUBLIC_SUBKEY:
|
||||
{
|
||||
PKT_public_key *pk = node->pkt->pkt.public_key;
|
||||
|
||||
keyid_from_pk (pk, NULL);
|
||||
|
||||
es_fprintf (fp, "%s:%08lX%08lX:%u:%u:%u:%u:%s%s:\n",
|
||||
node->pkt->pkttype==PKT_PUBLIC_KEY? "pub" : "sub",
|
||||
(ulong)pk->keyid[0], (ulong)pk->keyid[1],
|
||||
pk->pubkey_algo,
|
||||
nbits_from_pk (pk),
|
||||
pk->timestamp,
|
||||
pk->expiredate,
|
||||
pk->flags.revoked? "r":"",
|
||||
pk->has_expired? "e":"");
|
||||
}
|
||||
break;
|
||||
|
||||
case PKT_USER_ID:
|
||||
{
|
||||
PKT_user_id *uid = node->pkt->pkt.user_id;
|
||||
int r;
|
||||
|
||||
if (!uid->attrib_data)
|
||||
{
|
||||
es_fprintf (fp, "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))
|
||||
es_fprintf (fp, "%%%02X", (byte)uid->name[r]);
|
||||
else
|
||||
es_putc (uid->name[r], fp);
|
||||
}
|
||||
|
||||
es_fprintf (fp, ":%u:%u:%s%s:\n",
|
||||
uid->created,uid->expiredate,
|
||||
uid->is_revoked? "r":"",
|
||||
uid->is_expired? "e":"");
|
||||
}
|
||||
}
|
||||
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))
|
||||
{
|
||||
es_fprintf (fp, "sig:%08lX%08lX:%X:%u:%u:\n",
|
||||
(ulong)sig->keyid[0],(ulong)sig->keyid[1],
|
||||
sig->sig_class, sig->timestamp,
|
||||
sig->expiredate);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
/* Given that the last operation was an es_fprintf we should
|
||||
get the correct ERRNO if ferror indicates an error. */
|
||||
if (es_ferror (fp))
|
||||
err = gpg_error_from_syserror ();
|
||||
}
|
||||
|
||||
/* Without an error and if we have an keyblock at all, send the
|
||||
data back. */
|
||||
if (!err && parm->keyblock)
|
||||
{
|
||||
int rc;
|
||||
char buffer[512];
|
||||
size_t nread;
|
||||
|
||||
es_rewind (fp);
|
||||
while (!(rc=es_read (fp, buffer, sizeof buffer, &nread)) && nread)
|
||||
{
|
||||
err = assuan_send_data (parm->ctx, buffer, nread);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
if (!err && rc)
|
||||
err = gpg_error_from_syserror ();
|
||||
}
|
||||
es_fclose (fp);
|
||||
}
|
||||
else
|
||||
return gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Send a key to the configured server. {DATA,DATLEN} contains the
|
||||
key in OpenPGP binary transport format. If KEYBLOCK is not NULL it
|
||||
has the internal representaion of that key; this is for example
|
||||
used to convey meta data to LDAP keyservers. */
|
||||
gpg_error_t
|
||||
gpg_dirmngr_ks_put (ctrl_t ctrl, void *data, size_t datalen, kbnode_t keyblock)
|
||||
{
|
||||
gpg_error_t err;
|
||||
assuan_context_t ctx;
|
||||
struct ks_put_parm_s parm;
|
||||
|
||||
memset (&parm, 0, sizeof parm);
|
||||
|
||||
/* We are going to parse the keyblock, thus we better make sure the
|
||||
all information is readily available. */
|
||||
if (keyblock)
|
||||
merge_keys_and_selfsig (keyblock);
|
||||
|
||||
err = open_context (ctrl, &ctx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
parm.ctx = ctx;
|
||||
parm.keyblock = keyblock;
|
||||
parm.data = data;
|
||||
parm.datalen = datalen;
|
||||
|
||||
err = assuan_transact (ctx, "KS_PUT", NULL, NULL,
|
||||
ks_put_inq_cb, &parm, NULL, NULL);
|
||||
|
||||
close_context (ctrl, ctx);
|
||||
return err;
|
||||
}
|
32
g10/call-dirmngr.h
Normal file
32
g10/call-dirmngr.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* call-dirmngr.h - GPG operations to the Dirmngr
|
||||
* Copyright (C) 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef GNUPG_G10_CALL_DIRMNGR_H
|
||||
#define GNUPG_G10_CALL_DIRMNGR_H
|
||||
|
||||
void gpg_dirmngr_deinit_session_data (ctrl_t ctrl);
|
||||
|
||||
gpg_error_t gpg_dirmngr_ks_search (ctrl_t ctrl, const char *searchstr,
|
||||
gpg_error_t (*cb)(void*, char *),
|
||||
void *cb_value);
|
||||
gpg_error_t gpg_dirmngr_ks_get (ctrl_t ctrl, char *pattern[], estream_t *r_fp);
|
||||
gpg_error_t gpg_dirmngr_ks_put (ctrl_t ctrl, void *data, size_t datalen,
|
||||
kbnode_t keyblock);
|
||||
|
||||
|
||||
#endif /*GNUPG_G10_CALL_DIRMNGR_H*/
|
54
g10/export.c
54
g10/export.c
@ -114,6 +114,60 @@ export_pubkeys_stream (ctrl_t ctrl, iobuf_t out, strlist_t users,
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Export a single key into a memory buffer.
|
||||
*/
|
||||
gpg_error_t
|
||||
export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options,
|
||||
kbnode_t *r_keyblock, void **r_data, size_t *r_datalen)
|
||||
{
|
||||
gpg_error_t err;
|
||||
iobuf_t iobuf;
|
||||
int any;
|
||||
strlist_t helplist;
|
||||
|
||||
*r_keyblock = NULL;
|
||||
*r_data = NULL;
|
||||
*r_datalen = 0;
|
||||
|
||||
helplist = NULL;
|
||||
if (!add_to_strlist_try (&helplist, keyspec))
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
iobuf = iobuf_temp ();
|
||||
err = do_export_stream (ctrl, iobuf, helplist, 0, r_keyblock, options, &any);
|
||||
if (!err && !any)
|
||||
err = gpg_error (GPG_ERR_NOT_FOUND);
|
||||
if (!err)
|
||||
{
|
||||
const void *src;
|
||||
size_t datalen;
|
||||
|
||||
iobuf_flush_temp (iobuf);
|
||||
src = iobuf_get_temp_buffer (iobuf);
|
||||
datalen = iobuf_get_temp_length (iobuf);
|
||||
if (!datalen)
|
||||
err = gpg_error (GPG_ERR_NO_PUBKEY);
|
||||
else if (!(*r_data = xtrymalloc (datalen)))
|
||||
err = gpg_error_from_syserror ();
|
||||
else
|
||||
{
|
||||
memcpy (*r_data, src, datalen);
|
||||
*r_datalen = datalen;
|
||||
}
|
||||
}
|
||||
iobuf_close (iobuf);
|
||||
free_strlist (helplist);
|
||||
if (err && *r_keyblock)
|
||||
{
|
||||
release_kbnode (*r_keyblock);
|
||||
*r_keyblock = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
export_seckeys (ctrl_t ctrl, strlist_t users )
|
||||
{
|
||||
|
13
g10/gpg.c
13
g10/gpg.c
@ -1,6 +1,6 @@
|
||||
/* gpg.c - The GnuPG utility (main for gpg)
|
||||
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
|
||||
* 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
||||
* 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -54,6 +54,7 @@
|
||||
#include "exec.h"
|
||||
#include "gc-opt-flags.h"
|
||||
#include "asshelp.h"
|
||||
#include "call-dirmngr.h"
|
||||
|
||||
#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
|
||||
#define MY_O_BINARY O_BINARY
|
||||
@ -1822,7 +1823,7 @@ gpg_init_default_ctrl (ctrl_t ctrl)
|
||||
static void
|
||||
gpg_deinit_default_ctrl (ctrl_t ctrl)
|
||||
{
|
||||
(void)ctrl;
|
||||
gpg_dirmngr_deinit_session_data (ctrl);
|
||||
}
|
||||
|
||||
|
||||
@ -2658,7 +2659,7 @@ main (int argc, char **argv)
|
||||
break;
|
||||
case oKeyServer:
|
||||
{
|
||||
struct keyserver_spec *keyserver;
|
||||
keyserver_spec_t keyserver;
|
||||
keyserver = parse_keyserver_uri (pargs.r.ret_str,0,
|
||||
configname,configlineno);
|
||||
if (!keyserver)
|
||||
@ -2853,7 +2854,7 @@ main (int argc, char **argv)
|
||||
break;
|
||||
case oDefaultKeyserverURL:
|
||||
{
|
||||
struct keyserver_spec *keyserver;
|
||||
keyserver_spec_t keyserver;
|
||||
keyserver = parse_keyserver_uri (pargs.r.ret_str,1,
|
||||
configname,configlineno);
|
||||
if (!keyserver)
|
||||
@ -3755,7 +3756,7 @@ main (int argc, char **argv)
|
||||
append_to_strlist2 (&sl, *argv, utf8_strings);
|
||||
rc = keyserver_search (ctrl, sl);
|
||||
if (rc)
|
||||
log_error(_("keyserver search failed: %s\n"),g10_errstr(rc));
|
||||
log_error (_("keyserver search failed: %s\n"), gpg_strerror (rc));
|
||||
free_strlist (sl);
|
||||
break;
|
||||
|
||||
|
@ -48,6 +48,10 @@
|
||||
/* Object used to keep state locally to server.c . */
|
||||
struct server_local_s;
|
||||
|
||||
/* Object used to keep state locally to call-dirmngr.c . */
|
||||
struct dirmngr_local_s;
|
||||
typedef struct dirmngr_local_s *dirmngr_local_t;
|
||||
|
||||
/* Object used to describe a keyblok node. */
|
||||
typedef struct kbnode_struct *KBNODE;
|
||||
typedef struct kbnode_struct *kbnode_t;
|
||||
@ -58,7 +62,11 @@ typedef struct kbnode_struct *kbnode_t;
|
||||
gpg_init_default_ctrl(). */
|
||||
struct server_control_s
|
||||
{
|
||||
/* Local data for server.c */
|
||||
struct server_local_s *server_local;
|
||||
|
||||
/* Local data for call-dirmngr.c */
|
||||
dirmngr_local_t dirmngr_local;
|
||||
};
|
||||
|
||||
|
||||
|
26
g10/import.c
26
g10/import.c
@ -243,6 +243,32 @@ import_keys_stream (ctrl_t ctrl, IOBUF inp, void *stats_handle,
|
||||
fpr, fpr_len, options);
|
||||
}
|
||||
|
||||
|
||||
/* Variant of import_keys_stream reading from an estream_t. */
|
||||
int
|
||||
import_keys_es_stream (ctrl_t ctrl, estream_t fp, void *stats_handle,
|
||||
unsigned char **fpr, size_t *fpr_len,
|
||||
unsigned int options)
|
||||
{
|
||||
int rc;
|
||||
iobuf_t inp;
|
||||
|
||||
inp = iobuf_esopen (fp, "r", 1);
|
||||
if (!inp)
|
||||
{
|
||||
rc = gpg_error_from_syserror ();
|
||||
log_error ("iobuf_esopen failed: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = import_keys_internal (ctrl, inp, NULL, 0, stats_handle,
|
||||
fpr, fpr_len, options);
|
||||
|
||||
iobuf_close (inp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
import (ctrl_t ctrl, IOBUF inp, const char* fname,struct stats_s *stats,
|
||||
unsigned char **fpr,size_t *fpr_len,unsigned int options )
|
||||
|
@ -40,7 +40,7 @@ int keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len,
|
||||
int keyserver_import_keyid (ctrl_t ctrl, u32 *keyid,
|
||||
struct keyserver_spec *keyserver);
|
||||
int keyserver_refresh (ctrl_t ctrl, strlist_t users);
|
||||
int keyserver_search (ctrl_t ctrl, strlist_t tokens);
|
||||
gpg_error_t keyserver_search (ctrl_t ctrl, strlist_t tokens);
|
||||
int keyserver_fetch (ctrl_t ctrl, strlist_t urilist);
|
||||
int keyserver_import_cert (ctrl_t ctrl, const char *name,
|
||||
unsigned char **fpr,size_t *fpr_len);
|
||||
|
1332
g10/keyserver.c
1332
g10/keyserver.c
File diff suppressed because it is too large
Load Diff
@ -287,6 +287,9 @@ void import_keys (ctrl_t ctrl, char **fnames, int nnames,
|
||||
int import_keys_stream (ctrl_t ctrl, iobuf_t inp, void *stats_hd,
|
||||
unsigned char **fpr,
|
||||
size_t *fpr_len, unsigned int options);
|
||||
int import_keys_es_stream (ctrl_t ctrl, estream_t fp, void *stats_handle,
|
||||
unsigned char **fpr, size_t *fpr_len,
|
||||
unsigned int options);
|
||||
void *import_new_stats_handle (void);
|
||||
void import_release_stats_handle (void *p);
|
||||
void import_print_stats (void *hd);
|
||||
@ -299,6 +302,10 @@ int parse_export_options(char *str,unsigned int *options,int noisy);
|
||||
int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options );
|
||||
int export_pubkeys_stream (ctrl_t ctrl, iobuf_t out, strlist_t users,
|
||||
kbnode_t *keyblock_out, unsigned int options );
|
||||
gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec,
|
||||
unsigned int options,
|
||||
kbnode_t *r_keyblock,
|
||||
void **r_data, size_t *r_datalen);
|
||||
int export_seckeys (ctrl_t ctrl, strlist_t users);
|
||||
int export_secsubkeys (ctrl_t ctrl, strlist_t users);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* options.h
|
||||
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
|
||||
* 2007, 2010 Free Software Foundation, Inc.
|
||||
* 2007, 2010, 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -35,6 +35,13 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Declaration of a keyserver spec type. The definition is found in
|
||||
../common/keyserver.h. */
|
||||
struct keyserver_spec;
|
||||
typedef struct keyserver_spec *keyserver_spec_t;
|
||||
|
||||
|
||||
/* Global options for GPG. */
|
||||
EXTERN_UNLESS_MAIN_MODULE
|
||||
struct
|
||||
{
|
||||
@ -130,22 +137,7 @@ struct
|
||||
int not_dash_escaped;
|
||||
int escape_from;
|
||||
int lock_once;
|
||||
struct keyserver_spec
|
||||
{
|
||||
char *uri;
|
||||
char *scheme;
|
||||
char *auth;
|
||||
char *host;
|
||||
char *port;
|
||||
char *path;
|
||||
char *opaque;
|
||||
strlist_t options;
|
||||
struct
|
||||
{
|
||||
unsigned int direct_uri:1;
|
||||
} flags;
|
||||
struct keyserver_spec *next;
|
||||
} *keyserver;
|
||||
keyserver_spec_t keyserver; /* The list of configured keyservers. */
|
||||
struct
|
||||
{
|
||||
unsigned int options;
|
||||
@ -245,7 +237,7 @@ struct
|
||||
AKL_KEYSERVER,
|
||||
AKL_SPEC
|
||||
} type;
|
||||
struct keyserver_spec *spec;
|
||||
keyserver_spec_t spec;
|
||||
struct akl *next;
|
||||
} *auto_key_locate;
|
||||
|
||||
|
@ -1,3 +1,9 @@
|
||||
2011-01-20 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpgkeys_hkp.c (get_name): Remove test for KS_GETNAME. It is
|
||||
always true.
|
||||
(search_key): Remove test for KS_GETNAME. It is always false.
|
||||
|
||||
2009-08-26 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpgkeys_hkp.c: Include util.h.
|
||||
|
@ -340,7 +340,7 @@ get_name(const char *getkey)
|
||||
opt->path,
|
||||
appendable_path (opt->path,"/pks/lookup?op=get&options=mr&search="),
|
||||
searchkey_encoded,
|
||||
opt->action == KS_GETNAME? "&exact=on":"",
|
||||
"&exact=on",
|
||||
NULL);
|
||||
if(!request)
|
||||
{
|
||||
@ -429,7 +429,6 @@ search_key(const char *searchkey)
|
||||
appendable_path (opt->path, "/pks/lookup?op=index&options=mr&search="),
|
||||
hexprefix,
|
||||
searchkey_encoded,
|
||||
opt->action == KS_GETNAME? "&exact=on":"",
|
||||
NULL);
|
||||
if(!request)
|
||||
{
|
||||
@ -687,7 +686,7 @@ main(int argc,char *argv[])
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if(ks_strcasecmp(opt->scheme,"hkps")==0)
|
||||
if(ascii_strcasecmp(opt->scheme,"hkps")==0)
|
||||
{
|
||||
proto="https";
|
||||
port="443";
|
||||
|
@ -1,3 +1,7 @@
|
||||
2011-01-20 Werner Koch <wk@g10code.com>
|
||||
|
||||
* de.po: Fix two fuzzies.
|
||||
|
||||
2010-10-21 Werner Koch <wk@g10code.com>
|
||||
|
||||
* POTFILES.in: Add files in dirmngr/.
|
||||
|
Loading…
x
Reference in New Issue
Block a user