From a2434ccabdd1956876b44e05e07c3c3630c50f8f Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 15 Dec 2020 08:55:36 +0100 Subject: [PATCH] dirmngr: Store all version 2 schema attributes. * g10/call-dirmngr.c (ks_put_inq_cb): Emit "fpr" records. * dirmngr/ks-engine-ldap.c (extract_attributes): Add args extract-state and schemav2. Add data for the new schema version. remove the legacy code to handle UIDs in the "pub" line. (ks_ldap_put): Set new attributes for NTDS use the fingerprint as CN. Signed-off-by: Werner Koch --- dirmngr/ks-engine-ldap.c | 132 +++++++++++++++++++++++++-------------- g10/call-dirmngr.c | 3 + 2 files changed, 89 insertions(+), 46 deletions(-) diff --git a/dirmngr/ks-engine-ldap.c b/dirmngr/ks-engine-ldap.c index e8257fdbc..49d389266 100644 --- a/dirmngr/ks-engine-ldap.c +++ b/dirmngr/ks-engine-ldap.c @@ -46,6 +46,7 @@ #include "dirmngr.h" #include "misc.h" #include "../common/userids.h" +#include "../common/mbox-util.h" #include "ks-engine.h" #include "ldap-parse-uri.h" @@ -1629,15 +1630,16 @@ uncescape (char *str) /* Given one line from an info block (`gpg --list-{keys,sigs} --with-colons KEYID'), pull it apart and fill in the modlist with - the relevant (for the LDAP schema) attributes. */ + the relevant (for the LDAP schema) attributes. EXTRACT_STATE + should initally be set to 0 by the caller. SCHEMAV2 is set if the + server supports the version 2 schema. */ static void -extract_attributes (LDAPMod ***modlist, char *line) +extract_attributes (LDAPMod ***modlist, int *extract_state, + char *line, int schemav2) { int field_count; char **fields; - char *keyid; - int is_pub, is_sub, is_uid, is_sig; /* Remove trailing whitespace */ @@ -1652,24 +1654,42 @@ extract_attributes (LDAPMod ***modlist, char *line) if (field_count < 7) goto out; - is_pub = strcasecmp ("pub", fields[0]) == 0; - is_sub = strcasecmp ("sub", fields[0]) == 0; - is_uid = strcasecmp ("uid", fields[0]) == 0; - is_sig = strcasecmp ("sig", fields[0]) == 0; + is_pub = !ascii_strcasecmp ("pub", fields[0]); + is_sub = !ascii_strcasecmp ("sub", fields[0]); + is_uid = !ascii_strcasecmp ("uid", fields[0]); + is_sig = !ascii_strcasecmp ("sig", fields[0]); + if (!ascii_strcasecmp ("fpr", fields[0])) + { + /* Special treatment for a fingerprint. */ + if (!(*extract_state & 1)) + goto out; /* Stray fingerprint line - ignore. */ + *extract_state &= ~1; + if (field_count >= 10 && schemav2) + { + if ((*extract_state & 2)) + modlist_add (modlist, "gpgFingerprint", fields[9]); + else + modlist_add (modlist, "gpgSubFingerprint", fields[9]); + } + goto out; + } + + *extract_state &= ~(1|2); + if (is_pub) + *extract_state |= (1|2); + else if (is_sub) + *extract_state |= 1; if (!is_pub && !is_sub && !is_uid && !is_sig) - /* Not a relevant line. */ - goto out; + goto out; /* Not a relevant line. */ keyid = fields[4]; if (is_uid && strlen (keyid) == 0) - /* The uid record type can have an empty keyid. */ - ; + ; /* The uid record type can have an empty keyid. */ else if (strlen (keyid) == 16 && strspn (keyid, "0123456789aAbBcCdDeEfF") == 16) - /* Otherwise, we expect exactly 16 hex characters. */ - ; + ; /* Otherwise, we expect exactly 16 hex characters. */ else { log_error ("malformed record!\n"); @@ -1748,12 +1768,12 @@ extract_attributes (LDAPMod ***modlist, char *line) { if (is_pub) { - modlist_add (modlist, "pgpCertID", keyid); - modlist_add (modlist, "pgpKeyID", &keyid[8]); + modlist_add (modlist, "pgpCertID", keyid); /* Long keyid(!) */ + modlist_add (modlist, "pgpKeyID", &keyid[8]); /* Short keyid */ } if (is_sub) - modlist_add (modlist, "pgpSubKeyID", keyid); + modlist_add (modlist, "pgpSubKeyID", keyid); /* Long keyid(!) */ } if (is_pub) @@ -1851,25 +1871,22 @@ extract_attributes (LDAPMod ***modlist, char *line) } } - if ((is_uid || is_pub) && field_count >= 10) + if (is_uid && field_count >= 10) { char *uid = fields[9]; + char *mbox; - if (is_pub && strlen (uid) == 0) - /* When using gpg --list-keys, the uid is included. When - passed via gpg, it is not. It is important to process it - when it is present, because gpg 1 won't print a UID record - if there is only one key. */ - ; - else - { - uncescape (uid); - modlist_add (modlist, "pgpUserID", uid); - } + uncescape (uid); + modlist_add (modlist, "pgpUserID", uid); + if (schemav2 && (mbox = mailbox_from_userid (uid, 0))) + { + modlist_add (modlist, "gpgMailbox", mbox); + xfree (mbox); + } } out: - free (fields); + xfree (fields); } /* Send the key in {KEY,KEYLEN} with the metadata {INFO,INFOLEN} to @@ -1888,12 +1905,13 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, LDAPMod **modlist = NULL; LDAPMod **addlist = NULL; char *data_armored = NULL; + int extract_state; /* The last byte of the info block. */ const char *infoend = (const char *) info + infolen - 1; /* Enable this code to dump the modlist to /tmp/modlist.txt. */ -#if 0 +#if 1 # warning Disable debug code before checking in. const int dump_modlist = 1; #else @@ -1995,9 +2013,15 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, modlist_add (&modlist, "pgpKeySize", NULL); modlist_add (&modlist, "pgpKeyExpireTime", NULL); modlist_add (&modlist, "pgpCertID", NULL); + if ((serverinfo & SERVERINFO_SCHEMAV2)) + { + modlist_add (&modlist, "gpgFingerprint", NULL); + modlist_add (&modlist, "gpgSubFingerprint", NULL); + modlist_add (&modlist, "gpgMailbox", NULL); + } /* Assemble the INFO stuff into LDAP attributes */ - + extract_state = 0; while (infolen > 0) { char *temp = NULL; @@ -2015,7 +2039,8 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, *newline = '\0'; - extract_attributes (&addlist, info); + extract_attributes (&addlist, &extract_state, info, + (serverinfo & SERVERINFO_SCHEMAV2)); infolen = infolen - ((uintptr_t) newline - (uintptr_t) info + 1); info = newline + 1; @@ -2061,22 +2086,37 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, keyserver) this does NOT merge signatures, but replaces the whole key. This should make some people very happy. */ { - char **certid; + char **attrval; char *dn; - certid = modlist_lookup (addlist, "pgpCertID"); - /* We should have exactly one value. */ - if (!certid || !(certid[0] && !certid[1])) - { - log_error ("ks-ldap: bad pgpCertID provided\n"); - err = GPG_ERR_GENERAL; - goto out; - } - if ((serverinfo & SERVERINFO_NTDS)) - dn = xtryasprintf ("CN=%s,%s", certid[0], basedn); - else - dn = xtryasprintf ("pgpCertID=%s,%s", certid[0], basedn); + { + /* The modern way using a CN RDN with the fingerprint. This + * has the advantage that we won't have duplicate 64 bit + * keyids in the store. In particular NTDS requires the + * DN to be unique. */ + attrval = modlist_lookup (addlist, "gpgFingerprint"); + /* We should have exactly one value. */ + if (!attrval || !(attrval[0] && !attrval[1])) + { + log_error ("ks-ldap: bad gpgFingerprint provided\n"); + err = GPG_ERR_GENERAL; + goto out; + } + dn = xtryasprintf ("CN=%s,%s", attrval[0], basedn); + } + else /* The old style way. */ + { + attrval = modlist_lookup (addlist, "pgpCertID"); + /* We should have exactly one value. */ + if (!attrval || !(attrval[0] && !attrval[1])) + { + log_error ("ks-ldap: bad pgpCertID provided\n"); + err = GPG_ERR_GENERAL; + goto out; + } + dn = xtryasprintf ("pgpCertID=%s,%s", attrval[0], basedn); + } if (!dn) { err = gpg_error_from_syserror (); diff --git a/g10/call-dirmngr.c b/g10/call-dirmngr.c index 1a4d896c6..17f5fdcf3 100644 --- a/g10/call-dirmngr.c +++ b/g10/call-dirmngr.c @@ -931,6 +931,7 @@ ks_put_inq_cb (void *opaque, const char *line) { kbnode_t node; estream_t fp; + char hexfpr[2*MAX_FINGERPRINT_LEN+1]; /* Parse the keyblock and send info lines back to the server. */ fp = es_fopenmem (0, "rw,samethread"); @@ -988,6 +989,8 @@ ks_put_inq_cb (void *opaque, const char *line) nbits_from_pk (pk), pk->pubkey_algo, pk->keyid, pk->timestamp, pk->expiredate, NULL); + es_fprintf (fp, "fpr:::::::::%s:\n", + hexfingerprint (pk, hexfpr, sizeof hexfpr)); } break;