diff options
author | Werner Koch <[email protected]> | 2021-05-26 12:48:27 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2021-06-16 10:25:13 +0000 |
commit | eb3a629154de10a5414a5d2c2b9941ef8bf1eeaf (patch) | |
tree | e483f1cb8633c7459dfe69d8e5ad66526e0d8eb6 /dirmngr/ks-engine-ldap.c | |
parent | gpg,sm: Simplify keyserver spec parsing. (diff) | |
download | gnupg-eb3a629154de10a5414a5d2c2b9941ef8bf1eeaf.tar.gz gnupg-eb3a629154de10a5414a5d2c2b9941ef8bf1eeaf.zip |
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
Diffstat (limited to 'dirmngr/ks-engine-ldap.c')
-rw-r--r-- | dirmngr/ks-engine-ldap.c | 294 |
1 files changed, 190 insertions, 104 deletions
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; } |