diff --git a/dirmngr/crlfetch.c b/dirmngr/crlfetch.c
index 7da3d8b6e..c8091f6f6 100644
--- a/dirmngr/crlfetch.c
+++ b/dirmngr/crlfetch.c
@@ -1,589 +1,589 @@
/* crlfetch.c - LDAP access
* Copyright (C) 2002 Klarälvdalens Datakonsult AB
* Copyright (C) 2003, 2004, 2005, 2006, 2007 g10 Code GmbH
*
* This file is part of DirMngr.
*
* DirMngr 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.
*
* DirMngr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include "crlfetch.h"
#include "dirmngr.h"
#include "misc.h"
#include "http.h"
#include "ks-engine.h" /* For ks_http_fetch. */
#if USE_LDAP
# include "ldap-wrapper.h"
#endif
/* For detecting armored CRLs received via HTTP (yes, such CRLS really
exits, e.g. http://grid.fzk.de/ca/gridka-crl.pem at least in June
2008) we need a context in the reader callback. */
struct reader_cb_context_s
{
estream_t fp; /* The stream used with the ksba reader. */
int checked:1; /* PEM/binary detection ahs been done. */
int is_pem:1; /* The file stream is PEM encoded. */
struct b64state b64state; /* The state used for Base64 decoding. */
};
/* We need to associate a reader object with the reader callback
context. This table is used for it. */
struct file_reader_map_s
{
ksba_reader_t reader;
struct reader_cb_context_s *cb_ctx;
};
#define MAX_FILE_READER 50
static struct file_reader_map_s file_reader_map[MAX_FILE_READER];
/* Associate FP with READER. If the table is full wait until another
thread has removed an entry. */
static void
register_file_reader (ksba_reader_t reader, struct reader_cb_context_s *cb_ctx)
{
int i;
for (;;)
{
for (i=0; i < MAX_FILE_READER; i++)
if (!file_reader_map[i].reader)
{
file_reader_map[i].reader = reader;
file_reader_map[i].cb_ctx = cb_ctx;
return;
}
log_info (_("reader to file mapping table full - waiting\n"));
npth_sleep (2);
}
}
/* Scan the table for an entry matching READER, remove that entry and
return the associated file pointer. */
static struct reader_cb_context_s *
get_file_reader (ksba_reader_t reader)
{
struct reader_cb_context_s *cb_ctx = NULL;
int i;
for (i=0; i < MAX_FILE_READER; i++)
if (file_reader_map[i].reader == reader)
{
cb_ctx = file_reader_map[i].cb_ctx;
file_reader_map[i].reader = NULL;
file_reader_map[i].cb_ctx = NULL;
break;
}
return cb_ctx;
}
static int
my_es_read (void *opaque, char *buffer, size_t nbytes, size_t *nread)
{
struct reader_cb_context_s *cb_ctx = opaque;
int result;
result = es_read (cb_ctx->fp, buffer, nbytes, nread);
if (result)
return result;
/* Fixme we should check whether the semantics of es_read are okay
and well defined. I have some doubts. */
if (nbytes && !*nread && es_feof (cb_ctx->fp))
return gpg_error (GPG_ERR_EOF);
if (!nread && es_ferror (cb_ctx->fp))
return gpg_error (GPG_ERR_EIO);
if (!cb_ctx->checked && *nread)
{
int c = *(unsigned char *)buffer;
cb_ctx->checked = 1;
if ( ((c & 0xc0) >> 6) == 0 /* class: universal */
&& (c & 0x1f) == 16 /* sequence */
&& (c & 0x20) /* is constructed */ )
; /* Binary data. */
else
{
cb_ctx->is_pem = 1;
b64dec_start (&cb_ctx->b64state, "");
}
}
if (cb_ctx->is_pem && *nread)
{
size_t nread2;
if (b64dec_proc (&cb_ctx->b64state, buffer, *nread, &nread2))
{
/* EOF from decoder. */
*nread = 0;
result = gpg_error (GPG_ERR_EOF);
}
else
*nread = nread2;
}
return result;
}
/* Fetch CRL from URL and return the entire CRL using new ksba reader
object in READER. Note that this reader object should be closed
only using ldap_close_reader. */
gpg_error_t
crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader)
{
gpg_error_t err;
parsed_uri_t uri;
estream_t httpfp = NULL;
*reader = NULL;
if (!url)
return gpg_error (GPG_ERR_INV_ARG);
err = http_parse_uri (&uri, url, 0);
http_release_parsed_uri (uri);
if (!err) /* Yes, our HTTP code groks that. */
{
if (opt.disable_http)
{
log_error (_("CRL access not possible due to disabled %s\n"),
"HTTP");
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
}
else
{
/* Note that we also allow root certificates loaded from
* "/etc/gnupg/trusted-certs/". We also do not consult the
* CRL for the TLS connection - that may lead to a loop.
* Due to cacert.org redirecting their https URL to http we
* also allow such a downgrade. */
err = ks_http_fetch (ctrl, url,
(KS_HTTP_FETCH_TRUST_CFG
| KS_HTTP_FETCH_NO_CRL
| KS_HTTP_FETCH_ALLOW_DOWNGRADE ),
&httpfp);
}
if (err)
log_error (_("error retrieving '%s': %s\n"), url, gpg_strerror (err));
else
{
struct reader_cb_context_s *cb_ctx;
cb_ctx = xtrycalloc (1, sizeof *cb_ctx);
if (!cb_ctx)
err = gpg_error_from_syserror ();
else if (!(err = ksba_reader_new (reader)))
{
cb_ctx->fp = httpfp;
err = ksba_reader_set_cb (*reader, &my_es_read, cb_ctx);
if (!err)
{
/* The ksba reader misses a user pointer thus we
* need to come up with our own way of associating a
* file pointer (or well the callback context) with
* the reader. It is only required when closing the
* reader thus there is no performance issue doing
* it this way. FIXME: We now have a close
* notification which might be used here. */
register_file_reader (*reader, cb_ctx);
httpfp = NULL;
}
}
if (err)
{
log_error (_("error initializing reader object: %s\n"),
gpg_strerror (err));
ksba_reader_release (*reader);
*reader = NULL;
xfree (cb_ctx);
}
}
}
else /* Let the LDAP code parse other schemes. */
{
if (opt.disable_ldap)
{
log_error (_("CRL access not possible due to disabled %s\n"),
"LDAP");
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
}
else if (dirmngr_use_tor ())
{
/* For now we do not support LDAP over Tor. */
log_error (_("CRL access not possible due to Tor mode\n"));
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
}
else
{
# if USE_LDAP
err = url_fetch_ldap (ctrl, url, NULL, 0, reader);
# else /*!USE_LDAP*/
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
# endif /*!USE_LDAP*/
}
}
es_fclose (httpfp);
return err;
}
/* Fetch CRL for ISSUER using a default server. Return the entire CRL
as a newly opened stream returned in R_FP. */
gpg_error_t
crl_fetch_default (ctrl_t ctrl, const char *issuer, ksba_reader_t *reader)
{
if (dirmngr_use_tor ())
{
/* For now we do not support LDAP over Tor. */
log_error (_("CRL access not possible due to Tor mode\n"));
return gpg_error (GPG_ERR_NOT_SUPPORTED);
}
if (opt.disable_ldap)
{
log_error (_("CRL access not possible due to disabled %s\n"),
"LDAP");
return gpg_error (GPG_ERR_NOT_SUPPORTED);
}
#if USE_LDAP
return attr_fetch_ldap (ctrl, issuer, "certificateRevocationList",
reader);
#else
(void)ctrl;
(void)issuer;
(void)reader;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#endif
}
/* Fetch a CA certificate for DN using the default server. This
* function only initiates the fetch; fetch_next_cert must be used to
* actually read the certificate; end_cert_fetch to end the
* operation. */
gpg_error_t
ca_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context, const char *dn)
{
if (dirmngr_use_tor ())
{
/* For now we do not support LDAP over Tor. */
log_error (_("CRL access not possible due to Tor mode\n"));
return gpg_error (GPG_ERR_NOT_SUPPORTED);
}
if (opt.disable_ldap)
{
log_error (_("CRL access not possible due to disabled %s\n"),
"LDAP");
return gpg_error (GPG_ERR_NOT_SUPPORTED);
}
#if USE_LDAP
- return start_default_fetch_ldap (ctrl, context, dn, "cACertificate");
+ return start_cacert_fetch_ldap (ctrl, context, dn);
#else
(void)ctrl;
(void)context;
(void)dn;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#endif
}
gpg_error_t
start_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context,
strlist_t patterns, const ldap_server_t server)
{
if (dirmngr_use_tor ())
{
/* For now we do not support LDAP over Tor. */
log_error (_("CRL access not possible due to Tor mode\n"));
return gpg_error (GPG_ERR_NOT_SUPPORTED);
}
if (opt.disable_ldap)
{
log_error (_("certificate search not possible due to disabled %s\n"),
"LDAP");
return gpg_error (GPG_ERR_NOT_SUPPORTED);
}
#if USE_LDAP
return start_cert_fetch_ldap (ctrl, context, patterns, server);
#else
(void)ctrl;
(void)context;
(void)patterns;
(void)server;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#endif
}
gpg_error_t
fetch_next_cert (cert_fetch_context_t context,
unsigned char **value, size_t * valuelen)
{
#if USE_LDAP
return fetch_next_cert_ldap (context, value, valuelen);
#else
(void)context;
(void)value;
(void)valuelen;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#endif
}
/* Fetch the next data from CONTEXT, assuming it is a certificate and return
* it as a cert object in R_CERT. */
gpg_error_t
fetch_next_ksba_cert (cert_fetch_context_t context, ksba_cert_t *r_cert)
{
gpg_error_t err;
unsigned char *value;
size_t valuelen;
ksba_cert_t cert;
*r_cert = NULL;
#if USE_LDAP
err = fetch_next_cert_ldap (context, &value, &valuelen);
if (!err && !value)
err = gpg_error (GPG_ERR_BUG);
#else
(void)context;
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#endif
if (err)
return err;
err = ksba_cert_new (&cert);
if (err)
{
xfree (value);
return err;
}
err = ksba_cert_init_from_mem (cert, value, valuelen);
xfree (value);
if (err)
{
ksba_cert_release (cert);
return err;
}
*r_cert = cert;
return 0;
}
void
end_cert_fetch (cert_fetch_context_t context)
{
#if USE_LDAP
end_cert_fetch_ldap (context);
#else
(void)context;
#endif
}
/* Read a certificate from an HTTP URL and return it as an estream
* memory buffer at R_FP. */
static gpg_error_t
read_cert_via_http (ctrl_t ctrl, const char *url, estream_t *r_fp)
{
gpg_error_t err;
estream_t fp = NULL;
estream_t httpfp = NULL;
size_t nread, nwritten;
char buffer[1024];
if ((err = ks_http_fetch (ctrl, url, KS_HTTP_FETCH_TRUST_CFG, &httpfp)))
goto leave;
/* We now read the data from the web server into a memory buffer.
* To DOS we limit the certificate length to 32k. */
fp = es_fopenmem (32*1024, "rw");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
goto leave;
}
for (;;)
{
if (es_read (httpfp, buffer, sizeof buffer, &nread))
{
err = gpg_error_from_syserror ();
log_error ("error reading '%s': %s\n",
es_fname_get (httpfp), gpg_strerror (err));
goto leave;
}
if (!nread)
break; /* Ready. */
if (es_write (fp, buffer, nread, &nwritten))
{
err = gpg_error_from_syserror ();
log_error ("error writing '%s': %s\n",
es_fname_get (fp), gpg_strerror (err));
goto leave;
}
else if (nread != nwritten)
{
err = gpg_error (GPG_ERR_EIO);
log_error ("error writing '%s': %s\n",
es_fname_get (fp), "short write");
goto leave;
}
}
es_rewind (fp);
*r_fp = fp;
fp = NULL;
leave:
es_fclose (httpfp);
es_fclose (fp);
return err;
}
/* Lookup a cert by it's URL. */
gpg_error_t
fetch_cert_by_url (ctrl_t ctrl, const char *url,
unsigned char **value, size_t *valuelen)
{
const unsigned char *cert_image = NULL;
size_t cert_image_n;
ksba_reader_t reader = NULL;
ksba_cert_t cert = NULL;
gpg_error_t err;
*value = NULL;
*valuelen = 0;
err = ksba_cert_new (&cert);
if (err)
goto leave;
if (url && (!strncmp (url, "http:", 5) || !strncmp (url, "https:", 6)))
{
estream_t stream;
void *der;
size_t derlen;
err = read_cert_via_http (ctrl, url, &stream);
if (err)
goto leave;
if (es_fclose_snatch (stream, &der, &derlen))
{
err = gpg_error_from_syserror ();
goto leave;
}
err = ksba_cert_init_from_mem (cert, der, derlen);
xfree (der);
if (err)
goto leave;
}
else /* Assume LDAP. */
{
#if USE_LDAP
err = url_fetch_ldap (ctrl, url, NULL, 0, &reader);
#else
(void)ctrl;
(void)url;
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#endif /*USE_LDAP*/
if (err)
goto leave;
err = ksba_cert_read_der (cert, reader);
if (err)
goto leave;
}
cert_image = ksba_cert_get_image (cert, &cert_image_n);
if (!cert_image || !cert_image_n)
{
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
goto leave;
}
*value = xtrymalloc (cert_image_n);
if (!*value)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (*value, cert_image, cert_image_n);
*valuelen = cert_image_n;
leave:
ksba_cert_release (cert);
#if USE_LDAP
ldap_wrapper_release_context (reader);
#endif /*USE_LDAP*/
return err;
}
/* This function is to be used to close the reader object. In
addition to running ksba_reader_release it also releases the LDAP
or HTTP contexts associated with that reader. */
void
crl_close_reader (ksba_reader_t reader)
{
struct reader_cb_context_s *cb_ctx;
if (!reader)
return;
/* Check whether this is a HTTP one. */
cb_ctx = get_file_reader (reader);
if (cb_ctx)
{
/* This is an HTTP context. */
if (cb_ctx->fp)
es_fclose (cb_ctx->fp);
/* Release the base64 decoder state. */
if (cb_ctx->is_pem)
b64dec_finish (&cb_ctx->b64state);
/* Release the callback context. */
xfree (cb_ctx);
}
else /* This is an ldap wrapper context (Currently not used). */
{
#if USE_LDAP
ldap_wrapper_release_context (reader);
#endif /*USE_LDAP*/
}
/* Now get rid of the reader object. */
ksba_reader_release (reader);
}
diff --git a/dirmngr/crlfetch.h b/dirmngr/crlfetch.h
index cf4a3c0aa..3822adb54 100644
--- a/dirmngr/crlfetch.h
+++ b/dirmngr/crlfetch.h
@@ -1,88 +1,88 @@
/* crlfetch.h - LDAP access
* Copyright (C) 2002 Klarälvdalens Datakonsult AB
*
* This file is part of DirMngr.
*
* DirMngr 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.
*
* DirMngr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#ifndef CRLFETCH_H
#define CRLFETCH_H
#include "dirmngr.h"
struct cert_fetch_context_s;
typedef struct cert_fetch_context_s *cert_fetch_context_t;
/* Fetch CRL from URL. */
gpg_error_t crl_fetch (ctrl_t ctrl, const char* url, ksba_reader_t *reader);
/* Fetch CRL for ISSUER using default server. */
gpg_error_t crl_fetch_default (ctrl_t ctrl,
const char* issuer, ksba_reader_t *reader);
/* Fetch cert for DN. */
gpg_error_t ca_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context,
const char *dn);
/* Query the server for certs matching patterns. */
gpg_error_t start_cert_fetch (ctrl_t ctrl,
cert_fetch_context_t *context,
strlist_t patterns,
const ldap_server_t server);
gpg_error_t fetch_next_cert(cert_fetch_context_t context,
unsigned char **value, size_t *valuelen);
gpg_error_t fetch_next_ksba_cert (cert_fetch_context_t context,
ksba_cert_t *r_cert);
void end_cert_fetch (cert_fetch_context_t context);
/* Lookup a cert by it's URL. */
gpg_error_t fetch_cert_by_url (ctrl_t ctrl, const char *url,
unsigned char **value, size_t *valuelen);
/* Close a reader object. */
void crl_close_reader (ksba_reader_t reader);
/*-- ldap.c --*/
gpg_error_t url_fetch_ldap (ctrl_t ctrl,
const char *url, const char *host, int port,
ksba_reader_t *reader);
gpg_error_t attr_fetch_ldap (ctrl_t ctrl,
const char *dn, const char *attr,
ksba_reader_t *reader);
-gpg_error_t start_default_fetch_ldap (ctrl_t ctrl,
- cert_fetch_context_t *context,
- const char *dn, const char *attr);
+gpg_error_t start_cacert_fetch_ldap (ctrl_t ctrl,
+ cert_fetch_context_t *context,
+ const char *dn);
gpg_error_t start_cert_fetch_ldap( ctrl_t ctrl,
cert_fetch_context_t *context,
strlist_t patterns,
const ldap_server_t server );
gpg_error_t fetch_next_cert_ldap (cert_fetch_context_t context,
unsigned char **value, size_t *valuelen );
void end_cert_fetch_ldap (cert_fetch_context_t context);
#endif /* CRLFETCH_H */
diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c
index cb3c0b763..e016f6415 100644
--- a/dirmngr/ldap.c
+++ b/dirmngr/ldap.c
@@ -1,874 +1,874 @@
/* ldap.c - LDAP access
* Copyright (C) 2002 Klarälvdalens Datakonsult AB
* Copyright (C) 2003, 2004, 2005, 2007, 2008, 2010 g10 Code GmbH
*
* This file is part of DirMngr.
*
* DirMngr 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.
*
* DirMngr 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
#include
#include
#include
#include
#include
#include
#include
#include
#include "dirmngr.h"
#include "../common/exechelp.h"
#include "crlfetch.h"
#include "ldapserver.h"
#include "misc.h"
#include "ldap-wrapper.h"
#include "../common/host2net.h"
#define UNENCODED_URL_CHARS "abcdefghijklmnopqrstuvwxyz" \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
"01234567890" \
"$-_.+!*'(),"
#define USERCERTIFICATE "userCertificate"
#define CACERTIFICATE "caCertificate"
#define X509CACERT "x509caCert"
#define USERSMIMECERTIFICATE "userSMIMECertificate"
/* Definition for the context of the cert fetch functions. */
struct cert_fetch_context_s
{
ksba_reader_t reader; /* The reader used (shallow copy). */
unsigned char *tmpbuf; /* Helper buffer. */
size_t tmpbufsize; /* Allocated size of tmpbuf. */
int truncated; /* Flag to indicate a truncated output. */
};
/* Add HOST and PORT to our list of LDAP servers. Fixme: We should
better use an extra list of servers. */
static void
add_server_to_servers (const char *host, int port)
{
ldap_server_t server;
ldap_server_t last = NULL;
const char *s;
if (!port)
port = 389;
for (server=opt.ldapservers; server; server = server->next)
{
if (!strcmp (server->host, host) && server->port == port)
return; /* already in list... */
last = server;
}
/* We assume that the host names are all supplied by our
configuration files and thus are sane. To keep this assumption
we must reject all invalid host names. */
for (s=host; *s; s++)
if (!strchr ("abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"01234567890.-", *s))
{
log_error (_("invalid char 0x%02x in host name - not added\n"), *s);
return;
}
log_info (_("adding '%s:%d' to the ldap server list\n"), host, port);
server = xtrycalloc (1, sizeof *s);
if (!server)
log_error (_("malloc failed: %s\n"), strerror (errno));
else
{
server->host = xstrdup (host);
server->port = port;
if (last)
last->next = server;
else
opt.ldapservers = server;
}
}
/* Perform an LDAP query. Returns an gpg error code or 0 on success.
The function returns a new reader object at READER. */
static gpg_error_t
run_ldap_wrapper (ctrl_t ctrl,
int ignore_timeout,
int multi_mode,
const char *proxy,
const char *host, int port,
const char *user, const char *pass,
const char *dn, const char *filter, const char *attr,
const char *url,
ksba_reader_t *reader)
{
const char *argv[40];
int argc;
char portbuf[30], timeoutbuf[30];
*reader = NULL;
argc = 0;
if (pass) /* Note, that the password must be the first item. */
{
argv[argc++] = "--pass";
argv[argc++] = pass;
}
if (DBG_LOOKUP)
argv[argc++] = "-vv";
else if (DBG_EXTPROG)
argv[argc++] = "-v";
argv[argc++] = "--log-with-pid";
if (multi_mode)
argv[argc++] = "--multi";
if (opt.ldaptimeout)
{
sprintf (timeoutbuf, "%u", opt.ldaptimeout);
argv[argc++] = "--timeout";
argv[argc++] = timeoutbuf;
if (ignore_timeout)
argv[argc++] = "--only-search-timeout";
}
if (proxy)
{
argv[argc++] = "--proxy";
argv[argc++] = proxy;
}
if (host)
{
argv[argc++] = "--host";
argv[argc++] = host;
}
if (port)
{
sprintf (portbuf, "%d", port);
argv[argc++] = "--port";
argv[argc++] = portbuf;
}
if (user)
{
argv[argc++] = "--user";
argv[argc++] = user;
}
if (dn)
{
argv[argc++] = "--dn";
argv[argc++] = dn;
}
if (filter)
{
argv[argc++] = "--filter";
argv[argc++] = filter;
}
if (attr)
{
argv[argc++] = "--attr";
argv[argc++] = attr;
}
argv[argc++] = url? url : "ldap://";
argv[argc] = NULL;
return ldap_wrapper (ctrl, reader, argv);
}
/* Perform a LDAP query using a given URL. On success a new ksba
reader is returned. If HOST or PORT are not 0, they are used to
override the values from the URL. */
gpg_error_t
url_fetch_ldap (ctrl_t ctrl, const char *url, const char *host, int port,
ksba_reader_t *reader)
{
gpg_error_t err;
err = run_ldap_wrapper (ctrl,
1, /* Ignore explicit timeout because CRLs
might be very large. */
0,
opt.ldap_proxy,
host, port,
NULL, NULL,
NULL, NULL, NULL, url,
reader);
/* FIXME: This option might be used for DoS attacks. Because it
will enlarge the list of servers to consult without a limit and
all LDAP queries w/o a host are will then try each host in
turn. */
if (!err && opt.add_new_ldapservers && !opt.ldap_proxy)
{
if (host)
add_server_to_servers (host, port);
else if (url)
{
char *tmp = host_and_port_from_url (url, &port);
if (tmp)
{
add_server_to_servers (tmp, port);
xfree (tmp);
}
}
}
/* If the lookup failed and we are not only using the proxy, we try
again using our default list of servers. */
if (err && !(opt.ldap_proxy && opt.only_ldap_proxy))
{
struct ldapserver_iter iter;
if (DBG_LOOKUP)
log_debug ("no hostname in URL or query failed; "
"trying all default hostnames\n");
for (ldapserver_iter_begin (&iter, ctrl);
err && ! ldapserver_iter_end_p (&iter);
ldapserver_iter_next (&iter))
{
ldap_server_t server = iter.server;
err = run_ldap_wrapper (ctrl,
0,
0,
NULL,
server->host, server->port,
NULL, NULL,
NULL, NULL, NULL, url,
reader);
if (!err)
break;
}
}
return err;
}
/* Perform an LDAP query on all configured servers. On error the
error code of the last try is returned. */
gpg_error_t
attr_fetch_ldap (ctrl_t ctrl,
const char *dn, const char *attr, ksba_reader_t *reader)
{
gpg_error_t err = gpg_error (GPG_ERR_CONFIGURATION);
struct ldapserver_iter iter;
*reader = NULL;
/* FIXME; we might want to look at the Base SN to try matching
servers first. */
for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter);
ldapserver_iter_next (&iter))
{
ldap_server_t server = iter.server;
err = run_ldap_wrapper (ctrl,
0,
0,
opt.ldap_proxy,
server->host, server->port,
server->user, server->pass,
dn, "objectClass=*", attr, NULL,
reader);
if (!err)
break; /* Probably found a result. Ready. */
}
return err;
}
/* Parse PATTERN and return a new strlist to be used for the actual
LDAP query. Bit 0 of the flags field is set if that pattern is
actually a base specification. Caller must release the returned
strlist. NULL is returned on error.
* Possible patterns:
*
* KeyID
* Fingerprint
* OpenPGP userid
* x Email address Indicated by a left angle bracket.
* Exact word match in user id or subj. name
* x Subj. DN indicated bu a leading slash
* Issuer DN
* Serial number + subj. DN
* x Substring match indicated by a leading '*; is also the default.
*/
strlist_t
parse_one_pattern (const char *pattern)
{
strlist_t result = NULL;
char *p;
switch (*pattern)
{
case '<': /* Email. */
{
pattern++;
result = xmalloc (sizeof *result + 5 + strlen (pattern));
result->next = NULL;
result->flags = 0;
p = stpcpy (stpcpy (result->d, "mail="), pattern);
if (p[-1] == '>')
*--p = 0;
if (!*result->d) /* Error. */
{
xfree (result);
result = NULL;
}
break;
}
case '/': /* Subject DN. */
pattern++;
if (*pattern)
{
result = xmalloc (sizeof *result + strlen (pattern));
result->next = NULL;
result->flags = 1; /* Base spec. */
strcpy (result->d, pattern);
}
break;
case '#': /* Issuer DN. */
pattern++;
if (*pattern == '/') /* Just issuer DN. */
{
pattern++;
}
else /* Serial number + issuer DN */
{
}
break;
case '*':
pattern++;
/* fall through */
default: /* Take as substring match. */
{
const char format[] = "(|(sn=*%s*)(|(cn=*%s*)(mail=*%s*)))";
if (*pattern)
{
result = xmalloc (sizeof *result
+ strlen (format) + 3 * strlen (pattern));
result->next = NULL;
result->flags = 0;
sprintf (result->d, format, pattern, pattern, pattern);
}
}
break;
}
return result;
}
/* Take the string STRING and escape it according to the URL rules.
Retun a newly allocated string. */
static char *
escape4url (const char *string)
{
const char *s;
char *buf, *p;
size_t n;
if (!string)
string = "";
for (s=string,n=0; *s; s++)
if (strchr (UNENCODED_URL_CHARS, *s))
n++;
else
n += 3;
buf = malloc (n+1);
if (!buf)
return NULL;
for (s=string,p=buf; *s; s++)
if (strchr (UNENCODED_URL_CHARS, *s))
*p++ = *s;
else
{
sprintf (p, "%%%02X", *(const unsigned char *)s);
p += 3;
}
*p = 0;
return buf;
}
/* Create a LDAP URL from DN and FILTER and return it in URL. We don't
need the host and port because this will be specified using the
override options. */
static gpg_error_t
make_url (char **url, const char *dn, const char *filter)
{
gpg_error_t err;
char *u_dn, *u_filter;
char const attrs[] = (USERCERTIFICATE ","
/* USERSMIMECERTIFICATE "," */
CACERTIFICATE ","
X509CACERT );
*url = NULL;
u_dn = escape4url (dn);
if (!u_dn)
return gpg_error_from_errno (errno);
u_filter = escape4url (filter);
if (!u_filter)
{
err = gpg_error_from_errno (errno);
xfree (u_dn);
return err;
}
*url = strconcat ("ldap:///", u_dn, "?", attrs, "?sub?", u_filter, NULL);
if (!*url)
err = gpg_error_from_syserror ();
else
err = 0;
xfree (u_dn);
xfree (u_filter);
return err;
}
-/* Prepare an LDAP query to return the attribute ATTR for the DN. All
- configured default servers are queried until one responds. This
- function returns an error code or 0 and a CONTEXT on success. */
+/* Prepare an LDAP query to return the cACertificate attribute for DN.
+ * All configured default servers are queried until one responds.
+ * This function returns an error code or 0 and stored a newly
+ * allocated contect object at CONTEXT on success. */
gpg_error_t
-start_default_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context,
- const char *dn, const char *attr)
+start_cacert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *r_context,
+ const char *dn)
{
gpg_error_t err;
struct ldapserver_iter iter;
- *context = xtrycalloc (1, sizeof **context);
- if (!*context)
+ *r_context = xtrycalloc (1, sizeof **r_context);
+ if (!*r_context)
return gpg_error_from_errno (errno);
/* FIXME; we might want to look at the Base SN to try matching
servers first. */
err = gpg_error (GPG_ERR_CONFIGURATION);
for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter);
ldapserver_iter_next (&iter))
{
ldap_server_t server = iter.server;
err = run_ldap_wrapper (ctrl,
0,
- 1,
+ 1, /* --multi (record format) */
opt.ldap_proxy,
server->host, server->port,
server->user, server->pass,
- dn, "objectClass=*", attr, NULL,
- &(*context)->reader);
+ dn, "objectClass=*", "cACertificate", NULL,
+ &(*r_context)->reader);
if (!err)
break; /* Probably found a result. */
}
if (err)
{
- xfree (*context);
- *context = NULL;
+ xfree (*r_context);
+ *r_context = NULL;
}
return err;
}
-/* Prepare an LDAP query to return certificates matching PATTERNS using
- the SERVER. This function returns an error code or 0 and a CONTEXT
- on success. */
+/* Prepare an LDAP query to return certificates matching PATTERNS
+ * using the SERVER. This function returns an error code or 0 and
+ * stores a newly allocated object at R_CONTEXT on success. */
gpg_error_t
-start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context,
+start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *r_context,
strlist_t patterns, const ldap_server_t server)
{
gpg_error_t err;
char *proxy = NULL;
char *host = NULL;
int port;
char *user = NULL;
char *pass = NULL;
const char *base;
char *argv[50];
int argc = 0;
int argc_malloced = 0;
char portbuf[30], timeoutbuf[30];
- *context = NULL;
+ *r_context = NULL;
if (opt.ldap_proxy && !(proxy = xtrystrdup (opt.ldap_proxy)))
{
err = gpg_error_from_syserror ();
goto leave;
}
if (server)
{
if (server->host && !(host = xtrystrdup (server->host)))
{
err = gpg_error_from_syserror ();
goto leave;
}
port = server->port;
if (server->user && !(user = xtrystrdup (server->user)))
{
err = gpg_error_from_syserror ();
goto leave;
}
if (server->pass && !(pass = xtrystrdup (server->pass)))
{
err = gpg_error_from_syserror ();
goto leave;
}
base = server->base;
}
else /* Use a default server. */
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
if (!base)
base = "";
if (pass) /* Note: Must be the first item. */
{
argv[argc++] = "--pass";
argv[argc++] = pass;
}
if (DBG_LOOKUP)
argv[argc++] = "-vv";
else if (DBG_EXTPROG)
argv[argc++] = "-v";
argv[argc++] = "--log-with-pid";
argv[argc++] = "--multi";
if (opt.ldaptimeout)
{
snprintf (timeoutbuf, sizeof timeoutbuf, "%u", opt.ldaptimeout);
argv[argc++] = "--timeout";
argv[argc++] = timeoutbuf;
}
if (opt.ldap_proxy)
{
argv[argc++] = "--proxy";
argv[argc++] = proxy;
}
if (host)
{
argv[argc++] = "--host";
argv[argc++] = host;
}
if (port)
{
snprintf (portbuf, sizeof portbuf, "%d", port);
argv[argc++] = "--port";
argv[argc++] = portbuf;
}
if (user)
{
argv[argc++] = "--user";
argv[argc++] = user;
}
/* All entries in argv from this index on are malloc'ed. */
argc_malloced = argc;
for (; patterns; patterns = patterns->next)
{
strlist_t sl;
char *url;
if (argc >= DIM (argv) - 1)
{
/* Too many patterns. It does not make sense to allow an
arbitrary number of patters because the length of the
command line is limited anyway. */
/* fixme: cleanup. */
return gpg_error (GPG_ERR_RESOURCE_LIMIT);
}
sl = parse_one_pattern (patterns->d);
if (!sl)
{
log_error (_("start_cert_fetch: invalid pattern '%s'\n"),
patterns->d);
err = gpg_error (GPG_ERR_INV_USER_ID);
goto leave;
}
if ((sl->flags & 1))
err = make_url (&url, sl->d, "objectClass=*");
else
err = make_url (&url, base, sl->d);
free_strlist (sl);
if (err)
goto leave;
argv[argc++] = url;
}
argv[argc] = NULL;
- *context = xtrycalloc (1, sizeof **context);
- if (!*context)
+ *r_context = xtrycalloc (1, sizeof **r_context);
+ if (!*r_context)
{
err = gpg_error_from_errno (errno);
goto leave;
}
- err = ldap_wrapper (ctrl, &(*context)->reader, (const char**)argv);
+ err = ldap_wrapper (ctrl, &(*r_context)->reader, (const char**)argv);
if (err)
{
- xfree (*context);
- *context = NULL;
+ xfree (*r_context);
+ *r_context = NULL;
}
leave:
for (; argc_malloced < argc; argc_malloced++)
xfree (argv[argc_malloced]);
xfree (proxy);
xfree (host);
xfree (user);
xfree (pass);
return err;
}
/* Read a fixed amount of data from READER into BUFFER. */
static gpg_error_t
read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
{
gpg_error_t err;
size_t nread;
while (count)
{
err = ksba_reader_read (reader, buffer, count, &nread);
if (err)
return err;
buffer += nread;
count -= nread;
}
return 0;
}
/* Fetch the next certificate. Return 0 on success, GPG_ERR_EOF if no
(more) certificates are available or any other error
code. GPG_ERR_TRUNCATED may be returned to indicate that the result
has been truncated. */
gpg_error_t
fetch_next_cert_ldap (cert_fetch_context_t context,
unsigned char **value, size_t *valuelen)
{
gpg_error_t err;
unsigned char hdr[5];
char *p, *pend;
unsigned long n;
int okay = 0;
/* int is_cms = 0; */
*value = NULL;
*valuelen = 0;
err = 0;
while (!err)
{
err = read_buffer (context->reader, hdr, 5);
if (err)
break;
n = buf32_to_ulong (hdr+1);
if (*hdr == 'V' && okay)
{
-#if 0 /* That code is not yet ready. */
-
+#if 0 /* That code to extra a cert from a CMS object is not yet ready. */
if (is_cms)
{
/* The certificate needs to be parsed from CMS data. */
ksba_cms_t cms;
ksba_stop_reason_t stopreason;
int i;
err = ksba_cms_new (&cms);
if (err)
goto leave;
err = ksba_cms_set_reader_writer (cms, context->reader, NULL);
if (err)
{
log_error ("ksba_cms_set_reader_writer failed: %s\n",
gpg_strerror (err));
goto leave;
}
do
{
err = ksba_cms_parse (cms, &stopreason);
if (err)
{
log_error ("ksba_cms_parse failed: %s\n",
gpg_strerror (err));
goto leave;
}
if (stopreason == KSBA_SR_BEGIN_DATA)
log_error ("userSMIMECertificate is not "
"a certs-only message\n");
}
while (stopreason != KSBA_SR_READY);
for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
{
check_and_store (ctrl, stats, cert, 0);
ksba_cert_release (cert);
cert = NULL;
}
if (!i)
log_error ("no certificate found\n");
else
any = 1;
}
else
-#endif
+#endif /* End unfinished code to extract from a CMS object. */
{
*value = xtrymalloc (n);
if (!*value)
return gpg_error_from_errno (errno);
*valuelen = n;
err = read_buffer (context->reader, *value, n);
break; /* Ready or error. */
}
}
else if (!n && *hdr == 'A')
okay = 0;
else if (n)
{
if (n > context->tmpbufsize)
{
xfree (context->tmpbuf);
context->tmpbufsize = 0;
context->tmpbuf = xtrymalloc (n+1);
if (!context->tmpbuf)
return gpg_error_from_errno (errno);
context->tmpbufsize = n;
}
err = read_buffer (context->reader, context->tmpbuf, n);
if (err)
break;
if (*hdr == 'A')
{
p = context->tmpbuf;
p[n] = 0; /*(we allocated one extra byte for this.)*/
/* fixme: is_cms = 0; */
if ( (pend = strchr (p, ';')) )
*pend = 0; /* Strip off the extension. */
if (!ascii_strcasecmp (p, USERCERTIFICATE))
{
if (DBG_LOOKUP)
log_debug ("fetch_next_cert_ldap: got attribute '%s'\n",
USERCERTIFICATE);
okay = 1;
}
else if (!ascii_strcasecmp (p, CACERTIFICATE))
{
if (DBG_LOOKUP)
log_debug ("fetch_next_cert_ldap: got attribute '%s'\n",
CACERTIFICATE);
okay = 1;
}
else if (!ascii_strcasecmp (p, X509CACERT))
{
if (DBG_LOOKUP)
log_debug ("fetch_next_cert_ldap: got attribute '%s'\n",
CACERTIFICATE);
okay = 1;
}
/* else if (!ascii_strcasecmp (p, USERSMIMECERTIFICATE)) */
/* { */
/* if (DBG_LOOKUP) */
/* log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", */
/* USERSMIMECERTIFICATE); */
/* okay = 1; */
/* is_cms = 1; */
/* } */
else
{
if (DBG_LOOKUP)
log_debug ("fetch_next_cert_ldap: got attribute '%s'"
" - ignored\n", p);
okay = 0;
}
}
else if (*hdr == 'E')
{
p = context->tmpbuf;
p[n] = 0; /*(we allocated one extra byte for this.)*/
if (!strcmp (p, "truncated"))
{
context->truncated = 1;
log_info (_("ldap_search hit the size limit of"
" the server\n"));
}
}
}
}
if (err)
{
xfree (*value);
*value = NULL;
*valuelen = 0;
if (gpg_err_code (err) == GPG_ERR_EOF && context->truncated)
{
context->truncated = 0; /* So that the next call would return EOF. */
err = gpg_error (GPG_ERR_TRUNCATED);
}
}
return err;
}
void
end_cert_fetch_ldap (cert_fetch_context_t context)
{
if (context)
{
ksba_reader_t reader = context->reader;
xfree (context->tmpbuf);
xfree (context);
ldap_wrapper_release_context (reader);
ksba_reader_release (reader);
}
}