Page MenuHome GnuPG

No OneTemporary

diff --git a/keyserver/ChangeLog b/keyserver/ChangeLog
index 4b3855a62..134506098 100644
--- a/keyserver/ChangeLog
+++ b/keyserver/ChangeLog
@@ -1,585 +1,590 @@
+2004-10-11 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgkeys_finger.c, gpgkeys_hkp.c, gpgkeys_http.c, gpgkeys_ldap.c:
+ Fix a few occurances of "filename" to `filename'.
+
2004-10-11 Werner Koch <wk@g10code.com>
* gpgkeys_finger.c: New.
2004-08-27 Stefan Bellon <sbellon@sbellon.de>
* gpgkeys_hkp.c (search_key): Fix the prior faulty fix by
introducing a cast but leaving skey unsigned.
* gpgkeys_hkp.c (search_key): Change type of variable skey from
unsigned char* to char* to fix type incompatibility.
2004-08-23 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (get_key, search_key), gpgkeys_hkp.c (get_key,
search_key), gpgkeys_http.c (get_key): Do not give informational
logs since this is now done inside gpg.
* gpgkeys_hkp.c (dehtmlize): Understand the quote character
(i.e. "&quot;") in HTML responses.
(search_key): Search key must be unsigned for url encoder to work
properly for 8-bit values.
* gpgkeys_ldap.c (get_key): Factor out informational display into
new function build_info().
* gpgkeys_ldap.c (build_attrs): Properly terminate user ID strings
that got shrunk due to encoding.
2004-08-22 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (find_basekeyspacedn): Use LDAP_SCOPE_BASE along
with a full DN rather than LDAP_SCOPE_ONELEVEL plus a filter to
find the pgpServerInfo object. Some LDAP setups don't like the
search.
(main): Stop binding to the server since it seems no server really
requires it, and some require it not be there.
2004-07-29 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (main): Add "debug" option. This is only really
useful with OpenLDAP, but it's practically vital to debug SSL and
TLS setups. Add "basedn" option. This allows users to override
the autodetection for base DN. SSL overrides TLS, so TLS will not
be started on SSL connections (starting an already started car).
2004-07-28 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (build_attrs): Add "pgpKeySize" and "pgpSubKeyID"
attributes so we can do subkey searches.
* gpgkeys_ldap.c (main): Under certain error conditions, we might
try and unbind twice. Don't.
* gpgkeys_ldap.c (join_two_modlists): New.
(send_key): Use new function so we can try a modify operation
first, and fail over to an add if that fails. Add cannot cope
with the NULLs at the head of the modify request, so we jump into
the list in the middle.
2004-07-27 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (main): Don't try and error out before making a
ldaps connection to the NAI keyserver since we cannot tell if it
is a NAI keyserver until we connect. Fail if we cannot find a
base keyspace DN. Fix a false success message for TLS being
enabled.
2004-07-20 Werner Koch <wk@gnupg.org>
* gpgkeys_ldap.c [_WIN32]: Include Windows specific header files.
Suggested by Brian Gladman.
2004-05-26 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_http.c: General polish and removal of leftover stuff
from gpgkeys_hkp.c.
2004-05-21 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_http.c (get_key): Cosmetic fix - make sure that URLs
with no path use a path of "/".
* gpgkeys_ldap.c (ldap2epochtime): We can always rely on timegm()
being available now, since it's a replacement function.
2004-05-20 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_http.c: New program to do a simple HTTP file fetch using
the keyserver interface.
* Makefile.am: Build it.
2004-02-28 David Shaw <dshaw@jabberwocky.com>
* Makefile.am: Don't split LDADD across two lines since some make
programs can't handle blank lines after a \ continuation. Noted
by Christoph Moench-Tegeder.
2004-02-25 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (send_key): List pgpCertID as one of the deleted
attributes. This guarantees that if something goes wrong, we
won't be able to complete the transaction, thus leaving any key
already existing on the server intact.
2004-02-23 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (delete_one_attr): Removed.
(make_one_attr): Delete functionality added. Optional deduping
functionality added (currently only used for pgpSignerID).
(build_attrs): Translate sig entries into pgpSignerID. Properly
build the timestamp for pgpKeyCreateTime and pgpKeyExpireTime.
2004-02-22 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (delete_one_attr): New function to replace
attributes with NULL (a "delete" that works even for nonexistant
attributes).
(send_key): Use it here to remove attributes so a modify operation
starts with a clean playing field. Bias sends to modify before
add, since (I suspect) people update their existing keys more
often than they make and send new keys to the server.
2004-02-21 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (epoch2ldaptime): New. Converse of
ldap2epochtime.
(make_one_attr): New. Build a modification list in memory to send
to the LDAP server.
(build_attrs): New. Parse INFO lines sent over by gpg.
(free_mod_values): New. Unwinds a modification list.
(send_key_keyserver): Renamed from old send_key().
(send_key): New function to send a key to a LDAP server.
(main): Use send_key() for real LDAP servers, send_key_keyserver()
otherwise.
2004-02-20 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c: Replacement prototypes for setenv and unsetenv.
(search_key): Catch a SIZELIMIT_EXCEEDED error and show the user
whatever the server did give us.
(find_basekeyspacedn): There is no guarantee that namingContexts
will be readable.
* Makefile.am: Link gpgkeys_ldap with libutil.a to get the
replacement functions (and eventually translations, etc).
2004-02-19 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (ldap2epochtime): LDAP timestamps are UTC, so do
not correct for timezones.
(main): Find the basekeyspacedn before we try to start TLS, so we
can give a better error message when a user tries to use TLS with
a LDAP keyserver.
* Makefile.am: Add automake conditionals to symlink gpgkeys_ldaps
to gpgkeys_ldap when needed.
* gpgkeys_ldap.c (main): Add support for LDAPS and TLS
connections. These are only useful and usable when talking to
real LDAP keyservers. Add new "tls" option to tune TLS use from
off, to try quietly, to try loudly, or to require TLS.
* gpgkeys_ldap.c (find_basekeyspacedn): New function to figure out
what kind of LDAP server we're talking to (either real LDAP or the
LDAP keyserver), and return the baseKeySpaceDN to find keys under.
(main): Call it from here, and remove the old code that only
handled the LDAP keyserver.
2004-02-18 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (ldap_to_gpg_err): Make sure that
LDAP_OPT_ERROR_NUMBER is defined before we use it.
* gpgkeys_mailto.in: Fix VERSION number.
2004-01-13 Werner Koch <wk@gnupg.org>
* gpgkeys_hkp.c (send_key): Add a content type.
2004-01-11 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (search_key): Catch a mangled input file (useful
if something other than GnuPG is calling the program).
(main): Avoid possible pre-string write. Noted by Christian
Biere.
* gpgkeys_ldap.c (main): Avoid possible pre-string write.
2003-12-28 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (send_key, get_key, main): Work with new HTTP code
that passes the proxy in from the outside. If the command file
sends a proxy, use it. If it sends "http-proxy" with no
arguments, use $http_proxy from the environment. Suggested by
Christian Biere.
2003-12-28 Stefan Bellon <sbellon@sbellon.de>
* gpgkeys_hkp.c, gpgkeys_ldap.c [__riscos__]: Removal of
unnecessary #ifdef __riscos__ sections.
2003-11-27 Werner Koch <wk@gnupg.org>
* gpgkeys_hkp.c (get_key): Fixed invalid use of fprintf without
format string.
2003-10-25 Werner Koch <wk@gnupg.org>
* Makefile.am (gpgkeys_hkp_LDADD): Replaced INTLLIBS by LIBINTL.
2003-07-10 David Shaw <dshaw@jabberwocky.com>
* Makefile.am: Use W32LIBS where appropriate.
2003-05-30 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c, gpgkeys_ldap.c: #include <getopt.h> if it is
available. Also include extern references for optarg and optind
since there is no guarantee that any header file will include
them. Standards? We don't need no stinkin' standards.
* Makefile.am: Use @GETOPT@ to pull in libiberty on those
platforms that need it.
2003-04-08 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (dehtmlize, parse_hkp_index): Fix memory
corruption bug on some platforms.
2003-03-11 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (get_key): Properly handle CRLF line endings in
the armored key.
(main): Accept "try-dns-srv" option.
* Makefile.am: Use @CAPLIBS@ to link in -lcap if we are using
capabilities. Use @SRVLIBS@ to link in the resolver if we are
using DNS SRV.
2003-02-11 David Shaw <dshaw@jabberwocky.com>
* Makefile.am: Use a local copy of libexecdir along with @PACKAGE@
so it can be easily overridden at make time.
2003-01-29 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_mailto.in: Fix regexp to work properly if the "keyid" is
not a keyid, but rather a text string from the user ID.
2003-01-06 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (get_key): Use options=mr when getting a key so
keyserver doesn't attach the HTML header which we will just have
to discard.
2002-11-17 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (main), gpgkeys_hkp.c (main): Use new keyserver
protocol version.
2002-11-14 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (get_key): The deduping code requires
"pgpcertid", but that was not available when running without
verbose on. Noted by Stefan.
2002-11-10 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (get_key): Fix typo in deduping code.
2002-11-05 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (key_in_keylist, add_key_to_keylist,
free_keylist, get_key, search_key): The LDAP keyserver doesn't
remove duplicates, so remove them locally. Do not include the key
modification time in the search response.
2002-11-04 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (send_key), gpgkeys_ldap.c (send_key): Properly
handle an input file that does not include any key data at all.
2002-10-24 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (main), gpgkeys_ldap.c (main): Add -V flag to
output protocol and program version.
2002-10-21 David Shaw <dshaw@jabberwocky.com>
* Makefile.am: Anything linking with libutil.a needs INTLLIBS as
well on platforms where INTLLIBS is set.
2002-10-14 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (write_quoted): Use %-encoding instead of
\-encoding.
(parse_hkp_index): Use new keyserver key listing format, and add
support for disabled keys via include-disabled.
* gpgkeys_ldap.c (get_key): Don't print keysize unless it's >0.
(printquoted): Use %-encoding instead of \-encoding.
(search_key): Use new keyserver key listing format.
2002-10-08 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (search_key, main): Make sure LDAP values are
freed in case of error.
* gpgkeys_ldap.c (fail_all): New function to unwind a keylist and
error each item.
(main): Call fail_all from here, as needed. Also add a NO_MEMORY
error in an appropriate place and fix error return code.
(ldap_err_to_gpg_err): Add KEYSERVER_UNREACHABLE.
* gpgkeys_hkp.c (fail_all): New function to unwind a keylist and
error each item.
(main): Call fail_all from here. Also add a NO_MEMORY error in an
appropriate place.
(get_key): Use new UNREACHABLE error for network errors.
2002-09-26 Werner Koch <wk@gnupg.org>
* gpgkeys_ldap.c (send_key): Removed non-constant initializers.
2002-09-24 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (ldap_err_to_gpg_err, ldap_to_gpg_err, send_key,
get_key, search_key, main): Some minor error reporting
enhancements for use with GPA (show reasons for KEY FAILED).
* gpgkeys_hkp.c (send_key, get_key, search_key, main): Some minor
error reporting enhancements for use with GPA (show reasons for
KEY FAILED).
2002-09-20 Werner Koch <wk@gnupg.org>
* gpgkeys_hkp.c (handle_old_hkp_index): s/input/inp/ to avoid
shadowing warning.
2002-09-19 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (get_key, handle_old_hkp_index, search_key):
Properly handle line truncation.
2002-09-16 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_mailto.in: Add quasi-RFC-2368 mailto:email@addr?from=
syntax so people can set their own email address to respond to.
* gpgkeys_hkp.c (get_key): Properly respond with KEY FAILED (to
gpg) and "key not found" (to user) on failure.
2002-09-13 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c: (search_key, handle_old_hkp_index): Try and
request a machine-readable key index. If the server supports
this, pass it through. If the server does not support it, parse
the "index" page.
2002-09-12 Stefan Bellon <sbellon@sbellon.de>
* gpgkeys_hkp.c: Tidied up RISC OS initializations.
2002-09-12 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (main): Remove warning - this is no longer
experimental code.
2002-09-09 Werner Koch <wk@gnupg.org>
* gpgkeys_hkp.c (send_key, get_key, search_key): Check return
value of malloc.
(dehtmlize): Use ascii_tolower to protect against weird locales.
Cast the argument for isspace for the sake of broken HP/UXes.
(search_key): Check return value of realloc.
2002-09-09 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (get_key): Some compilers (RISC OS, HPUX c89)
don't like using variables as array initializers.
* gpgkeys_hkp.c (send_key): Use CRLF in headers.
2002-08-28 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (parse_hkp_index): Use same types on all
platforms. This was probably leftover from earlier code where the
typing mattered.
* gpgkeys_hkp.c: Overall cleanup from iobuf conversion. Be
consistent in m_alloc and malloc usage. Remove include-disabled
(meaningless on HKP). RISC OS tweak.
2002-08-27 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c, Makefile.am: Convert over to using iobufs.
* gpgkeys_hkp.c (http_get, http_post): Use CRLF for line endings.
* gpgkeys_hkp.c: Include util.h on RISC OS as per Stefan. Include
a replacement for hstrerror() for those platforms (such as RISC
OS) that don't have it.
2002-08-26 David Shaw <dshaw@jabberwocky.com>
* Makefile.am: May as well include gpgkeys_hkp.c in the
distribution now. It works well enough without proxies, and isn't
built by default. It would be good to get some test experience
with it.
* gpgkeys_hkp.c (main): Don't warn about include-subkeys - it
isn't unsupported, it's actually non-meaningful in the context of
HKP (yet).
* gpgkeys_hkp.c (parse_hkp_index, dehtmlize): Move HTML
functionality into new "dehtmlize" function. Remove HTML before
trying to parse each line from the keyserver. If the keyserver
provides key type information in the listing, use it. (Copy over
from g10/hkp.c).
2002-08-19 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (get_key, parse_hkp_index): Bring over latest code
from g10/hkp.c.
* gpgkeys_ldap.c (get_key): Fix cosmetic URL display problem
(extra ":" at the end).
2002-08-03 Stefan Bellon <sbellon@sbellon.de>
* gpgkeys_ldap.c: Tidied up RISC OS initializations.
2002-07-25 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c: "Warning" -> "WARNING"
2002-07-24 David Shaw <dshaw@jabberwocky.com>
* Makefile.am: Install keyserver helpers in @GNUPG_LIBEXECDIR@
2002-07-15 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (send_key, get_key, main): Consult the server
version string to determine whether to use pgpKey or pgpKeyV2.
2002-07-09 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_mailto.in: Use new OPAQUE tag for non net-path URIs.
Fail more elegantly if there is no email address to send to. Show
the GnuPG version in the message body.
2002-07-04 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (get_key), gpgkeys_hkp.c (get_key): Display
keyserver URI as a URI, but only if verbose.
2002-07-01 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (parse_hkp_index): Error if the keyserver returns
an unparseable HKP response.
* gpgkeys_hkp.c (main): Warn on honor-http-proxy,
broken-http-proxy, and include-subkeys (not supported yet).
* gpgkeys_ldap.c (main), gpgkeys_hkp.c (http_connect, main): Fix
some shadowing warnings.
2002-06-11 David Shaw <dshaw@jabberwocky.com>
* Makefile.am: Don't hard-code the LDAP libraries - get them from
LDAPLIBS via configure. Also, gpgkeys_hkp is a program, not a
script.
2002-06-10 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (include_subkeys): Default "include-subkeys" to
off, since GnuPG now defaults it to on.
2002-06-06 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_hkp.c (parse_hkp_index): Type tweaks.
* gpgkeys_hkp.c (main): Add experimental code warning.
2002-06-05 David Shaw <dshaw@jabberwocky.com>
* Makefile.am, gpgkeys_hkp.c (new): Experimental HKP keyserver
interface.
2002-05-08 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c: Include <lber.h> if we absolutely must. This
helps when compiling against a very old OpenLDAP.
2002-04-29 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_mailto.in: Properly handle key requests in full
fingerprint form.
2002-03-29 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (printquoted): Quote backslashes within keyserver
search responses.
2002-02-25 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap (get_key): LDAP keyservers do not support v3
fingerprints, so error out if someone tries. Actually, they don't
support any fingerprints, but at least we can calculate a keyid
from a v4 fingerprint.
2002-02-23 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap: Clarify the notion of a partial failure. This is
possible if more than one key is being handled in a batch, and one
fails while the other succeeds. Note that a search that comes up
with no results is not a failure - that is a valid response of "no
answer".
* gpgkeys_ldap.c (get_key): Allow GnuPG to send us full v4
fingerprints, long key ids, or short key ids while fetching.
Since the LDAP server doesn't actually handle fingerprints, chop
them down to long key ids for actual use.
* gpgkeys_ldap.c (main, get_key): When searching for a keyid,
search for subkeys as well as primary keys. This is mostly
significant when automatically fetching the key based on the id in
a header (i.e. "signature made by...."). "no-include-subkeys"
disables.
2002-02-14 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c: Fix compiler warning.
* gpgkeys_ldap.c: Be much more robust with mangled input files.
2001-12-28 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_mailto.in: Use the new OUTOFBAND indicator so gpg knows
not to try and import anything. Also turn on perl -w for
warnings.
* gpgkeys_ldap.c (main): If we're using temp files (rather than
stdin/stdout), make sure the file is closed when we're done.
2001-12-20 David Shaw <dshaw@jabberwocky.com>
* Properly free the LDAP response when we're done with it.
* Now that we handle multiple keys, we must remove duplicates as
the LDAP keyserver returns keys with multiple user IDs multiple
times.
* Properly handle multiple keys with the same key ID (it's really
rare, so fetch "0xDEADBEEF" to test this).
2001-12-17 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c, gpgkeys_mailto.in: Fix GNU capitalization
issues. Prefix log messages with "gpgkeys" to clarify which
program is generating them.
2001-12-14 David Shaw <dshaw@jabberwocky.com>
* gpgkeys_ldap.c (search_key): Use unsigned int rather than uint
for portability.
2001-12-04 David Shaw <dshaw@jabberwocky.com>
* Initial version of gpgkeys_ldap (LDAP keyserver helper) and
gpgkeys_mailto (email keyserver helper)
Copyright 1998, 1999, 2000, 2001, 2002, 2003,
2004 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
modifications, as long as this notice is preserved.
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/keyserver/gpgkeys_finger.c b/keyserver/gpgkeys_finger.c
index 967fa8176..d0755edaa 100644
--- a/keyserver/gpgkeys_finger.c
+++ b/keyserver/gpgkeys_finger.c
@@ -1,550 +1,550 @@
/* gpgkeys_finger.c - fetch a key via finger
* Copyright (C) 2004 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#define INCLUDED_BY_MAIN_MODULE 1
#include "util.h"
#include "keyserver.h"
#ifdef _WIN32
#define sock_close(a) closesocket(a)
#else
#define sock_close(a) close(a)
#endif
extern char *optarg;
extern int optind;
#define GET 0
#define MAX_LINE 80
static int verbose=0;
static char path[1024];
static FILE *input, *output, *console;
#define BEGIN "-----BEGIN PGP PUBLIC KEY BLOCK-----"
#define END "-----END PGP PUBLIC KEY BLOCK-----"
#ifdef _WIN32
static void
deinit_sockets (void)
{
WSACleanup();
}
static void
init_sockets (void)
{
static int initialized;
static WSADATA wsdata;
if (initialized)
return;
if (WSAStartup (0x0101, &wsdata) )
{
fprintf (console, "error initializing socket library: ec=%d\n",
(int)WSAGetLastError () );
return;
}
if (wsdata.wVersion < 0x0001)
{
fprintf (console, "socket library version is %x.%x - but 1.1 needed\n",
LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion));
WSACleanup();
return;
}
atexit (deinit_sockets);
initialized = 1;
}
#endif /*_WIN32*/
/* Connect to SERVER at PORT and return a file descriptor or -1 on
error. */
static int
connect_server (const char *server, unsigned short port)
{
int sock = -1;
#ifdef _WIN32
struct hostent *hp;
struct sockaddr_in addr;
unsigned long l;
init_sockets ();
memset (&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons (port);
/* Win32 gethostbyname doesn't handle IP addresses internally, so we
try inet_addr first on that platform only. */
if ((l = inet_addr (server)) != SOCKET_ERROR)
memcpy (&ad.sin_addr, &l, sizeof l);
else if ((hp = gethostbyname (server)))
{
if (hp->h_addrtype != AF_INET)
{
fprintf (console, "gpgkeys: unknown address family for `%s'\n",
server);
return -1;
}
if (hp->h_length != 4)
{
fprintf (console, "gpgkeys: illegal address length for`%s'\n",
server);
return -1;
}
memcpy (&addr.sin_addr, hp->h_addr, hp->h_length);
}
else
{
fprintf (console, "gpgkeys: host `%s' not found: ec=%d\n",
server, (int)WSAGetLastError ());
return -1;
}
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET)
{
fprintf (console, "gpgkeys: error creating socket: ec=%d\n",
(int)WSAGetLastError ());
return -1;
}
if (connect (sock, (struct sockaddr *)&addr, sizeof addr))
{
fprintf (console, "gpgkeys: error connecting `%s': ec=%d\n",
server, (int)WSAGetLastError ());
sock_close (sock);
return -1;
}
#else
struct sockaddr_in addr;
struct hostent *host;
addr.sin_family = AF_INET;
addr.sin_port = htons (port);
host = gethostbyname ((char*)server);
if (!host)
{
fprintf (console, "gpgkeys: host `%s' not found: %s\n",
server, strerror (errno));
return -1;
}
addr.sin_addr = *(struct in_addr*)host->h_addr;
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
fprintf (console, "gpgkeys: error creating socket: %s\n",
strerror (errno));
return -1;
}
if (connect (sock, (struct sockaddr *)&addr, sizeof addr) == -1)
{
fprintf (console, "gpgkeys: error connecting `%s': %s\n",
server, strerror (errno));
close (sock);
return -1;
}
#endif
return sock;
}
static int
write_server (int sock, const char *data, size_t length)
{
int nleft;
nleft = length;
while (nleft > 0)
{
int nwritten;
#ifdef _WIN32
nwritten = send (sock, data, nleft, 0);
if ( nwritten == SOCKET_ERROR )
{
fprintf (console, "gpgkeys: write failed: ec=%d\n",
(int)WSAGetLastError ());
return -1;
}
#else
nwritten = write (sock, data, nleft);
if (nwritten == -1)
{
if (errno == EINTR)
continue;
if (errno == EAGAIN)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50000;
select(0, NULL, NULL, NULL, &tv);
continue;
}
fprintf (console, "gpgkeys: write failed: %s\n", strerror(errno));
return -1;
}
#endif
nleft -=nwritten;
data += nwritten;
}
return 0;
}
/* Send the finger REQUEST to the server. Returns 0 and a file descriptor
in R_SOCK if the request was sucessful. */
static int
send_request (const char *request, int *r_sock)
{
char *server;
char *name;
int sock;
*r_sock = -1;
name = strdup (request);
if (!name)
{
fprintf(console,"gpgkeys: out of memory\n");
return KEYSERVER_NO_MEMORY;
}
server = strchr (name, '@');
if (!server)
{
fprintf (console, "gpgkeys: no name included in request\n");
free (name);
return KEYSERVER_GENERAL_ERROR;
}
*server++ = 0;
sock = connect_server (server, 79);
if (sock == -1)
{
free (name);
return KEYSERVER_UNREACHABLE;
}
if (write_server (sock, name, strlen (name))
|| write_server (sock, "\r\n", 2))
{
free (name);
sock_close (sock);
return KEYSERVER_GENERAL_ERROR;
}
free (name);
*r_sock = sock;
return 0;
}
static int
get_key (char *getkey)
{
int rc;
int sock;
IOBUF fp_read;
unsigned int maxlen, buflen, gotit=0;
unsigned char *line = NULL;
if (strncmp (getkey,"0x",2)==0)
getkey+=2;
/* Frankly we don't know what keys the server will return; we
indicated the requested key anyway. */
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
rc = send_request (path, &sock);
if(rc)
{
fprintf(output,"KEY 0x%s FAILED %d\n",getkey, rc);
sock_close (sock);
return KEYSERVER_OK;
}
/* Hmmm, we use iobuf here only to cope with Windows socket
peculiarities (we can't used fdopen). */
fp_read = iobuf_sockopen (sock , "r");
if (!fp_read)
{
fprintf(output,"KEY 0x%s FAILED %d\n",getkey, KEYSERVER_INTERNAL_ERROR);
sock_close (sock);
return KEYSERVER_OK;
}
while ( iobuf_read_line ( fp_read, &line, &buflen, &maxlen))
{
maxlen=1024;
if(gotit)
{
fputs (line, output);
if (!strncmp(line,END,strlen(END)))
break;
}
else if(!strncmp(line,BEGIN,strlen(BEGIN)))
{
fputs (line,output);
gotit=1;
}
}
if(gotit)
fprintf (output,"KEY 0x%s END\n", getkey);
else
{
fprintf (console,"gpgkeys: key %s not found on keyserver\n",getkey);
fprintf (output,"KEY 0x%s FAILED %d\n",
getkey,KEYSERVER_KEY_NOT_FOUND);
}
m_free(line);
iobuf_close (fp_read);
return KEYSERVER_OK;
}
int
main(int argc,char *argv[])
{
int arg,action=-1,ret=KEYSERVER_INTERNAL_ERROR;
char line[MAX_LINE];
char *thekey=NULL;
console=stderr;
while((arg=getopt(argc,argv,"hVo:"))!=-1)
switch(arg)
{
default:
case 'h':
fprintf(console,"-h\thelp\n");
fprintf(console,"-V\tversion\n");
fprintf(console,"-o\toutput to this file\n");
return KEYSERVER_OK;
case 'V':
fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
return KEYSERVER_OK;
case 'o':
output=fopen(optarg,"w");
if(output==NULL)
{
- fprintf(console,"gpgkeys: Cannot open output file \"%s\": %s\n",
+ fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
optarg,strerror(errno));
return KEYSERVER_INTERNAL_ERROR;
}
break;
}
if(argc>optind)
{
input=fopen(argv[optind],"r");
if(input==NULL)
{
- fprintf(console,"gpgkeys: Cannot open input file \"%s\": %s\n",
+ fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
argv[optind],strerror(errno));
return KEYSERVER_INTERNAL_ERROR;
}
}
if(input==NULL)
input=stdin;
if(output==NULL)
output=stdout;
/* Get the command and info block */
while(fgets(line,MAX_LINE,input)!=NULL)
{
int version;
char commandstr[7];
char optionstr[256];
char hash;
if(line[0]=='\n')
break;
if(sscanf(line,"%c",&hash)==1 && hash=='#')
continue;
if(sscanf(line,"COMMAND %6s\n",commandstr)==1)
{
commandstr[6]='\0';
if(strcasecmp(commandstr,"get")==0)
action=GET;
continue;
}
if(sscanf(line,"OPAQUE %1023s\n",path)==1)
{
path[1023]='\0';
continue;
}
if(sscanf(line,"VERSION %d\n",&version)==1)
{
if(version!=KEYSERVER_PROTO_VERSION)
{
ret=KEYSERVER_VERSION_ERROR;
goto fail;
}
continue;
}
if(sscanf(line,"OPTION %255s\n",optionstr)==1)
{
int no=0;
char *start=&optionstr[0];
optionstr[255]='\0';
if(strncasecmp(optionstr,"no-",3)==0)
{
no=1;
start=&optionstr[3];
}
if(strcasecmp(start,"verbose")==0)
{
if(no)
verbose--;
else
verbose++;
}
continue;
}
}
/* If it's a GET or a SEARCH, the next thing to come in is the
keyids. If it's a SEND, then there are no keyids. */
if(action==GET)
{
/* Eat the rest of the file */
for(;;)
{
if(fgets(line,MAX_LINE,input)==NULL)
break;
else
{
if(line[0]=='\n' || line[0]=='\0')
break;
if(!thekey)
{
thekey=strdup(line);
if(!thekey)
{
fprintf(console,"gpgkeys: out of memory while "
"building key list\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
/* Trim the trailing \n */
thekey[strlen(line)-1]='\0';
}
}
}
}
else
{
fprintf(console,
"gpgkeys: this keyserver type only supports key retrieval\n");
goto fail;
}
if(!thekey || !*path)
{
fprintf(console,"gpgkeys: invalid keyserver instructions\n");
goto fail;
}
/* Send the response */
fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
fprintf(output,"PROGRAM %s\n\n",VERSION);
if (verbose>1)
{
if(path[0])
fprintf(console,"Path:\t\t%s\n",path);
fprintf(console,"Command:\tGET\n");
}
ret = get_key(thekey);
fail:
free(thekey);
if(input!=stdin)
fclose(input);
if(output!=stdout)
fclose(output);
return ret;
}
diff --git a/keyserver/gpgkeys_hkp.c b/keyserver/gpgkeys_hkp.c
index a8457a598..c42925c31 100644
--- a/keyserver/gpgkeys_hkp.c
+++ b/keyserver/gpgkeys_hkp.c
@@ -1,1114 +1,1114 @@
/* gpgkeys_hkp.c - talk to an HKP keyserver
* Copyright (C) 2001, 2002, 2003, 2004 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#define INCLUDED_BY_MAIN_MODULE 1
#include "util.h"
#include "http.h"
#include "keyserver.h"
extern char *optarg;
extern int optind;
#define GET 0
#define SEND 1
#define SEARCH 2
#define MAX_LINE 80
int verbose=0,include_revoked=0,include_disabled=0;
unsigned int http_flags=0;
char host[80]={'\0'},proxy[80]={'\0'},port[10]={'\0'};
FILE *input=NULL,*output=NULL,*console=NULL;
#define BEGIN "-----BEGIN PGP PUBLIC KEY BLOCK-----"
#define END "-----END PGP PUBLIC KEY BLOCK-----"
struct keylist
{
char str[MAX_LINE];
struct keylist *next;
};
#ifdef __riscos__
#define HTTP_PROXY_ENV "GnuPG$HttpProxy"
#else
#define HTTP_PROXY_ENV "http_proxy"
#endif
int
urlencode_filter( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
int rc=0;
if( control == IOBUFCTRL_FLUSH ) {
const byte *p;
for(p=buf; size; p++, size-- ) {
if( isalnum(*p) || *p == '-' )
iobuf_put( a, *p );
else if( *p == ' ' )
iobuf_put( a, '+' );
else {
char numbuf[5];
sprintf(numbuf, "%%%02X", *p );
iobuf_writestr(a, numbuf );
}
}
}
else if( control == IOBUFCTRL_DESC )
*(char**)buf = "urlencode_filter";
return rc;
}
int
send_key(int *eof)
{
int rc,begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
char keyid[17];
char *request;
struct http_context hd;
unsigned int status;
IOBUF temp = iobuf_temp();
char line[MAX_LINE];
memset(&hd,0,sizeof(hd));
request=malloc(strlen(host)+100);
if(!request)
{
fprintf(console,"gpgkeys: out of memory\n");
return KEYSERVER_NO_MEMORY;
}
iobuf_push_filter(temp,urlencode_filter,NULL);
/* Read and throw away input until we see the BEGIN */
while(fgets(line,MAX_LINE,input)!=NULL)
if(sscanf(line,"KEY %16s BEGIN\n",keyid)==1)
{
begin=1;
break;
}
if(!begin)
{
/* i.e. eof before the KEY BEGIN was found. This isn't an
error. */
*eof=1;
ret=KEYSERVER_OK;
goto fail;
}
/* Now slurp up everything until we see the END */
while(fgets(line,MAX_LINE,input))
if(sscanf(line,"KEY %16s END\n",keyid)==1)
{
end=1;
break;
}
else
if(iobuf_writestr(temp,line))
{
fprintf(console,"gpgkeys: internal iobuf error\n");
goto fail;
}
if(!end)
{
fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
*eof=1;
ret=KEYSERVER_KEY_INCOMPLETE;
goto fail;
}
iobuf_flush_temp(temp);
sprintf(request,"x-hkp://%s%s%s/pks/add",
host,port[0]?":":"",port[0]?port:"");
if(verbose>2)
- fprintf(console,"gpgkeys: HTTP URL is \"%s\"\n",request);
+ fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
rc=http_open(&hd,HTTP_REQ_POST,request,http_flags,proxy[0]?proxy:NULL);
if(rc)
{
fprintf(console,"gpgkeys: unable to connect to `%s'\n",host);
goto fail;
}
/* Some keyservers require this Content-Type (e.g. CryptoEx). */
iobuf_writestr(hd.fp_write,
"Content-Type: application/x-www-form-urlencoded\r\n");
sprintf(request,"Content-Length: %u\r\n",
(unsigned)iobuf_get_temp_length(temp)+9);
iobuf_writestr(hd.fp_write,request);
http_start_data(&hd);
iobuf_writestr(hd.fp_write,"keytext=");
iobuf_write(hd.fp_write,
iobuf_get_temp_buffer(temp),iobuf_get_temp_length(temp));
iobuf_put(hd.fp_write,'\n');
rc=http_wait_response(&hd,&status);
if(rc)
{
fprintf(console,"gpgkeys: error sending to `%s': %s\n",
host,g10_errstr(rc));
goto fail;
}
if((status/100)!=2)
{
fprintf(console,"gpgkeys: remote server returned error %d\n",status);
goto fail;
}
fprintf(output,"KEY %s SENT\n",keyid);
ret=KEYSERVER_OK;
fail:
free(request);
iobuf_close(temp);
http_close(&hd);
if(ret!=0 && begin)
fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
return ret;
}
int
get_key(char *getkey)
{
int rc,gotit=0;
char search[29];
char *request;
struct http_context hd;
/* Build the search string. HKP only uses the short key IDs. */
if(strncmp(getkey,"0x",2)==0)
getkey+=2;
if(strlen(getkey)==32)
{
fprintf(console,
"gpgkeys: HKP keyservers do not support v3 fingerprints\n");
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
return KEYSERVER_NOT_SUPPORTED;
}
if(strlen(getkey)>8)
{
char *offset=&getkey[strlen(getkey)-8];
/* fingerprint or long key id. Take the last 8 characters and
treat it like a short key id */
sprintf(search,"0x%.8s",offset);
}
else
{
/* short key id */
sprintf(search,"0x%.8s",getkey);
}
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
request=malloc(strlen(host)+100);
if(!request)
{
fprintf(console,"gpgkeys: out of memory\n");
return KEYSERVER_NO_MEMORY;
}
sprintf(request,"x-hkp://%s%s%s/pks/lookup?op=get&options=mr&search=%s",
host,port[0]?":":"",port[0]?port:"", search);
if(verbose>2)
- fprintf(console,"gpgkeys: HTTP URL is \"%s\"\n",request);
+ fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
rc=http_open_document(&hd,request,http_flags,proxy[0]?proxy:NULL);
if(rc!=0)
{
fprintf(console,"gpgkeys: HKP fetch error: %s\n",
rc==G10ERR_NETWORK?strerror(errno):g10_errstr(rc));
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,
rc==G10ERR_NETWORK?KEYSERVER_UNREACHABLE:KEYSERVER_INTERNAL_ERROR);
}
else
{
unsigned int maxlen=1024,buflen;
byte *line=NULL;
while(iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen))
{
maxlen=1024;
if(gotit)
{
fputs (line, output);
if(strncmp(line,END,strlen(END))==0)
break;
}
else
if(strncmp(line,BEGIN,strlen(BEGIN))==0)
{
fputs (line,output);
gotit=1;
}
}
if(gotit)
fprintf(output,"KEY 0x%s END\n",getkey);
else
{
fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
fprintf(output,"KEY 0x%s FAILED %d\n",
getkey,KEYSERVER_KEY_NOT_FOUND);
}
m_free(line);
}
free(request);
return KEYSERVER_OK;
}
/* Remove anything <between brackets> and de-urlencode in place. Note
that this requires all brackets to be closed on the same line. It
also means that the result is never larger than the input. */
void
dehtmlize(char *line)
{
int parsedindex=0;
char *parsed=line;
while(*line!='\0')
{
switch(*line)
{
case '<':
while(*line!='>' && *line!='\0')
line++;
if(*line!='\0')
line++;
break;
case '&':
if((*(line+1)!='\0' && ascii_tolower(*(line+1))=='l') &&
(*(line+2)!='\0' && ascii_tolower(*(line+2))=='t') &&
(*(line+3)!='\0' && *(line+3)==';'))
{
parsed[parsedindex++]='<';
line+=4;
break;
}
else if((*(line+1)!='\0' && ascii_tolower(*(line+1))=='g') &&
(*(line+2)!='\0' && ascii_tolower(*(line+2))=='t') &&
(*(line+3)!='\0' && *(line+3)==';'))
{
parsed[parsedindex++]='>';
line+=4;
break;
}
else if((*(line+1)!='\0' && ascii_tolower(*(line+1))=='a') &&
(*(line+2)!='\0' && ascii_tolower(*(line+2))=='m') &&
(*(line+3)!='\0' && ascii_tolower(*(line+3))=='p') &&
(*(line+4)!='\0' && *(line+4)==';'))
{
parsed[parsedindex++]='&';
line+=5;
break;
}
else if((*(line+1)!='\0' && ascii_tolower(*(line+1))=='q') &&
(*(line+2)!='\0' && ascii_tolower(*(line+2))=='u') &&
(*(line+3)!='\0' && ascii_tolower(*(line+3))=='o') &&
(*(line+4)!='\0' && ascii_tolower(*(line+4))=='t') &&
(*(line+5)!='\0' && *(line+5)==';'))
{
parsed[parsedindex++]='"';
line+=6;
break;
}
default:
parsed[parsedindex++]=*line;
line++;
break;
}
}
parsed[parsedindex]='\0';
/* Chop off any trailing whitespace. Note that the HKP servers have
\r\n as line endings, and the NAI HKP servers have just \n. */
if(parsedindex>0)
{
parsedindex--;
while(isspace(((unsigned char *)parsed)[parsedindex]))
{
parsed[parsedindex]='\0';
if(parsedindex==0)
break;
parsedindex--;
}
}
}
int
write_quoted(IOBUF a, const char *buf, char delim)
{
while(*buf)
{
if(*buf==delim)
{
char quoted[5];
sprintf(quoted,"%%%02X",delim);
if(iobuf_writestr(a,quoted))
return -1;
}
else if(*buf=='%')
{
if(iobuf_writestr(a,"%25"))
return -1;
}
else
{
if(iobuf_writebyte(a,*buf))
return -1;
}
buf++;
}
return 0;
}
/* pub 2048/<a href="/pks/lookup?op=get&search=0x3CB3B415">3CB3B415</a> 1998/04/03 David M. Shaw &lt;<a href="/pks/lookup?op=get&search=0x3CB3B415">dshaw@jabberwocky.com</a>&gt; */
/* Luckily enough, both the HKP server and NAI HKP interface to their
LDAP server are close enough in output so the same function can
parse them both. */
int
parse_hkp_index(IOBUF buffer,char *line)
{
int ret=0;
- /* printf("Open %d, LINE: \"%s\"\n",open,line); */
+ /* printf("Open %d, LINE: `%s'\n",open,line); */
dehtmlize(line);
- /* printf("Now open %d, LINE: \"%s\"\n",open,line); */
+ /* printf("Now open %d, LINE: `%s'\n",open,line); */
if(line[0]=='\0')
return 0;
else if(ascii_strncasecmp(line,"pub",3)==0)
{
char *tok,*keyid,*uid=NULL,number[15];
int bits=0,type=0,disabled=0,revoked=0;
u32 createtime=0;
line+=3;
if(*line=='-')
{
disabled=1;
if(!include_disabled)
return 0;
}
line++;
tok=strsep(&line,"/");
if(tok==NULL || strlen(tok)==0)
return ret;
if(tok[strlen(tok)-1]=='R')
type=1;
else if(tok[strlen(tok)-1]=='D')
type=17;
bits=atoi(tok);
keyid=strsep(&line," ");
tok=strsep(&line," ");
if(tok!=NULL)
{
char *temp=tok;
/* The date parser wants '-' instead of '/', so... */
while(*temp!='\0')
{
if(*temp=='/')
*temp='-';
temp++;
}
createtime=scan_isodatestr(tok);
}
if(line!=NULL)
{
while(*line==' ' && *line!='\0')
line++;
if(*line!='\0')
{
if(strncmp(line,"*** KEY REVOKED ***",19)==0)
{
revoked=1;
if(!include_revoked)
return 0;
}
else
uid=line;
}
}
if(keyid)
{
iobuf_writestr(buffer,"pub:");
write_quoted(buffer,keyid,':');
iobuf_writestr(buffer,":");
if(type)
{
sprintf(number,"%d",type);
write_quoted(buffer,number,':');
}
iobuf_writestr(buffer,":");
if(bits)
{
sprintf(number,"%d",bits);
write_quoted(buffer,number,':');
}
iobuf_writestr(buffer,":");
if(createtime)
{
sprintf(number,"%d",createtime);
write_quoted(buffer,number,':');
}
iobuf_writestr(buffer,"::");
if(revoked)
write_quoted(buffer,"r",':');
if(disabled)
write_quoted(buffer,"d",':');
if(uid)
{
iobuf_writestr(buffer,"\nuid:");
write_quoted(buffer,uid,':');
}
iobuf_writestr(buffer,"\n");
ret=1;
}
}
else if(ascii_strncasecmp(line," ",3)==0)
{
while(*line==' ' && *line!='\0')
line++;
if(*line!='\0')
{
iobuf_writestr(buffer,"uid:");
write_quoted(buffer,line,':');
iobuf_writestr(buffer,"\n");
}
}
#if 0
else if(open)
{
/* Try and catch some bastardization of HKP. If we don't have
certain unchanging landmarks, we can't reliably parse the
response. This only complains about problems within the key
section itself. Headers and footers should not matter. */
fprintf(console,"gpgkeys: this keyserver does not support searching\n");
ret=-1;
}
#endif
return ret;
}
void
handle_old_hkp_index(IOBUF inp)
{
int ret,rc,count=0;
unsigned int buflen;
byte *line=NULL;
IOBUF buffer=iobuf_temp();
do
{
unsigned int maxlen=1024;
/* This is a judgement call. Is it better to slurp up all the
results before prompting the user? On the one hand, it
probably makes the keyserver happier to not be blocked on
sending for a long time while the user picks a key. On the
other hand, it might be nice for the server to be able to
stop sending before a large search result page is
complete. */
rc=iobuf_read_line(inp,&line,&buflen,&maxlen);
ret=parse_hkp_index(buffer,line);
if(ret==-1)
break;
if(rc!=0)
count+=ret;
}
while(rc!=0);
m_free(line);
if(ret>-1)
fprintf(output,"info:1:%d\n%s",count,iobuf_get_temp_buffer(buffer));
iobuf_close(buffer);
}
int
search_key(char *searchkey)
{
int max=0,len=0,ret=KEYSERVER_INTERNAL_ERROR,rc;
struct http_context hd;
char *search=NULL,*request=NULL;
unsigned char *skey=(unsigned char*) searchkey;
fprintf(output,"SEARCH %s BEGIN\n",searchkey);
/* Build the search string. It's going to need url-encoding. */
while(*skey!='\0')
{
if(max-len<3)
{
max+=100;
search=realloc(search,max+1); /* Note +1 for \0 */
if (!search)
{
fprintf(console,"gpgkeys: out of memory\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
}
if(isalnum(*skey) || *skey=='-')
search[len++]=*skey;
else if(*skey==' ')
search[len++]='+';
else
{
sprintf(&search[len],"%%%02X",*skey);
len+=3;
}
skey++;
}
if(!search)
{
fprintf(console,"gpgkeys: corrupt input?\n");
return -1;
}
search[len]='\0';
request=malloc(strlen(host)+100+strlen(search));
if(!request)
{
fprintf(console,"gpgkeys: out of memory\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
sprintf(request,"x-hkp://%s%s%s/pks/lookup?op=index&options=mr&search=%s",
host,port[0]?":":"",port[0]?port:"",search);
if(verbose>2)
- fprintf(console,"gpgkeys: HTTP URL is \"%s\"\n",request);
+ fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
rc=http_open_document(&hd,request,http_flags,proxy[0]?proxy:NULL);
if(rc)
{
fprintf(console,"gpgkeys: can't search keyserver `%s': %s\n",
host,rc==G10ERR_NETWORK?strerror(errno):g10_errstr(rc));
}
else
{
unsigned int maxlen=1024,buflen;
byte *line=NULL;
/* Is it a pksd that knows how to handle machine-readable
format? */
rc=iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen);
if(line[0]=='<')
handle_old_hkp_index(hd.fp_read);
else
do
{
fprintf(output,"%s",line);
maxlen=1024;
rc=iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen);
}
while(rc!=0);
m_free(line);
http_close(&hd);
fprintf(output,"SEARCH %s END\n",searchkey);
ret=KEYSERVER_OK;
}
fail:
free(request);
free(search);
if(ret!=KEYSERVER_OK)
fprintf(output,"SEARCH %s FAILED %d\n",searchkey,ret);
return ret;
}
void
fail_all(struct keylist *keylist,int action,int err)
{
if(!keylist)
return;
if(action==SEARCH)
{
fprintf(output,"SEARCH ");
while(keylist)
{
fprintf(output,"%s ",keylist->str);
keylist=keylist->next;
}
fprintf(output,"FAILED %d\n",err);
}
else
while(keylist)
{
fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
keylist=keylist->next;
}
}
int
main(int argc,char *argv[])
{
int arg,action=-1,ret=KEYSERVER_INTERNAL_ERROR;
char line[MAX_LINE];
int failed=0;
struct keylist *keylist=NULL,*keyptr=NULL;
console=stderr;
while((arg=getopt(argc,argv,"hVo:"))!=-1)
switch(arg)
{
default:
case 'h':
fprintf(console,"-h\thelp\n");
fprintf(console,"-V\tversion\n");
fprintf(console,"-o\toutput to this file\n");
return KEYSERVER_OK;
case 'V':
fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
return KEYSERVER_OK;
case 'o':
output=fopen(optarg,"w");
if(output==NULL)
{
- fprintf(console,"gpgkeys: Cannot open output file \"%s\": %s\n",
+ fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
optarg,strerror(errno));
return KEYSERVER_INTERNAL_ERROR;
}
break;
}
if(argc>optind)
{
input=fopen(argv[optind],"r");
if(input==NULL)
{
- fprintf(console,"gpgkeys: Cannot open input file \"%s\": %s\n",
+ fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
argv[optind],strerror(errno));
return KEYSERVER_INTERNAL_ERROR;
}
}
if(input==NULL)
input=stdin;
if(output==NULL)
output=stdout;
/* Get the command and info block */
while(fgets(line,MAX_LINE,input)!=NULL)
{
int version;
char commandstr[7];
char optionstr[110];
char hash;
if(line[0]=='\n')
break;
if(sscanf(line,"%c",&hash)==1 && hash=='#')
continue;
if(sscanf(line,"COMMAND %6s\n",commandstr)==1)
{
commandstr[6]='\0';
if(strcasecmp(commandstr,"get")==0)
action=GET;
else if(strcasecmp(commandstr,"send")==0)
action=SEND;
else if(strcasecmp(commandstr,"search")==0)
action=SEARCH;
continue;
}
if(sscanf(line,"HOST %79s\n",host)==1)
{
host[79]='\0';
continue;
}
if(sscanf(line,"PORT %9s\n",port)==1)
{
port[9]='\0';
continue;
}
if(sscanf(line,"VERSION %d\n",&version)==1)
{
if(version!=KEYSERVER_PROTO_VERSION)
{
ret=KEYSERVER_VERSION_ERROR;
goto fail;
}
continue;
}
if(sscanf(line,"OPTION %109s\n",optionstr)==1)
{
int no=0;
char *start=&optionstr[0];
optionstr[109]='\0';
if(strncasecmp(optionstr,"no-",3)==0)
{
no=1;
start=&optionstr[3];
}
if(strcasecmp(start,"verbose")==0)
{
if(no)
verbose--;
else
verbose++;
}
else if(strcasecmp(start,"include-revoked")==0)
{
if(no)
include_revoked=0;
else
include_revoked=1;
}
else if(strcasecmp(start,"include-disabled")==0)
{
if(no)
include_disabled=0;
else
include_disabled=1;
}
else if(strncasecmp(start,"http-proxy",10)==0)
{
if(no)
proxy[0]='\0';
else if(start[10]=='=')
{
strncpy(proxy,&start[11],79);
proxy[79]='\0';
}
else if(start[10]=='\0')
{
char *http_proxy=getenv(HTTP_PROXY_ENV);
if(http_proxy)
{
strncpy(proxy,http_proxy,79);
proxy[79]='\0';
}
}
}
else if(strcasecmp(start,"broken-http-proxy")==0)
{
if(no)
http_flags&=~HTTP_FLAG_NO_SHUTDOWN;
else
http_flags|=HTTP_FLAG_NO_SHUTDOWN;
}
else if(strcasecmp(start,"try-dns-srv")==0)
{
if(no)
http_flags&=~HTTP_FLAG_TRY_SRV;
else
http_flags|=HTTP_FLAG_TRY_SRV;
}
continue;
}
}
/* By suggested convention, if the user gives a :port, then disable
SRV. */
if(port[0])
http_flags&=~HTTP_FLAG_TRY_SRV;
/* If it's a GET or a SEARCH, the next thing to come in is the
keyids. If it's a SEND, then there are no keyids. */
if(action==SEND)
while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
else if(action==GET || action==SEARCH)
{
for(;;)
{
struct keylist *work;
if(fgets(line,MAX_LINE,input)==NULL)
break;
else
{
if(line[0]=='\n' || line[0]=='\0')
break;
work=malloc(sizeof(struct keylist));
if(work==NULL)
{
fprintf(console,"gpgkeys: out of memory while "
"building key list\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
strcpy(work->str,line);
/* Trim the trailing \n */
work->str[strlen(line)-1]='\0';
work->next=NULL;
/* Always attach at the end to keep the list in proper
order for searching */
if(keylist==NULL)
keylist=work;
else
keyptr->next=work;
keyptr=work;
}
}
}
else
{
fprintf(console,"gpgkeys: no keyserver command specified\n");
goto fail;
}
/* Send the response */
fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
fprintf(output,"PROGRAM %s\n\n",VERSION);
if(verbose>1)
{
fprintf(console,"Host:\t\t%s\n",host);
if(port[0])
fprintf(console,"Port:\t\t%s\n",port);
fprintf(console,"Command:\t%s\n",action==GET?"GET":
action==SEND?"SEND":"SEARCH");
}
#if 0
if(verbose>1)
{
vals=ldap_get_values(ldap,res,"software");
if(vals!=NULL)
{
fprintf(console,"Server: \t%s\n",vals[0]);
ldap_value_free(vals);
}
vals=ldap_get_values(ldap,res,"version");
if(vals!=NULL)
{
fprintf(console,"Version:\t%s\n",vals[0]);
ldap_value_free(vals);
}
}
#endif
switch(action)
{
case GET:
keyptr=keylist;
while(keyptr!=NULL)
{
if(get_key(keyptr->str)!=KEYSERVER_OK)
failed++;
keyptr=keyptr->next;
}
break;
case SEND:
{
int eof=0;
do
{
if(send_key(&eof)!=KEYSERVER_OK)
failed++;
}
while(!eof);
}
break;
case SEARCH:
{
char *searchkey=NULL;
int len=0;
/* To search, we stick a space in between each key to search
for. */
keyptr=keylist;
while(keyptr!=NULL)
{
len+=strlen(keyptr->str)+1;
keyptr=keyptr->next;
}
searchkey=malloc(len+1);
if(searchkey==NULL)
{
ret=KEYSERVER_NO_MEMORY;
fail_all(keylist,action,KEYSERVER_NO_MEMORY);
goto fail;
}
searchkey[0]='\0';
keyptr=keylist;
while(keyptr!=NULL)
{
strcat(searchkey,keyptr->str);
strcat(searchkey," ");
keyptr=keyptr->next;
}
/* Nail that last space */
if(*searchkey)
searchkey[strlen(searchkey)-1]='\0';
if(search_key(searchkey)!=KEYSERVER_OK)
failed++;
free(searchkey);
}
break;
}
if(!failed)
ret=KEYSERVER_OK;
fail:
while(keylist!=NULL)
{
struct keylist *current=keylist;
keylist=keylist->next;
free(current);
}
if(input!=stdin)
fclose(input);
if(output!=stdout)
fclose(output);
return ret;
}
diff --git a/keyserver/gpgkeys_http.c b/keyserver/gpgkeys_http.c
index e61351ec8..6eabdcff4 100644
--- a/keyserver/gpgkeys_http.c
+++ b/keyserver/gpgkeys_http.c
@@ -1,367 +1,367 @@
/* gpgkeys_http.c - fetch a key via HTTP
* Copyright (C) 2004 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#define INCLUDED_BY_MAIN_MODULE 1
#include "util.h"
#include "http.h"
#include "keyserver.h"
extern char *optarg;
extern int optind;
#define GET 0
#define MAX_LINE 80
int verbose=0;
unsigned int http_flags=0;
char host[80]={'\0'},proxy[80]={'\0'},port[10]={'\0'},path[1024]={'\0'};
FILE *input=NULL,*output=NULL,*console=NULL;
#define BEGIN "-----BEGIN PGP PUBLIC KEY BLOCK-----"
#define END "-----END PGP PUBLIC KEY BLOCK-----"
#ifdef __riscos__
#define HTTP_PROXY_ENV "GnuPG$HttpProxy"
#else
#define HTTP_PROXY_ENV "http_proxy"
#endif
static int
get_key(char *getkey)
{
int rc;
char *request;
struct http_context hd;
if(strncmp(getkey,"0x",2)==0)
getkey+=2;
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
request=malloc(4+3+strlen(host)+1+strlen(port)+1+strlen(path)+50);
if(!request)
{
fprintf(console,"gpgkeys: out of memory\n");
return KEYSERVER_NO_MEMORY;
}
sprintf(request,"http://%s%s%s%s%s",host,port[0]?":":"",
port[0]?port:"",path[0]?"":"/",path);
rc=http_open_document(&hd,request,http_flags,proxy[0]?proxy:NULL);
if(rc!=0)
{
fprintf(console,"gpgkeys: HTTP fetch error: %s\n",
rc==G10ERR_NETWORK?strerror(errno):g10_errstr(rc));
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,
rc==G10ERR_NETWORK?KEYSERVER_UNREACHABLE:KEYSERVER_INTERNAL_ERROR);
}
else
{
unsigned int maxlen=1024,buflen,gotit=0;
byte *line=NULL;
while(iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen))
{
maxlen=1024;
if(gotit)
{
fputs(line,output);
if(strncmp(line,END,strlen(END))==0)
break;
}
else
if(strncmp(line,BEGIN,strlen(BEGIN))==0)
{
fputs(line,output);
gotit=1;
}
}
if(gotit)
fprintf(output,"KEY 0x%s END\n",getkey);
else
{
fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
fprintf(output,"KEY 0x%s FAILED %d\n",
getkey,KEYSERVER_KEY_NOT_FOUND);
}
m_free(line);
}
free(request);
return KEYSERVER_OK;
}
int
main(int argc,char *argv[])
{
int arg,action=-1,ret=KEYSERVER_INTERNAL_ERROR;
char line[MAX_LINE];
char *thekey=NULL;
console=stderr;
while((arg=getopt(argc,argv,"hVo:"))!=-1)
switch(arg)
{
default:
case 'h':
fprintf(console,"-h\thelp\n");
fprintf(console,"-V\tversion\n");
fprintf(console,"-o\toutput to this file\n");
return KEYSERVER_OK;
case 'V':
fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
return KEYSERVER_OK;
case 'o':
output=fopen(optarg,"w");
if(output==NULL)
{
- fprintf(console,"gpgkeys: Cannot open output file \"%s\": %s\n",
+ fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
optarg,strerror(errno));
return KEYSERVER_INTERNAL_ERROR;
}
break;
}
if(argc>optind)
{
input=fopen(argv[optind],"r");
if(input==NULL)
{
- fprintf(console,"gpgkeys: Cannot open input file \"%s\": %s\n",
+ fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
argv[optind],strerror(errno));
return KEYSERVER_INTERNAL_ERROR;
}
}
if(input==NULL)
input=stdin;
if(output==NULL)
output=stdout;
/* Get the command and info block */
while(fgets(line,MAX_LINE,input)!=NULL)
{
int version;
char commandstr[7];
char optionstr[256];
char hash;
if(line[0]=='\n')
break;
if(sscanf(line,"%c",&hash)==1 && hash=='#')
continue;
if(sscanf(line,"COMMAND %6s\n",commandstr)==1)
{
commandstr[6]='\0';
if(strcasecmp(commandstr,"get")==0)
action=GET;
continue;
}
if(sscanf(line,"HOST %79s\n",host)==1)
{
host[79]='\0';
continue;
}
if(sscanf(line,"PORT %9s\n",port)==1)
{
port[9]='\0';
continue;
}
if(sscanf(line,"PATH %1023s\n",path)==1)
{
path[1023]='\0';
continue;
}
if(sscanf(line,"VERSION %d\n",&version)==1)
{
if(version!=KEYSERVER_PROTO_VERSION)
{
ret=KEYSERVER_VERSION_ERROR;
goto fail;
}
continue;
}
if(sscanf(line,"OPTION %255s\n",optionstr)==1)
{
int no=0;
char *start=&optionstr[0];
optionstr[255]='\0';
if(strncasecmp(optionstr,"no-",3)==0)
{
no=1;
start=&optionstr[3];
}
if(strcasecmp(start,"verbose")==0)
{
if(no)
verbose--;
else
verbose++;
}
else if(strncasecmp(start,"http-proxy",10)==0)
{
if(no)
proxy[0]='\0';
else if(start[10]=='=')
{
strncpy(proxy,&start[11],79);
proxy[79]='\0';
}
else if(start[10]=='\0')
{
char *http_proxy=getenv(HTTP_PROXY_ENV);
if(http_proxy)
{
strncpy(proxy,http_proxy,79);
proxy[79]='\0';
}
}
}
else if(strcasecmp(start,"broken-http-proxy")==0)
{
if(no)
http_flags&=~HTTP_FLAG_NO_SHUTDOWN;
else
http_flags|=HTTP_FLAG_NO_SHUTDOWN;
}
else if(strcasecmp(start,"try-dns-srv")==0)
{
if(no)
http_flags&=~HTTP_FLAG_TRY_SRV;
else
http_flags|=HTTP_FLAG_TRY_SRV;
}
continue;
}
}
/* By suggested convention, if the user gives a :port, then disable
SRV. */
if(port[0])
http_flags&=~HTTP_FLAG_TRY_SRV;
/* If it's a GET or a SEARCH, the next thing to come in is the
keyids. If it's a SEND, then there are no keyids. */
if(action==GET)
{
/* Eat the rest of the file */
for(;;)
{
if(fgets(line,MAX_LINE,input)==NULL)
break;
else
{
if(line[0]=='\n' || line[0]=='\0')
break;
if(!thekey)
{
thekey=strdup(line);
if(!thekey)
{
fprintf(console,"gpgkeys: out of memory while "
"building key list\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
/* Trim the trailing \n */
thekey[strlen(line)-1]='\0';
}
}
}
}
else
{
fprintf(console,
"gpgkeys: this keyserver type only supports key retrieval\n");
goto fail;
}
if(!thekey || !host[0])
{
fprintf(console,"gpgkeys: invalid keyserver instructions\n");
goto fail;
}
/* Send the response */
fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
fprintf(output,"PROGRAM %s\n\n",VERSION);
if(verbose>1)
{
fprintf(console,"Host:\t\t%s\n",host);
if(port[0])
fprintf(console,"Port:\t\t%s\n",port);
if(path[0])
fprintf(console,"Path:\t\t%s\n",path);
fprintf(console,"Command:\tGET\n");
}
ret=get_key(thekey);
fail:
free(thekey);
if(input!=stdin)
fclose(input);
if(output!=stdout)
fclose(output);
return ret;
}
diff --git a/keyserver/gpgkeys_ldap.c b/keyserver/gpgkeys_ldap.c
index e8fdc53ad..c8d0ca6d3 100644
--- a/keyserver/gpgkeys_ldap.c
+++ b/keyserver/gpgkeys_ldap.c
@@ -1,2049 +1,2049 @@
/* gpgkeys_ldap.c - talk to a LDAP keyserver
* Copyright (C) 2001, 2002, 2004 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <stdlib.h>
#include <errno.h>
#ifdef _WIN32
#include <winsock2.h>
#include <winldap.h>
#else
#include <ldap.h>
#endif
#include "util.h"
#include "keyserver.h"
#ifdef __riscos__
#include "util.h"
#endif
extern char *optarg;
extern int optind;
#define GET 0
#define SEND 1
#define SEARCH 2
#define MAX_LINE 256
static int verbose=0,include_disabled=0,include_revoked=0,include_subkeys=0;
static int real_ldap=0;
static char *basekeyspacedn=NULL;
static char host[80]={'\0'};
static char portstr[10]={'\0'};
static char *pgpkeystr="pgpKey";
static FILE *input=NULL,*output=NULL,*console=NULL;
static LDAP *ldap=NULL;
#ifndef HAVE_TIMEGM
time_t timegm(struct tm *tm);
#endif
struct keylist
{
char str[MAX_LINE];
struct keylist *next;
};
static int
ldap_err_to_gpg_err(int err)
{
int ret;
switch(err)
{
case LDAP_ALREADY_EXISTS:
ret=KEYSERVER_KEY_EXISTS;
break;
case LDAP_SERVER_DOWN:
ret=KEYSERVER_UNREACHABLE;
break;
default:
ret=KEYSERVER_GENERAL_ERROR;
break;
}
return ret;
}
static int
ldap_to_gpg_err(LDAP *ld)
{
#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER)
int err;
if(ldap_get_option(ld,LDAP_OPT_ERROR_NUMBER,&err)==0)
return ldap_err_to_gpg_err(err);
else
return KEYSERVER_GENERAL_ERROR;
#elif defined(HAVE_LDAP_LD_ERRNO)
return ldap_err_to_gpg_err(ld->ld_errno);
#else
/* We should never get here since the LDAP library should always
have either ldap_get_option or ld_errno, but just in case... */
return KEYSERVER_GENERAL_ERROR;
#endif
}
static int
key_in_keylist(const char *key,struct keylist *list)
{
struct keylist *keyptr=list;
while(keyptr!=NULL)
{
if(strcasecmp(key,keyptr->str)==0)
return 1;
keyptr=keyptr->next;
}
return 0;
}
static int
add_key_to_keylist(const char *key,struct keylist **list)
{
struct keylist *keyptr=malloc(sizeof(struct keylist));
if(keyptr==NULL)
{
fprintf(console,"gpgkeys: out of memory when deduping "
"key list\n");
return KEYSERVER_NO_MEMORY;
}
strncpy(keyptr->str,key,MAX_LINE);
keyptr->str[MAX_LINE-1]='\0';
keyptr->next=*list;
*list=keyptr;
return 0;
}
static void
free_keylist(struct keylist *list)
{
while(list!=NULL)
{
struct keylist *keyptr=list;
list=keyptr->next;
free(keyptr);
}
}
static time_t
ldap2epochtime(const char *timestr)
{
struct tm pgptime;
time_t answer;
memset(&pgptime,0,sizeof(pgptime));
/* YYYYMMDDHHmmssZ */
sscanf(timestr,"%4d%2d%2d%2d%2d%2d",
&pgptime.tm_year,
&pgptime.tm_mon,
&pgptime.tm_mday,
&pgptime.tm_hour,
&pgptime.tm_min,
&pgptime.tm_sec);
pgptime.tm_year-=1900;
pgptime.tm_isdst=-1;
pgptime.tm_mon--;
/* mktime() takes the timezone into account, so we use timegm() */
answer=timegm(&pgptime);
return answer;
}
/* Caller must free */
static char *
epoch2ldaptime(time_t stamp)
{
struct tm *ldaptime;
char buf[16];
ldaptime=gmtime(&stamp);
ldaptime->tm_year+=1900;
ldaptime->tm_mon++;
/* YYYYMMDDHHmmssZ */
sprintf(buf,"%04d%02d%02d%02d%02d%02dZ",
ldaptime->tm_year,
ldaptime->tm_mon,
ldaptime->tm_mday,
ldaptime->tm_hour,
ldaptime->tm_min,
ldaptime->tm_sec);
return strdup(buf);
}
/* Append two onto the end of one. Two is not freed, but its pointers
are now part of one. Make sure you don't free them both! */
static int
join_two_modlists(LDAPMod ***one,LDAPMod **two)
{
int i,one_count=0,two_count=0;
LDAPMod **grow;
for(grow=*one;*grow;grow++)
one_count++;
for(grow=two;*grow;grow++)
two_count++;
grow=realloc(*one,sizeof(LDAPMod *)*(one_count+two_count+1));
if(!grow)
return 0;
for(i=0;i<two_count;i++)
grow[one_count+i]=two[i];
grow[one_count+i]=NULL;
*one=grow;
return 1;
}
/* Passing a NULL for value effectively deletes that attribute. This
doesn't mean "delete" in the sense of removing something from the
modlist, but "delete" in the LDAP sense of adding a modlist item
that specifies LDAP_MOD_REPLACE and a null attribute for the given
attribute. LDAP_MOD_DELETE doesn't work here as we don't know if
the attribute in question exists or not. */
static int
make_one_attr(LDAPMod ***modlist,int unique,char *attr,const char *value)
{
LDAPMod **m;
int nummods=0;
/* Search modlist for the attribute we're playing with. */
for(m=*modlist;*m;m++)
{
if(strcasecmp((*m)->mod_type,attr)==0)
{
char **ptr=(*m)->mod_values;
int numvalues=0;
/* We have this attribute already, so when the REPLACE
happens, the server attributes will be replaced
anyway. */
if(!value)
return 1;
if(ptr)
for(ptr=(*m)->mod_values;*ptr;ptr++)
{
if(unique && strcmp(*ptr,value)==0)
return 1;
numvalues++;
}
ptr=realloc((*m)->mod_values,sizeof(char *)*(numvalues+2));
if(!ptr)
return 0;
(*m)->mod_values=ptr;
ptr[numvalues]=strdup(value);
if(!ptr[numvalues])
return 0;
ptr[numvalues+1]=NULL;
break;
}
nummods++;
}
/* We didn't find the attr, so make one and add it to the end */
if(!*m)
{
LDAPMod **grow;
grow=realloc(*modlist,sizeof(LDAPMod *)*(nummods+2));
if(!grow)
return 0;
*modlist=grow;
grow[nummods]=malloc(sizeof(LDAPMod));
if(!grow[nummods])
return 0;
grow[nummods]->mod_op=LDAP_MOD_REPLACE;
grow[nummods]->mod_type=attr;
if(value)
{
grow[nummods]->mod_values=malloc(sizeof(char *)*2);
if(!grow[nummods]->mod_values)
{
grow[nummods]=NULL;
return 0;
}
/* Is this the right thing? Can a UTF8-encoded user ID have
embedded nulls? */
grow[nummods]->mod_values[0]=strdup(value);
if(!grow[nummods]->mod_values[0])
{
free(grow[nummods]->mod_values);
grow[nummods]=NULL;
return 0;
}
grow[nummods]->mod_values[1]=NULL;
}
else
grow[nummods]->mod_values=NULL;
grow[nummods+1]=NULL;
}
return 1;
}
static void
build_attrs(LDAPMod ***modlist,char *line)
{
char *record;
int i;
/* Remove trailing whitespace */
for(i=strlen(line);i>0;i--)
if(ascii_isspace(line[i-1]))
line[i-1]='\0';
else
break;
if((record=strsep(&line,":"))==NULL)
return;
if(ascii_strcasecmp("pub",record)==0)
{
char *tok;
int disabled=0,revoked=0;
/* The long keyid */
if((tok=strsep(&line,":"))==NULL)
return;
if(strlen(tok)==16)
{
make_one_attr(modlist,0,"pgpCertID",tok);
make_one_attr(modlist,0,"pgpKeyID",&tok[8]);
}
else
return;
/* The primary pubkey algo */
if((tok=strsep(&line,":"))==NULL)
return;
switch(atoi(tok))
{
case 1:
make_one_attr(modlist,0,"pgpKeyType","RSA");
break;
case 17:
make_one_attr(modlist,0,"pgpKeyType","DSS/DH");
break;
}
/* Size of primary key */
if((tok=strsep(&line,":"))==NULL)
return;
if(atoi(tok)>0)
{
char padded[6];
int val=atoi(tok);
/* We zero pad this on the left to make PGP happy. */
if(val<99999 && val>0)
{
sprintf(padded,"%05u",atoi(tok));
make_one_attr(modlist,0,"pgpKeySize",padded);
}
}
/* pk timestamp */
if((tok=strsep(&line,":"))==NULL)
return;
if(atoi(tok)>0)
{
char *stamp=epoch2ldaptime(atoi(tok));
if(stamp)
{
make_one_attr(modlist,0,"pgpKeyCreateTime",stamp);
free(stamp);
}
}
/* pk expire */
if((tok=strsep(&line,":"))==NULL)
return;
if(atoi(tok)>0)
{
char *stamp=epoch2ldaptime(atoi(tok));
if(stamp)
{
make_one_attr(modlist,0,"pgpKeyExpireTime",stamp);
free(stamp);
}
}
/* flags */
if((tok=strsep(&line,":"))==NULL)
return;
while(*tok)
switch(*tok++)
{
case 'r':
case 'R':
revoked=1;
break;
case 'd':
case 'D':
disabled=1;
break;
}
/*
Note that we always create the pgpDisabled and pgpRevoked
attributes, regardless of whether the key is disabled/revoked
or not. This is because a very common search is like
"(&(pgpUserID=*isabella*)(pgpDisabled=0))"
*/
make_one_attr(modlist,0,"pgpDisabled",disabled?"1":"0");
make_one_attr(modlist,0,"pgpRevoked",revoked?"1":"0");
}
else if(ascii_strcasecmp("sub",record)==0)
{
char *tok;
/* The long keyid */
if((tok=strsep(&line,":"))==NULL)
return;
if(strlen(tok)==16)
make_one_attr(modlist,0,"pgpSubKeyID",tok);
else
return;
/* The subkey algo */
if((tok=strsep(&line,":"))==NULL)
return;
/* Size of subkey */
if((tok=strsep(&line,":"))==NULL)
return;
if(atoi(tok)>0)
{
char padded[6];
int val=atoi(tok);
/* We zero pad this on the left to make PGP happy. */
if(val<99999 && val>0)
{
sprintf(padded,"%05u",atoi(tok));
make_one_attr(modlist,0,"pgpKeySize",padded);
}
}
/* Ignore the rest of the items for subkeys since the LDAP
schema doesn't store them. */
}
else if(ascii_strcasecmp("uid",record)==0)
{
char *userid,*tok;
/* The user ID string */
if((tok=strsep(&line,":"))==NULL)
return;
if(strlen(tok)==0)
return;
userid=tok;
/* By definition, de-%-encoding is always smaller than the
original string so we can decode in place. */
i=0;
while(*tok)
if(tok[0]=='%' && tok[1] && tok[2])
{
if((userid[i]=hextobyte(&tok[1]))==-1)
userid[i]='?';
i++;
tok+=3;
}
else
userid[i++]=*tok++;
userid[i]='\0';
/* We don't care about the other info provided in the uid: line
since the LDAP schema doesn't need it. */
make_one_attr(modlist,0,"pgpUserID",userid);
}
else if(ascii_strcasecmp("sig",record)==0)
{
char *tok;
if((tok=strsep(&line,":"))==NULL)
return;
if(strlen(tok)==16)
make_one_attr(modlist,1,"pgpSignerID",tok);
}
}
static void
free_mod_values(LDAPMod *mod)
{
char **ptr;
if(!mod->mod_values)
return;
for(ptr=mod->mod_values;*ptr;ptr++)
free(*ptr);
free(mod->mod_values);
}
static int
send_key(int *eof)
{
int err,begin=0,end=0,keysize=1,ret=KEYSERVER_INTERNAL_ERROR;
char *dn=NULL,line[MAX_LINE],*key=NULL;
char keyid[17];
LDAPMod **modlist,**addlist,**ml;
modlist=malloc(sizeof(LDAPMod *));
if(!modlist)
{
fprintf(console,"gpgkeys: can't allocate memory for keyserver record\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
*modlist=NULL;
addlist=malloc(sizeof(LDAPMod *));
if(!addlist)
{
fprintf(console,"gpgkeys: can't allocate memory for keyserver record\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
*addlist=NULL;
/* Start by nulling out all attributes. We try and do a modify
operation first, so this ensures that we don't leave old
attributes lying around. */
make_one_attr(&modlist,0,"pgpDisabled",NULL);
make_one_attr(&modlist,0,"pgpKeyID",NULL);
make_one_attr(&modlist,0,"pgpKeyType",NULL);
make_one_attr(&modlist,0,"pgpUserID",NULL);
make_one_attr(&modlist,0,"pgpKeyCreateTime",NULL);
make_one_attr(&modlist,0,"pgpSignerID",NULL);
make_one_attr(&modlist,0,"pgpRevoked",NULL);
make_one_attr(&modlist,0,"pgpSubKeyID",NULL);
make_one_attr(&modlist,0,"pgpKeySize",NULL);
make_one_attr(&modlist,0,"pgpKeyExpireTime",NULL);
make_one_attr(&modlist,0,"pgpCertID",NULL);
/* Assemble the INFO stuff into LDAP attributes */
while(fgets(line,MAX_LINE,input)!=NULL)
if(sscanf(line,"INFO %16s BEGIN\n",keyid)==1)
{
begin=1;
break;
}
if(!begin)
{
/* i.e. eof before the INFO BEGIN was found. This isn't an
error. */
*eof=1;
ret=KEYSERVER_OK;
goto fail;
}
if(strlen(keyid)!=16)
{
*eof=1;
ret=KEYSERVER_KEY_INCOMPLETE;
goto fail;
}
dn=malloc(strlen("pgpCertID=")+16+1+strlen(basekeyspacedn)+1);
if(dn==NULL)
{
fprintf(console,"gpgkeys: can't allocate memory for keyserver record\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
sprintf(dn,"pgpCertID=%s,%s",keyid,basekeyspacedn);
key=malloc(1);
if(!key)
{
fprintf(console,"gpgkeys: unable to allocate memory for key\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
key[0]='\0';
/* Now parse each line until we see the END */
while(fgets(line,MAX_LINE,input)!=NULL)
if(sscanf(line,"INFO %16s END\n",keyid)==1)
{
end=1;
break;
}
else
build_attrs(&addlist,line);
if(!end)
{
fprintf(console,"gpgkeys: no INFO %s END found\n",keyid);
*eof=1;
ret=KEYSERVER_KEY_INCOMPLETE;
goto fail;
}
begin=end=0;
/* Read and throw away stdin until we see the BEGIN */
while(fgets(line,MAX_LINE,input)!=NULL)
if(sscanf(line,"KEY %16s BEGIN\n",keyid)==1)
{
begin=1;
break;
}
if(!begin)
{
/* i.e. eof before the KEY BEGIN was found. This isn't an
error. */
*eof=1;
ret=KEYSERVER_OK;
goto fail;
}
/* Now slurp up everything until we see the END */
while(fgets(line,MAX_LINE,input)!=NULL)
if(sscanf(line,"KEY %16s END\n",keyid)==1)
{
end=1;
break;
}
else
{
char *tempkey;
keysize+=strlen(line);
tempkey=realloc(key,keysize);
if(tempkey==NULL)
{
fprintf(console,"gpgkeys: unable to reallocate for key\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
else
key=tempkey;
strcat(key,line);
}
if(!end)
{
fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
*eof=1;
ret=KEYSERVER_KEY_INCOMPLETE;
goto fail;
}
make_one_attr(&addlist,0,"objectClass","pgpKeyInfo");
make_one_attr(&addlist,0,"pgpKey",key);
/* Now append addlist onto modlist */
if(!join_two_modlists(&modlist,addlist))
{
fprintf(console,"gpgkeys: unable to merge LDAP modification lists\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
/* Going on the assumption that modify operations are more frequent
than adds, we try a modify first. If it's not there, we just
turn around and send an add command for the same key. Otherwise,
the modify brings the server copy into compliance with our copy.
Note that unlike the LDAP keyserver (and really, any other
keyserver) this does NOT merge signatures, but replaces the whole
key. This should make some people very happy. */
err=ldap_modify_s(ldap,dn,modlist);
if(err==LDAP_NO_SUCH_OBJECT)
err=ldap_add_s(ldap,dn,addlist);
if(err!=LDAP_SUCCESS)
{
fprintf(console,"gpgkeys: error adding key %s to keyserver: %s\n",
keyid,ldap_err2string(err));
ret=ldap_err_to_gpg_err(err);
goto fail;
}
ret=KEYSERVER_OK;
fail:
/* Unwind and free the whole modlist structure */
for(ml=modlist;*ml;ml++)
{
free_mod_values(*ml);
free(*ml);
}
free(modlist);
free(addlist);
free(dn);
if(ret!=0 && begin)
fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
return ret;
}
static int
send_key_keyserver(int *eof)
{
int err,begin=0,end=0,keysize=1,ret=KEYSERVER_INTERNAL_ERROR;
char *dn=NULL,line[MAX_LINE],*key[2]={NULL,NULL};
char keyid[17];
LDAPMod mod, *attrs[2];
memset(&mod,0,sizeof(mod));
mod.mod_op=LDAP_MOD_ADD;
mod.mod_type=pgpkeystr;
mod.mod_values=key;
attrs[0]=&mod;
attrs[1]=NULL;
dn=malloc(strlen("pgpCertid=virtual,")+strlen(basekeyspacedn)+1);
if(dn==NULL)
{
fprintf(console,"gpgkeys: can't allocate memory for keyserver record\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
strcpy(dn,"pgpCertid=virtual,");
strcat(dn,basekeyspacedn);
key[0]=malloc(1);
if(key[0]==NULL)
{
fprintf(console,"gpgkeys: unable to allocate memory for key\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
key[0][0]='\0';
/* Read and throw away stdin until we see the BEGIN */
while(fgets(line,MAX_LINE,input)!=NULL)
if(sscanf(line,"KEY %16s BEGIN\n",keyid)==1)
{
begin=1;
break;
}
if(!begin)
{
/* i.e. eof before the KEY BEGIN was found. This isn't an
error. */
*eof=1;
ret=KEYSERVER_OK;
goto fail;
}
/* Now slurp up everything until we see the END */
while(fgets(line,MAX_LINE,input)!=NULL)
if(sscanf(line,"KEY %16s END\n",keyid)==1)
{
end=1;
break;
}
else
{
keysize+=strlen(line);
key[0]=realloc(key[0],keysize);
if(key[0]==NULL)
{
fprintf(console,"gpgkeys: unable to reallocate for key\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
strcat(key[0],line);
}
if(!end)
{
fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
*eof=1;
ret=KEYSERVER_KEY_INCOMPLETE;
goto fail;
}
err=ldap_add_s(ldap,dn,attrs);
if(err!=LDAP_SUCCESS)
{
fprintf(console,"gpgkeys: error adding key %s to keyserver: %s\n",
keyid,ldap_err2string(err));
ret=ldap_err_to_gpg_err(err);
goto fail;
}
ret=KEYSERVER_OK;
fail:
free(key[0]);
free(dn);
if(ret!=0 && begin)
fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
/* Not a fatal error */
if(ret==KEYSERVER_KEY_EXISTS)
ret=KEYSERVER_OK;
return ret;
}
static void
build_info(const char *certid,LDAPMessage *each)
{
char **vals;
fprintf(output,"INFO %s BEGIN\n",certid);
fprintf(output,"pub:%s:",certid);
vals=ldap_get_values(ldap,each,"pgpkeytype");
if(vals!=NULL)
{
if(strcmp(vals[0],"RSA")==0)
fprintf(output,"1");
else if(strcmp(vals[0],"DSS/DH")==0)
fprintf(output,"17");
ldap_value_free(vals);
}
fprintf(output,":");
vals=ldap_get_values(ldap,each,"pgpkeysize");
if(vals!=NULL)
{
if(atoi(vals[0])>0)
fprintf(output,"%d",atoi(vals[0]));
ldap_value_free(vals);
}
fprintf(output,":");
vals=ldap_get_values(ldap,each,"pgpkeycreatetime");
if(vals!=NULL)
{
if(strlen(vals[0])==15)
fprintf(output,"%u",(unsigned int)ldap2epochtime(vals[0]));
ldap_value_free(vals);
}
fprintf(output,":");
vals=ldap_get_values(ldap,each,"pgpkeyexpiretime");
if(vals!=NULL)
{
if(strlen(vals[0])==15)
fprintf(output,"%u",(unsigned int)ldap2epochtime(vals[0]));
ldap_value_free(vals);
}
fprintf(output,":");
vals=ldap_get_values(ldap,each,"pgprevoked");
if(vals!=NULL)
{
if(atoi(vals[0])==1)
fprintf(output,"r");
ldap_value_free(vals);
}
fprintf(output,"\n");
vals=ldap_get_values(ldap,each,"pgpuserid");
if(vals!=NULL)
{
int i;
for(i=0;vals[i];i++)
fprintf(output,"uid:%s\n",vals[i]);
ldap_value_free(vals);
}
fprintf(output,"INFO %s END\n",certid);
}
/* Note that key-not-found is not a fatal error */
static int
get_key(char *getkey)
{
LDAPMessage *res,*each;
int ret=KEYSERVER_INTERNAL_ERROR,err,count;
struct keylist *dupelist=NULL;
char search[62];
/* This ordering is significant - specifically, "pgpcertid" needs to
be the second item in the list, since everything after it may be
discarded if the user isn't in verbose mode. */
char *attrs[]={"replaceme","pgpcertid","pgpuserid","pgpkeyid","pgprevoked",
"pgpdisabled","pgpkeycreatetime","modifytimestamp",
"pgpkeysize","pgpkeytype",NULL};
attrs[0]=pgpkeystr; /* Some compilers don't like using variables as
array initializers. */
/* Build the search string */
/* GPG can send us a v4 fingerprint, a v3 or v4 long key id, or a v3
or v4 short key id */
if(strncmp(getkey,"0x",2)==0)
getkey+=2;
if(strlen(getkey)==32)
{
fprintf(console,
"gpgkeys: LDAP keyservers do not support v3 fingerprints\n");
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
return KEYSERVER_NOT_SUPPORTED;
}
if(strlen(getkey)>16)
{
char *offset=&getkey[strlen(getkey)-16];
/* fingerprint. Take the last 16 characters and treat it like a
long key id */
if(include_subkeys)
sprintf(search,"(|(pgpcertid=%.16s)(pgpsubkeyid=%.16s))",
offset,offset);
else
sprintf(search,"(pgpcertid=%.16s)",offset);
}
else if(strlen(getkey)>8)
{
/* long key id */
if(include_subkeys)
sprintf(search,"(|(pgpcertid=%.16s)(pgpsubkeyid=%.16s))",
getkey,getkey);
else
sprintf(search,"(pgpcertid=%.16s)",getkey);
}
else
{
/* short key id */
sprintf(search,"(pgpkeyid=%.8s)",getkey);
}
if(verbose>2)
fprintf(console,"gpgkeys: LDAP fetch for: %s\n",search);
if(!verbose)
attrs[2]=NULL; /* keep only pgpkey(v2) and pgpcertid */
err=ldap_search_s(ldap,basekeyspacedn,
LDAP_SCOPE_SUBTREE,search,attrs,0,&res);
if(err!=0)
{
int errtag=ldap_err_to_gpg_err(err);
fprintf(console,"gpgkeys: LDAP search error: %s\n",ldap_err2string(err));
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,errtag);
return errtag;
}
count=ldap_count_entries(ldap,res);
if(count<1)
{
fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_KEY_NOT_FOUND);
}
else
{
/* There may be more than one unique result for a given keyID,
so we should fetch them all (test this by fetching short key
id 0xDEADBEEF). */
each=ldap_first_entry(ldap,res);
while(each!=NULL)
{
char **vals,**certid;
/* Use the long keyid to remove duplicates. The LDAP server
returns the same keyid more than once if there are
multiple user IDs on the key. Note that this does NOT
mean that a keyid that exists multiple times on the
keyserver will not be fetched. It means that each KEY,
no matter how many user IDs share its keyid, will be
fetched only once. If a keyid that belongs to more than
one key is fetched, the server quite properly responds
with all matching keys. -ds */
certid=ldap_get_values(ldap,each,"pgpcertid");
if(certid!=NULL)
{
if(!key_in_keylist(certid[0],dupelist))
{
/* it's not a duplicate, so add it */
int rc=add_key_to_keylist(certid[0],&dupelist);
if(rc)
{
ret=rc;
goto fail;
}
build_info(certid[0],each);
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
vals=ldap_get_values(ldap,each,pgpkeystr);
if(vals==NULL)
{
int errtag=ldap_to_gpg_err(ldap);
fprintf(console,"gpgkeys: unable to retrieve key %s "
"from keyserver\n",getkey);
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,errtag);
}
else
{
fprintf(output,"%sKEY 0x%s END\n",vals[0],getkey);
ldap_value_free(vals);
}
}
ldap_value_free(certid);
}
each=ldap_next_entry(ldap,each);
}
}
ret=KEYSERVER_OK;
fail:
ldap_msgfree(res);
free_keylist(dupelist);
return ret;
}
static void
printquoted(FILE *stream,char *string,char delim)
{
while(*string)
{
if(*string==delim || *string=='%')
fprintf(stream,"%%%02x",*string);
else
fputc(*string,stream);
string++;
}
}
/* Returns 0 on success and -1 on error. Note that key-not-found is
not an error! */
static int
search_key(char *searchkey)
{
char **vals;
LDAPMessage *res,*each;
int err,count=0;
struct keylist *dupelist=NULL;
/* The maximum size of the search, including the optional stuff and
the trailing \0 */
char search[2+12+MAX_LINE+2+15+14+1+1];
char *attrs[]={"pgpcertid","pgpuserid","pgprevoked","pgpdisabled",
"pgpkeycreatetime","pgpkeyexpiretime","modifytimestamp",
"pgpkeysize","pgpkeytype",NULL};
fprintf(output,"SEARCH %s BEGIN\n",searchkey);
/* Build the search string */
sprintf(search,"%s(pgpuserid=*%s*)%s%s%s",
(!(include_disabled&&include_revoked))?"(&":"",
searchkey,
include_disabled?"":"(pgpdisabled=0)",
include_revoked?"":"(pgprevoked=0)",
!(include_disabled&&include_revoked)?")":"");
if(verbose>2)
fprintf(console,"gpgkeys: LDAP search for: %s\n",search);
err=ldap_search_s(ldap,basekeyspacedn,
LDAP_SCOPE_SUBTREE,search,attrs,0,&res);
if(err!=LDAP_SUCCESS && err!=LDAP_SIZELIMIT_EXCEEDED)
{
int errtag=ldap_err_to_gpg_err(err);
fprintf(output,"SEARCH %s FAILED %d\n",searchkey,errtag);
fprintf(console,"gpgkeys: LDAP search error: %s\n",ldap_err2string(err));
return errtag;
}
/* The LDAP server doesn't return a real count of unique keys, so we
can't use ldap_count_entries here. */
each=ldap_first_entry(ldap,res);
while(each!=NULL)
{
char **certid=ldap_get_values(ldap,each,"pgpcertid");
if(certid!=NULL)
{
if(!key_in_keylist(certid[0],dupelist))
{
int rc=add_key_to_keylist(certid[0],&dupelist);
if(rc!=0)
{
fprintf(output,"SEARCH %s FAILED %d\n",searchkey,rc);
free_keylist(dupelist);
return rc;
}
count++;
}
}
each=ldap_next_entry(ldap,each);
}
if(err==LDAP_SIZELIMIT_EXCEEDED)
fprintf(console,"gpgkeys: search results exceeded server limit. First %d results shown.\n",count);
free_keylist(dupelist);
dupelist=NULL;
if(count<1)
fprintf(output,"info:1:0\n");
else
{
fprintf(output,"info:1:%d\n",count);
each=ldap_first_entry(ldap,res);
while(each!=NULL)
{
char **certid;
certid=ldap_get_values(ldap,each,"pgpcertid");
if(certid!=NULL)
{
LDAPMessage *uids;
/* Have we seen this certid before? */
if(!key_in_keylist(certid[0],dupelist))
{
int rc=add_key_to_keylist(certid[0],&dupelist);
if(rc)
{
fprintf(output,"SEARCH %s FAILED %d\n",searchkey,rc);
free_keylist(dupelist);
ldap_value_free(certid);
ldap_msgfree(res);
return rc;
}
fprintf(output,"pub:%s:",certid[0]);
vals=ldap_get_values(ldap,each,"pgpkeytype");
if(vals!=NULL)
{
/* The LDAP server doesn't exactly handle this
well. */
if(strcasecmp(vals[0],"RSA")==0)
fprintf(output,"1");
else if(strcasecmp(vals[0],"DSS/DH")==0)
fprintf(output,"17");
ldap_value_free(vals);
}
fputc(':',output);
vals=ldap_get_values(ldap,each,"pgpkeysize");
if(vals!=NULL)
{
/* Not sure why, but some keys are listed with a
key size of 0. Treat that like an
unknown. */
if(atoi(vals[0])>0)
fprintf(output,"%d",atoi(vals[0]));
ldap_value_free(vals);
}
fputc(':',output);
/* YYYYMMDDHHmmssZ */
vals=ldap_get_values(ldap,each,"pgpkeycreatetime");
if(vals!=NULL && strlen(vals[0])==15)
{
fprintf(output,"%u",
(unsigned int)ldap2epochtime(vals[0]));
ldap_value_free(vals);
}
fputc(':',output);
vals=ldap_get_values(ldap,each,"pgpkeyexpiretime");
if(vals!=NULL && strlen(vals[0])==15)
{
fprintf(output,"%u",
(unsigned int)ldap2epochtime(vals[0]));
ldap_value_free(vals);
}
fputc(':',output);
vals=ldap_get_values(ldap,each,"pgprevoked");
if(vals!=NULL)
{
if(atoi(vals[0])==1)
fprintf(output,"r");
ldap_value_free(vals);
}
vals=ldap_get_values(ldap,each,"pgpdisabled");
if(vals!=NULL)
{
if(atoi(vals[0])==1)
fprintf(output,"d");
ldap_value_free(vals);
}
#if 0
/* This is not yet specified in the keyserver
protocol, but may be someday. */
fputc(':',output);
vals=ldap_get_values(ldap,each,"modifytimestamp");
if(vals!=NULL && strlen(vals[0])==15)
{
fprintf(output,"%u",
(unsigned int)ldap2epochtime(vals[0]));
ldap_value_free(vals);
}
#endif
fprintf(output,"\n");
/* Now print all the uids that have this certid */
uids=ldap_first_entry(ldap,res);
while(uids!=NULL)
{
vals=ldap_get_values(ldap,uids,"pgpcertid");
if(vals!=NULL)
{
if(strcasecmp(certid[0],vals[0])==0)
{
char **uidvals;
fprintf(output,"uid:");
uidvals=ldap_get_values(ldap,uids,"pgpuserid");
if(uidvals!=NULL)
{
/* Need to escape any colons */
printquoted(output,uidvals[0],':');
ldap_value_free(uidvals);
}
fprintf(output,"\n");
}
ldap_value_free(vals);
}
uids=ldap_next_entry(ldap,uids);
}
}
ldap_value_free(certid);
}
each=ldap_next_entry(ldap,each);
}
}
ldap_msgfree(res);
free_keylist(dupelist);
fprintf(output,"SEARCH %s END\n",searchkey);
return KEYSERVER_OK;
}
static void
fail_all(struct keylist *keylist,int action,int err)
{
if(!keylist)
return;
if(action==SEARCH)
{
fprintf(output,"SEARCH ");
while(keylist)
{
fprintf(output,"%s ",keylist->str);
keylist=keylist->next;
}
fprintf(output,"FAILED %d\n",err);
}
else
while(keylist)
{
fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
keylist=keylist->next;
}
}
static int
find_basekeyspacedn(void)
{
int err,i;
char *attr[]={"namingContexts",NULL,NULL,NULL};
LDAPMessage *res;
char **context;
/* Look for namingContexts */
err=ldap_search_s(ldap,"",LDAP_SCOPE_BASE,"(objectClass=*)",attr,0,&res);
if(err==LDAP_SUCCESS)
{
context=ldap_get_values(ldap,res,"namingContexts");
if(context)
{
attr[0]="pgpBaseKeySpaceDN";
attr[1]="pgpVersion";
attr[2]="pgpSoftware";
real_ldap=1;
/* We found some, so try each namingContext as the search base
and look for pgpBaseKeySpaceDN. Because we found this, we
know we're talking to a regular-ish LDAP server and not a
LDAP keyserver. */
for(i=0;context[i] && !basekeyspacedn;i++)
{
char **vals;
LDAPMessage *si_res;
char *object;
object=malloc(17+strlen(context[i])+1);
if(!object)
return -1;
strcpy(object,"cn=pgpServerInfo,");
strcat(object,context[i]);
err=ldap_search_s(ldap,object,LDAP_SCOPE_BASE,
"(objectClass=*)",attr,0,&si_res);
free(object);
if(err==LDAP_NO_SUCH_OBJECT)
continue;
else if(err!=LDAP_SUCCESS)
return err;
vals=ldap_get_values(ldap,si_res,"pgpBaseKeySpaceDN");
if(vals)
{
basekeyspacedn=strdup(vals[0]);
ldap_value_free(vals);
}
if(verbose>1)
{
vals=ldap_get_values(ldap,si_res,"pgpSoftware");
if(vals)
{
fprintf(console,"Server: \t%s\n",vals[0]);
ldap_value_free(vals);
}
vals=ldap_get_values(ldap,si_res,"pgpVersion");
if(vals)
{
fprintf(console,"Version:\t%s\n",vals[0]);
ldap_value_free(vals);
}
}
ldap_msgfree(si_res);
}
ldap_value_free(context);
}
ldap_msgfree(res);
}
else
{
/* We don't have an answer yet, which means the server might be
a LDAP keyserver. */
char **vals;
LDAPMessage *si_res;
attr[0]="pgpBaseKeySpaceDN";
attr[1]="version";
attr[2]="software";
err=ldap_search_s(ldap,"cn=pgpServerInfo",LDAP_SCOPE_BASE,
"(objectClass=*)",attr,0,&si_res);
if(err!=LDAP_SUCCESS)
return err;
/* For the LDAP keyserver, this is always "OU=ACTIVE,O=PGP
KEYSPACE,C=US", but it might not be in the future. */
vals=ldap_get_values(ldap,si_res,"baseKeySpaceDN");
if(vals)
{
basekeyspacedn=strdup(vals[0]);
ldap_value_free(vals);
}
if(verbose>1)
{
vals=ldap_get_values(ldap,si_res,"software");
if(vals)
{
fprintf(console,"Server: \t%s\n",vals[0]);
ldap_value_free(vals);
}
}
vals=ldap_get_values(ldap,si_res,"version");
if(vals)
{
if(verbose>1)
fprintf(console,"Version:\t%s\n",vals[0]);
/* If the version is high enough, use the new pgpKeyV2
attribute. This design if iffy at best, but it matches how
PGP does it. I figure the NAI folks assumed that there would
never be a LDAP keyserver vendor with a different numbering
scheme. */
if(atoi(vals[0])>1)
pgpkeystr="pgpKeyV2";
ldap_value_free(vals);
}
ldap_msgfree(si_res);
}
return LDAP_SUCCESS;
}
int
main(int argc,char *argv[])
{
int debug=0,port=0,arg,err,action=-1,ret=KEYSERVER_INTERNAL_ERROR;
char line[MAX_LINE];
int version,failed=0,use_ssl=0,use_tls=0,bound=0;
struct keylist *keylist=NULL,*keyptr=NULL;
console=stderr;
while((arg=getopt(argc,argv,"hVo:"))!=-1)
switch(arg)
{
default:
case 'h':
fprintf(console,"-h\thelp\n");
fprintf(console,"-V\tversion\n");
fprintf(console,"-o\toutput to this file\n");
return KEYSERVER_OK;
case 'V':
fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
return KEYSERVER_OK;
case 'o':
output=fopen(optarg,"w");
if(output==NULL)
{
- fprintf(console,"gpgkeys: Cannot open output file \"%s\": %s\n",
+ fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
optarg,strerror(errno));
return KEYSERVER_INTERNAL_ERROR;
}
break;
}
if(argc>optind)
{
input=fopen(argv[optind],"r");
if(input==NULL)
{
- fprintf(console,"gpgkeys: Cannot open input file \"%s\": %s\n",
+ fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
argv[optind],strerror(errno));
return KEYSERVER_INTERNAL_ERROR;
}
}
if(input==NULL)
input=stdin;
if(output==NULL)
output=stdout;
/* Get the command and info block */
while(fgets(line,MAX_LINE,input)!=NULL)
{
char commandstr[7];
char optionstr[256];
char schemestr[80];
char hash;
if(line[0]=='\n')
break;
if(sscanf(line,"%c",&hash)==1 && hash=='#')
continue;
if(sscanf(line,"COMMAND %6s\n",commandstr)==1)
{
commandstr[6]='\0';
if(strcasecmp(commandstr,"get")==0)
action=GET;
else if(strcasecmp(commandstr,"send")==0)
action=SEND;
else if(strcasecmp(commandstr,"search")==0)
action=SEARCH;
continue;
}
if(sscanf(line,"HOST %79s\n",host)==1)
{
host[79]='\0';
continue;
}
if(sscanf(line,"PORT %9s\n",portstr)==1)
{
portstr[9]='\0';
port=atoi(portstr);
continue;
}
if(sscanf(line,"SCHEME %79s\n",schemestr)==1)
{
schemestr[79]='\0';
if(strcasecmp(schemestr,"ldaps")==0)
{
port=636;
use_ssl=1;
}
continue;
}
if(sscanf(line,"VERSION %d\n",&version)==1)
{
if(version!=KEYSERVER_PROTO_VERSION)
{
ret=KEYSERVER_VERSION_ERROR;
goto fail;
}
continue;
}
if(sscanf(line,"OPTION %255[^\n]\n",optionstr)==1)
{
int no=0;
char *start=&optionstr[0];
optionstr[255]='\0';
if(strncasecmp(optionstr,"no-",3)==0)
{
no=1;
start=&optionstr[3];
}
if(strcasecmp(start,"verbose")==0)
{
if(no)
verbose--;
else
verbose++;
}
else if(strcasecmp(start,"include-disabled")==0)
{
if(no)
include_disabled=0;
else
include_disabled=1;
}
else if(strcasecmp(start,"include-revoked")==0)
{
if(no)
include_revoked=0;
else
include_revoked=1;
}
else if(strcasecmp(start,"include-subkeys")==0)
{
if(no)
include_subkeys=0;
else
include_subkeys=1;
}
else if(strncasecmp(start,"tls",3)==0)
{
if(no)
use_tls=0;
else if(start[3]=='=')
{
if(strcasecmp(&start[4],"no")==0)
use_tls=0;
else if(strcasecmp(&start[4],"try")==0)
use_tls=1;
else if(strcasecmp(&start[4],"warn")==0)
use_tls=2;
else if(strcasecmp(&start[4],"require")==0)
use_tls=3;
else
use_tls=1;
}
else if(start[3]=='\0')
use_tls=1;
}
else if(strncasecmp(start,"debug",5)==0)
{
if(no)
debug=0;
else if(start[5]=='=')
debug=atoi(&start[6]);
}
else if(strncasecmp(start,"basedn",6)==0)
{
if(no)
{
free(basekeyspacedn);
basekeyspacedn=NULL;
}
else if(start[6]=='=')
{
free(basekeyspacedn);
basekeyspacedn=strdup(&start[7]);
if(!basekeyspacedn)
{
fprintf(console,"gpgkeys: out of memory while creating "
"base DN\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
real_ldap=1;
}
}
continue;
}
}
/* SSL trumps TLS */
if(use_ssl)
use_tls=0;
/* If it's a GET or a SEARCH, the next thing to come in is the
keyids. If it's a SEND, then there are no keyids. */
if(action==SEND)
while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
else if(action==GET || action==SEARCH)
{
for(;;)
{
struct keylist *work;
if(fgets(line,MAX_LINE,input)==NULL)
break;
else
{
if(line[0]=='\n' || line[0]=='\0')
break;
work=malloc(sizeof(struct keylist));
if(work==NULL)
{
fprintf(console,"gpgkeys: out of memory while "
"building key list\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
strcpy(work->str,line);
/* Trim the trailing \n */
work->str[strlen(line)-1]='\0';
work->next=NULL;
/* Always attach at the end to keep the list in proper
order for searching */
if(keylist==NULL)
keylist=work;
else
keyptr->next=work;
keyptr=work;
}
}
}
else
{
fprintf(console,"gpgkeys: no keyserver command specified\n");
goto fail;
}
/* Send the response */
fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
fprintf(output,"PROGRAM %s\n\n",VERSION);
if(verbose>1)
{
fprintf(console,"Host:\t\t%s\n",host);
if(port)
fprintf(console,"Port:\t\t%d\n",port);
fprintf(console,"Command:\t%s\n",action==GET?"GET":
action==SEND?"SEND":"SEARCH");
}
if(debug)
{
#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(HAVE_LDAP_SET_OPTION)
err=ldap_set_option(NULL,LDAP_OPT_DEBUG_LEVEL,&debug);
if(err!=LDAP_SUCCESS)
fprintf(console,"gpgkeys: unable to set debug mode: %s\n",
ldap_err2string(err));
else
fprintf(console,"gpgkeys: debug level %d\n",debug);
#else
fprintf(console,"gpgkeys: not built with debugging support\n");
#endif
}
/* Note that this tries all A records on a given host (or at least,
OpenLDAP does). */
ldap=ldap_init(host,port);
if(ldap==NULL)
{
fprintf(console,"gpgkeys: internal LDAP init error: %s\n",
strerror(errno));
fail_all(keylist,action,KEYSERVER_INTERNAL_ERROR);
goto fail;
}
if(use_ssl)
{
#if defined(LDAP_OPT_X_TLS_HARD) && defined(HAVE_LDAP_SET_OPTION)
int ssl=LDAP_OPT_X_TLS_HARD;
err=ldap_set_option(ldap,LDAP_OPT_X_TLS,&ssl);
if(err!=LDAP_SUCCESS)
{
fprintf(console,"gpgkeys: unable to make SSL connection: %s\n",
ldap_err2string(err));
fail_all(keylist,action,ldap_err_to_gpg_err(err));
goto fail;
}
#else
fprintf(console,"gpgkeys: unable to make SSL connection: %s\n",
"not built with LDAPS support");
fail_all(keylist,action,KEYSERVER_INTERNAL_ERROR);
goto fail;
#endif
}
if(!basekeyspacedn)
if((err=find_basekeyspacedn()) || !basekeyspacedn)
{
fprintf(console,"gpgkeys: unable to retrieve LDAP base: %s\n",
err?ldap_err2string(err):"not found");
fail_all(keylist,action,ldap_err_to_gpg_err(err));
goto fail;
}
/* use_tls: 0=don't use, 1=try silently to use, 2=try loudly to use,
3=force use. */
if(use_tls)
{
if(!real_ldap)
{
if(use_tls>=2)
fprintf(console,"gpgkeys: unable to start TLS: %s\n",
"not supported by the NAI LDAP keyserver");
if(use_tls==3)
{
fail_all(keylist,action,KEYSERVER_INTERNAL_ERROR);
goto fail;
}
}
else
{
#if defined(HAVE_LDAP_START_TLS_S) && defined(HAVE_LDAP_SET_OPTION)
int ver=LDAP_VERSION3;
err=LDAP_SUCCESS;
err=ldap_set_option(ldap,LDAP_OPT_PROTOCOL_VERSION,&ver);
if(err==LDAP_SUCCESS)
err=ldap_start_tls_s(ldap,NULL,NULL);
if(err!=LDAP_SUCCESS)
{
if(use_tls==2 || verbose>2)
fprintf(console,"gpgkeys: unable to start TLS: %s\n",
ldap_err2string(err));
/* Are we forcing it? */
if(use_tls==3)
{
fail_all(keylist,action,ldap_err_to_gpg_err(err));
goto fail;
}
}
else if(err==LDAP_SUCCESS && verbose>1)
fprintf(console,"gpgkeys: TLS started successfully.\n");
#else
if(use_tls>=2)
fprintf(console,"gpgkeys: unable to start TLS: %s\n",
"not built with TLS support");
if(use_tls==3)
{
fail_all(keylist,action,KEYSERVER_INTERNAL_ERROR);
goto fail;
}
#endif
}
}
#if 0
/* The LDAP keyserver doesn't require this, but it might be useful
if someone stores keys on a V2 LDAP server somewhere. (V3
doesn't require a bind). Leave this out for now since it is not
clear if anyone server we're likely to use really cares, plus
there are some servers that don't allow it. */
err=ldap_simple_bind_s(ldap,NULL,NULL);
if(err!=0)
{
fprintf(console,"gpgkeys: internal LDAP bind error: %s\n",
ldap_err2string(err));
fail_all(keylist,action,ldap_err_to_gpg_err(err));
goto fail;
}
else
bound=1;
#endif
switch(action)
{
case GET:
keyptr=keylist;
while(keyptr!=NULL)
{
if(get_key(keyptr->str)!=KEYSERVER_OK)
failed++;
keyptr=keyptr->next;
}
break;
case SEND:
{
int eof=0;
do
{
if(real_ldap)
{
if(send_key(&eof)!=KEYSERVER_OK)
failed++;
}
else
{
if(send_key_keyserver(&eof)!=KEYSERVER_OK)
failed++;
}
}
while(!eof);
}
break;
case SEARCH:
{
char *searchkey=NULL;
int len=0;
/* To search, we stick a * in between each key to search for.
This means that if the user enters words, they'll get
"enters*words". If the user "enters words", they'll get
"enters words" */
keyptr=keylist;
while(keyptr!=NULL)
{
len+=strlen(keyptr->str)+1;
keyptr=keyptr->next;
}
searchkey=malloc(len+1);
if(searchkey==NULL)
{
ret=KEYSERVER_NO_MEMORY;
fail_all(keylist,action,KEYSERVER_NO_MEMORY);
goto fail;
}
searchkey[0]='\0';
keyptr=keylist;
while(keyptr!=NULL)
{
strcat(searchkey,keyptr->str);
strcat(searchkey,"*");
keyptr=keyptr->next;
}
/* Nail that last "*" */
if(*searchkey)
searchkey[strlen(searchkey)-1]='\0';
if(search_key(searchkey)!=KEYSERVER_OK)
failed++;
free(searchkey);
}
break;
}
if(!failed)
ret=KEYSERVER_OK;
fail:
while(keylist!=NULL)
{
struct keylist *current=keylist;
keylist=keylist->next;
free(current);
}
if(input!=stdin)
fclose(input);
if(output!=stdout)
fclose(output);
if(ldap!=NULL && bound)
ldap_unbind_s(ldap);
free(basekeyspacedn);
return ret;
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Dec 19, 3:52 AM (1 d, 17 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ca/20/335cc0b888f78e567a14f25f4e73

Event Timeline