From eb3a629154de10a5414a5d2c2b9941ef8bf1eeaf Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 26 May 2021 14:48:27 +0200 Subject: [PATCH] dirmngr: Allow for non-URL specified ldap keyservers. * dirmngr/server.c (cmd_ldapserver): Strip an optional prefix. (make_keyserver_item): Handle non-URL ldap specs. * dirmngr/dirmngr.h (struct ldap_server_s): Add fields starttls, ldap_over_tls, and ntds. * dirmngr/ldapserver.c (ldapserver_parse_one): Add for an empty host string. Improve error messages for the non-file case. Support flags. * dirmngr/ks-action.c (ks_action_help): Handle non-URL ldap specs. (ks_action_search, ks_action_get, ks_action_put): Ditto. * dirmngr/ks-engine-ldap.c: Include ldapserver.h. (ks_ldap_help): Handle non-URL ldap specs. (my_ldap_connect): Add args r_host and r_use_tls. Rewrite to support URLs and non-URL specified keyservers. (ks_ldap_get): Adjust for changes in my_ldap_connect. (ks_ldap_search): Ditto. (ks_ldap_put): Ditto. -- The idea here is to unify our use of URLS or colon delimited ldap keyserver specification. The requirement for percent escaping, for example the bindname in an URLs, is cumbersome and prone to errors. This we allow our classic colon delimited format as an alternative. That format makes it also easy to specify flags to tell dirmngr whether to use starttls or ldap-over-tls. The code is nearly 100% compatible to existing specification. There is one ambiguity if the hostname for CRL/X509 searches is just "ldap"; this can be solved by prefixing it with "ldap:" (already implemented in gpgsm). GnuPG-bug-id: 5405, 5452 Ported-from: 2b4cddf9086faaf5b35f64a7db97a5ce8804c05b --- dirmngr/dirmngr.h | 4 +- dirmngr/ks-action.c | 52 +++++-- dirmngr/ks-engine-ldap.c | 292 +++++++++++++++++++++++++-------------- dirmngr/ldap.c | 2 +- dirmngr/ldapserver.c | 122 +++++++++++----- dirmngr/ldapserver.h | 12 -- dirmngr/server.c | 49 ++++++- 7 files changed, 366 insertions(+), 167 deletions(-) diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h index 92d9d4b6a..498a3d7b1 100644 --- a/dirmngr/dirmngr.h +++ b/dirmngr/dirmngr.h @@ -50,7 +50,9 @@ struct ldap_server_s char *user; char *pass; char *base; - unsigned int use_ldaps:1; + unsigned int starttls:1; /* Use STARTTLS. */ + unsigned int ldap_over_tls:1; /* Use LDAP over an TLS tunnel */ + unsigned int ntds:1; /* Use Active Directory authentication. */ }; typedef struct ldap_server_s *ldap_server_t; diff --git a/dirmngr/ks-action.c b/dirmngr/ks-action.c index 5c097754e..57cf04a7e 100644 --- a/dirmngr/ks-action.c +++ b/dirmngr/ks-action.c @@ -67,6 +67,8 @@ ks_action_help (ctrl_t ctrl, const char *url) { gpg_error_t err; parsed_uri_t parsed_uri; /* The broken down URI. */ + char *tmpstr; + const char *s; if (!url || !*url) { @@ -76,7 +78,34 @@ ks_action_help (ctrl_t ctrl, const char *url) else { #if USE_LDAP - if (ldap_uri_p (url)) + if (!strncmp (url, "ldap:", 5) && !(url[5] == '/' && url[6] == '/')) + { + /* Special ldap scheme given. This differs from a valid + * ldap scheme in that no double slash follows. Use + * http_parse_uri to put it as opaque value into parsed_uri. */ + tmpstr = strconcat ("opaque:", url+5, NULL); + if (!tmpstr) + err = gpg_error_from_syserror (); + else + { + err = http_parse_uri (&parsed_uri, tmpstr, 0); + xfree (tmpstr); + } + } + else if ((s=strchr (url, ':')) && !(s[1] == '/' && s[2] == '/')) + { + /* No scheme given. Use http_parse_uri to put the string as + * opaque value into parsed_uri. */ + tmpstr = strconcat ("opaque:", url, NULL); + if (!tmpstr) + err = gpg_error_from_syserror (); + else + { + err = http_parse_uri (&parsed_uri, tmpstr, 0); + xfree (tmpstr); + } + } + else if (ldap_uri_p (url)) err = ldap_parse_uri (&parsed_uri, url); else #endif @@ -164,9 +193,10 @@ ks_action_search (ctrl_t ctrl, uri_item_t keyservers, int is_ldap = 0; unsigned int http_status = 0; #if USE_LDAP - is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0 - || strcmp (uri->parsed_uri->scheme, "ldaps") == 0 - || strcmp (uri->parsed_uri->scheme, "ldapi") == 0); + is_ldap = (!strcmp (uri->parsed_uri->scheme, "ldap") + || !strcmp (uri->parsed_uri->scheme, "ldaps") + || !strcmp (uri->parsed_uri->scheme, "ldapi") + || uri->parsed_uri->opaque); #endif if (is_http || is_ldap) { @@ -242,9 +272,10 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers, is_hkp_s = is_http_s = 0; #if USE_LDAP - is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0 - || strcmp (uri->parsed_uri->scheme, "ldaps") == 0 - || strcmp (uri->parsed_uri->scheme, "ldapi") == 0); + is_ldap = (!strcmp (uri->parsed_uri->scheme, "ldap") + || !strcmp (uri->parsed_uri->scheme, "ldaps") + || !strcmp (uri->parsed_uri->scheme, "ldapi") + || uri->parsed_uri->opaque); #endif if (is_hkp_s || is_http_s || is_ldap) @@ -382,9 +413,10 @@ ks_action_put (ctrl_t ctrl, uri_item_t keyservers, int is_ldap = 0; #if USE_LDAP - is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0 - || strcmp (uri->parsed_uri->scheme, "ldaps") == 0 - || strcmp (uri->parsed_uri->scheme, "ldapi") == 0); + is_ldap = (!strcmp (uri->parsed_uri->scheme, "ldap") + || !strcmp (uri->parsed_uri->scheme, "ldaps") + || !strcmp (uri->parsed_uri->scheme, "ldapi") + || uri->parsed_uri->opaque); #endif if (is_http || is_ldap) diff --git a/dirmngr/ks-engine-ldap.c b/dirmngr/ks-engine-ldap.c index b7ff0633c..1b5c2ca32 100644 --- a/dirmngr/ks-engine-ldap.c +++ b/dirmngr/ks-engine-ldap.c @@ -50,6 +50,7 @@ #include "../common/mbox-util.h" #include "ks-engine.h" #include "ldap-parse-uri.h" +#include "ldapserver.h" /* Flags with infos from the connected server. */ @@ -312,11 +313,11 @@ ks_ldap_help (ctrl_t ctrl, parsed_uri_t uri) { const char data[] = "Handler for LDAP URLs:\n" - " ldap://host:port/[BASEDN]???[bindname=BINDNAME,password=PASSWORD]\n" + " ldap://HOST:PORT/[BASEDN]???[bindname=BINDNAME,password=PASSWORD]\n" "\n" "Note: basedn, bindname and password need to be percent escaped. In\n" "particular, spaces need to be replaced with %20 and commas with %2c.\n" - "bindname will typically be of the form:\n" + "Thus bindname will typically be of the form:\n" "\n" " uid=user%2cou=PGP%20Users%2cdc=EXAMPLE%2cdc=ORG\n" "\n" @@ -324,12 +325,19 @@ ks_ldap_help (ctrl_t ctrl, parsed_uri_t uri) "then the server's certificate will be checked. If it is not valid, any\n" "operation will be aborted. Note that ldaps means LDAP with STARTTLS\n" "\n" + "As an alternative to an URL a string in this form may be used:\n" + "\n" + " HOST:PORT:BINDNAME:PASSWORD:BASEDN:FLAGS:\n" + "\n" + "The use of the percent sign or a colon in one of the string values is\n" + "currently not supported.\n" + "\n" "Supported methods: search, get, put\n"; gpg_error_t err; if(!uri) err = ks_print_help (ctrl, " ldap"); - else if (uri->is_ldap) + else if (uri->is_ldap || uri->opaque) err = ks_print_help (ctrl, data); else err = 0; @@ -491,96 +499,164 @@ keyspec_to_ldap_filter (const char *keyspec, char **filter, int only_exact, If no LDAP error occurred, you still need to check that *basednp is valid. If it is NULL, then the server does not appear to be an OpenPGP Keyserver. */ -static int +static gpg_error_t my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, - char **basednp, unsigned int *r_serverinfo) + char **r_basedn, char **r_host, int *r_use_tls, + unsigned int *r_serverinfo) { - int err = 0; + gpg_error_t err = 0; + int lerr; + ldap_server_t server = NULL; LDAP *ldap_conn = NULL; - char *user = uri->auth; - struct uri_tuple_s *password_param; - char *password; char *basedn = NULL; + char *host = NULL; /* Host to use. */ + int port; /* Port to use. */ + int use_tls; /* 1 = starttls, 2 = ldap-over-tls */ + int use_ntds; /* Use Active Directory authentication. */ + const char *bindname; + const char *password; + const char *basedn_arg; + char *tmpstr; + if (r_basedn) + *r_basedn = NULL; + if (r_host) + *r_host = NULL; + if (r_use_tls) + *r_use_tls = 0; *r_serverinfo = 0; - password_param = uri_query_lookup (uri, "password"); - password = password_param ? password_param->value : NULL; + if (uri->opaque) + { + server = ldapserver_parse_one (uri->path, NULL, 0); + if (!server) + return gpg_error (GPG_ERR_LDAP_OTHER); + host = server->host; + port = server->port; + bindname = server->user; + password = bindname? server->pass : NULL; + basedn_arg = server->base; + use_tls = server->starttls? 1 : server->ldap_over_tls? 2 : 0; + use_ntds = server->ntds; + } + else + { + struct uri_tuple_s *password_param; + + password_param = uri_query_lookup (uri, "password"); + password = password_param ? password_param->value : NULL; + + host = uri->host; + port = uri->port; + bindname = uri->auth; + password = bindname? uri_query_value (uri, "password") : NULL; + basedn_arg = uri->path; + use_tls = uri->use_tls ? 1 : 0; + use_ntds = uri->ad_current; + } + + if (!port) + port = use_tls == 2? 636 : 389; + + + if (host) + { + host = xtrystrdup (host); + if (!host) + { + err = gpg_error_from_syserror (); + goto out; + } + } if (opt.debug) - log_debug ("my_ldap_connect(%s:%d/%s????%s%s%s%s%s%s)\n", - uri->host, uri->port, - uri->path ? uri->path : "", - uri->auth ? "bindname=" : "", - uri->auth ? uri->auth : "", - uri->auth && password ? "," : "", - password ? "password=" : "", - password ? ">not shown<": "", - uri->ad_current? " auth=>current_user<":""); + log_debug ("my_ldap_connect(%s:%d/%s????%s%s%s%s%s)\n", + host, port, + basedn_arg ? basedn_arg : "", + bindname ? "bindname=" : "", + bindname ? bindname : "", + password ? "," : "", + password ? "password=>not_shown<" : "", + use_ntds ? " auth=>current_user<":""); + /* If the uri specifies a secure connection and we don't support TLS, then fail; don't silently revert to an insecure connection. */ - if (uri->use_tls) + if (use_tls) { #ifndef HAVE_LDAP_START_TLS_S - log_error ("Can't use LDAP to connect to the server: no TLS support."); + log_error ("ldap: can't connect to the server: no TLS support."); err = GPG_ERR_LDAP_NOT_SUPPORTED; goto out; #endif } - ldap_conn = ldap_init (uri->host, uri->port); + +#ifdef HAVE_W32_SYSTEM + npth_unprotect (); + ldap_conn = ldap_sslinit (host, port, (use_tls == 2)); + npth_protect (); if (!ldap_conn) { - err = gpg_err_code_from_syserror (); - log_error ("error initializing LDAP for (%s://%s:%d)\n", - uri->scheme, uri->host, uri->port); + lerr = LdapGetLastError (); + err = ldap_err_to_gpg_err (lerr); + log_error ("error initializing LDAP '%s:%d': %s\n", + host, port, ldap_err2string (lerr)); goto out; } +#else /* Unix */ + tmpstr = xtryasprintf ("%s://%s:%d", + use_tls == 2? "ldaps" : "ldap", + host, port); + if (!tmpstr) + { + err = gpg_error_from_syserror (); + goto out; + } + npth_unprotect (); + lerr = ldap_initialize (&ldap_conn, tmpstr); + npth_protect (); + if (lerr || !ldap_conn) + { + err = ldap_err_to_gpg_err (lerr); + log_error ("error initializing LDAP '%s': %s\n", + tmpstr, ldap_err2string (lerr)); + xfree (tmpstr); + goto out; + } + xfree (tmpstr); +#endif /* Unix */ #ifdef HAVE_LDAP_SET_OPTION { int ver = LDAP_VERSION3; - err = ldap_set_option (ldap_conn, LDAP_OPT_PROTOCOL_VERSION, &ver); - if (err != LDAP_SUCCESS) + lerr = ldap_set_option (ldap_conn, LDAP_OPT_PROTOCOL_VERSION, &ver); + if (lerr != LDAP_SUCCESS) { log_error ("ks-ldap: unable to go to LDAP 3: %s\n", - ldap_err2string (err)); + ldap_err2string (lerr)); + err = ldap_err_to_gpg_err (lerr); goto out; } } #endif - /* XXX: It would be nice to have an option to provide the server's - certificate. */ -#if 0 -#if defined(LDAP_OPT_X_TLS_CACERTFILE) && defined(HAVE_LDAP_SET_OPTION) - err = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE, ca_cert_file); - if (err) - { - log_error ("unable to set ca-cert-file to '%s': %s\n", - ca_cert_file, ldap_err2string (err)); - goto out; - } -#endif /* LDAP_OPT_X_TLS_CACERTFILE && HAVE_LDAP_SET_OPTION */ -#endif #ifdef HAVE_LDAP_START_TLS_S - if (uri->use_tls) + if (use_tls == 1) { - /* XXX: We need an option to determine whether to abort if the - certificate is bad or not. Right now we conservatively - default to checking the certificate and aborting. */ #ifndef HAVE_W32_SYSTEM int check_cert = LDAP_OPT_X_TLS_HARD; /* LDAP_OPT_X_TLS_NEVER */ - err = ldap_set_option (ldap_conn, - LDAP_OPT_X_TLS_REQUIRE_CERT, &check_cert); - if (err) + lerr = ldap_set_option (ldap_conn, + LDAP_OPT_X_TLS_REQUIRE_CERT, &check_cert); + if (lerr) { - log_error ("error setting TLS option on LDAP connection\n"); + log_error ("ldap: error setting an TLS option: %s\n", + ldap_err2string (lerr)); + err = ldap_err_to_gpg_err (lerr); goto out; } #else @@ -592,7 +668,7 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, #endif npth_unprotect (); - err = ldap_start_tls_s (ldap_conn, + lerr = ldap_start_tls_s (ldap_conn, #ifdef HAVE_W32_SYSTEM /* ServerReturnValue, result */ NULL, NULL, @@ -600,26 +676,29 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, /* ServerControls, ClientControls */ NULL, NULL); npth_protect (); - if (err) + if (lerr) { - log_error ("error connecting to LDAP server with TLS\n"); + log_error ("ldap: error switching to STARTTLS mode: %s\n", + ldap_err2string (lerr)); + err = ldap_err_to_gpg_err (lerr); goto out; } } #endif - if (uri->ad_current) + if (use_ntds) { if (opt.debug) - log_debug ("LDAP bind to current user via AD\n"); + log_debug ("ldap: binding to current user via AD\n"); #ifdef HAVE_W32_SYSTEM npth_unprotect (); - err = ldap_bind_s (ldap_conn, NULL, NULL, LDAP_AUTH_NEGOTIATE); + lerr = ldap_bind_s (ldap_conn, NULL, NULL, LDAP_AUTH_NEGOTIATE); npth_protect (); - if (err != LDAP_SUCCESS) + if (lerr != LDAP_SUCCESS) { log_error ("error binding to LDAP via AD: %s\n", - ldap_err2string (err)); + ldap_err2string (lerr)); + err = ldap_err_to_gpg_err (lerr); goto out; } #else @@ -627,18 +706,19 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, goto out; #endif } - else if (uri->auth) + else if (bindname) { if (opt.debug) - log_debug ("LDAP bind to %s, password %s\n", - user, password ? ">not shown<" : ">none<"); + log_debug ("LDAP bind to '%s', password '%s'\n", + bindname, password ? ">not_shown<" : ">none<"); npth_unprotect (); - err = ldap_simple_bind_s (ldap_conn, user, password); + lerr = ldap_simple_bind_s (ldap_conn, bindname, password); npth_protect (); - if (err != LDAP_SUCCESS) + if (lerr != LDAP_SUCCESS) { - log_error ("error binding to LDAP: %s\n", ldap_err2string (err)); + log_error ("error binding to LDAP: %s\n", ldap_err2string (lerr)); + err = ldap_err_to_gpg_err (lerr); goto out; } } @@ -647,13 +727,16 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, /* By default we don't bind as there is usually no need to. */ } - if (uri->path && *uri->path) + if (basedn_arg && *basedn_arg) { - /* User specified base DN. */ - basedn = xstrdup (uri->path); - - /* If the user specifies a base DN, then we know the server is a + /* User specified base DN. In this case we know the server is a * real LDAP server. */ + basedn = xtrystrdup (basedn_arg); + if (!basedn) + { + err = gpg_error_from_syserror (); + goto out; + } *r_serverinfo |= SERVERINFO_REALLDAP; } else @@ -662,11 +745,11 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, char *attr[] = { "namingContexts", NULL }; npth_unprotect (); - err = ldap_search_s (ldap_conn, "", LDAP_SCOPE_BASE, + lerr = ldap_search_s (ldap_conn, "", LDAP_SCOPE_BASE, "(objectClass=*)", attr, 0, &res); npth_protect (); - if (err == LDAP_SUCCESS) + if (lerr == LDAP_SUCCESS) { char **context; @@ -685,7 +768,7 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, *r_serverinfo |= SERVERINFO_REALLDAP; - for (i = 0; context[i] && ! basedn; i++) + for (i = 0; context[i] && !basedn; i++) { char **vals; LDAPMessage *si_res; @@ -695,13 +778,13 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, char *object = xasprintf ("cn=pgpServerInfo,%s", context[i]); npth_unprotect (); - err = ldap_search_s (ldap_conn, object, LDAP_SCOPE_BASE, + lerr = ldap_search_s (ldap_conn, object, LDAP_SCOPE_BASE, "(objectClass=*)", attr2, 0, &si_res); npth_protect (); xfree (object); } - if (err == LDAP_SUCCESS) + if (lerr == LDAP_SUCCESS) { vals = ldap_get_values (ldap_conn, si_res, "pgpBaseKeySpaceDN"); @@ -754,7 +837,7 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, ldap_value_free (context); } } - else + else /* ldap_search failed. */ { /* We don't have an answer yet, which means the server might be a PGP.com keyserver. */ @@ -764,10 +847,10 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, char *attr2[] = { "pgpBaseKeySpaceDN", "version", "software", NULL }; npth_unprotect (); - err = ldap_search_s (ldap_conn, "cn=pgpServerInfo", LDAP_SCOPE_BASE, + lerr = ldap_search_s (ldap_conn, "cn=pgpServerInfo", LDAP_SCOPE_BASE, "(objectClass=*)", attr2, 0, &si_res); npth_protect (); - if (err == LDAP_SUCCESS) + if (lerr == LDAP_SUCCESS) { /* For the PGP LDAP keyserver, this is always * "OU=ACTIVE,O=PGP KEYSPACE,C=US", but it might not be @@ -827,23 +910,27 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, (*r_serverinfo & SERVERINFO_PGPKEYV2)? "pgpKeyV2":"pgpKey"); } - if (err) - xfree (basedn); - else - { - if (basednp) - *basednp = basedn; - else - xfree (basedn); - } + ldapserver_list_free (server); if (err) { + xfree (basedn); if (ldap_conn) ldap_unbind (ldap_conn); } else - *ldap_connp = ldap_conn; + { + if (r_basedn) + *r_basedn = basedn; + else + xfree (basedn); + if (r_host) + *r_host = host; + else + xfree (host); + + *ldap_connp = ldap_conn; + } return err; } @@ -944,6 +1031,8 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, gpg_error_t err = 0; int ldap_err; unsigned int serverinfo; + char *host = NULL; + int use_tls; char *filter = NULL; LDAP *ldap_conn = NULL; char *basedn = NULL; @@ -960,12 +1049,11 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, } /* Make sure we are talking to an OpenPGP LDAP server. */ - ldap_err = my_ldap_connect (uri, &ldap_conn, &basedn, &serverinfo); - if (ldap_err || !basedn) + err = my_ldap_connect (uri, &ldap_conn, + &basedn, &host, &use_tls, &serverinfo); + if (err || !basedn) { - if (ldap_err) - err = ldap_err_to_gpg_err (ldap_err); - else + if (!err) err = gpg_error (GPG_ERR_GENERAL); goto out; } @@ -1101,7 +1189,8 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, if (!err && anykey) err = dirmngr_status_printf (ctrl, "SOURCE", "%s://%s", - uri->scheme, uri->host? uri->host:""); + use_tls? "ldaps" : "ldap", + host? host:""); } } @@ -1123,6 +1212,7 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, } xfree (basedn); + xfree (host); if (ldap_conn) ldap_unbind (ldap_conn); @@ -1157,12 +1247,10 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, } /* Make sure we are talking to an OpenPGP LDAP server. */ - ldap_err = my_ldap_connect (uri, &ldap_conn, &basedn, &serverinfo); - if (ldap_err || !basedn) + err = my_ldap_connect (uri, &ldap_conn, &basedn, NULL, NULL, &serverinfo); + if (err || !basedn) { - if (ldap_err) - err = ldap_err_to_gpg_err (ldap_err); - else + if (!err) err = GPG_ERR_GENERAL; goto out; } @@ -2050,12 +2138,10 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, return gpg_error (GPG_ERR_NOT_SUPPORTED); } - ldap_err = my_ldap_connect (uri, &ldap_conn, &basedn, &serverinfo); - if (ldap_err || !basedn) + err = my_ldap_connect (uri, &ldap_conn, &basedn, NULL, NULL, &serverinfo); + if (err || !basedn) { - if (ldap_err) - err = ldap_err_to_gpg_err (ldap_err); - else + if (!err) err = GPG_ERR_GENERAL; goto out; } diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c index 96abc89d0..42efcfd49 100644 --- a/dirmngr/ldap.c +++ b/dirmngr/ldap.c @@ -560,7 +560,7 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *r_context, goto leave; } base = server->base; - use_ldaps = server->use_ldaps; + use_ldaps = server->ldap_over_tls; } else /* Use a default server. */ { diff --git a/dirmngr/ldapserver.c b/dirmngr/ldapserver.c index 20a2bb18f..4ef28431c 100644 --- a/dirmngr/ldapserver.c +++ b/dirmngr/ldapserver.c @@ -47,18 +47,26 @@ ldapserver_list_free (ldap_server_t servers) /* Parse a single LDAP server configuration line. Returns the server - or NULL in case of errors. The configuration line is assumed to be - colon separated with these fields: - - 1. field: Hostname - 2. field: Portnumber - 3. field: Username - 4. field: Password - 5. field: Base DN - 6. field: Flags - - FILENAME and LINENO are used for diagnostic purposes only. -*/ + * or NULL in case of errors. The configuration line is assumed to be + * colon separated with these fields: + * + * 1. field: Hostname + * 2. field: Portnumber + * 3. field: Username + * 4. field: Password + * 5. field: Base DN + * 6. field: Flags + * + * Flags are: + * + * starttls := Use STARTTLS with a default port of 389 + * ldaptls := Tunnel LDAP trough a TLS tunnel with default port 636 + * plain := Switch to plain unsecured LDAP. + * (The last of these 3 flags is the effective one) + * ntds := Use Active Directory authentication + * + * FILENAME and LINENO are used for diagnostic purposes only. + */ ldap_server_t ldapserver_parse_one (char *line, const char *filename, unsigned int lineno) @@ -72,7 +80,13 @@ ldapserver_parse_one (char *line, int i; /* Parse the colon separated fields. */ - server = xcalloc (1, sizeof *server); + server = xtrycalloc (1, sizeof *server); + if (!server) + { + fail = 1; + goto leave; + } + for (fieldno = 1, p = line; p; p = endp, fieldno++ ) { endp = strchr (p, ':'); @@ -82,14 +96,9 @@ ldapserver_parse_one (char *line, switch (fieldno) { case 1: - if (*p) - server->host = xstrdup (p); - else - { - log_error (_("%s:%u: no hostname given\n"), - filename, lineno); - fail = 1; - } + server->host = xtrystrdup (p); + if (!server->host) + fail = 1; break; case 2: @@ -98,24 +107,36 @@ ldapserver_parse_one (char *line, break; case 3: - if (*p) - server->user = xstrdup (p); + server->user = xtrystrdup (p); + if (!server->user) + fail = 1; break; case 4: if (*p && !server->user) { - log_error (_("%s:%u: password given without user\n"), - filename, lineno); + if (filename) + log_error (_("%s:%u: password given without user\n"), + filename, lineno); + else + log_error ("ldap: password given without user ('%s')\n", line); fail = 1; } else if (*p) - server->pass = xstrdup (p); + { + server->pass = xtrystrdup (p); + if (!server->pass) + fail = 1; + } break; case 5: if (*p) - server->base = xstrdup (p); + { + server->base = xtrystrdup (p); + if (!server->base) + fail = 1;; + } break; case 6: @@ -124,20 +145,45 @@ ldapserver_parse_one (char *line, flags = strtokenize (p, ","); if (!flags) - log_fatal ("strtokenize failed: %s\n", - gpg_strerror (gpg_error_from_syserror ())); + { + log_error ("strtokenize failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + fail = 1; + break; + } 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 if (!ascii_strcasecmp (s, "starttls")) + { + server->starttls = 1; + server->ldap_over_tls = 0; + } + else if (!ascii_strcasecmp (s, "ldaptls")) + { + server->starttls = 0; + server->ldap_over_tls = 1; + } + else if (!ascii_strcasecmp (s, "plain")) + { + server->starttls = 0; + server->ldap_over_tls = 0; + } + else if (!ascii_strcasecmp (s, "ntds")) + { + server->ntds = 1; + } else - log_info (_("%s:%u: ignoring unknown flag '%s'\n"), - filename, lineno, s); + { + if (filename) + log_info (_("%s:%u: ignoring unknown flag '%s'\n"), + filename, lineno, s); + else + log_info ("ldap: unknown flag '%s' ignored in (%s)\n", + s, line); + } } xfree (flags); @@ -150,9 +196,13 @@ ldapserver_parse_one (char *line, } } + leave: if (fail) { - log_info (_("%s:%u: skipping this line\n"), filename, lineno); + if (filename) + log_info (_("%s:%u: skipping this line\n"), filename, lineno); + else + log_info ("ldap: error in server spec ('%s')\n", line); ldapserver_list_free (server); server = NULL; } diff --git a/dirmngr/ldapserver.h b/dirmngr/ldapserver.h index 1b20508db..c62f5a939 100644 --- a/dirmngr/ldapserver.h +++ b/dirmngr/ldapserver.h @@ -26,18 +26,6 @@ void ldapserver_list_free (ldap_server_t servers); -/* Parse a single LDAP server configuration line. Returns the server - or NULL in case of errors. The configuration line is assumed to be - colon separated with these fields: - - 1. field: Hostname - 2. field: Portnumber - 3. field: Username - 4. field: Password - 5. field: Base DN - - FILENAME and LINENO are used for diagnostic purposes only. -*/ ldap_server_t ldapserver_parse_one (char *line, const char *filename, unsigned int lineno); diff --git a/dirmngr/server.c b/dirmngr/server.c index a35402271..105275845 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -1143,7 +1143,8 @@ static const char hlp_ldapserver[] = "LDAPSERVER \n" "\n" "Add a new LDAP server to the list of configured LDAP servers.\n" - "DATA is in the same format as expected in the configure file."; + "DATA is in the same format as expected in the configure file.\n" + "An optional prefix \"ldap:\" is allowed."; static gpg_error_t cmd_ldapserver (assuan_context_t ctx, char *line) { @@ -1157,7 +1158,11 @@ cmd_ldapserver (assuan_context_t ctx, char *line) if (*line == '\0') return leave_cmd (ctx, PARM_ERROR (_("ldapserver missing"))); - server = ldapserver_parse_one (line, "", 0); + /* Skip an "ldap:" prefix unless it is a valid ldap url. */ + if (!strncmp (line, "ldap:", 5) && !(line[5] == '/' && line[6] == '/')) + line += 5; + + server = ldapserver_parse_one (line, NULL, 0); if (! server) return leave_cmd (ctx, gpg_error (GPG_ERR_INV_ARG)); @@ -2065,6 +2070,7 @@ make_keyserver_item (const char *uri, uri_item_t *r_item) { gpg_error_t err; uri_item_t item; + const char *s; *r_item = NULL; @@ -2108,8 +2114,43 @@ make_keyserver_item (const char *uri, uri_item_t *r_item) strcpy (item->uri, uri); #if USE_LDAP - if (ldap_uri_p (item->uri)) - err = ldap_parse_uri (&item->parsed_uri, uri); + if (!strncmp (uri, "ldap:", 5) && !(uri[5] == '/' && uri[6] == '/')) + { + char *tmpstr; + /* Special ldap scheme given. This differs from a valid ldap + * scheme in that no double slash follows.. Use http_parse_uri + * to put it as opaque value into parsed_uri. */ + tmpstr = strconcat ("opaque:", uri+5, NULL); + if (!tmpstr) + err = gpg_error_from_syserror (); + else + { + log_debug ("tmpstr='%s'\n", tmpstr); + err = http_parse_uri (&item->parsed_uri, tmpstr, 0); + xfree (tmpstr); + } + } + else if ((s=strchr (uri, ':')) && !(s[1] == '/' && s[2] == '/')) + { + char *tmpstr; + /* No valid scheme given. Use http_parse_uri to put the string + * as opaque value into parsed_uri. */ + tmpstr = strconcat ("opaque:", uri, NULL); + if (!tmpstr) + err = gpg_error_from_syserror (); + else + { + log_debug ("tmpstr2='%s'\n", tmpstr); + err = http_parse_uri (&item->parsed_uri, tmpstr, 0); + xfree (tmpstr); + } + } + else if (ldap_uri_p (uri)) + { + /* Fixme: We should get rid of that parser and replace it with + * our generic (http) URI parser. */ + err = ldap_parse_uri (&item->parsed_uri, uri); + } else #endif {