From 1ac3981cfc9023246b53fc838d92138e8e1878d3 Mon Sep 17 00:00:00 2001 From: Joey Berkovitz Date: Mon, 4 Jul 2022 20:43:21 -0400 Subject: [PATCH gnupg] dirmngr: Interrogate LDAP server when base DN specified * dirmngr/ks-engine-ldap.c (interrogate_ldap_dn): refactored out of my_ldap_connect (my_ldap_connect): interrogate LDAP server when basedn specified -- This patch implements the first proposed solution in bug 6047. Using the old logic, if a base DN is specified in dirmngr, then dirmngr would force usage of schema version 1 instead of checking if the LDAP server is capable of version 2. With the new functionality, dirmngr will first check if the provided base DN has a `cn=PGPServerInfo` as a direct descendant. If the PGPServerInfo entry is not found immediately, it then does a search again in the parent DN. The second search is useful for backwards compatibility since any users that had specified a base DN likely were pointing directly to the pgp keyspace DN, which is commonly a sibling of PGPServerInfo. Note that dirmngr does not seem to update/replace LDAP entries, so if a user wants to update their keys from schema V1 to V2, they will need to manually delete the entry before re-sending the keys. GnuPG-bug-id: 6047 Signed-off-by: Joey Berkovitz --- dirmngr/ks-engine-ldap.c | 163 +++++++++++++++++++++++---------------- 1 file changed, 97 insertions(+), 66 deletions(-) diff --git a/dirmngr/ks-engine-ldap.c b/dirmngr/ks-engine-ldap.c index dd796a326..192749550 100644 --- a/dirmngr/ks-engine-ldap.c +++ b/dirmngr/ks-engine-ldap.c @@ -281,6 +281,89 @@ keyspec_to_ldap_filter (const char *keyspec, char **filter, int only_exact, return 0; } +/* Attempt to interrogate an LDAP server to determine connection capabilities + Safe to use same string for basedn_search and r_basedn + Adds flags to server_info + Sets basedn to pgpKeySpaceDN if found, signifying end of search + Returns true if any information is found */ +static int +interrogate_ldap_dn (LDAP *ldap_conn, char *basedn_search, + unsigned int *r_serverinfo, char **r_basedn) +{ + int lerr; + char **vals; + LDAPMessage *si_res; + int is_gnupg = 0; + int res = 0; + char *attr2[] = + { "pgpBaseKeySpaceDN", "pgpVersion", "pgpSoftware", NULL }; + + { + char *object = xasprintf ("cn=pgpServerInfo,%s", + basedn_search); + npth_unprotect (); + lerr = ldap_search_s (ldap_conn, object, LDAP_SCOPE_BASE, + "(objectClass=*)", attr2, 0, &si_res); + npth_protect (); + xfree (object); + } + + if (lerr == LDAP_SUCCESS) + { + // We found a pgpServerInfo entry, let the caller know + if(ldap_count_entries) + { + res = 1; + } + + vals = ldap_get_values (ldap_conn, si_res, + "pgpBaseKeySpaceDN"); + if (vals && vals[0] && r_basedn) + { + *r_basedn = xtrystrdup (vals[0]); + } + my_ldap_value_free (vals); + + vals = ldap_get_values (ldap_conn, si_res, + "pgpSoftware"); + if (vals && vals[0]) + { + if (opt.debug) + log_debug ("Server: \t%s\n", vals[0]); + if (!ascii_strcasecmp (vals[0], "GnuPG")) + is_gnupg = 1; + } + my_ldap_value_free (vals); + + vals = ldap_get_values (ldap_conn, si_res, + "pgpVersion"); + if (vals && vals[0]) + { + if (opt.debug) + log_debug ("Version:\t%s\n", vals[0]); + if (is_gnupg) + { + const char *fields[2]; + int nfields; + nfields = split_fields (vals[0], + fields, DIM(fields)); + if (nfields > 0 && atoi(fields[0]) > 1) + *r_serverinfo |= SERVERINFO_SCHEMAV2; + if (nfields > 1 + && !ascii_strcasecmp (fields[1], "ntds")) + *r_serverinfo |= SERVERINFO_NTDS; + } + } + my_ldap_value_free (vals); + } + + /* From man ldap_search_s: "res parameter of + ldap_search_ext_s() and ldap_search_s() should be + freed with ldap_msgfree() regardless of return + value of these functions. */ + ldap_msgfree (si_res); + return res; +} /* Connect to an LDAP server and interrogate it. @@ -564,6 +647,18 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, goto out; } *r_serverinfo |= SERVERINFO_REALLDAP; + + /* First try with provided basedn, else retry up one level + * Retry assumes that provided entry is for keyspace, + * matching old behavior */ + if(!interrogate_ldap_dn(ldap_conn, basedn, r_serverinfo, &basedn)) + { + char *basedn_parent = strchr(basedn, ','); + if(basedn_parent) + { + interrogate_ldap_dn(ldap_conn, basedn_parent + 1, r_serverinfo, &basedn); + } + } } else { /* Look for namingContexts. */ @@ -589,75 +684,11 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, * we found this, we know we're talking to a regular-ish * LDAP server and not an LDAP keyserver. */ int i; - char *attr2[] = - { "pgpBaseKeySpaceDN", "pgpVersion", "pgpSoftware", NULL }; - - *r_serverinfo |= SERVERINFO_REALLDAP; + *r_serverinfo |= SERVERINFO_REALLDAP; for (i = 0; context[i] && !basedn; i++) { - char **vals; - LDAPMessage *si_res; - int is_gnupg = 0; - - { - char *object = xasprintf ("cn=pgpServerInfo,%s", - context[i]); - npth_unprotect (); - lerr = ldap_search_s (ldap_conn, object, LDAP_SCOPE_BASE, - "(objectClass=*)", attr2, 0, &si_res); - npth_protect (); - xfree (object); - } - - if (lerr == LDAP_SUCCESS) - { - vals = ldap_get_values (ldap_conn, si_res, - "pgpBaseKeySpaceDN"); - if (vals && vals[0]) - { - basedn = xtrystrdup (vals[0]); - } - my_ldap_value_free (vals); - - vals = ldap_get_values (ldap_conn, si_res, - "pgpSoftware"); - if (vals && vals[0]) - { - if (opt.debug) - log_debug ("Server: \t%s\n", vals[0]); - if (!ascii_strcasecmp (vals[0], "GnuPG")) - is_gnupg = 1; - } - my_ldap_value_free (vals); - - vals = ldap_get_values (ldap_conn, si_res, - "pgpVersion"); - if (vals && vals[0]) - { - if (opt.debug) - log_debug ("Version:\t%s\n", vals[0]); - if (is_gnupg) - { - const char *fields[2]; - int nfields; - nfields = split_fields (vals[0], - fields, DIM(fields)); - if (nfields > 0 && atoi(fields[0]) > 1) - *r_serverinfo |= SERVERINFO_SCHEMAV2; - if (nfields > 1 - && !ascii_strcasecmp (fields[1], "ntds")) - *r_serverinfo |= SERVERINFO_NTDS; - } - } - my_ldap_value_free (vals); - } - - /* From man ldap_search_s: "res parameter of - ldap_search_ext_s() and ldap_search_s() should be - freed with ldap_msgfree() regardless of return - value of these functions. */ - ldap_msgfree (si_res); + interrogate_ldap_dn(ldap_conn, context[i], r_serverinfo, &basedn); } ldap_value_free (context); -- 2.34.1