summaryrefslogtreecommitdiffstats
path: root/kbx
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2020-09-10 12:50:45 +0200
committerWerner Koch <wk@gnupg.org>2020-09-10 12:50:45 +0200
commitc9677d416e6ff190c589af35b514a01a787085fb (patch)
tree979cca6df7aa51346b0165b4bbfaeecc782377b8 /kbx
parentkeyboxd: Use D-lines instead of a separate thread. (diff)
downloadgnupg2-c9677d416e6ff190c589af35b514a01a787085fb.tar.xz
gnupg2-c9677d416e6ff190c589af35b514a01a787085fb.zip
keyboxd: Add basic support for X.509.
* kbx/keybox-blob.c (x509_email_kludge): Rename to ... (_keybox_x509_email_kludge): this and make global. * kbx/backend.h: Include ksba.h. * kbx/backend-support.c (be_get_x509_serial): New. (be_get_x509_keygrip): New. * kbx/backend-sqlite.c (table_definitions): New table 'issuers'. (run_select_statement): Implements modes ISSUER, ISSUER_SN, SUBJECT. (store_into_userid): Add arg override_mbox. (store_into_issuer): New. (be_sqlite_store): Implement x509 part. Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to 'kbx')
-rw-r--r--kbx/backend-sqlite.c293
-rw-r--r--kbx/backend-support.c70
-rw-r--r--kbx/backend.h3
-rw-r--r--kbx/keybox-blob.c10
-rw-r--r--kbx/keybox-defs.h2
5 files changed, 311 insertions, 67 deletions
diff --git a/kbx/backend-sqlite.c b/kbx/backend-sqlite.c
index c4e54298e..3576d3d6d 100644
--- a/kbx/backend-sqlite.c
+++ b/kbx/backend-sqlite.c
@@ -123,12 +123,14 @@ static struct
* It is also used for the primary key and the X.509 fingerprint
* because we want to be able to use the keyid and keygrip. */
{ "CREATE TABLE IF NOT EXISTS fingerprint ("
+ /* The fingerprint, for OpenPGP either 20 octets or 32 octets;
+ * for X.509 it is the same as the UBID. */
"fpr BLOB NOT NULL PRIMARY KEY,"
/* The long keyid as 64 bit integer. */
"kid INTEGER NOT NULL,"
/* The keygrip for this key. */
"keygrip BLOB NOT NULL,"
- /* 0 = primary, > 0 = subkey. */
+ /* 0 = primary or X.509, > 0 = subkey. */
"subkey INTEGER NOT NULL,"
/* The Unique Blob ID (possibly truncated fingerprint). */
"ubid BLOB NOT NULL REFERENCES pubkey"
@@ -139,10 +141,9 @@ static struct
{ "CREATE INDEX IF NOT EXISTS fingerprintidx1 on fingerprint (fpr)" },
{ "CREATE INDEX IF NOT EXISTS fingerprintidx2 on fingerprint (keygrip)" },
-
/* Table to allow fast access via user ids or mail addresses. */
{ "CREATE TABLE IF NOT EXISTS userid ("
- /* The full user id. */
+ /* The full user id - for X.509 the Subject or altSubject. */
"uid TEXT NOT NULL,"
/* The mail address if available or NULL. */
"addrspec TEXT,"
@@ -155,7 +156,18 @@ static struct
/* Indices for the userid table. */
{ "CREATE INDEX IF NOT EXISTS userididx0 on userid (ubid)" },
{ "CREATE INDEX IF NOT EXISTS userididx1 on userid (uid)" },
- { "CREATE INDEX IF NOT EXISTS userididx3 on userid (addrspec)" }
+ { "CREATE INDEX IF NOT EXISTS userididx3 on userid (addrspec)" },
+
+ /* Table to allow fast access via s/n + issuer DN (X.509 only). */
+ { "CREATE TABLE IF NOT EXISTS issuer ("
+ /* The hex encoded S/N. */
+ "sn TEXT NOT NULL,"
+ /* The RFC2253 issuer DN. */
+ "dn TEXT NOT NULL,"
+ /* The Unique Blob ID (usually the truncated fingerprint). */
+ "ubid BLOB NOT NULL REFERENCES pubkey"
+ ")" },
+ { "CREATE INDEX IF NOT EXISTS issueridx1 on issuer (dn)" }
};
@@ -786,28 +798,57 @@ run_select_statement (ctrl_t ctrl, be_sqlite_local_t ctx,
break;
case KEYDB_SEARCH_MODE_ISSUER:
- err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */
- /* if (has_issuer (blob, desc[n].u.name)) */
- /* goto found; */
+ if (!ctx->select_stmt)
+ err = run_sql_prepare ("SELECT p.ubid, p.type, p.keyblob"
+ " FROM pubkey as p, issuer as i"
+ " WHERE p.ubid = i.ubid"
+ " AND i.dn = $1",
+ extra, &ctx->select_stmt);
+ if (!err)
+ err = run_sql_bind_text (ctx->select_stmt, 1,
+ desc[descidx].u.name);
break;
case KEYDB_SEARCH_MODE_ISSUER_SN:
- err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */
- /* if (has_issuer_sn (blob, desc[n].u.name, */
- /* sn_array? sn_array[n].sn : desc[n].sn, */
- /* sn_array? sn_array[n].snlen : desc[n].snlen)) */
- /* goto found; */
+ if (!desc[descidx].snhex)
+ {
+ /* We should never get a binary S/N here. */
+ log_debug ("%s: issuer_sn with binary s/n\n", __func__);
+ err = gpg_error (GPG_ERR_INTERNAL);
+ }
+ else
+ {
+ if (!ctx->select_stmt)
+ err = run_sql_prepare ("SELECT p.ubid, p.type, p.keyblob"
+ " FROM pubkey as p, issuer as i"
+ " WHERE p.ubid = i.ubid"
+ " AND i.sn = $1 AND i.dn = $2",
+ extra, &ctx->select_stmt);
+ if (!err)
+ err = run_sql_bind_ntext (ctx->select_stmt, 1,
+ desc[descidx].sn, desc[descidx].snlen);
+ if (!err)
+ err = run_sql_bind_text (ctx->select_stmt, 2,
+ desc[descidx].u.name);
+ }
break;
+
case KEYDB_SEARCH_MODE_SN:
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */
/* if (has_sn (blob, sn_array? sn_array[n].sn : desc[n].sn, */
/* sn_array? sn_array[n].snlen : desc[n].snlen)) */
/* goto found; */
break;
+
case KEYDB_SEARCH_MODE_SUBJECT:
- err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */
- /* if (has_subject (blob, desc[n].u.name)) */
- /* goto found; */
+ err = run_sql_prepare ("SELECT p.ubid, p.type, p.keyblob"
+ " FROM pubkey as p, userid as u"
+ " WHERE p.ubid = u.ubid"
+ " AND u.uid = $1",
+ extra, &ctx->select_stmt);
+ if (!err)
+ err = run_sql_bind_text (ctx->select_stmt, 1,
+ desc[descidx].u.name);
break;
case KEYDB_SEARCH_MODE_SHORT_KID:
@@ -1101,11 +1142,12 @@ store_into_fingerprint (const unsigned char *ubid, int subkey,
}
-/* Helper for be_sqlite_store to update or insert a row in the
- * userid table. */
+/* Helper for be_sqlite_store to update or insert a row in the userid
+ * table. If OVERRIDE_MBOX is set, that value is used instead of a
+ * value extracted from UID. */
static gpg_error_t
store_into_userid (const unsigned char *ubid, enum pubkey_types pktype,
- const char *uid)
+ const char *uid, const char *override_mbox)
{
gpg_error_t err;
const char *sqlstr;
@@ -1121,10 +1163,17 @@ store_into_userid (const unsigned char *ubid, enum pubkey_types pktype,
err = run_sql_bind_text (stmt, 1, uid);
if (err)
goto leave;
- addrspec = mailbox_from_userid (uid, 0);
- err = run_sql_bind_text (stmt, 2, addrspec);
+
+ if (override_mbox)
+ err = run_sql_bind_text (stmt, 2, override_mbox);
+ else
+ {
+ addrspec = mailbox_from_userid (uid, 0);
+ err = run_sql_bind_text (stmt, 2, addrspec);
+ }
if (err)
goto leave;
+
err = run_sql_bind_int (stmt, 3, pktype);
if (err)
goto leave;
@@ -1142,6 +1191,43 @@ store_into_userid (const unsigned char *ubid, enum pubkey_types pktype,
}
+/* Helper for be_sqlite_store to update or insert a row in the
+ * issuer table. */
+static gpg_error_t
+store_into_issuer (const unsigned char *ubid,
+ const char *sn, const char *issuer)
+{
+ gpg_error_t err;
+ const char *sqlstr;
+ sqlite3_stmt *stmt = NULL;
+ char *addrspec = NULL;
+
+ sqlstr = ("INSERT OR REPLACE INTO issuer(sn,dn,ubid)"
+ " VALUES(:1,:2,:3)");
+ err = run_sql_prepare (sqlstr, NULL, &stmt);
+ if (err)
+ goto leave;
+
+ err = run_sql_bind_text (stmt, 1, sn);
+ if (err)
+ goto leave;
+ err = run_sql_bind_text (stmt, 2, issuer);
+ if (err)
+ goto leave;
+ err = run_sql_bind_blob (stmt, 3, ubid, UBID_LEN);
+ if (err)
+ goto leave;
+
+ err = run_sql_step (stmt);
+
+ leave:
+ if (stmt)
+ sqlite3_finalize (stmt);
+ xfree (addrspec);
+ return err;
+}
+
+
/* Store (BLOB,BLOBLEN) into the database. UBID is the UBID matching
* that blob. BACKEND_HD is the handle for this backend and REQUEST
* is the current database request object. MODE is the store
@@ -1159,7 +1245,10 @@ be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd,
int in_transaction = 0;
int info_valid = 0;
struct _keybox_openpgp_info info;
- struct _keybox_openpgp_key_info *kinfo;
+ ksba_cert_t cert = NULL;
+ char *sn = NULL;
+ char *dn = NULL;
+ char *kludge_mbox = NULL;
(void)ctrl;
@@ -1172,10 +1261,14 @@ be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd,
if (be_is_x509_blob (blob, bloblen))
{
- /* The UBID is also our fingerprint. */
- /* FIXME: Extract keygrip and KID. */
- err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
- goto leave;
+ log_assert (pktype == PUBKEY_TYPE_X509);
+
+ err = ksba_cert_new (&cert);
+ if (err)
+ goto leave;
+ err = ksba_cert_init_from_mem (cert, blob, bloblen);
+ if (err)
+ goto leave;
}
else
{
@@ -1221,56 +1314,127 @@ be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd,
("DELETE FROM userid WHERE ubid = :1", ubid);
if (err)
goto leave;
+ if (cert)
+ {
+ err = run_sql_statement_bind_ubid
+ ("DELETE FROM issuer WHERE ubid = :1", ubid);
+ if (err)
+ goto leave;
+ }
- kinfo = &info.primary;
- err = store_into_fingerprint (ubid, 0, kinfo->grip,
- kid_from_mem (kinfo->keyid),
- kinfo->fpr, kinfo->fprlen);
- if (err)
- goto leave;
-
- if (info.nsubkeys)
+ if (cert) /* X.509 */
{
- int subkey = 1;
- for (kinfo = &info.subkeys; kinfo; kinfo = kinfo->next, subkey++)
+ unsigned char grip[KEYGRIP_LEN];
+ int idx;
+
+ err = be_get_x509_keygrip (cert, grip);
+ if (err)
+ goto leave;
+
+ /* Note that for X.509 the UBID is also the fingerprint. */
+ err = store_into_fingerprint (ubid, 0, grip,
+ kid_from_mem (ubid+12),
+ ubid, UBID_LEN);
+ if (err)
+ goto leave;
+
+ /* Now the issuer. */
+ sn = be_get_x509_serial (cert);
+ if (!sn)
{
- err = store_into_fingerprint (ubid, subkey, kinfo->grip,
- kid_from_mem (kinfo->keyid),
- kinfo->fpr, kinfo->fprlen);
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ dn = ksba_cert_get_issuer (cert, 0);
+ if (!dn)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = store_into_issuer (ubid, sn, dn);
+ if (err)
+ goto leave;
+
+ /* Loop over the subject and alternate subjects. */
+ for (idx=0; (xfree (dn), dn = ksba_cert_get_subject (cert, idx)); idx++)
+ {
+ /* In the case that the same email address is in the
+ * subject DN as well as in an alternate subject name
+ * we avoid printing it a second time. */
+ if (kludge_mbox && !strcmp (kludge_mbox, dn))
+ continue;
+
+ err = store_into_userid (ubid, PUBKEY_TYPE_X509, dn, NULL);
if (err)
goto leave;
- }
- }
- if (info.nuids)
+ if (!idx)
+ {
+ kludge_mbox = _keybox_x509_email_kludge (dn);
+ if (kludge_mbox)
+ {
+ err = store_into_userid (ubid, PUBKEY_TYPE_X509,
+ dn, kludge_mbox);
+ if (err)
+ goto leave;
+ }
+ }
+ } /* end loop over the subjects. */
+ }
+ else /* OpenPGP */
{
- struct _keybox_openpgp_uid_info *u;
+ struct _keybox_openpgp_key_info *kinfo;
- u = &info.uids;
- do
+ kinfo = &info.primary;
+ err = store_into_fingerprint (ubid, 0, kinfo->grip,
+ kid_from_mem (kinfo->keyid),
+ kinfo->fpr, kinfo->fprlen);
+ if (err)
+ goto leave;
+
+ if (info.nsubkeys)
{
- log_assert (u->off <= bloblen);
- log_assert (u->off + u->len <= bloblen);
- {
- char *uid = xtrymalloc (u->len + 1);
- if (!uid)
- {
- err = gpg_error_from_syserror ();
+ int subkey = 1;
+ for (kinfo = &info.subkeys; kinfo; kinfo = kinfo->next, subkey++)
+ {
+ err = store_into_fingerprint (ubid, subkey, kinfo->grip,
+ kid_from_mem (kinfo->keyid),
+ kinfo->fpr, kinfo->fprlen);
+ if (err)
goto leave;
+ }
+ }
+
+ if (info.nuids)
+ {
+ struct _keybox_openpgp_uid_info *u;
+
+ u = &info.uids;
+ do
+ {
+ log_assert (u->off <= bloblen);
+ log_assert (u->off + u->len <= bloblen);
+ {
+ char *uid = xtrymalloc (u->len + 1);
+ if (!uid)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (uid, (const unsigned char *)blob + u->off, u->len);
+ uid[u->len] = 0;
+ /* Note that we ignore embedded zeros in the user id;
+ * this is what we do all over the place. */
+ err = store_into_userid (ubid, pktype, uid, NULL);
+ xfree (uid);
}
- memcpy (uid, (const unsigned char *)blob + u->off, u->len);
- uid[u->len] = 0;
- /* Note that we ignore embedded zeros in the user id; this
- * is what we do all over the place. */
- err = store_into_userid (ubid, pktype, uid);
- xfree (uid);
- }
- if (err)
- goto leave;
+ if (err)
+ goto leave;
- u = u->next;
+ u = u->next;
+ }
+ while (u);
}
- while (u);
}
leave:
@@ -1285,6 +1449,11 @@ be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd,
release_mutex ();
if (info_valid)
_keybox_destroy_openpgp_info (&info);
+ if (cert)
+ ksba_cert_release (cert);
+ ksba_free (dn);
+ xfree (sn);
+ xfree (kludge_mbox);
return err;
}
diff --git a/kbx/backend-support.c b/kbx/backend-support.c
index c8965da9a..7a7d11b90 100644
--- a/kbx/backend-support.c
+++ b/kbx/backend-support.c
@@ -282,3 +282,73 @@ be_ubid_from_blob (const void *blob, size_t bloblen,
return err;
}
+
+
+
+/* Return a certificates serial number in hex encoding. Caller must
+ * free the returned string. NULL is returned on error but ERRNO
+ * might not be set if the certificate and thus Libksba is broken. */
+char *
+be_get_x509_serial (ksba_cert_t cert)
+{
+ const char *p;
+ unsigned long n;
+ char *endp;
+
+ p = (const char *)ksba_cert_get_serial (cert);
+ if (!p)
+ {
+ log_debug ("oops: Libksba returned a certificate w/o a serial\n");
+ return NULL;
+ }
+
+ if (*p != '(')
+ {
+ log_debug ("oops: Libksba returned an invalid s-expression\n");
+ return NULL;
+ }
+
+ p++;
+ n = strtoul (p, &endp, 10);
+ p = endp;
+ if (*p != ':')
+ {
+ log_debug ("oops: Libksba returned an invalid s-expression\n");
+ return NULL;
+ }
+ p++;
+
+ return bin2hex (p, n, NULL);
+}
+
+
+/* Return the keygrip for the X.509 certificate CERT. The grip is
+ * stored at KEYGRIP which must have been allocated by the caller
+ * with a size of KEYGRIP_LEN. */
+gpg_error_t
+be_get_x509_keygrip (ksba_cert_t cert, unsigned char *keygrip)
+{
+ gpg_error_t err;
+ size_t n;
+ ksba_sexp_t p;
+ gcry_sexp_t s_pkey;
+
+ p = ksba_cert_get_public_key (cert);
+ if (!p)
+ return gpg_error (GPG_ERR_NO_PUBKEY);
+ n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+ if (!n)
+ {
+ ksba_free (p);
+ return gpg_error (GPG_ERR_NO_PUBKEY);
+ }
+ err = gcry_sexp_sscan (&s_pkey, NULL, (char*)p, n);
+ ksba_free (p);
+ if (err)
+ return err;
+
+ if (!gcry_pk_get_keygrip (s_pkey, keygrip))
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+ gcry_sexp_release (s_pkey);
+ return err;
+}
diff --git a/kbx/backend.h b/kbx/backend.h
index 70988419a..7086ac900 100644
--- a/kbx/backend.h
+++ b/kbx/backend.h
@@ -20,6 +20,7 @@
#ifndef KBX_BACKEND_H
#define KBX_BACKEND_H
+#include <ksba.h>
#include "keybox-search-desc.h"
/* Forward declaration of the keybox handle type. */
@@ -118,6 +119,8 @@ gpg_error_t be_return_pubkey (ctrl_t ctrl, const void *buffer, size_t buflen,
int be_is_x509_blob (const unsigned char *blob, size_t bloblen);
gpg_error_t be_ubid_from_blob (const void *blob, size_t bloblen,
enum pubkey_types *r_pktype, char *r_ubid);
+char *be_get_x509_serial (ksba_cert_t cert);
+gpg_error_t be_get_x509_keygrip (ksba_cert_t cert, unsigned char *keygrip);
/*-- backend-cache.c --*/
diff --git a/kbx/keybox-blob.c b/kbx/keybox-blob.c
index 1210f3773..a0ba40502 100644
--- a/kbx/keybox-blob.c
+++ b/kbx/keybox-blob.c
@@ -841,12 +841,10 @@ _keybox_create_openpgp_blob (KEYBOXBLOB *r_blob,
}
-#ifdef KEYBOX_WITH_X509
-
/* Return an allocated string with the email address extracted from a
DN. Note hat we use this code also in ../sm/keylist.c. */
-static char *
-x509_email_kludge (const char *name)
+char *
+_keybox_x509_email_kludge (const char *name)
{
const char *p, *string;
unsigned char *buf;
@@ -887,6 +885,8 @@ x509_email_kludge (const char *name)
+#ifdef KEYBOX_WITH_X509
+
/* Note: We should move calculation of the digest into libksba and
remove that parameter */
int
@@ -965,7 +965,7 @@ _keybox_create_x509_blob (KEYBOXBLOB *r_blob, ksba_cert_t cert,
names = tmp;
}
names[blob->nuids++] = p;
- if (!i && (p=x509_email_kludge (p)))
+ if (!i && (p=_keybox_x509_email_kludge (p)))
names[blob->nuids++] = p; /* due to !i we don't need to check bounds*/
}
diff --git a/kbx/keybox-defs.h b/kbx/keybox-defs.h
index 354d5fd11..da23289d3 100644
--- a/kbx/keybox-defs.h
+++ b/kbx/keybox-defs.h
@@ -149,6 +149,8 @@ gpg_error_t _keybox_create_openpgp_blob (KEYBOXBLOB *r_blob,
const unsigned char *image,
size_t imagelen,
int as_ephemeral);
+char *_keybox_x509_email_kludge (const char *name);
+
#ifdef KEYBOX_WITH_X509
int _keybox_create_x509_blob (KEYBOXBLOB *r_blob, ksba_cert_t cert,
unsigned char *sha1_digest, int as_ephemeral);