mirror of
git://git.gnupg.org/gnupg.git
synced 2024-11-04 20:38:50 +01:00
d22506a343
* dirmngr/dirmngr_ldap.c: Remove assert.h. (main): Replace assert by log_assert. * dirmngr/ldap.c (run_ldap_wrapper): Use debug options to pass verbose options to dirmngr_ldap. (start_cert_fetch_ldap): Ditto. -- verbose is a pretty common option in dirmngr.conf and it would clutter the logs with output from dirmngr_ldap. Now we require DBG_EXTPROG or DBG_LOOKUP to make dirmngr_ldap more verbose. Signed-off-by: Werner Koch <wk@gnupg.org>
875 lines
23 KiB
C
875 lines
23 KiB
C
/* 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 <config.h>
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#include <unistd.h>
|
||
#include <fcntl.h>
|
||
#include <time.h>
|
||
#include <npth.h>
|
||
|
||
#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. */
|
||
gpg_error_t
|
||
start_default_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context,
|
||
const char *dn, const char *attr)
|
||
{
|
||
gpg_error_t err;
|
||
struct ldapserver_iter iter;
|
||
|
||
*context = xtrycalloc (1, sizeof **context);
|
||
if (!*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,
|
||
opt.ldap_proxy,
|
||
server->host, server->port,
|
||
server->user, server->pass,
|
||
dn, "objectClass=*", attr, NULL,
|
||
&(*context)->reader);
|
||
if (!err)
|
||
break; /* Probably found a result. */
|
||
}
|
||
|
||
if (err)
|
||
{
|
||
xfree (*context);
|
||
*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. */
|
||
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 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;
|
||
|
||
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)
|
||
{
|
||
err = gpg_error_from_errno (errno);
|
||
goto leave;
|
||
}
|
||
|
||
err = ldap_wrapper (ctrl, &(*context)->reader, (const char**)argv);
|
||
|
||
if (err)
|
||
{
|
||
xfree (*context);
|
||
*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 (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
|
||
{
|
||
*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);
|
||
}
|
||
}
|