From 0e892bda4e0bf9db9116d7d5585d4e7b0d2eae57 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 22 Sep 2020 15:42:12 +0200 Subject: [PATCH] keyboxd: Extend PUBKEY_INFO status line with an uid ordinal. * kbx/backend-sqlite.c (table_definitions): Add column UINO to userids. (be_sqlite_local_s): Add fields select_col_uidno and select_col_subkey. (run_select_statement): Also select subkey or uidno column. (be_sqlite_search): Return their values. (store_into_userid): Store the UIDNO. * kbx/backend-support.c (be_return_pubkey): Extend PUBKEY_INFO. -- For an existing database adding the new column to the table userid is straightforward. However if the original version of the schema used an integer for the keyid column, that column has likely be renamed. Make sure that the NOT NULL constraint has also be removed; check the SQLite documentation on how this can be done. Signed-off-by: Werner Koch --- doc/DETAILS | 6 ++- kbx/backend-cache.c | 5 +- kbx/backend-kbx.c | 3 +- kbx/backend-sqlite.c | 110 +++++++++++++++++++++++++++++++++--------- kbx/backend-support.c | 7 +-- kbx/backend.h | 3 +- 6 files changed, 104 insertions(+), 30 deletions(-) diff --git a/doc/DETAILS b/doc/DETAILS index 98f4af511..6eefb8b4f 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -1171,7 +1171,7 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB: Status codes are also used between the components of the GnuPG system via the Assuan S lines. Some of them are documented here: -*** PUBKEY_INFO +*** PUBKEY_INFO The type of the public key in the following D-lines or communicated via a pipe. is the value of =enum pubkey_types= and the Unique Blob ID (UBID) which is the fingerprint of @@ -1183,6 +1183,10 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB: byte is either '-' for standard key or 'e' for an ephemeral key. The second byte is either '-' or 'r' for a known revoked key. + and are the ordinal numbers for the the user id or + public key which matches the search criteria. A value of 0 means + not known. + *** KEYPAIRINFO [] [] [] This status is emitted by scdaemon and gpg-agent to convey brief diff --git a/kbx/backend-cache.c b/kbx/backend-cache.c index c52bcc5de..f30fa7dcb 100644 --- a/kbx/backend-cache.c +++ b/kbx/backend-cache.c @@ -1016,7 +1016,8 @@ be_cache_search (ctrl_t ctrl, backend_handle_t backend_hd, db_request_t request, if (b) { err = be_return_pubkey (ctrl, b->data, b->datalen, - b->pktype, desc[n].u.ubid, 0, 0); + b->pktype, desc[n].u.ubid, + 0, 0, 0, 0); blob_unref (b); reqpart->cache_seqno.ubid++; } @@ -1058,7 +1059,7 @@ be_cache_search (ctrl_t ctrl, backend_handle_t backend_hd, db_request_t request, if (b) { err = be_return_pubkey (ctrl, b->data, b->datalen, - PUBKEY_TYPE_OPGP, bl->ubid, 0, 0); + PUBKEY_TYPE_OPGP, bl->ubid, 0, 0, 0, 0); blob_unref (b); } else diff --git a/kbx/backend-kbx.c b/kbx/backend-kbx.c index b48795296..ca72169a0 100644 --- a/kbx/backend-kbx.c +++ b/kbx/backend-kbx.c @@ -278,7 +278,8 @@ be_kbx_search (ctrl_t ctrl, backend_handle_t backend_hd, db_request_t request, if (err) goto leave; /* FIXME: Return the ephemeral flag. */ - err = be_return_pubkey (ctrl, buffer, buflen, pubkey_type, ubid, 0, 0); + err = be_return_pubkey (ctrl, buffer, buflen, pubkey_type, ubid, + 0, 0, 0, 0); if (!err) be_cache_pubkey (ctrl, ubid, buffer, buflen, pubkey_type); xfree (buffer); diff --git a/kbx/backend-sqlite.c b/kbx/backend-sqlite.c index ab68163ab..118c69a2a 100644 --- a/kbx/backend-sqlite.c +++ b/kbx/backend-sqlite.c @@ -67,6 +67,10 @@ struct be_sqlite_local_s /* The statement object of the current select command. */ sqlite3_stmt *select_stmt; + /* The column numbers for UIDNO and SUBKEY or 0. */ + int select_col_uidno; + int select_col_subkey; + /* The search mode represented by the current select command. */ KeydbSearchMode select_mode; @@ -134,7 +138,8 @@ static struct "kid BLOB NOT NULL," /* The keygrip for this key. */ "keygrip BLOB NOT NULL," - /* 0 = primary or X.509, > 0 = subkey. */ + /* 0 = primary or X.509, > 0 = subkey. Also used as + * order number for the keys similar to uidno. */ "subkey INTEGER NOT NULL," /* The Unique Blob ID (possibly truncated fingerprint). */ "ubid BLOB NOT NULL REFERENCES pubkey" @@ -153,6 +158,11 @@ static struct "addrspec TEXT," /* The type of the public key: 1 = openpgp, 2 = X.509. */ "type INTEGER NOT NULL," + /* The order number of the user id within the keyblock or + * certificates. For X.509 0 is reserved for the issuer, 1 the + * subject, 2 and up the altSubjects. For OpenPGP this starts + * with 1 for the first user id in the keyblock. */ + "uidno INTEGER NOT NULL," /* The Unique Blob ID (possibly truncated fingerprint). */ "ubid BLOB NOT NULL REFERENCES pubkey" ")" }, @@ -726,6 +736,7 @@ run_select_statement (ctrl_t ctrl, be_sqlite_local_t ctx, } + ctx->select_col_uidno = ctx->select_col_subkey = 0; switch (desc[descidx].mode) { case KEYDB_SEARCH_MODE_NONE: @@ -734,9 +745,10 @@ run_select_statement (ctrl_t ctrl, be_sqlite_local_t ctx, break; case KEYDB_SEARCH_MODE_EXACT: + ctx->select_col_uidno = 5; if (!ctx->select_stmt) err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked," - " p.keyblob" + " p.keyblob, u.uidno" " FROM pubkey as p, userid as u" " WHERE p.ubid = u.ubid AND u.uid = ?1", extra, &ctx->select_stmt); @@ -744,9 +756,10 @@ run_select_statement (ctrl_t ctrl, be_sqlite_local_t ctx, err = run_sql_bind_text (ctx->select_stmt, 1, desc[descidx].u.name); break; case KEYDB_SEARCH_MODE_MAIL: + ctx->select_col_uidno = 5; if (!ctx->select_stmt) err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked," - " p.keyblob" + " p.keyblob, u.uidno" " FROM pubkey as p, userid as u" " WHERE p.ubid = u.ubid AND u.addrspec = ?1", extra, &ctx->select_stmt); @@ -755,9 +768,10 @@ run_select_statement (ctrl_t ctrl, be_sqlite_local_t ctx, break; case KEYDB_SEARCH_MODE_MAILSUB: + ctx->select_col_uidno = 5; if (!ctx->select_stmt) err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked," - " p.keyblob" + " p.keyblob, u.uidno" " FROM pubkey as p, userid as u" " WHERE p.ubid = u.ubid AND u.addrspec LIKE ?1", extra, &ctx->select_stmt); @@ -767,9 +781,10 @@ run_select_statement (ctrl_t ctrl, be_sqlite_local_t ctx, break; case KEYDB_SEARCH_MODE_SUBSTR: + ctx->select_col_uidno = 5; if (!ctx->select_stmt) err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked," - " p.keyblob" + " p.keyblob, u.uidno" " FROM pubkey as p, userid as u" " WHERE p.ubid = u.ubid AND u.uid LIKE ?1", extra, &ctx->select_stmt); @@ -829,21 +844,24 @@ run_select_statement (ctrl_t ctrl, be_sqlite_local_t ctx, break; case KEYDB_SEARCH_MODE_SUBJECT: - err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked," - " p.keyblob" - " FROM pubkey as p, userid as u" - " WHERE p.ubid = u.ubid" - " AND u.uid = $1", - extra, &ctx->select_stmt); + ctx->select_col_uidno = 5; + if (!ctx->select_stmt) + err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked," + " p.keyblob, u.uidno" + " 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: + ctx->select_col_subkey = 5; if (!ctx->select_stmt) err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral," - " p.revoked, p.keyblob" + " p.revoked, p.keyblob, f.subkey" " FROM pubkey as p, fingerprint as f" " WHERE p.ubid = f.ubid AND" " substr(f.kid,5) = ?1", @@ -855,9 +873,10 @@ run_select_statement (ctrl_t ctrl, be_sqlite_local_t ctx, break; case KEYDB_SEARCH_MODE_LONG_KID: + ctx->select_col_subkey = 5; if (!ctx->select_stmt) err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral," - " p.revoked, p.keyblob" + " p.revoked, p.keyblob, f.subkey" " FROM pubkey as p, fingerprint as f" " WHERE p.ubid = f.ubid AND f.kid = ?1", extra, &ctx->select_stmt); @@ -868,9 +887,10 @@ run_select_statement (ctrl_t ctrl, be_sqlite_local_t ctx, break; case KEYDB_SEARCH_MODE_FPR: + ctx->select_col_subkey = 5; if (!ctx->select_stmt) err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral," - " p.revoked, p.keyblob" + " p.revoked, p.keyblob, f.subkey" " FROM pubkey as p, fingerprint as f" " WHERE p.ubid = f.ubid AND f.fpr = ?1", extra, &ctx->select_stmt); @@ -880,9 +900,10 @@ run_select_statement (ctrl_t ctrl, be_sqlite_local_t ctx, break; case KEYDB_SEARCH_MODE_KEYGRIP: + ctx->select_col_subkey = 5; if (!ctx->select_stmt) err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked," - " p.keyblob" + " p.keyblob, f.subkey" " FROM pubkey as p, fingerprint as f" " WHERE p.ubid = f.ubid AND f.keygrip = ?1", extra, &ctx->select_stmt); @@ -994,6 +1015,7 @@ be_sqlite_search (ctrl_t ctrl, size_t keybloblen; enum pubkey_types pubkey_type; int is_ephemeral, is_revoked; + int pk_no, uid_no; ubid = sqlite3_column_blob (ctx->select_stmt, 0); n = sqlite3_column_bytes (ctx->select_stmt, 0); @@ -1063,8 +1085,46 @@ be_sqlite_search (ctrl_t ctrl, } keybloblen = n; + if (ctx->select_col_uidno) + { + n = sqlite3_column_int (ctx->select_stmt, ctx->select_col_uidno); + if (!n && sqlite3_errcode (database_hd) == SQLITE_NOMEM) + { + err = gpg_error (gpg_err_code_from_sqlite (SQLITE_NOMEM)); + show_sqlstmt (ctx->select_stmt); + log_error ("error in returned SQL column UIDNO: %s)\n", + gpg_strerror (err)); + uid_no = 0; + } + else if (n < 0) + uid_no = 0; + else + uid_no = n + 1; + } + else + uid_no = 0; + + if (ctx->select_col_subkey) + { + n = sqlite3_column_int (ctx->select_stmt, ctx->select_col_subkey); + if (!n && sqlite3_errcode (database_hd) == SQLITE_NOMEM) + { + err = gpg_error (gpg_err_code_from_sqlite (SQLITE_NOMEM)); + show_sqlstmt (ctx->select_stmt); + log_error ("error in returned SQL column SUBKEY: %s)\n", + gpg_strerror (err)); + goto leave; + } + else if (n < 0) + pk_no = 0; + else + pk_no = n + 1; + } + else + pk_no = 0; + err = be_return_pubkey (ctrl, keyblob, keybloblen, pubkey_type, - ubid, is_ephemeral, is_revoked); + ubid, is_ephemeral, is_revoked, uid_no, pk_no); if (!err) be_cache_pubkey (ctrl, ubid, keyblob, keybloblen, pubkey_type); } @@ -1173,15 +1233,15 @@ store_into_fingerprint (const unsigned char *ubid, int subkey, * value extracted from UID. */ static gpg_error_t store_into_userid (const unsigned char *ubid, enum pubkey_types pktype, - const char *uid, const char *override_mbox) + const char *uid, int uidno, const char *override_mbox) { gpg_error_t err; const char *sqlstr; sqlite3_stmt *stmt = NULL; char *addrspec = NULL; - sqlstr = ("INSERT OR REPLACE INTO userid(uid,addrspec,type,ubid)" - " VALUES(:1,:2,:3,:4)"); + sqlstr = ("INSERT OR REPLACE INTO userid(uid,addrspec,type,ubid,uidno)" + " VALUES(:1,:2,:3,:4,:5)"); err = run_sql_prepare (sqlstr, NULL, &stmt); if (err) goto leave; @@ -1204,6 +1264,9 @@ store_into_userid (const unsigned char *ubid, enum pubkey_types pktype, if (err) goto leave; err = run_sql_bind_blob (stmt, 4, ubid, UBID_LEN); + if (err) + goto leave; + err = run_sql_bind_int (stmt, 5, uidno); if (err) goto leave; @@ -1275,6 +1338,7 @@ be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd, char *sn = NULL; char *dn = NULL; char *kludge_mbox = NULL; + int uidno; (void)ctrl; @@ -1382,6 +1446,7 @@ be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd, goto leave; /* Loop over the subject and alternate subjects. */ + uidno = 0; for (idx=0; (xfree (dn), dn = ksba_cert_get_subject (cert, idx)); idx++) { /* In the case that the same email address is in the @@ -1390,7 +1455,7 @@ be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd, if (kludge_mbox && !strcmp (kludge_mbox, dn)) continue; - err = store_into_userid (ubid, PUBKEY_TYPE_X509, dn, NULL); + err = store_into_userid (ubid, PUBKEY_TYPE_X509, dn, ++uidno, NULL); if (err) goto leave; @@ -1400,7 +1465,7 @@ be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd, if (kludge_mbox) { err = store_into_userid (ubid, PUBKEY_TYPE_X509, - dn, kludge_mbox); + dn, ++uidno, kludge_mbox); if (err) goto leave; } @@ -1435,6 +1500,7 @@ be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd, { struct _keybox_openpgp_uid_info *u; + uidno = 0; u = &info.uids; do { @@ -1451,7 +1517,7 @@ be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd, 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); + err = store_into_userid (ubid, pktype, uid, ++uidno, NULL); xfree (uid); } if (err) diff --git a/kbx/backend-support.c b/kbx/backend-support.c index a35088c74..1821129ea 100644 --- a/kbx/backend-support.c +++ b/kbx/backend-support.c @@ -167,16 +167,17 @@ be_find_request_part (backend_handle_t backend_hd, db_request_t request, gpg_error_t be_return_pubkey (ctrl_t ctrl, const void *buffer, size_t buflen, enum pubkey_types pubkey_type, const unsigned char *ubid, - int is_ephemeral, int is_revoked) + int is_ephemeral, int is_revoked, int uid_no, int pk_no) { gpg_error_t err; char hexubid[2*UBID_LEN+1]; bin2hex (ubid, UBID_LEN, hexubid); - err = status_printf (ctrl, "PUBKEY_INFO", "%d %s %c%c", + err = status_printf (ctrl, "PUBKEY_INFO", "%d %s %c%c %d %d", pubkey_type, hexubid, is_ephemeral? 'e':'-', - is_revoked? 'r':'-' ); + is_revoked? 'r':'-', + uid_no, pk_no); if (err) goto leave; diff --git a/kbx/backend.h b/kbx/backend.h index a857fc77c..a241490a7 100644 --- a/kbx/backend.h +++ b/kbx/backend.h @@ -116,7 +116,8 @@ gpg_error_t be_find_request_part (backend_handle_t backend_hd, gpg_error_t be_return_pubkey (ctrl_t ctrl, const void *buffer, size_t buflen, enum pubkey_types pubkey_type, const unsigned char *ubid, - int is_ephemeral, int is_revoked); + int is_ephemeral, int is_revoked, + int uidno, int pkno); 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);