diff options
author | Werner Koch <wk@gnupg.org> | 2020-12-14 19:28:25 +0100 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2020-12-14 19:29:49 +0100 |
commit | e9ddd61fe979b1b8e1a4801f7f916d0222397245 (patch) | |
tree | 4acb86b0c643e333e4b3f9ebc10a1277d797dce8 /dirmngr/ks-engine-ldap.c | |
parent | dirmngr: Do not store the useless pgpSignerID in the LDAP. (diff) | |
download | gnupg2-e9ddd61fe979b1b8e1a4801f7f916d0222397245.tar.xz gnupg2-e9ddd61fe979b1b8e1a4801f7f916d0222397245.zip |
dirmngr: Support the new Active Directory schema
* dirmngr/ks-engine-ldap.c (SERVERINFO_): New constants.
(my_ldap_connect): Relace args pgpkeyattrp and real_ldapp by a new
serverinfo arg. Set the new info flags.
(ks_ldap_get): Adjust for change.
(ks_ldap_search): Ditto.
(ks_ldap_put): Ditto. Replace xmalloc by xtrymalloc. Change the DN
for use with NTDS (aka Active Directory).
* doc/ldap/gnupg-ldap-init.ldif (pgpSoftware): Update definition of
pgpVersion.
* doc/ldap/gnupg-ldap-ad-init.ldif: New.
* doc/ldap/gnupg-ldap-ad-schema.ldif: New.
--
This is a first take on better Active Directory support. The main
change for NTDS in the code is that the an top-RDN of CN is used
instead of the old pgpCertID. More changes to come; for example using
and storing the fingerprint.
Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to 'dirmngr/ks-engine-ldap.c')
-rw-r--r-- | dirmngr/ks-engine-ldap.c | 247 |
1 files changed, 141 insertions, 106 deletions
diff --git a/dirmngr/ks-engine-ldap.c b/dirmngr/ks-engine-ldap.c index 7d61313d9..e8257fdbc 100644 --- a/dirmngr/ks-engine-ldap.c +++ b/dirmngr/ks-engine-ldap.c @@ -1,7 +1,7 @@ /* ks-engine-ldap.c - talk to a LDAP keyserver * Copyright (C) 2001, 2002, 2004, 2005, 2006 * 2007 Free Software Foundation, Inc. - * Copyright (C) 2015 g10 Code GmbH + * Copyright (C) 2015, 2020 g10 Code GmbH * * This file is part of GnuPG. * @@ -49,6 +49,15 @@ #include "ks-engine.h" #include "ldap-parse-uri.h" + +/* Flags with infos from the connected server. */ +#define SERVERINFO_REALLDAP 1 /* This is not the PGP keyserver. */ +#define SERVERINFO_PGPKEYV2 2 /* Needs "pgpeyV2" instead of "pgpKey" */ +#define SERVERINFO_SCHEMAV2 4 /* Version 2 of the Schema. */ +#define SERVERINFO_NTDS 8 /* Server is an Active Directory. */ + + + #ifndef HAVE_TIMEGM time_t timegm(struct tm *tm); #endif @@ -427,40 +436,42 @@ keyspec_to_ldap_filter (const char *keyspec, char **filter, int only_exact) The values are returned in the passed variables. If you pass NULL, then the value won't be returned. It is the caller's responsibility to release *LDAP_CONNP with ldap_unbind and xfree - *BASEDNP and *PGPKEYATTRP. + *BASEDNP. If this function successfully interrogated the server, it returns 0. If there was an LDAP error, it returns the LDAP error code. If an error occurred, *basednp, etc., are undefined (and don't need to be freed.) + R_SERVERINFO receives information about the server. + 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. In this case, you also do not need to xfree - *pgpkeyattrp. */ + OpenPGP Keyserver. */ static int my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, - char **basednp, char **pgpkeyattrp, int *real_ldapp) + char **basednp, unsigned int *r_serverinfo) { int err = 0; - LDAP *ldap_conn = NULL; - char *user = uri->auth; - struct uri_tuple_s *password_param = uri_query_lookup (uri, "password"); - char *password = password_param ? password_param->value : NULL; - + struct uri_tuple_s *password_param; + char *password; char *basedn = NULL; - /* Whether to look for the pgpKey or pgpKeyv2 attribute. */ - char *pgpkeyattr = "pgpKey"; - int real_ldap = 0; - log_debug ("my_ldap_connect(%s:%d/%s????%s%s%s%s%s)\n", - uri->host, uri->port, - uri->path ?: "", - uri->auth ? "bindname=" : "", uri->auth ?: "", - uri->auth && password ? "," : "", - password ? "password=" : "", password ?: ""); + *r_serverinfo = 0; + + password_param = uri_query_lookup (uri, "password"); + password = password_param ? password_param->value : NULL; + + if (opt.debug) + log_debug ("my_ldap_connect(%s:%d/%s????%s%s%s%s%s)\n", + uri->host, uri->port, + uri->path ?: "", + uri->auth ? "bindname=" : "", uri->auth ?: "", + uri->auth && password ? "," : "", + password ? "password=" : "", + password ? ">not shown<": ""); /* If the uri specifies a secure connection and we don't support TLS, then fail; don't silently revert to an insecure @@ -490,7 +501,7 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, err = ldap_set_option (ldap_conn, LDAP_OPT_PROTOCOL_VERSION, &ver); if (err != LDAP_SUCCESS) { - log_error ("gpgkeys: unable to go to LDAP 3: %s\n", + log_error ("ks-ldap: unable to go to LDAP 3: %s\n", ldap_err2string (err)); goto out; } @@ -553,8 +564,9 @@ 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->auth) { - log_debug ("LDAP bind to %s, password %s\n", - user, password ? ">not shown<" : ">none<"); + if (opt.debug) + log_debug ("LDAP bind to %s, password %s\n", + user, password ? ">not shown<" : ">none<"); err = ldap_simple_bind_s (ldap_conn, user, password); if (err != LDAP_SUCCESS) @@ -566,18 +578,17 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, } if (uri->path && *uri->path) - /* User specified base DN. */ { + /* User specified base DN. */ basedn = xstrdup (uri->path); /* If the user specifies a base DN, then we know the server is a - real LDAP server. */ - real_ldap = 1; + * real LDAP server. */ + *r_serverinfo |= SERVERINFO_REALLDAP; } else - { + { /* Look for namingContexts. */ LDAPMessage *res = NULL; - /* Look for namingContexts. */ char *attr[] = { "namingContexts", NULL }; err = ldap_search_s (ldap_conn, "", LDAP_SCOPE_BASE, @@ -586,21 +597,22 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, { char **context = ldap_get_values (ldap_conn, res, "namingContexts"); if (context) - /* We found some, so try each namingContext as the search - base and look for pgpBaseKeySpaceDN. Because we found - this, we know we're talking to a regular-ish LDAP - server and not an LDAP keyserver. */ { + /* We found some, so try each namingContext as the + * search base and look for pgpBaseKeySpaceDN. Because + * 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 }; - real_ldap = 1; + *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", @@ -624,7 +636,10 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, "pgpSoftware"); if (vals) { - log_debug ("Server: \t%s\n", vals[0]); + if (opt.debug) + log_debug ("Server: \t%s\n", vals[0]); + if (!ascii_strcasecmp (vals[0], "GnuPG")) + is_gnupg = 1; ldap_value_free (vals); } @@ -632,7 +647,20 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, "pgpVersion"); if (vals) { - log_debug ("Version:\t%s\n", 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; + } ldap_value_free (vals); } } @@ -650,7 +678,7 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, else { /* We don't have an answer yet, which means the server might - be an LDAP keyserver. */ + be a PGP.com keyserver. */ char **vals; LDAPMessage *si_res = NULL; @@ -660,9 +688,9 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, "(objectClass=*)", attr2, 0, &si_res); if (err == LDAP_SUCCESS) { - /* For the LDAP keyserver, this is always - "OU=ACTIVE,O=PGP KEYSPACE,C=US", but it might not be - in the future. */ + /* For the PGP LDAP keyserver, this is always + * "OU=ACTIVE,O=PGP KEYSPACE,C=US", but it might not be + * in the future. */ vals = ldap_get_values (ldap_conn, si_res, "baseKeySpaceDN"); if (vals) @@ -674,14 +702,16 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, vals = ldap_get_values (ldap_conn, si_res, "software"); if (vals) { - log_debug ("ldap: Server: \t%s\n", vals[0]); + if (opt.debug) + log_debug ("ks-ldap: PGP Server: \t%s\n", vals[0]); ldap_value_free (vals); } vals = ldap_get_values (ldap_conn, si_res, "version"); if (vals) { - log_debug ("ldap: Version:\t%s\n", vals[0]); + if (opt.debug) + log_debug ("ks-ldap: PGP Server Version:\t%s\n", vals[0]); /* If the version is high enough, use the new pgpKeyV2 attribute. This design is iffy at best, @@ -690,7 +720,7 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, keyserver vendor with a different numbering scheme. */ if (atoi (vals[0]) > 1) - pgpkeyattr = "pgpKeyV2"; + *r_serverinfo |= SERVERINFO_PGPKEYV2; ldap_value_free (vals); } @@ -706,29 +736,20 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, } out: - if (! err) + if (!err && opt.debug) { log_debug ("ldap_conn: %p\n", ldap_conn); - log_debug ("real_ldap: %d\n", real_ldap); + log_debug ("server_type: %s\n", ((*r_serverinfo & SERVERINFO_REALLDAP) + ? "LDAP" : "PGP.com keyserver") ); log_debug ("basedn: %s\n", basedn); - log_debug ("pgpkeyattr: %s\n", pgpkeyattr); + log_debug ("pgpkeyattr: %s\n", + (*r_serverinfo & SERVERINFO_PGPKEYV2)? "pgpKeyV2":"pgpKey"); } - if (! err && real_ldapp) - *real_ldapp = real_ldap; - if (err) xfree (basedn); else { - if (pgpkeyattrp) - { - if (basedn) - *pgpkeyattrp = xstrdup (pgpkeyattr); - else - *pgpkeyattrp = NULL; - } - if (basednp) *basednp = basedn; else @@ -834,16 +855,11 @@ 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 *filter = NULL; - LDAP *ldap_conn = NULL; - char *basedn = NULL; - char *pgpkeyattr = NULL; - estream_t fp = NULL; - LDAPMessage *message = NULL; (void) ctrl; @@ -863,7 +879,7 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, return (err); /* Make sure we are talking to an OpenPGP LDAP server. */ - ldap_err = my_ldap_connect (uri, &ldap_conn, &basedn, &pgpkeyattr, NULL); + ldap_err = my_ldap_connect (uri, &ldap_conn, &basedn, &serverinfo); if (ldap_err || !basedn) { if (ldap_err) @@ -879,24 +895,26 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, may be discarded we aren't in verbose mode. */ char *attrs[] = { - pgpkeyattr, + "dummy", "pgpcertid", "pgpuserid", "pgpkeyid", "pgprevoked", "pgpdisabled", "pgpkeycreatetime", "modifytimestamp", "pgpkeysize", "pgpkeytype", NULL }; /* 1 if we want just attribute types; 0 if we want both attribute - types and values. */ + * types and values. */ int attrsonly = 0; - int count; + /* Replace "dummy". */ + attrs[0] = (serverinfo & SERVERINFO_PGPKEYV2)? "pgpKeyV2" : "pgpKey"; + ldap_err = ldap_search_s (ldap_conn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, attrsonly, &message); if (ldap_err) { err = ldap_err_to_gpg_err (ldap_err); - log_error ("gpgkeys: LDAP search error: %s\n", + log_error ("ks-ldap: LDAP search error: %s\n", ldap_err2string (ldap_err)); goto out; } @@ -904,7 +922,7 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, count = ldap_count_entries (ldap_conn, message); if (count < 1) { - log_error ("gpgkeys: key %s not found on keyserver\n", keyspec); + log_info ("ks-ldap: key %s not found on keyserver\n", keyspec); if (count == -1) err = ldap_to_gpg_err (ldap_conn); @@ -954,11 +972,11 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, extract_keys (fp, ldap_conn, certid[0], each); - vals = ldap_get_values (ldap_conn, each, pgpkeyattr); + vals = ldap_get_values (ldap_conn, each, attrs[0]); if (! vals) { err = ldap_to_gpg_err (ldap_conn); - log_error("gpgkeys: unable to retrieve key %s " + log_error("ks-ldap: unable to retrieve key %s " "from keyserver\n", certid[0]); goto out; } @@ -1001,7 +1019,6 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, *r_fp = fp; } - xfree (pgpkeyattr); xfree (basedn); if (ldap_conn) @@ -1012,6 +1029,7 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, return err; } + /* Search the keyserver identified by URI for keys matching PATTERN. On success R_FP has an open stream to read the data. */ gpg_error_t @@ -1020,13 +1038,10 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, { gpg_error_t err; int ldap_err; - + unsigned int serverinfo; char *filter = NULL; - LDAP *ldap_conn = NULL; - char *basedn = NULL; - estream_t fp = NULL; (void) ctrl; @@ -1049,7 +1064,7 @@ 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, NULL, NULL); + ldap_err = my_ldap_connect (uri, &ldap_conn, &basedn, &serverinfo); if (ldap_err || !basedn) { if (ldap_err) @@ -1082,7 +1097,8 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, "pgpkeysize", "pgpkeytype", NULL }; - log_debug ("SEARCH '%s' => '%s' BEGIN\n", pattern, filter); + if (opt.debug) + log_debug ("SEARCH '%s' => '%s' BEGIN\n", pattern, filter); ldap_err = ldap_search_s (ldap_conn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &res); @@ -1095,7 +1111,7 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, err = ldap_err_to_gpg_err (ldap_err); log_error ("SEARCH %s FAILED %d\n", pattern, err); - log_error ("gpgkeys: LDAP search error: %s\n", + log_error ("ks-ldap: LDAP search error: %s\n", ldap_err2string (err)); goto out; } @@ -1117,10 +1133,10 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, if (ldap_err == LDAP_SIZELIMIT_EXCEEDED) { if (count == 1) - log_error ("gpgkeys: search results exceeded server limit." + log_error ("ks-ldap: search results exceeded server limit." " First 1 result shown.\n"); else - log_error ("gpgkeys: search results exceeded server limit." + log_error ("ks-ldap: search results exceeded server limit." " First %d results shown.\n", count); } @@ -1272,7 +1288,8 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, free_strlist (dupelist); } - log_debug ("SEARCH %s END\n", pattern); + if (opt.debug) + log_debug ("SEARCH %s END\n", pattern); out: if (err) @@ -1865,15 +1882,11 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, { gpg_error_t err = 0; int ldap_err; - + unsigned int serverinfo; LDAP *ldap_conn = NULL; char *basedn = NULL; - char *pgpkeyattr = NULL; - int real_ldap; - LDAPMod **modlist = NULL; LDAPMod **addlist = NULL; - char *data_armored = NULL; /* The last byte of the info block. */ @@ -1898,8 +1911,7 @@ 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, &pgpkeyattr, &real_ldap); + ldap_err = my_ldap_connect (uri, &ldap_conn, &basedn, &serverinfo); if (ldap_err || !basedn) { if (ldap_err) @@ -1909,22 +1921,31 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, goto out; } - if (! real_ldap) - /* We appear to have an OpenPGP Keyserver, which can unpack the key - on its own (not just a dumb LDAP server). */ + if (!(serverinfo & SERVERINFO_REALLDAP)) { - LDAPMod mod, *attrs[2]; - char *key[] = { data, NULL }; + /* We appear to have a PGP.com Keyserver, which can unpack the + * key on its own (not just a dump LDAP server). This will + * rarely be the case these days. */ + LDAPMod mod; + LDAPMod *attrs[2]; + char *key[2]; char *dn; + key[0] = data; + key[1] = NULL; memset (&mod, 0, sizeof (mod)); mod.mod_op = LDAP_MOD_ADD; - mod.mod_type = pgpkeyattr; + mod.mod_type = (serverinfo & SERVERINFO_PGPKEYV2)? "pgpKeyV2":"pgpKey"; mod.mod_values = key; attrs[0] = &mod; attrs[1] = NULL; - dn = xasprintf ("pgpCertid=virtual,%s", basedn); + dn = xtryasprintf ("pgpCertid=virtual,%s", basedn); + if (!dn) + { + err = gpg_error_from_syserror (); + goto out; + } ldap_err = ldap_add_s (ldap_conn, dn, attrs); xfree (dn); @@ -1937,7 +1958,12 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, goto out; } - modlist = xmalloc (sizeof (LDAPMod *)); + modlist = xtrymalloc (sizeof (LDAPMod *)); + if (!modlist) + { + err = gpg_error_from_syserror (); + goto out; + } *modlist = NULL; if (dump_modlist) @@ -1996,10 +2022,10 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, /* Sanity check. */ if (! temp) - assert ((char *) info + infolen - 1 == infoend); + log_assert ((char *) info + infolen - 1 == infoend); else { - assert (infolen == -1); + log_assert (infolen == -1); xfree (temp); } } @@ -2010,7 +2036,9 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, if (err) goto out; - modlist_add (&addlist, pgpkeyattr, data_armored); + modlist_add (&addlist, + (serverinfo & SERVERINFO_PGPKEYV2)? "pgpKeyV2":"pgpKey", + data_armored); /* Now append addlist onto modlist. */ modlists_join (&modlist, addlist); @@ -2037,17 +2065,25 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, char *dn; certid = modlist_lookup (addlist, "pgpCertID"); - if (/* We should have a value. */ - ! certid - /* Exactly one. */ - || !(certid[0] && !certid[1])) + /* We should have exactly one value. */ + if (!certid || !(certid[0] && !certid[1])) { - log_error ("Bad certid.\n"); + log_error ("ks-ldap: bad pgpCertID provided\n"); err = GPG_ERR_GENERAL; goto out; } - dn = xasprintf ("pgpCertID=%s,%s", certid[0], basedn); + if ((serverinfo & SERVERINFO_NTDS)) + dn = xtryasprintf ("CN=%s,%s", certid[0], basedn); + else + dn = xtryasprintf ("pgpCertID=%s,%s", certid[0], basedn); + if (!dn) + { + err = gpg_error_from_syserror (); + goto out; + } + if (opt.debug) + log_debug ("ks-ldap: using DN: %s\n", dn); err = ldap_modify_s (ldap_conn, dn, modlist); if (err == LDAP_NO_SUCH_OBJECT) @@ -2057,7 +2093,7 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, if (err != LDAP_SUCCESS) { - log_error ("gpgkeys: error adding key to keyserver: %s\n", + log_error ("ks-ldap: error adding key to keyserver: %s\n", ldap_err2string (err)); err = ldap_err_to_gpg_err (err); } @@ -2071,7 +2107,6 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, ldap_unbind (ldap_conn); xfree (basedn); - xfree (pgpkeyattr); modlist_free (modlist); xfree (addlist); |