dirmngr: Rewrite the LDAP wrapper tool

* dirmngr/ldap-misc.c: New.
* dirmngr/ldap-misc.h: New.
* dirmngr/ks-engine-ldap.c: Include ldap-misc.h.
(ldap_err_to_gpg_err, ldap_to_gpg_err): Move to ldap-misc.c.
* dirmngr/ldap-wrapper.c (ldap_wrapper): Print list of args in debug
mode.
* dirmngr/server.c (lookup_cert_by_pattern): Handle GPG_ERR_NOT_FOUND
the saqme as GPG_ERR_NO_DATA.
* dirmngr/ldap.c (run_ldap_wrapper): Add args tls_mode and ntds.
Remove arg url.  Adjust for changes in dirmngr_ldap.
(url_fetch_ldap): Remove args host and port.  Parse the URL and use
these values to call run_ldap_wrapper.
(attr_fetch_ldap): Pass tls flags to run_ldap_wrapper.
(rfc2254_need_escape, rfc2254_escape): New.
(extfilt_need_escape, extfilt_escape): New.
(parse_one_pattern): Rename to ...
(make_one_filter): this.  Change for new dirmngr_ldap calling
convention.  Make issuer DN searching partly work.
(escape4url, make_url): Remove.
(start_cert_fetch_ldap): Change for new dirmngr_ldap calling
convention.
* dirmngr/dirmngr_ldap.c: Major rewrite.

* dirmngr/t-ldap-misc.c: New.
* dirmngr/t-support.h (DIM, DIMof): New.
* dirmngr/Makefile.am (dirmngr_ldap_SOURCES): Add ldap-misc.c
(module_tests) [USE_LDAP]: Add t-ldap-misc.
(t_ldap_parse_uri_SOURCES): Ditto.
(t_ldap_misc_SOURCES): New.
--

This rewrite allows to properly handle TLS and avoids some code
duplication.

Signed-off-by: Werner Koch <wk@gnupg.org>
(cherry picked from commit 39815c023f)
This commit is contained in:
Werner Koch 2021-06-08 08:46:59 +02:00
parent bcb9931562
commit 864ea25198
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
12 changed files with 1252 additions and 696 deletions

View File

@ -76,6 +76,7 @@ endif
if USE_LDAP if USE_LDAP
dirmngr_SOURCES += ldapserver.h ldapserver.c ldap.c w32-ldap-help.h \ dirmngr_SOURCES += ldapserver.h ldapserver.c ldap.c w32-ldap-help.h \
ldap-wrapper.h ldap-parse-uri.c ldap-parse-uri.h \ ldap-wrapper.h ldap-parse-uri.c ldap-parse-uri.h \
ldap-misc.c ldap-misc.h \
ks-engine-ldap.c $(ldap_url) ldap-wrapper.c ks-engine-ldap.c $(ldap_url) ldap-wrapper.c
ldaplibs = $(LDAPLIBS) ldaplibs = $(LDAPLIBS)
else else
@ -93,7 +94,7 @@ endif
dirmngr_LDFLAGS = $(extra_bin_ldflags) dirmngr_LDFLAGS = $(extra_bin_ldflags)
if USE_LDAP if USE_LDAP
dirmngr_ldap_SOURCES = dirmngr_ldap.c $(ldap_url) dirmngr_ldap_SOURCES = dirmngr_ldap.c ldap-misc.c ldap-misc.h $(ldap_url)
dirmngr_ldap_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBGCRYPT_CFLAGS) dirmngr_ldap_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBGCRYPT_CFLAGS)
dirmngr_ldap_LDFLAGS = dirmngr_ldap_LDFLAGS =
dirmngr_ldap_LDADD = $(libcommon) \ dirmngr_ldap_LDADD = $(libcommon) \
@ -120,7 +121,7 @@ t_common_ldadd = $(libcommon) $(LIBASSUAN_LIBS) $(LIBGCRYPT_LIBS) \
module_tests = t-http-basic module_tests = t-http-basic
if USE_LDAP if USE_LDAP
module_tests += t-ldap-parse-uri module_tests += t-ldap-parse-uri t-ldap-misc
endif endif
# Test which need a network connections are only used in maintainer mode. # Test which need a network connections are only used in maintainer mode.
@ -156,16 +157,23 @@ t_http_basic_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \
t_http_basic_LDADD = $(t_common_ldadd) \ t_http_basic_LDADD = $(t_common_ldadd) \
$(NTBTLS_LIBS) $(KSBA_LIBS) $(LIBGNUTLS_LIBS) $(DNSLIBS) $(NTBTLS_LIBS) $(KSBA_LIBS) $(LIBGNUTLS_LIBS) $(DNSLIBS)
t_ldap_parse_uri_SOURCES = \ t_ldap_parse_uri_SOURCES = \
t-ldap-parse-uri.c ldap-parse-uri.c ldap-parse-uri.h \ t-ldap-parse-uri.c ldap-parse-uri.c ldap-parse-uri.h \
http.c http-common.c dns-stuff.c \ http.c http-common.c dns-stuff.c ldap-misc.c \
$(ldap_url) $(t_common_src) $(ldap_url) $(t_common_src)
t_ldap_parse_uri_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \ t_ldap_parse_uri_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \
$(LIBGCRYPT_CFLAGS) \ $(LIBGCRYPT_CFLAGS) \
$(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS)
t_ldap_parse_uri_LDADD = $(ldaplibs) $(t_common_ldadd) $(DNSLIBS) t_ldap_parse_uri_LDADD = $(ldaplibs) $(t_common_ldadd) $(DNSLIBS)
t_ldap_misc_SOURCES = t-ldap-misc.c ldap-misc.c ldap-misc.h $(ldap_url)
t_ldap_misc_CFLAGS = -DWITHOUT_NPTH=1 $(GPG_ERROR_CFLAGS) $(LIBGCRYPT_CFLAGS)
t_ldap_misc_LDFLAGS =
t_ldap_misc_LDADD = $(libcommon) \
$(GPG_ERROR_LIBS) $(LIBGCRYPT_LIBS) $(LDAPLIBS) \
$(LBER_LIBS) $(LIBINTL) $(LIBICONV) $(NETLIBS)
t_dns_stuff_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \ t_dns_stuff_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \
$(LIBGCRYPT_CFLAGS) \ $(LIBGCRYPT_CFLAGS) \
$(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS)

View File

@ -240,7 +240,7 @@ crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader)
else else
{ {
# if USE_LDAP # if USE_LDAP
err = url_fetch_ldap (ctrl, url, NULL, 0, reader); err = url_fetch_ldap (ctrl, url, reader);
# else /*!USE_LDAP*/ # else /*!USE_LDAP*/
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
# endif /*!USE_LDAP*/ # endif /*!USE_LDAP*/
@ -513,7 +513,7 @@ fetch_cert_by_url (ctrl_t ctrl, const char *url,
else /* Assume LDAP. */ else /* Assume LDAP. */
{ {
#if USE_LDAP #if USE_LDAP
err = url_fetch_ldap (ctrl, url, NULL, 0, &reader); err = url_fetch_ldap (ctrl, url, &reader);
#else #else
(void)ctrl; (void)ctrl;
(void)url; (void)url;

View File

@ -62,8 +62,7 @@ void crl_close_reader (ksba_reader_t reader);
/*-- ldap.c --*/ /*-- ldap.c --*/
gpg_error_t url_fetch_ldap (ctrl_t ctrl, gpg_error_t url_fetch_ldap (ctrl_t ctrl,
const char *url, const char *host, int port, const char *url, ksba_reader_t *reader);
ksba_reader_t *reader);
gpg_error_t attr_fetch_ldap (ctrl_t ctrl, gpg_error_t attr_fetch_ldap (ctrl_t ctrl,
const char *dn, const char *attr, const char *dn, const char *attr,
ksba_reader_t *reader); ksba_reader_t *reader);

View File

@ -1,5 +1,5 @@
/* dirmngr-ldap.c - The LDAP helper for dirmngr. /* dirmngr-ldap.c - The LDAP helper for dirmngr.
* Copyright (C) 2004 g10 Code GmbH * Copyright (C) 2004, 2021 g10 Code GmbH
* Copyright (C) 2010 Free Software Foundation, Inc. * Copyright (C) 2010 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
@ -52,10 +52,10 @@
#include "../common/stringhelp.h" #include "../common/stringhelp.h"
#include "../common/mischelp.h" #include "../common/mischelp.h"
#include "../common/strlist.h" #include "../common/strlist.h"
#include "../common/i18n.h"
#include "../common/util.h" #include "../common/util.h"
#include "../common/init.h" #include "../common/init.h"
#include "ldap-misc.h"
/* There is no need for the npth_unprotect and leave functions here; /* There is no need for the npth_unprotect and leave functions here;
* thus we redefine them to nops. We keep them in the code just for * thus we redefine them to nops. We keep them in the code just for
@ -87,11 +87,11 @@ enum
oUser, oUser,
oPass, oPass,
oEnvPass, oEnvPass,
oDN, oBase,
oFilter,
oAttr, oAttr,
oTls, oStartTLS,
oLdapTLS,
oNtds,
oOnlySearchTimeout, oOnlySearchTimeout,
oLogWithPID oLogWithPID
}; };
@ -99,40 +99,41 @@ enum
/* The list of options as used by the argparse.c code. */ /* The list of options as used by the argparse.c code. */
static gpgrt_opt_t opts[] = { static gpgrt_opt_t opts[] = {
{ oVerbose, "verbose", 0, N_("verbose") }, { oVerbose, "verbose", 0, "verbose" },
{ oQuiet, "quiet", 0, N_("be somewhat more quiet") }, { oQuiet, "quiet", 0, "be somewhat more quiet" },
{ oTimeout, "timeout", 1, N_("|N|set LDAP timeout to N seconds")}, { oTimeout, "timeout", 1, "|N|set LDAP timeout to N seconds"},
{ oMulti, "multi", 0, N_("return all values in" { oMulti, "multi", 0, "return all values in"
" a record oriented format")}, " a record oriented format"},
{ oProxy, "proxy", 2, { oProxy, "proxy", 2,
N_("|NAME|ignore host part and connect through NAME")}, "|NAME|ignore host part and connect through NAME"},
{ oTls, "tls", 0, N_("force a TLS connection")}, { oStartTLS, "starttls", 0, "use STARTLS for the conenction"},
{ oHost, "host", 2, N_("|NAME|connect to host NAME")}, { oLdapTLS, "ldaptls", 0, "use a TLS for the connection"},
{ oPort, "port", 1, N_("|N|connect to port N")}, { oNtds, "ntds", 0, "authenticate using AD"},
{ oUser, "user", 2, N_("|NAME|use user NAME for authentication")}, { oHost, "host", 2, "|NAME|connect to host NAME"},
{ oPass, "pass", 2, N_("|PASS|use password PASS" { oPort, "port", 1, "|N|connect to port N"},
" for authentication")}, { oUser, "user", 2, "|NAME|use NAME for authentication"},
{ oEnvPass, "env-pass", 0, N_("take password from $DIRMNGR_LDAP_PASS")}, { oPass, "pass", 2, "|PASS|use password PASS"
{ oDN, "dn", 2, N_("|STRING|query DN STRING")}, " for authentication"},
{ oFilter, "filter", 2, N_("|STRING|use STRING as filter expression")}, { oEnvPass, "env-pass", 0, "take password from $DIRMNGR_LDAP_PASS"},
{ oAttr, "attr", 2, N_("|STRING|return the attribute STRING")}, { oBase, "base", 2, "|DN|Start query at DN"},
{ oAttr, "attr", 2, "|STRING|return the attribute STRING"},
{ oOnlySearchTimeout, "only-search-timeout", 0, "@"}, { oOnlySearchTimeout, "only-search-timeout", 0, "@"},
{ oLogWithPID,"log-with-pid", 0, "@"}, { oLogWithPID,"log-with-pid", 0, "@"},
ARGPARSE_end () ARGPARSE_end ()
}; };
/* A structure with module options. This is not a static variable /* A structure with module options. */
because if we are not build as a standalone binary, each thread static struct
using this module needs to handle its own values. */
struct my_opt_s
{ {
int quiet; int quiet;
int verbose; int verbose;
my_ldap_timeval_t timeout;/* Timeout for the LDAP search functions. */ my_ldap_timeval_t timeout;/* Timeout for the LDAP search functions. */
unsigned int alarm_timeout; /* And for the alarm based timeout. */ unsigned int alarm_timeout; /* And for the alarm based timeout. */
int multi; int multi;
int force_tls; int starttls;
int ldaptls;
int ntds;
estream_t outstream; /* Send output to this stream. */ estream_t outstream; /* Send output to this stream. */
@ -142,19 +143,18 @@ struct my_opt_s
char *user; /* Authentication user. */ char *user; /* Authentication user. */
char *pass; /* Authentication password. */ char *pass; /* Authentication password. */
char *host; /* Override host. */ char *host; /* Override host. */
int port; /* Override port. */ int port; /* Override port. */
char *dn; /* Override DN. */ char *base; /* Override DN. */
char *filter;/* Override filter. */
char *attr; /* Override attribute. */ char *attr; /* Override attribute. */
}; } opt;
typedef struct my_opt_s *my_opt_t;
/* Prototypes. */ /* Prototypes. */
#ifndef HAVE_W32_SYSTEM #ifndef HAVE_W32_SYSTEM
static void catch_alarm (int dummy); static void catch_alarm (int dummy);
#endif #endif
static int process_url (my_opt_t myopt, const char *url); static gpg_error_t connect_ldap (LDAP **r_ld);
static gpg_error_t process_filter (LDAP *ld, const char *string);
@ -172,14 +172,14 @@ my_strusage (int level)
case 13: p = VERSION; break; case 13: p = VERSION; break;
case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break; case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break;
case 17: p = PRINTABLE_OS_NAME; break; case 17: p = PRINTABLE_OS_NAME; break;
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; case 19: p = "Please report bugs to <@EMAIL@>.\n"; break;
case 49: p = PACKAGE_BUGREPORT; break; case 49: p = PACKAGE_BUGREPORT; break;
case 1: case 1:
case 40: p = case 40: p =
_("Usage: dirmngr_ldap [options] [URL] (-h for help)\n"); "Usage: dirmngr_ldap [options] filters (-h for help)\n";
break; break;
case 41: p = case 41: p =
_("Syntax: dirmngr_ldap [options] [URL]\n" ("Syntax: dirmngr_ldap [options] filters\n"
"Internal LDAP helper for Dirmngr\n" "Internal LDAP helper for Dirmngr\n"
"Interface and options may change without notice\n"); "Interface and options may change without notice\n");
break; break;
@ -197,29 +197,23 @@ main (int argc, char **argv)
int any_err = 0; int any_err = 0;
char *p; char *p;
int only_search_timeout = 0; int only_search_timeout = 0;
struct my_opt_s my_opt_buffer;
my_opt_t myopt = &my_opt_buffer;
char *malloced_buffer1 = NULL; char *malloced_buffer1 = NULL;
LDAP *ld;
memset (&my_opt_buffer, 0, sizeof my_opt_buffer);
early_system_init (); early_system_init ();
gpgrt_set_strusage (my_strusage); gpgrt_set_strusage (my_strusage);
log_set_prefix ("dirmngr_ldap", GPGRT_LOG_WITH_PREFIX); log_set_prefix ("dirmngr_ldap", GPGRT_LOG_WITH_PREFIX);
/* Setup I18N and common subsystems. */
i18n_init();
init_common_subsystems (&argc, &argv); init_common_subsystems (&argc, &argv);
es_set_binary (es_stdout); es_set_binary (es_stdout);
myopt->outstream = es_stdout; opt.outstream = es_stdout;
/* LDAP defaults */ /* LDAP defaults */
myopt->timeout.tv_sec = DEFAULT_LDAP_TIMEOUT; opt.timeout.tv_sec = DEFAULT_LDAP_TIMEOUT;
myopt->timeout.tv_usec = 0; opt.timeout.tv_usec = 0;
myopt->alarm_timeout = 0; opt.alarm_timeout = 0;
/* Parse the command line. */ /* Parse the command line. */
pargs.argc = &argc; pargs.argc = &argc;
@ -229,27 +223,28 @@ main (int argc, char **argv)
{ {
switch (pargs.r_opt) switch (pargs.r_opt)
{ {
case oVerbose: myopt->verbose++; break; case oVerbose: opt.verbose++; break;
case oQuiet: myopt->quiet++; break; case oQuiet: opt.quiet++; break;
case oTimeout: case oTimeout:
myopt->timeout.tv_sec = pargs.r.ret_int; opt.timeout.tv_sec = pargs.r.ret_int;
myopt->timeout.tv_usec = 0; opt.timeout.tv_usec = 0;
myopt->alarm_timeout = pargs.r.ret_int; opt.alarm_timeout = pargs.r.ret_int;
break; break;
case oOnlySearchTimeout: only_search_timeout = 1; break; case oOnlySearchTimeout: only_search_timeout = 1; break;
case oMulti: myopt->multi = 1; break; case oStartTLS: opt.starttls = 1; opt.ldaptls = 0; break;
case oUser: myopt->user = pargs.r.ret_str; break; case oLdapTLS: opt.starttls = 0; opt.ldaptls = 1; break;
case oPass: myopt->pass = pargs.r.ret_str; break; case oNtds: opt.ntds = 1; break;
case oMulti: opt.multi = 1; break;
case oUser: opt.user = pargs.r.ret_str; break;
case oPass: opt.pass = pargs.r.ret_str; break;
case oEnvPass: case oEnvPass:
myopt->pass = getenv ("DIRMNGR_LDAP_PASS"); opt.pass = getenv ("DIRMNGR_LDAP_PASS");
break; break;
case oProxy: myopt->proxy = pargs.r.ret_str; break; case oProxy: opt.proxy = pargs.r.ret_str; break;
case oTls: myopt->force_tls = 1; break; case oHost: opt.host = pargs.r.ret_str; break;
case oHost: myopt->host = pargs.r.ret_str; break; case oPort: opt.port = pargs.r.ret_int; break;
case oPort: myopt->port = pargs.r.ret_int; break; case oBase: opt.base = pargs.r.ret_str; break;
case oDN: myopt->dn = pargs.r.ret_str; break; case oAttr: opt.attr = pargs.r.ret_str; break;
case oFilter: myopt->filter = pargs.r.ret_str; break;
case oAttr: myopt->attr = pargs.r.ret_str; break;
case oLogWithPID: case oLogWithPID:
{ {
unsigned int oldflags; unsigned int oldflags;
@ -266,36 +261,45 @@ main (int argc, char **argv)
gpgrt_argparse (NULL, &pargs, NULL); gpgrt_argparse (NULL, &pargs, NULL);
if (only_search_timeout) if (only_search_timeout)
myopt->alarm_timeout = 0; opt.alarm_timeout = 0;
if (myopt->proxy) if (opt.proxy)
{ {
malloced_buffer1 = xtrystrdup (myopt->proxy); malloced_buffer1 = xtrystrdup (opt.proxy);
if (!malloced_buffer1) if (!malloced_buffer1)
{ {
log_error ("error copying string: %s\n", strerror (errno)); log_error ("error copying string: %s\n", strerror (errno));
return 1; return 1;
} }
myopt->host = malloced_buffer1; opt.host = malloced_buffer1;
p = strchr (myopt->host, ':'); p = strchr (opt.host, ':');
if (p) if (p)
{ {
*p++ = 0; *p++ = 0;
myopt->port = atoi (p); opt.port = atoi (p);
} }
if (!myopt->port) if (!opt.port)
myopt->port = 389; /* make sure ports gets overridden. */ opt.port = 389; /* make sure ports gets overridden. */
} }
if (myopt->port < 0 || myopt->port > 65535) if (opt.port < 0 || opt.port > 65535)
log_error (_("invalid port number %d\n"), myopt->port); log_error ("invalid port number %d\n", opt.port);
if (!opt.port)
opt.port = opt.ldaptls? 636 : 389;
#ifndef HAVE_W32_SYSTEM
if (!opt.host)
opt.host = "localhost";
#endif
if (log_get_errorcount (0)) if (log_get_errorcount (0))
exit (2); exit (2);
if (argc < 1) if (argc < 1)
gpgrt_usage (1); gpgrt_usage (1);
if (myopt->alarm_timeout) if (opt.alarm_timeout)
{ {
#ifndef HAVE_W32_SYSTEM #ifndef HAVE_W32_SYSTEM
# if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION) # if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION)
@ -312,9 +316,15 @@ main (int argc, char **argv)
#endif #endif
} }
for (; argc; argc--, argv++) if (connect_ldap (&ld))
if (process_url (myopt, *argv)) any_err = 1;
any_err = 1; else
{
for (; argc; argc--, argv++)
if (process_filter (ld, *argv))
any_err = 1;
ldap_unbind (ld);
}
xfree (malloced_buffer1); xfree (malloced_buffer1);
return any_err; return any_err;
@ -345,16 +355,16 @@ alarm_thread (void *arg)
static void static void
set_timeout (my_opt_t myopt) set_timeout (void)
{ {
if (myopt->alarm_timeout) if (opt.alarm_timeout)
{ {
#ifdef HAVE_W32_SYSTEM #ifdef HAVE_W32_SYSTEM
static HANDLE timer; static HANDLE timer;
LARGE_INTEGER due_time; LARGE_INTEGER due_time;
/* A negative value is a relative time. */ /* A negative value is a relative time. */
due_time.QuadPart = (unsigned long long)-10000000 * myopt->alarm_timeout; due_time.QuadPart = (unsigned long long)-10000000 * opt.alarm_timeout;
if (!timer) if (!timer)
{ {
@ -376,15 +386,192 @@ set_timeout (my_opt_t myopt)
else /* Retrigger the timer. */ else /* Retrigger the timer. */
SetWaitableTimer (timer, &due_time, 0, NULL, NULL, 0); SetWaitableTimer (timer, &due_time, 0, NULL, NULL, 0);
#else #else
alarm (myopt->alarm_timeout); alarm (opt.alarm_timeout);
#endif #endif
} }
} }
/* Connect to the ldap server. On success the connection handle is
* stored at R_LD. */
static gpg_error_t
connect_ldap (LDAP **r_ld)
{
gpg_error_t err = 0;
int lerr;
LDAP *ld = NULL;
#ifndef HAVE_W32_SYSTEM
char *tmpstr;
#endif
*r_ld = NULL;
if (opt.starttls || opt.ldaptls)
{
#ifndef HAVE_LDAP_START_TLS_S
log_error ("ldap: can't connect to the server: no TLS support.");
err = GPG_ERR_LDAP_NOT_SUPPORTED;
goto leave;
#endif
}
set_timeout ();
#ifdef HAVE_W32_SYSTEM
npth_unprotect ();
ld = ldap_sslinit (opt.host, opt.port, opt.ldaptls);
npth_protect ();
if (!ld)
{
lerr = LdapGetLastError ();
err = ldap_err_to_gpg_err (lerr);
log_error ("error initializing LDAP '%s:%d': %s\n",
opt.host, opt.port, ldap_err2string (lerr));
goto leave;
}
#else /* Unix */
tmpstr = xtryasprintf ("%s://%s:%d",
opt.ldaptls? "ldaps" : "ldap",
opt.host, opt.port);
if (!tmpstr)
{
err = gpg_error_from_syserror ();
goto leave;
}
npth_unprotect ();
lerr = ldap_initialize (&ld, tmpstr);
npth_protect ();
if (lerr || !ld)
{
err = ldap_err_to_gpg_err (lerr);
log_error ("error initializing LDAP '%s': %s\n",
tmpstr, ldap_err2string (lerr));
xfree (tmpstr);
goto leave;
}
xfree (tmpstr);
#endif /* Unix */
if (opt.verbose)
log_info ("LDAP connected to '%s:%d'%s\n",
opt.host, opt.port,
opt.starttls? " using STARTTLS" :
opt.ldaptls? " using LDAP-over-TLS" : "");
#ifdef HAVE_LDAP_SET_OPTION
{
int ver = LDAP_VERSION3;
lerr = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &ver);
if (lerr != LDAP_SUCCESS)
{
log_error ("unable to go to LDAP 3: %s\n", ldap_err2string (lerr));
err = ldap_err_to_gpg_err (lerr);
goto leave;
}
}
#endif
#ifdef HAVE_LDAP_START_TLS_S
if (opt.starttls)
{
#ifndef HAVE_W32_SYSTEM
int check_cert = LDAP_OPT_X_TLS_HARD; /* LDAP_OPT_X_TLS_NEVER */
lerr = ldap_set_option (ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &check_cert);
if (lerr)
{
log_error ("ldap: error setting an TLS option: %s\n",
ldap_err2string (lerr));
err = ldap_err_to_gpg_err (lerr);
goto leave;
}
#else
/* On Windows, the certificates are checked by default. If the
option to disable checking mentioned above is ever
implemented, the way to do that on Windows is to install a
callback routine using ldap_set_option (..,
LDAP_OPT_SERVER_CERTIFICATE, ..); */
#endif
npth_unprotect ();
lerr = ldap_start_tls_s (ld,
#ifdef HAVE_W32_SYSTEM
/* ServerReturnValue, result */
NULL, NULL,
#endif
/* ServerControls, ClientControls */
NULL, NULL);
npth_protect ();
if (lerr)
{
log_error ("ldap: error switching to STARTTLS mode: %s\n",
ldap_err2string (lerr));
err = ldap_err_to_gpg_err (lerr);
goto leave;
}
}
#endif
if (opt.ntds)
{
if (opt.verbose)
log_info ("binding to current user via AD\n");
#ifdef HAVE_W32_SYSTEM
npth_unprotect ();
lerr = ldap_bind_s (ld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
npth_protect ();
if (lerr != LDAP_SUCCESS)
{
log_error ("error binding to LDAP via AD: %s\n",
ldap_err2string (lerr));
err = ldap_err_to_gpg_err (lerr);
goto leave;
}
#else /* Unix */
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
goto leave;
#endif /* Unix */
}
else if (opt.user)
{
if (opt.verbose)
log_info ("LDAP bind to '%s', password '%s'\n",
opt.user, opt.pass ? ">not_shown<" : ">none<");
npth_unprotect ();
lerr = ldap_simple_bind_s (ld, opt.user, opt.pass);
npth_protect ();
if (lerr != LDAP_SUCCESS)
{
log_error ("error binding to LDAP: %s\n", ldap_err2string (lerr));
err = ldap_err_to_gpg_err (lerr);
goto leave;
}
}
else
{
/* By default we don't bind as there is usually no need to. */
}
leave:
if (err)
{
if (ld)
ldap_unbind (ld);
}
else
*r_ld = ld;
return err;
}
/* Helper for fetch_ldap(). */ /* Helper for fetch_ldap(). */
static int static int
print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr) print_ldap_entries (LDAP *ld, LDAPMessage *msg, char *want_attr)
{ {
LDAPMessage *item; LDAPMessage *item;
int any = 0; int any = 0;
@ -396,15 +583,15 @@ print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
BerElement *berctx; BerElement *berctx;
char *attr; char *attr;
if (myopt->verbose > 1) if (opt.verbose > 1)
log_info (_("scanning result for attribute '%s'\n"), log_info ("scanning result for attribute '%s'\n",
want_attr? want_attr : "[all]"); want_attr? want_attr : "[all]");
if (myopt->multi) if (opt.multi)
{ /* Write item marker. */ { /* Write item marker. */
if (es_fwrite ("I\0\0\0\0", 5, 1, myopt->outstream) != 1) if (es_fwrite ("I\0\0\0\0", 5, 1, opt.outstream) != 1)
{ {
log_error (_("error writing to stdout: %s\n"), log_error ("error writing to stdout: %s\n",
strerror (errno)); strerror (errno));
return -1; return -1;
} }
@ -420,10 +607,10 @@ print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
struct berval **values; struct berval **values;
int idx; int idx;
if (myopt->verbose > 1) if (opt.verbose > 1)
log_info (_(" available attribute '%s'\n"), attr); log_info (" available attribute '%s'\n", attr);
set_timeout (myopt); set_timeout ();
/* I case we want only one attribute we do a case /* I case we want only one attribute we do a case
insensitive compare without the optional extension insensitive compare without the optional extension
@ -458,23 +645,23 @@ print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
if (!values) if (!values)
{ {
if (myopt->verbose) if (opt.verbose)
log_info (_("attribute '%s' not found\n"), attr); log_info ("attribute '%s' not found\n", attr);
ldap_memfree (attr); ldap_memfree (attr);
continue; continue;
} }
if (myopt->verbose) if (opt.verbose)
{ {
log_info (_("found attribute '%s'\n"), attr); log_info ("found attribute '%s'\n", attr);
if (myopt->verbose > 1) if (opt.verbose > 1)
for (idx=0; values[idx]; idx++) for (idx=0; values[idx]; idx++)
log_info (" length[%d]=%d\n", log_info (" length[%d]=%d\n",
idx, (int)values[0]->bv_len); idx, (int)values[0]->bv_len);
} }
if (myopt->multi) if (opt.multi)
{ /* Write attribute marker. */ { /* Write attribute marker. */
unsigned char tmp[5]; unsigned char tmp[5];
size_t n = strlen (attr); size_t n = strlen (attr);
@ -484,10 +671,10 @@ print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
tmp[2] = (n >> 16); tmp[2] = (n >> 16);
tmp[3] = (n >> 8); tmp[3] = (n >> 8);
tmp[4] = (n); tmp[4] = (n);
if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1 if (es_fwrite (tmp, 5, 1, opt.outstream) != 1
|| es_fwrite (attr, n, 1, myopt->outstream) != 1) || es_fwrite (attr, n, 1, opt.outstream) != 1)
{ {
log_error (_("error writing to stdout: %s\n"), log_error ("error writing to stdout: %s\n",
strerror (errno)); strerror (errno));
ldap_value_free_len (values); ldap_value_free_len (values);
ldap_memfree (attr); ldap_memfree (attr);
@ -498,7 +685,7 @@ print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
for (idx=0; values[idx]; idx++) for (idx=0; values[idx]; idx++)
{ {
if (myopt->multi) if (opt.multi)
{ /* Write value marker. */ { /* Write value marker. */
unsigned char tmp[5]; unsigned char tmp[5];
size_t n = values[0]->bv_len; size_t n = values[0]->bv_len;
@ -509,9 +696,9 @@ print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
tmp[3] = (n >> 8); tmp[3] = (n >> 8);
tmp[4] = (n); tmp[4] = (n);
if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1) if (es_fwrite (tmp, 5, 1, opt.outstream) != 1)
{ {
log_error (_("error writing to stdout: %s\n"), log_error ("error writing to stdout: %s\n",
strerror (errno)); strerror (errno));
ldap_value_free_len (values); ldap_value_free_len (values);
ldap_memfree (attr); ldap_memfree (attr);
@ -521,9 +708,9 @@ print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
} }
if (es_fwrite (values[0]->bv_val, values[0]->bv_len, if (es_fwrite (values[0]->bv_val, values[0]->bv_len,
1, myopt->outstream) != 1) 1, opt.outstream) != 1)
{ {
log_error (_("error writing to stdout: %s\n"), log_error ("error writing to stdout: %s\n",
strerror (errno)); strerror (errno));
ldap_value_free_len (values); ldap_value_free_len (values);
ldap_memfree (attr); ldap_memfree (attr);
@ -532,18 +719,18 @@ print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
} }
any = 1; any = 1;
if (!myopt->multi) if (!opt.multi)
break; /* Print only the first value. */ break; /* Print only the first value. */
} }
ldap_value_free_len (values); ldap_value_free_len (values);
ldap_memfree (attr); ldap_memfree (attr);
if (want_attr || !myopt->multi) if (want_attr || !opt.multi)
break; /* We only want to return the first attribute. */ break; /* We only want to return the first attribute. */
} }
ber_free (berctx, 0); ber_free (berctx, 0);
} }
if (myopt->verbose > 1 && any) if (opt.verbose > 1 && any)
log_info ("result has been printed\n"); log_info ("result has been printed\n");
return any?0:-1; return any?0:-1;
@ -551,183 +738,51 @@ print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
/* Helper for the URL based LDAP query. */ /* Fetch data from the server at LD using FILTER. */
static int static int
fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp) fetch_ldap (LDAP *ld, const char *base, int scope, const char *filter)
{ {
LDAP *ld; gpg_error_t err;
int lerr;
LDAPMessage *msg; LDAPMessage *msg;
int rc = 0; char *attrs[2];
char *host, *dn, *filter, *attrs[2], *attr;
int port;
int ret;
int usetls;
host = myopt->host? myopt->host : ludp->lud_host; if (filter && !*filter)
port = myopt->port? myopt->port : ludp->lud_port; filter = NULL;
dn = myopt->dn? myopt->dn : ludp->lud_dn;
filter = myopt->filter? myopt->filter : ludp->lud_filter;
attrs[0] = myopt->attr? myopt->attr : ludp->lud_attrs? ludp->lud_attrs[0]:NULL;
attrs[1] = NULL;
attr = attrs[0];
if (!port && myopt->force_tls) if (opt.verbose)
port = 636;
else if (!port)
port = (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))? 636:389;
if (myopt->verbose)
{ {
log_info (_("processing url '%s'\n"), url); log_info ("fetching using");
if (myopt->force_tls) if (base)
log_info ("forcing tls\n"); log_printf (" base '%s'", base);
else
log_info ("not forcing tls\n");
if (myopt->user)
log_info (_(" user '%s'\n"), myopt->user);
if (myopt->pass)
log_info (_(" pass '%s'\n"), *myopt->pass?"*****":"");
if (host)
log_info (_(" host '%s'\n"), host);
log_info (_(" port %d\n"), port);
if (dn)
log_info (_(" DN '%s'\n"), dn);
if (filter) if (filter)
log_info (_(" filter '%s'\n"), filter); log_printf (" filter '%s'", filter);
if (myopt->multi && !myopt->attr && ludp->lud_attrs) log_printf ("\n");
{
int i;
for (i=0; ludp->lud_attrs[i]; i++)
log_info (_(" attr '%s'\n"), ludp->lud_attrs[i]);
}
else if (attr)
log_info (_(" attr '%s'\n"), attr);
} }
attrs[0] = opt.attr;
attrs[1] = NULL;
if (!host || !*host) set_timeout ();
{
log_error (_("no host name in '%s'\n"), url);
return -1;
}
if (!myopt->multi && !attr)
{
log_error (_("no attribute given for query '%s'\n"), url);
return -1;
}
if (!myopt->multi && !myopt->attr
&& ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1])
log_info (_("WARNING: using first attribute only\n"));
set_timeout (myopt);
usetls = (myopt->force_tls
|| (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps")));
#if HAVE_W32_SYSTEM
if (1)
{
npth_unprotect ();
ld = ldap_sslinit (host, port, usetls);
npth_protect ();
if (!ld)
{
ret = LdapGetLastError ();
log_error (_("LDAP init to '%s:%d' failed: %s\n"),
host, port, ldap_err2string (ret));
return -1;
}
}
#else /*!W32*/
if (usetls)
{
char *uri;
uri = xtryasprintf ("ldaps://%s:%d", host, port);
if (!uri)
{
log_error (_("error allocating memory: %s\n"),
gpg_strerror (gpg_error_from_syserror ()));
return -1;
}
npth_unprotect ();
ret = ldap_initialize (&ld, uri);
npth_protect ();
if (ret)
{
log_error (_("LDAP init to '%s' failed: %s\n"),
uri, ldap_err2string (ret));
xfree (uri);
return -1;
}
else if (myopt->verbose)
log_info (_("LDAP init to '%s' done\n"), uri);
xfree (uri);
}
else
{
/* Keep the old way so to avoid regressions. Eventually we
* should really consider the supplied scheme and use only
* ldap_initialize. */
npth_unprotect ();
ld = ldap_init (host, port);
npth_protect ();
if (!ld)
{
log_error (_("LDAP init to '%s:%d' failed: %s\n"),
host, port, strerror (errno));
return -1;
}
}
#endif /*!W32*/
npth_unprotect (); npth_unprotect ();
/* Fixme: Can we use MYOPT->user or is it shared with other theeads?. */ lerr = ldap_search_st (ld, base, scope, filter,
ret = ldap_simple_bind_s (ld, myopt->user, myopt->pass); attrs,
0,
&opt.timeout, &msg);
npth_protect (); npth_protect ();
#ifdef LDAP_VERSION3 if (lerr == LDAP_SIZELIMIT_EXCEEDED && opt.multi)
if (ret == LDAP_PROTOCOL_ERROR)
{ {
/* Protocol error could mean that the server only supports v3. */ if (es_fwrite ("E\0\0\0\x09truncated", 14, 1, opt.outstream) != 1)
int version = LDAP_VERSION3;
if (myopt->verbose)
log_info ("protocol error; retrying bind with v3 protocol\n");
npth_unprotect ();
ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
ret = ldap_simple_bind_s (ld, myopt->user, myopt->pass);
npth_protect ();
}
#endif
if (ret)
{
log_error (_("binding to '%s:%d' failed: %s\n"),
host, port, ldap_err2string (ret));
ldap_unbind (ld);
return -1;
}
set_timeout (myopt);
npth_unprotect ();
rc = ldap_search_st (ld, dn, ludp->lud_scope, filter,
myopt->multi && !myopt->attr && ludp->lud_attrs?
ludp->lud_attrs:attrs,
0,
&myopt->timeout, &msg);
npth_protect ();
if (rc == LDAP_SIZELIMIT_EXCEEDED && myopt->multi)
{
if (es_fwrite ("E\0\0\0\x09truncated", 14, 1, myopt->outstream) != 1)
{ {
log_error (_("error writing to stdout: %s\n"), strerror (errno)); log_error ("error writing to stdout: %s\n", strerror (errno));
return -1; return -1;
} }
} }
else if (rc) else if (lerr)
{ {
log_error (_("searching '%s' failed: %s\n"), log_error ("searching '%s' failed: %s\n",
url, ldap_err2string (rc)); filter, ldap_err2string (lerr));
if (rc != LDAP_NO_SUCH_OBJECT) if (lerr != LDAP_NO_SUCH_OBJECT)
{ {
/* FIXME: Need deinit (ld)? */ /* FIXME: Need deinit (ld)? */
/* Hmmm: Do we need to released MSG in case of an error? */ /* Hmmm: Do we need to released MSG in case of an error? */
@ -735,39 +790,43 @@ fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp)
} }
} }
rc = print_ldap_entries (myopt, ld, msg, myopt->multi? NULL:attr); err = print_ldap_entries (ld, msg, opt.multi? NULL:opt.attr);
ldap_msgfree (msg); ldap_msgfree (msg);
ldap_unbind (ld); return err;
return rc;
} }
/* Main processing. Take the URL and run the LDAP query. The result /* Main processing. Take the filter and run the LDAP query. The
is printed to stdout, errors are logged to the log stream. */ * result is printed to stdout, errors are logged to the log stream.
static int * To allow searching with a different base it is possible to extend
process_url (my_opt_t myopt, const char *url) * the filer. For example:
*
* ^CN=foo, OU=My Users&(objectClasses=*)
*
* Uses "CN=foo, OU=My Users" as base DN and "(objectClasses=*)" as
* filter. If the base prefix includes an ampersand, it needs to be
* doubled. The usual escaping rules for DNs (for the base) and
* filters apply. If no scope is given (see ldap_parse_extfilter for
* the syntax) subtree scope is used.
*/
static gpg_error_t
process_filter (LDAP *ld, const char *string)
{ {
int rc; gpg_error_t err;
LDAPURLDesc *ludp = NULL; char *base, *filter;
int scope = -1;
err = ldap_parse_extfilter (string, 0, &base, &scope, &filter);
if (!err)
err = fetch_ldap (ld,
base? base : opt.base,
scope == -1? LDAP_SCOPE_SUBTREE : scope,
filter);
if (!ldap_is_ldap_url (url)) xfree (base);
{ xfree (filter);
log_error (_("'%s' is not an LDAP URL\n"), url); return err;
return -1;
}
if (ldap_url_parse (url, &ludp))
{
log_error (_("'%s' is an invalid LDAP URL\n"), url);
return -1;
}
rc = fetch_ldap (myopt, url, ludp);
ldap_free_urldesc (ludp);
return rc;
} }

View File

@ -28,27 +28,15 @@
# include <getopt.h> # include <getopt.h>
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <errno.h>
#include <assert.h>
#ifdef _WIN32
# include <winsock2.h>
# include <winldap.h>
#else
# ifdef NEED_LBER_H
# include <lber.h>
# endif
/* For OpenLDAP, to enable the API that we're using. */
# define LDAP_DEPRECATED 1
# include <ldap.h>
#endif
#include <npth.h> #include <npth.h>
#include "dirmngr.h" #include "dirmngr.h"
#include "misc.h" #include "misc.h"
#include "../common/userids.h" #include "../common/userids.h"
#include "../common/mbox-util.h" #include "../common/mbox-util.h"
#include "ks-engine.h" #include "ks-engine.h"
#include "ldap-misc.h"
#include "ldap-parse-uri.h" #include "ldap-parse-uri.h"
#include "ldapserver.h" #include "ldapserver.h"
@ -60,177 +48,11 @@
#define SERVERINFO_NTDS 8 /* Server is an Active Directory. */ #define SERVERINFO_NTDS 8 /* Server is an Active Directory. */
#ifndef HAVE_TIMEGM #ifndef HAVE_TIMEGM
time_t timegm(struct tm *tm); time_t timegm(struct tm *tm);
#endif #endif
/* Convert an LDAP error to a GPG error. */
static int
ldap_err_to_gpg_err (int code)
{
gpg_err_code_t ec;
switch (code)
{
#ifdef LDAP_X_CONNECTING
case LDAP_X_CONNECTING: ec = GPG_ERR_LDAP_X_CONNECTING; break;
#endif
case LDAP_REFERRAL_LIMIT_EXCEEDED: ec = GPG_ERR_LDAP_REFERRAL_LIMIT; break;
case LDAP_CLIENT_LOOP: ec = GPG_ERR_LDAP_CLIENT_LOOP; break;
case LDAP_NO_RESULTS_RETURNED: ec = GPG_ERR_LDAP_NO_RESULTS; break;
case LDAP_CONTROL_NOT_FOUND: ec = GPG_ERR_LDAP_CONTROL_NOT_FOUND; break;
case LDAP_NOT_SUPPORTED: ec = GPG_ERR_LDAP_NOT_SUPPORTED; break;
case LDAP_CONNECT_ERROR: ec = GPG_ERR_LDAP_CONNECT; break;
case LDAP_NO_MEMORY: ec = GPG_ERR_LDAP_NO_MEMORY; break;
case LDAP_PARAM_ERROR: ec = GPG_ERR_LDAP_PARAM; break;
case LDAP_USER_CANCELLED: ec = GPG_ERR_LDAP_USER_CANCELLED; break;
case LDAP_FILTER_ERROR: ec = GPG_ERR_LDAP_FILTER; break;
case LDAP_AUTH_UNKNOWN: ec = GPG_ERR_LDAP_AUTH_UNKNOWN; break;
case LDAP_TIMEOUT: ec = GPG_ERR_LDAP_TIMEOUT; break;
case LDAP_DECODING_ERROR: ec = GPG_ERR_LDAP_DECODING; break;
case LDAP_ENCODING_ERROR: ec = GPG_ERR_LDAP_ENCODING; break;
case LDAP_LOCAL_ERROR: ec = GPG_ERR_LDAP_LOCAL; break;
case LDAP_SERVER_DOWN: ec = GPG_ERR_LDAP_SERVER_DOWN; break;
case LDAP_SUCCESS: ec = GPG_ERR_LDAP_SUCCESS; break;
case LDAP_OPERATIONS_ERROR: ec = GPG_ERR_LDAP_OPERATIONS; break;
case LDAP_PROTOCOL_ERROR: ec = GPG_ERR_LDAP_PROTOCOL; break;
case LDAP_TIMELIMIT_EXCEEDED: ec = GPG_ERR_LDAP_TIMELIMIT; break;
case LDAP_SIZELIMIT_EXCEEDED: ec = GPG_ERR_LDAP_SIZELIMIT; break;
case LDAP_COMPARE_FALSE: ec = GPG_ERR_LDAP_COMPARE_FALSE; break;
case LDAP_COMPARE_TRUE: ec = GPG_ERR_LDAP_COMPARE_TRUE; break;
case LDAP_AUTH_METHOD_NOT_SUPPORTED: ec=GPG_ERR_LDAP_UNSUPPORTED_AUTH;break;
case LDAP_STRONG_AUTH_REQUIRED: ec = GPG_ERR_LDAP_STRONG_AUTH_RQRD; break;
case LDAP_PARTIAL_RESULTS: ec = GPG_ERR_LDAP_PARTIAL_RESULTS; break;
case LDAP_REFERRAL: ec = GPG_ERR_LDAP_REFERRAL; break;
#ifdef LDAP_ADMINLIMIT_EXCEEDED
case LDAP_ADMINLIMIT_EXCEEDED: ec = GPG_ERR_LDAP_ADMINLIMIT; break;
#endif
#ifdef LDAP_UNAVAILABLE_CRITICAL_EXTENSION
case LDAP_UNAVAILABLE_CRITICAL_EXTENSION:
ec = GPG_ERR_LDAP_UNAVAIL_CRIT_EXTN; break;
#endif
case LDAP_CONFIDENTIALITY_REQUIRED: ec = GPG_ERR_LDAP_CONFIDENT_RQRD; break;
case LDAP_SASL_BIND_IN_PROGRESS: ec = GPG_ERR_LDAP_SASL_BIND_INPROG; break;
case LDAP_NO_SUCH_ATTRIBUTE: ec = GPG_ERR_LDAP_NO_SUCH_ATTRIBUTE; break;
case LDAP_UNDEFINED_TYPE: ec = GPG_ERR_LDAP_UNDEFINED_TYPE; break;
case LDAP_INAPPROPRIATE_MATCHING: ec = GPG_ERR_LDAP_BAD_MATCHING; break;
case LDAP_CONSTRAINT_VIOLATION: ec = GPG_ERR_LDAP_CONST_VIOLATION; break;
#ifdef LDAP_TYPE_OR_VALUE_EXISTS
case LDAP_TYPE_OR_VALUE_EXISTS: ec = GPG_ERR_LDAP_TYPE_VALUE_EXISTS; break;
#endif
case LDAP_INVALID_SYNTAX: ec = GPG_ERR_LDAP_INV_SYNTAX; break;
case LDAP_NO_SUCH_OBJECT: ec = GPG_ERR_LDAP_NO_SUCH_OBJ; break;
case LDAP_ALIAS_PROBLEM: ec = GPG_ERR_LDAP_ALIAS_PROBLEM; break;
case LDAP_INVALID_DN_SYNTAX: ec = GPG_ERR_LDAP_INV_DN_SYNTAX; break;
case LDAP_IS_LEAF: ec = GPG_ERR_LDAP_IS_LEAF; break;
case LDAP_ALIAS_DEREF_PROBLEM: ec = GPG_ERR_LDAP_ALIAS_DEREF; break;
#ifdef LDAP_X_PROXY_AUTHZ_FAILURE
case LDAP_X_PROXY_AUTHZ_FAILURE: ec = GPG_ERR_LDAP_X_PROXY_AUTH_FAIL; break;
#endif
case LDAP_INAPPROPRIATE_AUTH: ec = GPG_ERR_LDAP_BAD_AUTH; break;
case LDAP_INVALID_CREDENTIALS: ec = GPG_ERR_LDAP_INV_CREDENTIALS; break;
#ifdef LDAP_INSUFFICIENT_ACCESS
case LDAP_INSUFFICIENT_ACCESS: ec = GPG_ERR_LDAP_INSUFFICIENT_ACC; break;
#endif
case LDAP_BUSY: ec = GPG_ERR_LDAP_BUSY; break;
case LDAP_UNAVAILABLE: ec = GPG_ERR_LDAP_UNAVAILABLE; break;
case LDAP_UNWILLING_TO_PERFORM: ec = GPG_ERR_LDAP_UNWILL_TO_PERFORM; break;
case LDAP_LOOP_DETECT: ec = GPG_ERR_LDAP_LOOP_DETECT; break;
case LDAP_NAMING_VIOLATION: ec = GPG_ERR_LDAP_NAMING_VIOLATION; break;
case LDAP_OBJECT_CLASS_VIOLATION: ec = GPG_ERR_LDAP_OBJ_CLS_VIOLATION; break;
case LDAP_NOT_ALLOWED_ON_NONLEAF: ec=GPG_ERR_LDAP_NOT_ALLOW_NONLEAF;break;
case LDAP_NOT_ALLOWED_ON_RDN: ec = GPG_ERR_LDAP_NOT_ALLOW_ON_RDN; break;
case LDAP_ALREADY_EXISTS: ec = GPG_ERR_LDAP_ALREADY_EXISTS; break;
case LDAP_NO_OBJECT_CLASS_MODS: ec = GPG_ERR_LDAP_NO_OBJ_CLASS_MODS; break;
case LDAP_RESULTS_TOO_LARGE: ec = GPG_ERR_LDAP_RESULTS_TOO_LARGE; break;
case LDAP_AFFECTS_MULTIPLE_DSAS: ec = GPG_ERR_LDAP_AFFECTS_MULT_DSAS; break;
#ifdef LDAP_VLV_ERROR
case LDAP_VLV_ERROR: ec = GPG_ERR_LDAP_VLV; break;
#endif
case LDAP_OTHER: ec = GPG_ERR_LDAP_OTHER; break;
#ifdef LDAP_CUP_RESOURCES_EXHAUSTED
case LDAP_CUP_RESOURCES_EXHAUSTED: ec=GPG_ERR_LDAP_CUP_RESOURCE_LIMIT;break;
case LDAP_CUP_SECURITY_VIOLATION: ec=GPG_ERR_LDAP_CUP_SEC_VIOLATION; break;
case LDAP_CUP_INVALID_DATA: ec = GPG_ERR_LDAP_CUP_INV_DATA; break;
case LDAP_CUP_UNSUPPORTED_SCHEME: ec = GPG_ERR_LDAP_CUP_UNSUP_SCHEME; break;
case LDAP_CUP_RELOAD_REQUIRED: ec = GPG_ERR_LDAP_CUP_RELOAD; break;
#endif
#ifdef LDAP_CANCELLED
case LDAP_CANCELLED: ec = GPG_ERR_LDAP_CANCELLED; break;
#endif
#ifdef LDAP_NO_SUCH_OPERATION
case LDAP_NO_SUCH_OPERATION: ec = GPG_ERR_LDAP_NO_SUCH_OPERATION; break;
#endif
#ifdef LDAP_TOO_LATE
case LDAP_TOO_LATE: ec = GPG_ERR_LDAP_TOO_LATE; break;
#endif
#ifdef LDAP_CANNOT_CANCEL
case LDAP_CANNOT_CANCEL: ec = GPG_ERR_LDAP_CANNOT_CANCEL; break;
#endif
#ifdef LDAP_ASSERTION_FAILED
case LDAP_ASSERTION_FAILED: ec = GPG_ERR_LDAP_ASSERTION_FAILED; break;
#endif
#ifdef LDAP_PROXIED_AUTHORIZATION_DENIED
case LDAP_PROXIED_AUTHORIZATION_DENIED:
ec = GPG_ERR_LDAP_PROX_AUTH_DENIED; break;
#endif
default:
#if defined(LDAP_E_ERROR) && defined(LDAP_X_ERROR)
if (LDAP_E_ERROR (code))
ec = GPG_ERR_LDAP_E_GENERAL;
else if (LDAP_X_ERROR (code))
ec = GPG_ERR_LDAP_X_GENERAL;
else
#endif
ec = GPG_ERR_LDAP_GENERAL;
break;
}
return ec;
}
/* Retrieve an LDAP error and return it's GPG equivalent. */
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 GPG_ERR_GENERAL;
#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 GPG_ERR_INTERNAL;
#endif
}
static time_t static time_t
ldap2epochtime (const char *timestr) ldap2epochtime (const char *timestr)
@ -1829,7 +1651,7 @@ uncescape (char *str)
&& hexdigitp (str + r + 3)) && hexdigitp (str + r + 3))
{ {
int x = hextobyte (&str[r + 2]); int x = hextobyte (&str[r + 2]);
assert (0 <= x && x <= 0xff); log_assert (0 <= x && x <= 0xff);
str[w] = x; str[w] = x;
@ -2202,8 +2024,8 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri,
{ {
dump = es_fopen("/tmp/modlist.txt", "w"); dump = es_fopen("/tmp/modlist.txt", "w");
if (! dump) if (! dump)
log_error ("Failed to open /tmp/modlist.txt: %s\n", log_error ("failed to open /tmp/modlist.txt: %s\n",
strerror (errno)); gpg_strerror (gpg_error_from_syserror ()));
if (dump) if (dump)
{ {

332
dirmngr/ldap-misc.c Normal file
View File

@ -0,0 +1,332 @@
/* ldap-misc.c - Miscellaneous helpers for LDAP functions
* Copyright (C) 2015, 2021 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "dirmngr-err.h"
#include "../common/util.h"
#include "ldap-misc.h"
/* Convert an LDAP error to a GPG error. */
gpg_err_code_t
ldap_err_to_gpg_err (int code)
{
gpg_err_code_t ec;
switch (code)
{
#ifdef LDAP_X_CONNECTING
case LDAP_X_CONNECTING: ec = GPG_ERR_LDAP_X_CONNECTING; break;
#endif
case LDAP_REFERRAL_LIMIT_EXCEEDED: ec = GPG_ERR_LDAP_REFERRAL_LIMIT; break;
case LDAP_CLIENT_LOOP: ec = GPG_ERR_LDAP_CLIENT_LOOP; break;
case LDAP_NO_RESULTS_RETURNED: ec = GPG_ERR_LDAP_NO_RESULTS; break;
case LDAP_CONTROL_NOT_FOUND: ec = GPG_ERR_LDAP_CONTROL_NOT_FOUND; break;
case LDAP_NOT_SUPPORTED: ec = GPG_ERR_LDAP_NOT_SUPPORTED; break;
case LDAP_CONNECT_ERROR: ec = GPG_ERR_LDAP_CONNECT; break;
case LDAP_NO_MEMORY: ec = GPG_ERR_LDAP_NO_MEMORY; break;
case LDAP_PARAM_ERROR: ec = GPG_ERR_LDAP_PARAM; break;
case LDAP_USER_CANCELLED: ec = GPG_ERR_LDAP_USER_CANCELLED; break;
case LDAP_FILTER_ERROR: ec = GPG_ERR_LDAP_FILTER; break;
case LDAP_AUTH_UNKNOWN: ec = GPG_ERR_LDAP_AUTH_UNKNOWN; break;
case LDAP_TIMEOUT: ec = GPG_ERR_LDAP_TIMEOUT; break;
case LDAP_DECODING_ERROR: ec = GPG_ERR_LDAP_DECODING; break;
case LDAP_ENCODING_ERROR: ec = GPG_ERR_LDAP_ENCODING; break;
case LDAP_LOCAL_ERROR: ec = GPG_ERR_LDAP_LOCAL; break;
case LDAP_SERVER_DOWN: ec = GPG_ERR_LDAP_SERVER_DOWN; break;
case LDAP_SUCCESS: ec = GPG_ERR_LDAP_SUCCESS; break;
case LDAP_OPERATIONS_ERROR: ec = GPG_ERR_LDAP_OPERATIONS; break;
case LDAP_PROTOCOL_ERROR: ec = GPG_ERR_LDAP_PROTOCOL; break;
case LDAP_TIMELIMIT_EXCEEDED: ec = GPG_ERR_LDAP_TIMELIMIT; break;
case LDAP_SIZELIMIT_EXCEEDED: ec = GPG_ERR_LDAP_SIZELIMIT; break;
case LDAP_COMPARE_FALSE: ec = GPG_ERR_LDAP_COMPARE_FALSE; break;
case LDAP_COMPARE_TRUE: ec = GPG_ERR_LDAP_COMPARE_TRUE; break;
case LDAP_AUTH_METHOD_NOT_SUPPORTED: ec=GPG_ERR_LDAP_UNSUPPORTED_AUTH;break;
case LDAP_STRONG_AUTH_REQUIRED: ec = GPG_ERR_LDAP_STRONG_AUTH_RQRD; break;
case LDAP_PARTIAL_RESULTS: ec = GPG_ERR_LDAP_PARTIAL_RESULTS; break;
case LDAP_REFERRAL: ec = GPG_ERR_LDAP_REFERRAL; break;
#ifdef LDAP_ADMINLIMIT_EXCEEDED
case LDAP_ADMINLIMIT_EXCEEDED: ec = GPG_ERR_LDAP_ADMINLIMIT; break;
#endif
#ifdef LDAP_UNAVAILABLE_CRITICAL_EXTENSION
case LDAP_UNAVAILABLE_CRITICAL_EXTENSION:
ec = GPG_ERR_LDAP_UNAVAIL_CRIT_EXTN; break;
#endif
case LDAP_CONFIDENTIALITY_REQUIRED: ec = GPG_ERR_LDAP_CONFIDENT_RQRD; break;
case LDAP_SASL_BIND_IN_PROGRESS: ec = GPG_ERR_LDAP_SASL_BIND_INPROG; break;
case LDAP_NO_SUCH_ATTRIBUTE: ec = GPG_ERR_LDAP_NO_SUCH_ATTRIBUTE; break;
case LDAP_UNDEFINED_TYPE: ec = GPG_ERR_LDAP_UNDEFINED_TYPE; break;
case LDAP_INAPPROPRIATE_MATCHING: ec = GPG_ERR_LDAP_BAD_MATCHING; break;
case LDAP_CONSTRAINT_VIOLATION: ec = GPG_ERR_LDAP_CONST_VIOLATION; break;
#ifdef LDAP_TYPE_OR_VALUE_EXISTS
case LDAP_TYPE_OR_VALUE_EXISTS: ec = GPG_ERR_LDAP_TYPE_VALUE_EXISTS; break;
#endif
case LDAP_INVALID_SYNTAX: ec = GPG_ERR_LDAP_INV_SYNTAX; break;
case LDAP_NO_SUCH_OBJECT: ec = GPG_ERR_LDAP_NO_SUCH_OBJ; break;
case LDAP_ALIAS_PROBLEM: ec = GPG_ERR_LDAP_ALIAS_PROBLEM; break;
case LDAP_INVALID_DN_SYNTAX: ec = GPG_ERR_LDAP_INV_DN_SYNTAX; break;
case LDAP_IS_LEAF: ec = GPG_ERR_LDAP_IS_LEAF; break;
case LDAP_ALIAS_DEREF_PROBLEM: ec = GPG_ERR_LDAP_ALIAS_DEREF; break;
#ifdef LDAP_X_PROXY_AUTHZ_FAILURE
case LDAP_X_PROXY_AUTHZ_FAILURE: ec = GPG_ERR_LDAP_X_PROXY_AUTH_FAIL; break;
#endif
case LDAP_INAPPROPRIATE_AUTH: ec = GPG_ERR_LDAP_BAD_AUTH; break;
case LDAP_INVALID_CREDENTIALS: ec = GPG_ERR_LDAP_INV_CREDENTIALS; break;
#ifdef LDAP_INSUFFICIENT_ACCESS
case LDAP_INSUFFICIENT_ACCESS: ec = GPG_ERR_LDAP_INSUFFICIENT_ACC; break;
#endif
case LDAP_BUSY: ec = GPG_ERR_LDAP_BUSY; break;
case LDAP_UNAVAILABLE: ec = GPG_ERR_LDAP_UNAVAILABLE; break;
case LDAP_UNWILLING_TO_PERFORM: ec = GPG_ERR_LDAP_UNWILL_TO_PERFORM; break;
case LDAP_LOOP_DETECT: ec = GPG_ERR_LDAP_LOOP_DETECT; break;
case LDAP_NAMING_VIOLATION: ec = GPG_ERR_LDAP_NAMING_VIOLATION; break;
case LDAP_OBJECT_CLASS_VIOLATION: ec = GPG_ERR_LDAP_OBJ_CLS_VIOLATION; break;
case LDAP_NOT_ALLOWED_ON_NONLEAF: ec=GPG_ERR_LDAP_NOT_ALLOW_NONLEAF;break;
case LDAP_NOT_ALLOWED_ON_RDN: ec = GPG_ERR_LDAP_NOT_ALLOW_ON_RDN; break;
case LDAP_ALREADY_EXISTS: ec = GPG_ERR_LDAP_ALREADY_EXISTS; break;
case LDAP_NO_OBJECT_CLASS_MODS: ec = GPG_ERR_LDAP_NO_OBJ_CLASS_MODS; break;
case LDAP_RESULTS_TOO_LARGE: ec = GPG_ERR_LDAP_RESULTS_TOO_LARGE; break;
case LDAP_AFFECTS_MULTIPLE_DSAS: ec = GPG_ERR_LDAP_AFFECTS_MULT_DSAS; break;
#ifdef LDAP_VLV_ERROR
case LDAP_VLV_ERROR: ec = GPG_ERR_LDAP_VLV; break;
#endif
case LDAP_OTHER: ec = GPG_ERR_LDAP_OTHER; break;
#ifdef LDAP_CUP_RESOURCES_EXHAUSTED
case LDAP_CUP_RESOURCES_EXHAUSTED: ec=GPG_ERR_LDAP_CUP_RESOURCE_LIMIT;break;
case LDAP_CUP_SECURITY_VIOLATION: ec=GPG_ERR_LDAP_CUP_SEC_VIOLATION; break;
case LDAP_CUP_INVALID_DATA: ec = GPG_ERR_LDAP_CUP_INV_DATA; break;
case LDAP_CUP_UNSUPPORTED_SCHEME: ec = GPG_ERR_LDAP_CUP_UNSUP_SCHEME; break;
case LDAP_CUP_RELOAD_REQUIRED: ec = GPG_ERR_LDAP_CUP_RELOAD; break;
#endif
#ifdef LDAP_CANCELLED
case LDAP_CANCELLED: ec = GPG_ERR_LDAP_CANCELLED; break;
#endif
#ifdef LDAP_NO_SUCH_OPERATION
case LDAP_NO_SUCH_OPERATION: ec = GPG_ERR_LDAP_NO_SUCH_OPERATION; break;
#endif
#ifdef LDAP_TOO_LATE
case LDAP_TOO_LATE: ec = GPG_ERR_LDAP_TOO_LATE; break;
#endif
#ifdef LDAP_CANNOT_CANCEL
case LDAP_CANNOT_CANCEL: ec = GPG_ERR_LDAP_CANNOT_CANCEL; break;
#endif
#ifdef LDAP_ASSERTION_FAILED
case LDAP_ASSERTION_FAILED: ec = GPG_ERR_LDAP_ASSERTION_FAILED; break;
#endif
#ifdef LDAP_PROXIED_AUTHORIZATION_DENIED
case LDAP_PROXIED_AUTHORIZATION_DENIED:
ec = GPG_ERR_LDAP_PROX_AUTH_DENIED; break;
#endif
default:
#if defined(LDAP_E_ERROR) && defined(LDAP_X_ERROR)
if (LDAP_E_ERROR (code))
ec = GPG_ERR_LDAP_E_GENERAL;
else if (LDAP_X_ERROR (code))
ec = GPG_ERR_LDAP_X_GENERAL;
else
#endif
ec = GPG_ERR_LDAP_GENERAL;
break;
}
return ec;
}
/* Retrieve an LDAP error and return it's GPG equivalent. */
gpg_err_code_t
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 GPG_ERR_GENERAL;
#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 GPG_ERR_INTERNAL;
#endif
}
/* Parse an extended filter syntax as used by dirmngr_ldap.c
* For example:
*
* ^CN=foo, OU=My Users&(objectClasses=*)
*
* Uses "CN=foo, OU=My Users" as base DN and "(objectClasses=*)" as
* filter. If the base prefix includes an ampersand, it needs to be
* doubled. The usual escaping rules for DNs (for the base) and
* filters apply. Other examples:
*
* ^CN=foo, OU=My Users&
*
* Use just the base DN.
*
* ^CN=foo, OU=My Users&SCOPE&
*
* Specify the scope which is "base", "one", or "sub". May of course
* also be followed by a filter.
*
* ^&SCOPE&(objectClasses=*)
*
* Give a scope and a filter. Note that R_SCOPE is only changed if a
* STRING has scope parameter. Setting this initally to -1 allows to
* detect this case.
*/
gpg_error_t
ldap_parse_extfilter (const char *string, int silent,
char **r_base, int *r_scope, char **r_filter)
{
gpg_error_t err = 0;
char *base = NULL;
char *filter = NULL;
const char *s;
char *p;
if (r_base)
*r_base = NULL;
if (r_filter)
*r_filter = NULL;
if (*string == '^')
{
string++;
base = xtrymalloc (strlen (string)+1);
if (!base)
{
err = gpg_error_from_syserror ();
goto leave;
}
for (s=string, p=base; *s; s++)
{
*p++ = *s;
if (*s == '&' && s[1] == '&')
s++; /* Skip quoted ampersand. */
else if (*s == '&')
{
p--;
break;
}
}
*p = 0;
if (!*s)
{
if (!silent)
log_info ("LDAP extended filter is not terminated\n");
err = gpg_error (GPG_ERR_SYNTAX);
goto leave;
}
string = s + 1;
}
if (!*string)
goto leave; /* ready. */
if (!strncmp (string, "base&", 5))
{
string += 5;
if (r_scope)
*r_scope = LDAP_SCOPE_BASE;
}
else if (!strncmp (string, "one&", 4))
{
string += 4;
if (r_scope)
*r_scope = LDAP_SCOPE_ONELEVEL;
}
else if (!strncmp (string, "sub&", 4))
{
string += 4;
if (r_scope)
*r_scope = LDAP_SCOPE_SUBTREE;
}
if (!*string)
goto leave; /* ready. */
if (*string != '(')
{
if (!silent)
log_info ("LDAP filter does not start with a left parentheses\n");
return gpg_error (GPG_ERR_SYNTAX);
}
if (string[strlen(string)-1] != ')')
{
if (!silent)
log_info ("LDAP filter does not end with a right parentheses\n");
return gpg_error (GPG_ERR_SYNTAX);
}
filter = xtrystrdup (string);
if (!filter)
err = gpg_error_from_syserror ();
leave:
if (err)
{
xfree (base);
xfree (filter);
}
else
{
if (r_base)
*r_base = base;
else
xfree (base);
if (r_filter)
*r_filter = filter;
else
xfree (filter);
}
return err;
}

43
dirmngr/ldap-misc.h Normal file
View File

@ -0,0 +1,43 @@
/* ldap-misc.h - Miscellaneous helpers for LDAP functions
* Copyright (C) 2015, 2021 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef DIRMNGR_LDAP_MISC_H
#define DIRMNGR_LDAP_MISC_H
#ifdef _WIN32
# include <winsock2.h>
# include <winldap.h>
#else
# ifdef NEED_LBER_H
# include <lber.h>
# endif
/* For OpenLDAP, to enable the API that we're using. */
# define LDAP_DEPRECATED 1
# include <ldap.h>
#endif
gpg_err_code_t ldap_err_to_gpg_err (int code);
gpg_err_code_t ldap_to_gpg_err (LDAP *ld);
gpg_error_t ldap_parse_extfilter (const char *string, int silent,
char **r_base, int *r_scope, char **r_filter);
#endif /*DIRMNGR_LDAP_MISC_H*/

View File

@ -548,7 +548,7 @@ ldap_reaper_launch_thread (void)
#ifdef HAVE_W32_SYSTEM #ifdef HAVE_W32_SYSTEM
/* Static init does not yet work in W32 nPth. */ /* Static init does not yet work in W32 nPth. */
if (npth_cond_init (&reaper_run_cond, NULL)) if (npth_cond_init (&reaper_run_cond, NULL))
log_fatal ("%s: failed to init condition variabale: %s\n", log_fatal ("%s: failed to init condition variable: %s\n",
__func__, gpg_strerror (gpg_error_from_syserror ())); __func__, gpg_strerror (gpg_error_from_syserror ()));
#endif #endif
@ -857,9 +857,9 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
err = gnupg_spawn_process (pgmname, arg_list, err = gnupg_spawn_process (pgmname, arg_list,
NULL, NULL, GNUPG_SPAWN_NONBLOCK, NULL, NULL, GNUPG_SPAWN_NONBLOCK,
NULL, &outfp, &errfp, &pid); NULL, &outfp, &errfp, &pid);
xfree (arg_list);
if (err) if (err)
{ {
xfree (arg_list);
xfree (ctx); xfree (ctx);
log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err)); log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
return err; return err;
@ -878,6 +878,7 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
err = ksba_reader_set_cb (*reader, reader_callback, ctx); err = ksba_reader_set_cb (*reader, reader_callback, ctx);
if (err) if (err)
{ {
xfree (arg_list);
log_error (_("error initializing reader object: %s\n"), log_error (_("error initializing reader object: %s\n"),
gpg_strerror (err)); gpg_strerror (err));
destroy_wrapper (ctx); destroy_wrapper (ctx);
@ -899,8 +900,15 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
unlock_reaper_list (); unlock_reaper_list ();
if (DBG_EXTPROG) if (DBG_EXTPROG)
log_debug ("ldap wrapper %d started (%p, %s)\n", {
(int)ctx->pid, ctx->reader, pgmname); log_debug ("ldap wrapper %d started (%p, %s)",
(int)ctx->pid, ctx->reader, pgmname);
for (i=0; arg_list[i]; i++)
log_printf (" [%s]", arg_list[i]);
log_printf ("\n");
}
xfree (arg_list);
/* Need to wait for the first byte so we are able to detect an empty /* Need to wait for the first byte so we are able to detect an empty
output and not let the consumer see an EOF without further error output and not let the consumer see an EOF without further error

View File

@ -1,12 +1,12 @@
/* ldap.c - LDAP access /* ldap.c - LDAP access
* Copyright (C) 2002 Klarälvdalens Datakonsult AB * Copyright (C) 2002 Klarälvdalens Datakonsult AB
* Copyright (C) 2003, 2004, 2005, 2007, 2008, 2010 g10 Code GmbH * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2010, 2021 g10 Code GmbH
* *
* This file is part of DirMngr. * This file is part of GnuPG.
* *
* DirMngr is free software; you can redistribute it and/or modify * GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* DirMngr is distributed in the hope that it will be useful, * DirMngr is distributed in the hope that it will be useful,
@ -15,8 +15,8 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, see <https://www.gnu.org/licenses/>.
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
#include <config.h> #include <config.h>
@ -36,6 +36,7 @@
#include "ldapserver.h" #include "ldapserver.h"
#include "misc.h" #include "misc.h"
#include "ldap-wrapper.h" #include "ldap-wrapper.h"
#include "ldap-url.h"
#include "../common/host2net.h" #include "../common/host2net.h"
@ -116,14 +117,15 @@ static gpg_error_t
run_ldap_wrapper (ctrl_t ctrl, run_ldap_wrapper (ctrl_t ctrl,
int ignore_timeout, int ignore_timeout,
int multi_mode, int multi_mode,
int tls_mode,
int ntds,
const char *proxy, const char *proxy,
const char *host, int port, const char *host, int port,
const char *user, const char *pass, const char *user, const char *pass,
const char *dn, const char *filter, const char *attr, const char *base, const char *filter, const char *attr,
const char *url,
ksba_reader_t *reader) ksba_reader_t *reader)
{ {
const char *argv[40]; const char *argv[50];
int argc; int argc;
char portbuf[30], timeoutbuf[30]; char portbuf[30], timeoutbuf[30];
@ -131,7 +133,7 @@ run_ldap_wrapper (ctrl_t ctrl,
*reader = NULL; *reader = NULL;
argc = 0; argc = 0;
if (pass) /* Note, that the password must be the first item. */ if (pass && *pass) /* Note, that the password must be the first item. */
{ {
argv[argc++] = "--pass"; argv[argc++] = "--pass";
argv[argc++] = pass; argv[argc++] = pass;
@ -145,9 +147,18 @@ run_ldap_wrapper (ctrl_t ctrl,
argv[argc++] = "--log-with-pid"; argv[argc++] = "--log-with-pid";
if (multi_mode) if (multi_mode)
argv[argc++] = "--multi"; argv[argc++] = "--multi";
if (tls_mode == 1)
argv[argc++] = "--starttls";
else if (tls_mode)
argv[argc++] = "--ldaptls";
if (ntds)
argv[argc++] = "--ntds";
if (opt.ldaptimeout) if (opt.ldaptimeout)
{ {
sprintf (timeoutbuf, "%u", opt.ldaptimeout); snprintf (timeoutbuf, sizeof timeoutbuf, "%u", opt.ldaptimeout);
argv[argc++] = "--timeout"; argv[argc++] = "--timeout";
argv[argc++] = timeoutbuf; argv[argc++] = timeoutbuf;
if (ignore_timeout) if (ignore_timeout)
@ -158,7 +169,7 @@ run_ldap_wrapper (ctrl_t ctrl,
argv[argc++] = "--proxy"; argv[argc++] = "--proxy";
argv[argc++] = proxy; argv[argc++] = proxy;
} }
if (host) if (host && *host)
{ {
argv[argc++] = "--host"; argv[argc++] = "--host";
argv[argc++] = host; argv[argc++] = host;
@ -169,27 +180,24 @@ run_ldap_wrapper (ctrl_t ctrl,
argv[argc++] = "--port"; argv[argc++] = "--port";
argv[argc++] = portbuf; argv[argc++] = portbuf;
} }
if (user) if (user && *user)
{ {
argv[argc++] = "--user"; argv[argc++] = "--user";
argv[argc++] = user; argv[argc++] = user;
} }
if (dn) if (base && *base)
{ {
argv[argc++] = "--dn"; argv[argc++] = "--base";
argv[argc++] = dn; argv[argc++] = base;
}
if (filter)
{
argv[argc++] = "--filter";
argv[argc++] = filter;
} }
if (attr) if (attr)
{ {
argv[argc++] = "--attr"; argv[argc++] = "--attr";
argv[argc++] = attr; argv[argc++] = attr;
} }
argv[argc++] = url? url : "ldap://";
if (filter)
argv[argc++] = filter;
argv[argc] = NULL; argv[argc] = NULL;
return ldap_wrapper (ctrl, reader, argv); return ldap_wrapper (ctrl, reader, argv);
@ -202,19 +210,48 @@ run_ldap_wrapper (ctrl_t ctrl,
reader is returned. If HOST or PORT are not 0, they are used to reader is returned. If HOST or PORT are not 0, they are used to
override the values from the URL. */ override the values from the URL. */
gpg_error_t gpg_error_t
url_fetch_ldap (ctrl_t ctrl, const char *url, const char *host, int port, url_fetch_ldap (ctrl_t ctrl, const char *url, ksba_reader_t *reader)
ksba_reader_t *reader)
{ {
gpg_error_t err; gpg_error_t err;
LDAPURLDesc *ludp = NULL;
int tls_mode;
if (!ldap_is_ldap_url (url))
{
log_error (_("'%s' is not an LDAP URL\n"), url);
return gpg_error (GPG_ERR_INV_URI);
}
if (ldap_url_parse (url, &ludp))
{
log_error (_("'%s' is an invalid LDAP URL\n"), url);
return gpg_error (GPG_ERR_INV_URI);
}
if (ludp->lud_filter && ludp->lud_filter[0] != '(')
{
log_error (_("'%s' is an invalid LDAP URL\n"), url);
err = gpg_error (GPG_ERR_BAD_URI);
goto leave;
}
if (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))
tls_mode = 2; /* LDAP-over-TLS here becuase we get it from certs. */
else
tls_mode = 0;
err = run_ldap_wrapper (ctrl, err = run_ldap_wrapper (ctrl,
1, /* Ignore explicit timeout because CRLs 1, /* Ignore explicit timeout because CRLs
might be very large. */ might be very large. */
0, 0, /* No Multi-mode. */
tls_mode,
0, /* No AD authentication. */
opt.ldap_proxy, opt.ldap_proxy,
host, port, ludp->lud_host, ludp->lud_port,
NULL, NULL, NULL, NULL, /* user, password */
NULL, NULL, NULL, url, ludp->lud_dn, /* Base DN */
ludp->lud_filter,
ludp->lud_attrs? ludp->lud_attrs[0] : NULL,
reader); reader);
/* FIXME: This option might be used for DoS attacks. Because it /* FIXME: This option might be used for DoS attacks. Because it
@ -223,17 +260,8 @@ url_fetch_ldap (ctrl_t ctrl, const char *url, const char *host, int port,
turn. */ turn. */
if (!err && opt.add_new_ldapservers && !opt.ldap_proxy) if (!err && opt.add_new_ldapservers && !opt.ldap_proxy)
{ {
if (host) if (ludp->lud_host)
add_server_to_servers (host, port); add_server_to_servers (ludp->lud_host, ludp->lud_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 /* If the lookup failed and we are not only using the proxy, we try
@ -252,19 +280,32 @@ url_fetch_ldap (ctrl_t ctrl, const char *url, const char *host, int port,
{ {
ldap_server_t server = iter.server; ldap_server_t server = iter.server;
if (server->starttls)
tls_mode = 1;
else if (server->ldap_over_tls)
tls_mode = 2;
else
tls_mode = 0;
err = run_ldap_wrapper (ctrl, err = run_ldap_wrapper (ctrl,
0, 0,
0, 0, /* No Multi-mode */
tls_mode,
server->ntds,
NULL, NULL,
server->host, server->port, server->host, server->port,
NULL, NULL, server->user, server->pass,
NULL, NULL, NULL, url, server->base,
ludp->lud_filter,
ludp->lud_attrs? ludp->lud_attrs[0] : NULL,
reader); reader);
if (!err) if (!err)
break; break;
} }
} }
leave:
ldap_free_urldesc (ludp);
return err; return err;
} }
@ -281,20 +322,32 @@ attr_fetch_ldap (ctrl_t ctrl,
*reader = NULL; *reader = NULL;
/* FIXME; we might want to look at the Base SN to try matching /* FIXME; we might want to look at the Base DN to try matching
servers first. */ servers first. */
for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter); for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter);
ldapserver_iter_next (&iter)) ldapserver_iter_next (&iter))
{ {
ldap_server_t server = iter.server; ldap_server_t server = iter.server;
int tls_mode;
if (server->starttls)
tls_mode = 1;
else if (server->ldap_over_tls)
tls_mode = 2;
else
tls_mode = 0;
err = run_ldap_wrapper (ctrl, err = run_ldap_wrapper (ctrl,
0, 0,
0, 0,
tls_mode,
server->ntds,
opt.ldap_proxy, opt.ldap_proxy,
server->host, server->port, server->host, server->port,
server->user, server->pass, server->user, server->pass,
dn, "objectClass=*", attr, NULL, dn,
"(objectClass=*)",
attr,
reader); reader);
if (!err) if (!err)
break; /* Probably found a result. Ready. */ break; /* Probably found a result. Ready. */
@ -302,46 +355,144 @@ attr_fetch_ldap (ctrl_t ctrl,
return err; return err;
} }
/* Parse PATTERN and return a new strlist to be used for the actual /* Return true if VALUE needs escaping. */
LDAP query. Bit 0 of the flags field is set if that pattern is static int
actually a base specification. Caller must release the returned rfc2254_need_escape (const char *value)
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; /* NUL needs to be escaped as well but we can represent that in
char *p; * VALUE, so no need for it. */
return !!strpbrk (value, "*()\\");
}
/* Escape VALUE using RFC-2254 rules. Returns NULL on error. */
static char *
rfc2254_escape (const char *value)
{
const char *s;
char *buffer, *p;
size_t length = 0;
for (s=value; *s; s++)
switch (*s)
{
case '*':
case '(':
case ')':
case '\\': length += 3; break;
default: length++; break;
}
buffer = xtrymalloc (length+1);
if (!buffer)
return NULL;
p = buffer;
for (s=value; *s; s++)
switch (*s)
{
case '*': p = stpcpy (p, "\\2a"); break;
case '(': p = stpcpy (p, "\\28"); break;
case ')': p = stpcpy (p, "\\29"); break;
case '\\': p = stpcpy (p, "\\5c"); break;
default: *p++ = *s; break;
}
*p = 0;
return buffer;
}
/* Return true if VALUE needs escaping. */
static int
extfilt_need_escape (const char *value)
{
/* NUL needs to be escaped as well but we can represent that in
* VALUE, so no need for it. */
return !!strchr (value, '&');
}
/* Escape VALUE using our extended filter rules from dirmngr_ldap.c.
* Returns NULL on error. */
static char *
extfilt_escape (const char *value)
{
const char *s;
char *buffer, *p;
size_t length = 0;
for (s=value; *s; s++)
{
length++;
if (*s == '&')
length++;
}
buffer = xtrymalloc (length+1);
if (!buffer)
return NULL;
p = buffer;
for (s=value; *s; s++)
{
*p++ = *s;
if (*s == '&')
*p++ = '&';
}
*p = 0;
return buffer;
}
/* Parse PATTERN and return a new filter expression for an LDAP query.
* The extended filter syntax as known by dirmngr_ldap.c is used.
* Caller must release the returned value. R_RESULT is set to NULL on
* error.
*
* Supported patterns:
*
* | Ok | gpg style user id type |
* |-----+------------------------------------------------------|
* | no | KeyID |
* | no | Fingerprint |
* | no | OpenPGP userid |
* | yes | Email address Indicated by a left angle bracket. |
* | no | Exact word match in user id or subj. name |
* | yes | Subj. DN indicated by a leading slash |
* | no | Issuer DN |
* | no | Serial number + subj. DN |
* | yes | Substring match indicated by a leading '*; (default) |
*/
static gpg_error_t
make_one_filter (const char *pattern, char **r_result)
{
gpg_error_t err = 0;
char *pattern_buffer = NULL;
char *result = NULL;
size_t n;
*r_result = NULL;
switch (*pattern) switch (*pattern)
{ {
case '<': /* Email. */ case '<': /* Email. */
{ {
pattern++; pattern++;
result = xmalloc (sizeof *result + 5 + strlen (pattern)); if (rfc2254_need_escape (pattern)
result->next = NULL; && !(pattern = pattern_buffer = rfc2254_escape (pattern)))
result->flags = 0;
p = stpcpy (stpcpy (result->d, "mail="), pattern);
if (p[-1] == '>')
*--p = 0;
if (!*result->d) /* Error. */
{ {
xfree (result); err = gpg_error_from_syserror ();
result = NULL; goto leave;
}
result = strconcat ("(mail=", pattern, ")", NULL);
if (!result)
{
err = gpg_error_from_syserror ();
goto leave;
}
n = strlen (result);
if (result[n-2] == '>') /* Strip trailing '>' */
{
result[n-2] = ')';
result[n-1] = 0;
} }
break; break;
} }
@ -349,125 +500,86 @@ parse_one_pattern (const char *pattern)
pattern++; pattern++;
if (*pattern) if (*pattern)
{ {
result = xmalloc (sizeof *result + strlen (pattern)); /* We need just the BaseDN. This assumes that the Subject
result->next = NULL; * is correcly stored in the DT. This is however not always
result->flags = 1; /* Base spec. */ * the case and the actual DN is different ffrom the
strcpy (result->d, pattern); * subject. In this case we won't find anything. */
if (extfilt_need_escape (pattern)
&& !(pattern = pattern_buffer = extfilt_escape (pattern)))
{
err = gpg_error_from_syserror ();
goto leave;
}
result = strconcat ("^", pattern, "&base&", NULL);
if (!result)
{
err = gpg_error_from_syserror ();
goto leave;
}
} }
break; break;
case '#': /* Issuer DN. */ case '#': /* Issuer DN - Not yet working. */
pattern++; pattern++;
if (*pattern == '/') /* Just issuer DN. */ if (*pattern == '/') /* Just issuer DN. */
{ {
pattern++; pattern++;
if (extfilt_need_escape (pattern)
&& !(pattern = pattern_buffer = extfilt_escape (pattern)))
{
err = gpg_error_from_syserror ();
goto leave;
}
result = strconcat ("^", pattern, "&base&", NULL);
if (!result)
{
err = gpg_error_from_syserror ();
goto leave;
}
} }
else /* Serial number + issuer DN */ else /* Serial number + issuer DN */
{ {
} }
break; break;
case '*': case '*':
pattern++; pattern++;
/* fall through */ /* fall through */
default: /* Take as substring match. */ default: /* Take as substring match. */
{ if (*pattern)
const char format[] = "(|(sn=*%s*)(|(cn=*%s*)(mail=*%s*)))"; {
if (rfc2254_need_escape (pattern)
if (*pattern) && !(pattern = pattern_buffer = rfc2254_escape (pattern)))
{ {
result = xmalloc (sizeof *result err = gpg_error_from_syserror ();
+ strlen (format) + 3 * strlen (pattern)); goto leave;
result->next = NULL; }
result->flags = 0; result = strconcat ("(|(sn=*", pattern,
sprintf (result->d, format, pattern, pattern, pattern); "*)(|(cn=*", pattern,
} "*)(mail=*", pattern,
} "*)))", NULL);
if (!result)
{
err = gpg_error_from_syserror ();
goto leave;
}
}
break; break;
} }
return result; if (!result)
} err = gpg_error (GPG_ERR_INV_USER_ID);
/* Take the string STRING and escape it according to the URL rules. leave:
Return a newly allocated string. */ xfree (pattern_buffer);
static char * if (err)
escape4url (const char *string) xfree (result);
{
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 ","
/* In 2005 wk mentioned in the changelog that
* work on the userSMIMECertificate has
* started but it seems that no further
* progress was made or the whole thing was
* simply forgotten. */
/* 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 else
err = 0; *r_result = result;
xfree (u_dn);
xfree (u_filter);
return err; return err;
} }
/* Prepare an LDAP query to return the cACertificate attribute for DN. /* Prepare an LDAP query to return the cACertificate attribute for DN.
* All configured default servers are queried until one responds. * All configured default servers are queried until one responds.
* This function returns an error code or 0 and stored a newly * This function returns an error code or 0 and stored a newly
@ -483,7 +595,7 @@ start_cacert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *r_context,
if (!*r_context) if (!*r_context)
return gpg_error_from_errno (errno); return gpg_error_from_errno (errno);
/* FIXME; we might want to look at the Base SN to try matching /* FIXME; we might want to look at the Base DN to try matching
servers first. */ servers first. */
err = gpg_error (GPG_ERR_CONFIGURATION); err = gpg_error (GPG_ERR_CONFIGURATION);
@ -495,10 +607,12 @@ start_cacert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *r_context,
err = run_ldap_wrapper (ctrl, err = run_ldap_wrapper (ctrl,
0, 0,
1, /* --multi (record format) */ 1, /* --multi (record format) */
0, /* No TLS */
0, /* No AD authentication. */
opt.ldap_proxy, opt.ldap_proxy,
server->host, server->port, server->host, server->port,
server->user, server->pass, server->user, server->pass,
dn, "objectClass=*", "cACertificate", NULL, dn, "objectClass=*", "cACertificate",
&(*r_context)->reader); &(*r_context)->reader);
if (!err) if (!err)
break; /* Probably found a result. */ break; /* Probably found a result. */
@ -526,12 +640,12 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *r_context,
int port; int port;
char *user = NULL; char *user = NULL;
char *pass = NULL; char *pass = NULL;
const char *base; char *base = NULL;
char *argv[50]; char *argv[50];
int argc = 0; int argc = 0;
int argc_malloced = 0; int argc_malloced = 0;
char portbuf[30], timeoutbuf[30]; char portbuf[30], timeoutbuf[30];
int use_ldaps = 0; int starttls, ldaptls, ntds;
*r_context = NULL; *r_context = NULL;
@ -559,19 +673,24 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *r_context,
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
goto leave; goto leave;
} }
base = server->base; if (server->base && !(base = xtrystrdup (server->base)))
use_ldaps = server->ldap_over_tls; {
err = gpg_error_from_syserror ();
goto leave;
}
starttls = server->starttls;
ldaptls = server->ldap_over_tls;
ntds = server->ntds;
} }
else /* Use a default server. */ else /* Use a default server. */
{ {
xfree (proxy); err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
return gpg_error (GPG_ERR_NOT_IMPLEMENTED); goto leave;
} }
if (!base)
base = "";
if (pass) /* Note: Must be the first item. */ if (pass && *pass) /* Note: Must be the first item. */
{ {
argv[argc++] = "--pass"; argv[argc++] = "--pass";
argv[argc++] = pass; argv[argc++] = pass;
@ -584,20 +703,27 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *r_context,
argv[argc++] = "--log-with-pid"; argv[argc++] = "--log-with-pid";
argv[argc++] = "--multi"; argv[argc++] = "--multi";
if (starttls)
argv[argc++] = "--starttls";
else if (ldaptls)
argv[argc++] = "--ldaptls";
if (ntds)
argv[argc++] = "--ntds";
if (opt.ldaptimeout) if (opt.ldaptimeout)
{ {
snprintf (timeoutbuf, sizeof timeoutbuf, "%u", opt.ldaptimeout); snprintf (timeoutbuf, sizeof timeoutbuf, "%u", opt.ldaptimeout);
argv[argc++] = "--timeout"; argv[argc++] = "--timeout";
argv[argc++] = timeoutbuf; argv[argc++] = timeoutbuf;
} }
if (opt.ldap_proxy) if (proxy && *proxy)
{ {
argv[argc++] = "--proxy"; argv[argc++] = "--proxy";
argv[argc++] = proxy; argv[argc++] = proxy;
} }
if (use_ldaps) if (host && *host)
argv[argc++] = "--tls";
if (host)
{ {
argv[argc++] = "--host"; argv[argc++] = "--host";
argv[argc++] = host; argv[argc++] = host;
@ -608,56 +734,49 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *r_context,
argv[argc++] = "--port"; argv[argc++] = "--port";
argv[argc++] = portbuf; argv[argc++] = portbuf;
} }
if (user) if (user && *user)
{ {
argv[argc++] = "--user"; argv[argc++] = "--user";
argv[argc++] = user; argv[argc++] = user;
} }
if (base && *base)
{
argv[argc++] = "--base";
argv[argc++] = base;
}
/* All entries in argv from this index on are malloc'ed. */ /* All entries in argv from this index on are malloc'ed. */
argc_malloced = argc; argc_malloced = argc;
for (; patterns; patterns = patterns->next) for (; patterns; patterns = patterns->next)
{ {
strlist_t sl;
char *url;
if (argc >= DIM (argv) - 1) if (argc >= DIM (argv) - 1)
{ {
/* Too many patterns. It does not make sense to allow an /* Too many patterns. It does not make sense to allow an
arbitrary number of patters because the length of the arbitrary number of patters because the length of the
command line is limited anyway. */ command line is limited anyway. */
/* fixme: cleanup. */ err = gpg_error (GPG_ERR_RESOURCE_LIMIT);
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; goto leave;
} }
if ((sl->flags & 1)) if (*patterns->d)
err = make_url (&url, sl->d, "objectClass=*"); {
else err = make_one_filter (patterns->d, &argv[argc]);
err = make_url (&url, base, sl->d); if (err)
free_strlist (sl); goto leave;
if (err) argc++;
goto leave; }
argv[argc++] = url;
} }
argv[argc] = NULL; argv[argc] = NULL;
*r_context = xtrycalloc (1, sizeof **r_context); *r_context = xtrycalloc (1, sizeof **r_context);
if (!*r_context) if (!*r_context)
{ {
err = gpg_error_from_errno (errno); err = gpg_error_from_syserror ();
goto leave; goto leave;
} }
err = ldap_wrapper (ctrl, &(*r_context)->reader, (const char**)argv); err = ldap_wrapper (ctrl, &(*r_context)->reader, (const char**)argv);
if (err) if (err)
{ {
xfree (*r_context); xfree (*r_context);
@ -669,6 +788,7 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *r_context,
xfree (argv[argc_malloced]); xfree (argv[argc_malloced]);
xfree (proxy); xfree (proxy);
xfree (host); xfree (host);
xfree (base);
xfree (user); xfree (user);
xfree (pass); xfree (pass);
return err; return err;

View File

@ -1672,7 +1672,8 @@ lookup_cert_by_pattern (assuan_context_t ctx, char *line,
if (!err && single) if (!err && single)
goto ready; goto ready;
if (gpg_err_code (err) == GPG_ERR_NO_DATA) if (gpg_err_code (err) == GPG_ERR_NO_DATA
|| gpg_err_code (err) == GPG_ERR_NOT_FOUND)
{ {
err = 0; err = 0;
if (cache_only) if (cache_only)

158
dirmngr/t-ldap-misc.c Normal file
View File

@ -0,0 +1,158 @@
/* t-ldap-parse-uri.c - Tests for ldap-parse-uri.c and ldap-misc.c
* Copyright (C) 2015 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <gpg-error.h>
#include "../common/util.h"
#include "t-support.h"
#include "ldap-misc.h"
static void
test_ldap_parse_extfilter (void)
{
struct {
const char *string;
const char *base;
const char *filter;
int scope;
gpg_err_code_t ec;
} tests[] =
{
{ "^CN=foo, OU=My Users&(objectClasses=*)",
"CN=foo, OU=My Users", "(objectClasses=*)",
-1 },
{ "^CN=foo, OU=My Users&base&(objectClasses=*)",
"CN=foo, OU=My Users", "(objectClasses=*)",
LDAP_SCOPE_BASE },
{ "^CN=foo, OU=My Users&one&(objectClasses=*)",
"CN=foo, OU=My Users", "(objectClasses=*)",
LDAP_SCOPE_ONELEVEL },
{ "^CN=foo, OU=My Users&sub&(objectClasses=*)",
"CN=foo, OU=My Users", "(objectClasses=*)",
LDAP_SCOPE_SUBTREE },
/* { "^CN=foo, OU=My Users&children&(objectClasses=*)", */
/* "CN=foo, OU=My Users", "(objectClasses=*)", */
/* LDAP_SCOPE_CHILDREN }, */
{ "^CN=foo, OU=My Users&",
"CN=foo, OU=My Users", NULL,
-1 },
{ "^CN=foo, OU=My Users&sub&",
"CN=foo, OU=My Users", NULL,
LDAP_SCOPE_SUBTREE },
/* { "^&children&(objectClasses=*)", */
/* "", "(objectClasses=*)", */
/* LDAP_SCOPE_CHILDREN }, */
{ "^CN=foo, OU=My &&Users&base&(objectClasses=*)",
"CN=foo, OU=My &Users", "(objectClasses=*)",
LDAP_SCOPE_BASE },
{ "^CN=foo, OU=My Users&&&base&(objectClasses=*)",
"CN=foo, OU=My Users&", "(objectClasses=*)",
LDAP_SCOPE_BASE },
{ "^CN=foo, OU=My Users",
NULL, NULL,
LDAP_SCOPE_BASE, GPG_ERR_SYNTAX },
{ "^CN=foo, OU=My Users&base(objectClasses=*)",
NULL, NULL,
LDAP_SCOPE_BASE, GPG_ERR_SYNTAX },
{ "^CN=foo, OU=My Users&base&objectClasses=*)",
NULL, NULL,
LDAP_SCOPE_BASE, GPG_ERR_SYNTAX },
{ "^CN=foo, OU=My Users&base&(objectClasses=*",
NULL, NULL,
LDAP_SCOPE_BASE, GPG_ERR_SYNTAX }
};
int idx;
gpg_error_t err;
int errcount = 0;
char *base, *filter;
int scope;
for (idx= 0; idx < DIM (tests); idx++)
{
scope = -1;
err = ldap_parse_extfilter (tests[idx].string, 1, &base, &scope, &filter);
if (err && tests[idx].ec)
{
if (gpg_err_code (err) != tests[idx].ec)
{
fprintf (stderr, "%s: test %d failed: wrong error code %d\n",
__func__, idx, err);
errcount++;
}
continue;
}
if (err)
{
fprintf (stderr, "%s: test %d failed: %s\n",
__func__, idx, gpg_strerror (err));
errcount++;
continue;
}
if (tests[idx].ec)
{
fprintf (stderr, "%s: test %d failed: error not detected\n",
__func__, idx);
errcount++;
continue;
}
if ((!tests[idx].base ^ !base)
|| (tests[idx].base && strcmp (tests[idx].base, base)))
{
fprintf (stderr, "%s: test %d failed: base mismatch ('%s')\n",
__func__, idx, base? base : "(null");
errcount++;
}
if ((!tests[idx].filter ^ !filter)
|| (tests[idx].filter && strcmp (tests[idx].filter, filter)))
{
fprintf (stderr, "%s: test %d failed: filter mismatch ('%s')\n",
__func__, idx, filter? filter : "(null");
errcount++;
}
if (tests[idx].scope != scope)
{
fprintf (stderr, "%s: test %d failed: scope mismatch (%d)\n",
__func__, idx, scope);
errcount++;
}
xfree (base);
xfree (filter);
}
if (errcount)
exit (1);
}
int
main (int argc, char **argv)
{
(void)argc;
(void)argv;
test_ldap_parse_extfilter ();
return 0;
}

View File

@ -31,6 +31,12 @@
#ifndef DIRMNGR_T_SUPPORT_H #ifndef DIRMNGR_T_SUPPORT_H
#define DIRMNGR_T_SUPPORT_H 1 #define DIRMNGR_T_SUPPORT_H 1
#ifndef DIM
# define DIM(v) (sizeof(v)/sizeof((v)[0]))
# define DIMof(type,member) DIM(((type *)0)->member)
#endif
/* Macros to print the result of a test. */ /* Macros to print the result of a test. */
#define pass() do { ; } while(0) #define pass() do { ; } while(0)
#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ #define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\