From 7f51955301273d11911924f4a1fde241f84669f8 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 25 Jul 2025 15:06:38 +0200 Subject: dirmngr: Implement command KS_DEL for ldap servers. * dirmngr/server.c (cmd_ks_del): New. (percentplus_line_to_strlist): New. * dirmngr/ks-action.c (ks_action_del): New. * dirmngr/ks-engine-ldap.c (ks_ldap_del): New. -- GnuPG-bug-id: 5447 Backported-from-master: 9d356a172e7cc492707a6466d30ede8eb0dcf92d --- NEWS | 12 ++++++ dirmngr/ks-action.c | 33 ++++++++++++++ dirmngr/ks-action.h | 2 + dirmngr/ks-engine-ldap.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++ dirmngr/ks-engine.h | 1 + dirmngr/server.c | 86 ++++++++++++++++++++++++++++++++++++ 6 files changed, 244 insertions(+) diff --git a/NEWS b/NEWS index cedda3727..20c48afc6 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,18 @@ Noteworthy changes in version 2.2.48 (unreleased) ------------------------------------------------- + * scd:p15: Accept P15 cards with a zero-length label. [rG84229829b5] + + * scd:p15: Make signing work for Nexus cards. [rGe1576eee04] + + * gpg: Fully implement the group key flag. [rG924f09d1f3] + + * gpgsm: Make use of the de-vs flag in the trustlist.txt. [rG14383ff052] + + * gpgsm: Fix caching of the trustlist's flags. [T7738] + + * dirmngr: Implement command KS_DEL for LDAP. [T5447] + Noteworthy changes in version 2.2.47 (2025-04-09) ------------------------------------------------- diff --git a/dirmngr/ks-action.c b/dirmngr/ks-action.c index ad200ece6..81582f206 100644 --- a/dirmngr/ks-action.c +++ b/dirmngr/ks-action.c @@ -543,6 +543,39 @@ ks_action_put (ctrl_t ctrl, uri_item_t keyservers, } +/* Delete an OpenPGP key from all KEYSERVERS which use LDAP. The key + * is specifified by PATTERNS. */ +gpg_error_t +ks_action_del (ctrl_t ctrl, uri_item_t keyservers, strlist_t fprlist) +{ + gpg_error_t err = 0; + gpg_error_t first_err = 0; + int any_server = 0; + uri_item_t uri; + + for (uri = keyservers; uri; uri = uri->next) + { +#if USE_LDAP + if ( !strcmp (uri->parsed_uri->scheme, "ldap") + || !strcmp (uri->parsed_uri->scheme, "ldaps") + || !strcmp (uri->parsed_uri->scheme, "ldapi") + || uri->parsed_uri->opaque ) + { + any_server = 1; + err = ks_ldap_del (ctrl, uri->parsed_uri, fprlist); + if (err && !first_err) + first_err = err; + } +#endif + } + + if (!any_server) + err = gpg_error (GPG_ERR_NO_KEYSERVER); /* No LDAP keyserver */ + else if (!err && first_err) + err = first_err; + return err; +} + /* Query the default LDAP server or the one given by URL using * the filter expression FILTER. Write the result to OUTFP. */ diff --git a/dirmngr/ks-action.h b/dirmngr/ks-action.h index 223aae2da..0df497266 100644 --- a/dirmngr/ks-action.h +++ b/dirmngr/ks-action.h @@ -33,6 +33,8 @@ gpg_error_t ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp); gpg_error_t ks_action_put (ctrl_t ctrl, uri_item_t keyservers, void *data, size_t datalen, void *info, size_t infolen); +gpg_error_t ks_action_del (ctrl_t ctrl, uri_item_t keyservers, + strlist_t fprlist); gpg_error_t ks_action_query (ctrl_t ctrl, const char *ldapserver, unsigned int ks_get_flags, const char *filter, char **attr, diff --git a/dirmngr/ks-engine-ldap.c b/dirmngr/ks-engine-ldap.c index b416ac004..e1b0b6caa 100644 --- a/dirmngr/ks-engine-ldap.c +++ b/dirmngr/ks-engine-ldap.c @@ -2988,6 +2988,116 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, } +/* Delete the keys given by the list of fingerprints in FPRLIST from + * the keyserver identified by URI. The function stops at the first + * error encountered. */ +gpg_error_t +ks_ldap_del (ctrl_t ctrl, parsed_uri_t uri, strlist_t fprlist) +{ + gpg_error_t err = 0; + int ldap_err; + unsigned int serverinfo; + LDAP *ldap_conn = NULL; + char *basedn = NULL; + char *dn = NULL; + strlist_t fpr; + unsigned int count = 0; + unsigned int totalcount = 0; + + if (dirmngr_use_tor ()) + { + return no_ldap_due_to_tor (ctrl); + } + + for (fpr = fprlist; fpr; fpr = fpr->next) + totalcount++; + + err = my_ldap_connect (uri, 0, &ldap_conn, &basedn, NULL, NULL, &serverinfo); + if (err || !basedn) + { + if(opt.verbose) + log_info ("%s: connecting to server failed\n", __func__); + if (!err) + err = gpg_error (GPG_ERR_GENERAL); /* (no baseDN) */ + goto leave; + } + + if (!(serverinfo & SERVERINFO_REALLDAP)) + { + if(opt.verbose) + log_info ("%s: The PGP.com keyserver is not supported\n", __func__); + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + goto leave; + } + + if (!(serverinfo & SERVERINFO_SCHEMAV2)) + { + if(opt.verbose) + log_info ("%s: The keyserver does not support the v2 schema\n", + __func__); + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + goto leave; + } + + if (opt.verbose) + log_info ("%s: Using DN: %s,%s\n", __func__, + (serverinfo & SERVERINFO_NTDS)? "CN=" + /* */ : "pgpCertID=", + basedn); + for (fpr = fprlist; fpr; fpr = fpr->next) + { + if ((serverinfo & SERVERINFO_NTDS)) + { + xfree (dn); + dn = xtryasprintf ("CN=%s,%s", fpr->d, basedn); + } + else + { + unsigned int off; + + /* Simle method to get the keyID. Note that a v5 key + * (len>40) has the keyid at the left. If the length is + * less than 17 we assume a keyid has been given. */ + off = strlen (fpr->d); + if (off <= 40 && off > 16) + off = off - 16; + else + off = 0; + + xfree (dn); + dn = xtryasprintf ("pgpCertID=%.16s,%s", fpr->d+off, basedn); + } + + npth_unprotect (); + ldap_err = ldap_delete_ext_s (ldap_conn, dn, NULL, NULL); + npth_protect (); + if (ldap_err == LDAP_SUCCESS) + { + if (opt.verbose) + log_info ("%s: key %s deleted\n", __func__, fpr->d); + count++; + } + else + { + log_error ("%s: error deleting key %s: %s\n", + __func__, fpr->d, ldap_err2string (ldap_err)); + err = ldap_err_to_gpg_err (ldap_err); + break; /* Stop at the first failed deletion. */ + } + } + log_info ("%s: number of keys deleted: %u of %u\n", + __func__, count, totalcount); + + + leave: + if (ldap_conn) + ldap_unbind (ldap_conn); + xfree (dn); + xfree (basedn); + return err; +} + + /* Get the data described by FILTER_ARG from URI. On success R_FP has * an open stream to read the data. KS_GET_FLAGS conveys flags from diff --git a/dirmngr/ks-engine.h b/dirmngr/ks-engine.h index 6de77ccb2..005d07490 100644 --- a/dirmngr/ks-engine.h +++ b/dirmngr/ks-engine.h @@ -82,6 +82,7 @@ gpg_error_t ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, gpg_error_t ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, void *data, size_t datalen, void *info, size_t infolen); +gpg_error_t ks_ldap_del (ctrl_t ctrl, parsed_uri_t uri, strlist_t fprlist); gpg_error_t ks_ldap_query (ctrl_t ctrl, parsed_uri_t uri, unsigned int ks_get_flags, const char *filter, char **attrs, diff --git a/dirmngr/server.c b/dirmngr/server.c index bf4e891f3..b43390be0 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -324,6 +324,46 @@ strcpy_escaped_plus (char *d, const unsigned char *s) } +/* Break the LINE into space delimited tokens, put them into a new + * strlist and return it at R_LIST. On error an erro code is + * returned. If no tokens are found the list is set to NULL. + * Percent-plus encoding is removed from each token. Note that the + * function will modify LINE. */ +static gpg_error_t +percentplus_line_to_strlist (char *line, strlist_t *r_list) +{ + strlist_t list = NULL; + strlist_t sl; + char *p; + + for (p=line; *p; line = p) + { + while (*p && *p != ' ') + p++; + if (*p) + *p++ = 0; + if (*line) + { + sl = xtrymalloc (sizeof *sl + strlen (line)); + if (!sl) + { + gpg_error_t err = gpg_error_from_syserror (); + free_strlist (list); + *r_list = NULL; + return err; + } + sl->flags = 0; + strcpy_escaped_plus (sl->d, line); + sl->next = list; + list = sl; + } + } + + *r_list = list; + return 0; +} + + /* This function returns true if a Tor server is running. The status * is cached for the current connection. */ static int @@ -2702,6 +2742,51 @@ cmd_ks_put (assuan_context_t ctx, char *line) } +static const char hlp_ks_del[] = + "KS_DEL --ldap {}\n" + "\n" + "Delete the keys specified by primary keys FINGERPRINTS from the\n" + "configured OpenPGP LDAP server. The option --ldap is mandatory."; +static gpg_error_t +cmd_ks_del (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + strlist_t list = NULL; + unsigned int flags = 0; + + if (has_option (line, "--ldap")) + flags |= KS_GET_FLAG_ONLY_LDAP; + line = skip_options (line); + + err = percentplus_line_to_strlist (line, &list); + if (err) + goto leave; + + if (!(flags & KS_GET_FLAG_ONLY_LDAP)) + { + err = set_error (GPG_ERR_SYNTAX, "option --ldap is mandatory"); + goto leave; + } + + if (!list) + { + err = set_error (GPG_ERR_SYNTAX, "no fingerprints given"); + goto leave; + } + + err = ensure_keyserver (ctrl); + if (err) + goto leave; + + err = ks_action_del (ctrl, ctrl->server_local->keyservers, list); + + leave: + free_strlist (list); + return leave_cmd (ctx, err); +} + + static const char hlp_ad_query[] = "AD_QUERY [--first|--next] [--] \n" @@ -3035,6 +3120,7 @@ register_commands (assuan_context_t ctx) { "KS_GET", cmd_ks_get, hlp_ks_get }, { "KS_FETCH", cmd_ks_fetch, hlp_ks_fetch }, { "KS_PUT", cmd_ks_put, hlp_ks_put }, + { "KS_DEL", cmd_ks_del, hlp_ks_del }, { "AD_QUERY", cmd_ad_query, hlp_ad_query }, { "GETINFO", cmd_getinfo, hlp_getinfo }, { "LOADSWDB", cmd_loadswdb, hlp_loadswdb }, -- cgit v1.2.3