diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h index 9c26c09e6..b27b8e6fb 100644 --- a/dirmngr/dirmngr.h +++ b/dirmngr/dirmngr.h @@ -50,6 +50,7 @@ struct ldap_server_s char *user; char *pass; char *base; + unsigned int use_ldaps:1; }; typedef struct ldap_server_s *ldap_server_t; diff --git a/dirmngr/dirmngr_ldap.c b/dirmngr/dirmngr_ldap.c index dd7e4bda5..72d88b9be 100644 --- a/dirmngr/dirmngr_ldap.c +++ b/dirmngr/dirmngr_ldap.c @@ -123,6 +123,7 @@ enum oDN, oFilter, oAttr, + oTls, oOnlySearchTimeout, oLogWithPID @@ -138,6 +139,7 @@ static ARGPARSE_OPTS opts[] = { " a record oriented format")}, { oProxy, "proxy", 2, N_("|NAME|ignore host part and connect through NAME")}, + { oTls, "tls", 0, N_("force a TLS connection")}, { oHost, "host", 2, N_("|NAME|connect to host NAME")}, { oPort, "port", 1, N_("|N|connect to port N")}, { oUser, "user", 2, N_("|NAME|use user NAME for authentication")}, @@ -163,6 +165,7 @@ struct my_opt_s my_ldap_timeval_t timeout;/* Timeout for the LDAP search functions. */ unsigned int alarm_timeout; /* And for the alarm based timeout. */ int multi; + int force_tls; estream_t outstream; /* Send output to this stream. */ @@ -287,6 +290,7 @@ ldap_wrapper_main (char **argv, estream_t outstream) myopt->pass = getenv ("DIRMNGR_LDAP_PASS"); break; case oProxy: myopt->proxy = pargs.r.ret_str; break; + case oTls: myopt->force_tls = 1; break; case oHost: myopt->host = pargs.r.ret_str; break; case oPort: myopt->port = pargs.r.ret_int; break; case oDN: myopt->dn = pargs.r.ret_str; break; @@ -622,12 +626,19 @@ fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp) attrs[1] = NULL; attr = attrs[0]; - if (!port) + if (!port && myopt->force_tls) + 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); + if (myopt->force_tls) + log_info ("forcing tls\n"); + else + log_info ("not forcing tls\n"); + if (myopt->user) log_info (_(" user '%s'\n"), myopt->user); if (myopt->pass) @@ -665,17 +676,48 @@ fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp) && ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1]) log_info (_("WARNING: using first attribute only\n")); - set_timeout (myopt); - npth_unprotect (); - ld = my_ldap_init (host, port); - npth_protect (); - if (!ld) + + if (myopt->force_tls + || (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))) { - log_error (_("LDAP init to '%s:%d' failed: %s\n"), - host, port, strerror (errno)); - return -1; + 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; + } + ret = ldap_initialize (&ld, uri); + 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 = my_ldap_init (host, port); + npth_protect (); + if (!ld) + { + log_error (_("LDAP init to '%s:%d' failed: %s\n"), + host, port, strerror (errno)); + return -1; + } + } + npth_unprotect (); /* Fixme: Can we use MYOPT->user or is it shared with other theeads?. */ ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass); diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c index a04bb97a2..ad6b0889b 100644 --- a/dirmngr/ldap.c +++ b/dirmngr/ldap.c @@ -525,7 +525,7 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context, int argc = 0; int argc_malloced = 0; char portbuf[30], timeoutbuf[30]; - + int use_ldaps = 0; *context = NULL; @@ -554,7 +554,7 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context, goto leave; } base = server->base; - + use_ldaps = server->use_ldaps; } else /* Use a default server. */ return gpg_error (GPG_ERR_NOT_IMPLEMENTED); @@ -587,6 +587,8 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context, argv[argc++] = "--proxy"; argv[argc++] = proxy; } + if (use_ldaps) + argv[argc++] = "--tls"; if (host) { argv[argc++] = "--host"; diff --git a/dirmngr/ldapserver.c b/dirmngr/ldapserver.c index 913e94f16..20a2bb18f 100644 --- a/dirmngr/ldapserver.c +++ b/dirmngr/ldapserver.c @@ -55,6 +55,7 @@ ldapserver_list_free (ldap_server_t servers) 3. field: Username 4. field: Password 5. field: Base DN + 6. field: Flags FILENAME and LINENO are used for diagnostic purposes only. */ @@ -64,9 +65,11 @@ ldapserver_parse_one (char *line, { char *p; char *endp; + const char *s; ldap_server_t server; int fieldno; int fail = 0; + int i; /* Parse the colon separated fields. */ server = xcalloc (1, sizeof *server); @@ -115,6 +118,32 @@ ldapserver_parse_one (char *line, server->base = xstrdup (p); break; + case 6: + { + char **flags = NULL; + + flags = strtokenize (p, ","); + if (!flags) + log_fatal ("strtokenize failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + + for (i=0; (s = flags[i]); i++) + { + if (!*s) + ; + else if (!ascii_strcasecmp (s, "ldaps")) + server->use_ldaps = 1; + else if (!ascii_strcasecmp (s, "ldap")) + server->use_ldaps = 0; + else + log_info (_("%s:%u: ignoring unknown flag '%s'\n"), + filename, lineno, s); + } + + xfree (flags); + } + break; + default: /* (We silently ignore extra fields.) */ break; diff --git a/doc/dirmngr.texi b/doc/dirmngr.texi index c841de77e..a6fafbb14 100644 --- a/doc/dirmngr.texi +++ b/doc/dirmngr.texi @@ -406,10 +406,14 @@ client for its session. The default value for @var{file} is This server list file contains one LDAP server per line in the format -@sc{hostname:port:username:password:base_dn} +@sc{hostname:port:username:password:base_dn:flags} Lines starting with a @samp{#} are comments. +The only defined flag is @code{ldaps} to specify that a TLS +connections shall be used. Flags are comma delimited; unknown flags +are ignored. + Note that as usual all strings entered are expected to be UTF-8 encoded. Obviously this will lead to problems if the password has originally been encoded as Latin-1. There is no other solution here than to put such a diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index 0745f8626..130b217a5 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -356,13 +356,27 @@ Note that the @command{dirmngr} can in addition be configured with a default list of LDAP servers to be used after those configured with this option. The syntax of @var{string} is: -@sc{hostname:port:username:password:base_dn} +@sc{hostname:port:username:password:base_dn:flags} + +The only defined flag is @code{ldaps} to specify that a TLS +connections shall be used. Flags are comma delimited; unknown flags +are ignored. Note that all parts of that string are expected to be UTF-8 encoded. This may lead to problems if the @sc{password} has originally been -encoded as Latin-1; in such a case better configure this LDAP server +encoded as Latin-1; in such a case better configure tsuch an LDAP server using the global configuration of @command{dirmngr}. +Here is an example which uses the default port, no username, no +password, and requests a TLS connection: + +@c man:.RS +@example +--keyserver ldap.pca.dfn.de::::o=DFN-Verein,c=DE:ldaps +@end example +@c man:.RE + + @item --policy-file @var{filename} @opindex policy-file Change the default name of the policy file to @var{filename}. The diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index bff7dd652..f3fe1d663 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -223,8 +223,9 @@ prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err) char *pass = server->pass ? server->pass : ""; char *base = server->base ? server->base : ""; - snprintf (line, DIM (line), "LDAPSERVER %s:%i:%s:%s:%s", - server->host, server->port, user, pass, base); + snprintf (line, DIM (line), "LDAPSERVER %s:%i:%s:%s:%s:%s", + server->host, server->port, user, pass, base, + server->use_ldaps? "ldaps":""); assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); /* The code below is not required because we don't return an error. */ diff --git a/sm/gpgsm.c b/sm/gpgsm.c index f5837079d..2cd3b0c4f 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -817,9 +817,17 @@ parse_keyserver_line (char *line, { char *p; char *endp; + const char *s; struct keyserver_spec *server; int fieldno; int fail = 0; + int i; + + if (!filename) + { + filename = "[cmd]"; + lineno = 0; + } /* Parse the colon separated fields. */ server = xcalloc (1, sizeof *server); @@ -833,7 +841,7 @@ parse_keyserver_line (char *line, { case 1: if (*p) - server->host = xstrdup (p); + server->host = xstrdup (p); else { log_error (_("%s:%u: no hostname given\n"), @@ -868,6 +876,32 @@ parse_keyserver_line (char *line, server->base = xstrdup (p); break; + case 6: + { + char **flags = NULL; + + flags = strtokenize (p, ","); + if (!flags) + log_fatal ("strtokenize failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + + for (i=0; (s = flags[i]); i++) + { + if (!*s) + ; + else if (!ascii_strcasecmp (s, "ldaps")) + server->use_ldaps = 1; + else if (!ascii_strcasecmp (s, "ldap")) + server->use_ldaps = 0; + else + log_info (_("%s:%u: ignoring unknown flag '%s'\n"), + filename, lineno, s); + } + + xfree (flags); + } + break; + default: /* (We silently ignore extra fields.) */ break; diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 65fff853a..43793dcdf 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -48,6 +48,7 @@ struct keyserver_spec char *user; char *pass; char *base; + unsigned int use_ldaps:1; };