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>
This commit is contained in:
Werner Koch 2020-09-10 12:50:45 +02:00
parent 6fcc263c18
commit c9677d416e
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 314 additions and 70 deletions

View File

@ -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;
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)
{
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;
}
err = run_sql_statement_bind_ubid
("DELETE FROM issuer WHERE ubid = :1", ubid);
if (err)
goto leave;
}
if (info.nuids)
if (cert) /* X.509 */
{
struct _keybox_openpgp_uid_info *u;
unsigned char grip[KEYGRIP_LEN];
int idx;
u = &info.uids;
do
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)
{
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);
xfree (uid);
}
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;
u = u->next;
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_key_info *kinfo;
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)
{
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);
}
if (err)
goto leave;
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;
}

View File

@ -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;
}

View File

@ -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 --*/

View File

@ -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*/
}

View File

@ -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);