From e7068bf92ec5ca5d440346d43a382c1f625b924d Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 24 Jul 2017 20:05:28 +0200 Subject: [PATCH] gpg: Store key origin info for new DANE and WKD retrieved keys. * g10/import.c (apply_meta_data): Remove arg 'merge'. Add arg 'url'. Implement WKD and DANE key origin. (import_keys_internal): Add arg 'url' and change all callers. (import_keys_es_stream): Ditto. (import): Ditto. (import_one): Ditto. * g10/keylist.c (list_keyblock_print): Fix update URL printing. * g10/call-dirmngr.c (gpg_dirmngr_wkd_get): Add arg 'r_url' to return the SOURCE. Pass ks_status_cb to assuan_transact. * g10/keyserver.c (keyserver_import_wkd): Get that URL and pass it to the import function. -- Note that this only for new keys. Merging this info will be added soon. Signed-off-by: Werner Koch --- g10/call-dirmngr.c | 29 +++++++++++++---- g10/call-dirmngr.h | 2 +- g10/gpg.c | 2 +- g10/import.c | 81 +++++++++++++++++++++++++++++++++++----------- g10/keylist.c | 6 ++-- g10/keyserver.c | 14 ++++---- g10/main.h | 5 +-- 7 files changed, 101 insertions(+), 38 deletions(-) diff --git a/g10/call-dirmngr.c b/g10/call-dirmngr.c index 76fa07257..9bae59f47 100644 --- a/g10/call-dirmngr.c +++ b/g10/call-dirmngr.c @@ -41,7 +41,8 @@ #include "call-dirmngr.h" -/* Parameter structure used to gather status info. */ +/* Parameter structure used to gather status info. Note that it is + * also used for WKD requests. */ struct ks_status_parm_s { const char *keyword; /* Look for this keyword or NULL for "SOURCE". */ @@ -368,7 +369,7 @@ clear_context_flags (ctrl_t ctrl, assuan_context_t ctx) -/* Status callback for ks_list, ks_get and ks_search. */ +/* Status callback for ks_list, ks_get, ks_search, and wkd_get */ static gpg_error_t ks_status_cb (void *opaque, const char *line) { @@ -1317,17 +1318,24 @@ gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid, /* Ask the dirmngr to retrieve a key via the Web Key Directory * protocol. If QUICK is set the dirmngr is advised to use a shorter - * timeout. On success a new estream with the key is stored at R_KEY. + * timeout. On success a new estream with the key stored at R_KEY and the + * url of the lookup (if any) stored at R_URL. Note that */ gpg_error_t -gpg_dirmngr_wkd_get (ctrl_t ctrl, const char *name, int quick, estream_t *r_key) +gpg_dirmngr_wkd_get (ctrl_t ctrl, const char *name, int quick, + estream_t *r_key, char **r_url) { gpg_error_t err; assuan_context_t ctx; - struct dns_cert_parm_s parm; + struct ks_status_parm_s stparm = { NULL }; + struct dns_cert_parm_s parm = { NULL }; char *line = NULL; - memset (&parm, 0, sizeof parm); + if (r_key) + *r_key = NULL; + + if (r_url) + *r_url = NULL; err = open_context (ctrl, &ctx); if (err) @@ -1352,7 +1360,7 @@ gpg_dirmngr_wkd_get (ctrl_t ctrl, const char *name, int quick, estream_t *r_key) goto leave; } err = assuan_transact (ctx, line, dns_cert_data_cb, &parm, - NULL, NULL, NULL, &parm); + NULL, NULL, ks_status_cb, &stparm); if (err) goto leave; @@ -1363,7 +1371,14 @@ gpg_dirmngr_wkd_get (ctrl_t ctrl, const char *name, int quick, estream_t *r_key) parm.memfp = NULL; } + if (r_url) + { + *r_url = stparm.source; + stparm.source = NULL; + } + leave: + xfree (stparm.source); xfree (parm.fpr); xfree (parm.url); es_fclose (parm.memfp); diff --git a/g10/call-dirmngr.h b/g10/call-dirmngr.h index 95a8c4a3b..285c4cb4d 100644 --- a/g10/call-dirmngr.h +++ b/g10/call-dirmngr.h @@ -41,7 +41,7 @@ gpg_error_t gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid, unsigned char **r_fpr, size_t *r_fprlen, char **r_url); gpg_error_t gpg_dirmngr_wkd_get (ctrl_t ctrl, const char *name, int quick, - estream_t *r_key); + estream_t *r_key, char **r_url); #endif /*GNUPG_G10_CALL_DIRMNGR_H*/ diff --git a/g10/gpg.c b/g10/gpg.c index e32e14a9d..7495e179d 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -4515,7 +4515,7 @@ main (int argc, char **argv) opt.import_options |= IMPORT_FAST; /* fall through */ case aImport: import_keys (ctrl, argc? argv:NULL, argc, NULL, - opt.import_options, opt.key_origin); + opt.import_options, opt.key_origin, NULL); break; /* TODO: There are a number of command that use this same diff --git a/g10/import.c b/g10/import.c index c87f49b06..e3c8c37b4 100644 --- a/g10/import.c +++ b/g10/import.c @@ -97,7 +97,8 @@ struct import_filter_s import_filter; static int import (ctrl_t ctrl, IOBUF inp, const char* fname, struct import_stats_s *stats, unsigned char **fpr, size_t *fpr_len, unsigned int options, - import_screener_t screener, void *screener_arg, int origin); + import_screener_t screener, void *screener_arg, + int origin, const char *url); static int read_block (IOBUF a, int with_meta, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys); static void revocation_present (ctrl_t ctrl, kbnode_t keyblock); @@ -107,7 +108,7 @@ static int import_one (ctrl_t ctrl, unsigned char **fpr, size_t *fpr_len, unsigned int options, int from_sk, int silent, import_screener_t screener, void *screener_arg, - int origin); + int origin, const char *url); static int import_secret_one (ctrl_t ctrl, kbnode_t keyblock, struct import_stats_s *stats, int batch, unsigned int options, int for_migration, @@ -432,7 +433,7 @@ import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, - int origin) + int origin, const char *url) { int i; int rc = 0; @@ -444,7 +445,7 @@ import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames, if (inp) { rc = import (ctrl, inp, "[stream]", stats, fpr, fpr_len, options, - screener, screener_arg, origin); + screener, screener_arg, origin, url); } else { @@ -469,7 +470,7 @@ import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames, else { rc = import (ctrl, inp2, fname, stats, fpr, fpr_len, options, - screener, screener_arg, origin); + screener, screener_arg, origin, url); iobuf_close (inp2); /* Must invalidate that ugly cache to actually close it. */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); @@ -503,10 +504,11 @@ import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames, void import_keys (ctrl_t ctrl, char **fnames, int nnames, - import_stats_t stats_handle, unsigned int options, int origin) + import_stats_t stats_handle, unsigned int options, + int origin, const char *url) { import_keys_internal (ctrl, NULL, fnames, nnames, stats_handle, - NULL, NULL, options, NULL, NULL, origin); + NULL, NULL, options, NULL, NULL, origin, url); } @@ -516,7 +518,7 @@ import_keys_es_stream (ctrl_t ctrl, estream_t fp, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, - int origin) + int origin, const char *url) { int rc; iobuf_t inp; @@ -531,7 +533,7 @@ import_keys_es_stream (ctrl_t ctrl, estream_t fp, rc = import_keys_internal (ctrl, inp, NULL, 0, stats_handle, fpr, fpr_len, options, - screener, screener_arg, origin); + screener, screener_arg, origin, url); iobuf_close (inp); return rc; @@ -541,7 +543,8 @@ import_keys_es_stream (ctrl_t ctrl, estream_t fp, static int import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats, unsigned char **fpr,size_t *fpr_len, unsigned int options, - import_screener_t screener, void *screener_arg, int origin) + import_screener_t screener, void *screener_arg, + int origin, const char *url) { PACKET *pending_pkt = NULL; kbnode_t keyblock = NULL; /* Need to initialize because gcc can't @@ -569,7 +572,7 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats, if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) rc = import_one (ctrl, keyblock, stats, fpr, fpr_len, options, 0, 0, - screener, screener_arg, origin); + screener, screener_arg, origin, url); else if (keyblock->pkt->pkttype == PKT_SECRET_KEY) rc = import_secret_one (ctrl, keyblock, stats, opt.batch, options, 0, @@ -1379,11 +1382,53 @@ apply_drop_sig_filter (ctrl_t ctrl, kbnode_t keyblock, recsel_expr_t selector) /* Apply meta data to KEYBLOCK. This sets the origin of the key to - * ORIGIN. If MERGE is true KEYBLOCK has been updated and the meta - * data is merged and not simply inserted. */ + * ORIGIN and the updateurl to URL. Note that this function is only + * used for a new key, that is not when we are merging keys. */ static gpg_error_t -apply_meta_data (kbnode_t keyblock, int merge, int origin) +apply_meta_data (kbnode_t keyblock, int origin, const char *url) { + kbnode_t node; + u32 curtime = make_timestamp (); + + for (node = keyblock; node; node = node->next) + { + if (is_deleted_kbnode (node)) + ; + else if (node->pkt->pkttype == PKT_PUBLIC_KEY + && (origin == KEYORG_WKD || origin == KEYORG_DANE)) + { + /* For WKD and DANE we insert origin information also for + * the key but we don't record the URL because we have have + * no use for that: An update using a keyserver has higher + * precedence and will thus update this origin info. For + * refresh using WKD or DANE we need to go via the User ID + * anyway. Recall that we are only inserting a new key. */ + PKT_public_key *pk = node->pkt->pkt.public_key; + + pk->keyorg = origin; + pk->keyupdate = curtime; + } + else if (node->pkt->pkttype == PKT_USER_ID + && (origin == KEYORG_WKD || origin == KEYORG_DANE)) + { + /* We insert origin information on a UID only when we + * received them via the Web Key Directory or a DANE record. + * The key we receive here from the WKD has been filtered to + * contain only the user ID as looked up in the WKD. For a + * DANE origin we this should also be the case. Thus we + * will see here only one user id. */ + PKT_user_id *uid = node->pkt->pkt.user_id; + + uid->keyorg = origin; + uid->keyupdate = curtime; + if (url) + { + uid->updateurl = xtrystrdup (url); + if (!uid->updateurl) + return gpg_error_from_syserror (); + } + } + } return 0; } @@ -1395,7 +1440,7 @@ apply_meta_data (kbnode_t keyblock, int merge, int origin) * the internal errorcount, so that invalid input can be detected by * programs which called gpg. If SILENT is no messages are printed - * even most error messages are suppressed. ORIGIN is the origin of - * the key (0 for unknown). + * the key (0 for unknown) and URL the corresponding URL. */ static int import_one (ctrl_t ctrl, @@ -1403,7 +1448,7 @@ import_one (ctrl_t ctrl, unsigned char **fpr, size_t *fpr_len, unsigned int options, int from_sk, int silent, import_screener_t screener, void *screener_arg, - int origin) + int origin, const char *url) { PKT_public_key *pk; PKT_public_key *pk_orig = NULL; @@ -1627,7 +1672,7 @@ import_one (ctrl_t ctrl, * and thus the address of KEYBLOCK won't change. */ if ( !(options & IMPORT_RESTORE) ) { - rc = apply_meta_data (keyblock, 0, origin); + rc = apply_meta_data (keyblock, origin, url); if (rc) { log_error ("apply_meta_data failed: %s\n", gpg_strerror (rc)); @@ -2313,7 +2358,7 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock, the secret keys. FIXME? */ import_one (ctrl, pub_keyblock, stats, NULL, NULL, options, 1, for_migration, - screener, screener_arg, 0); + screener, screener_arg, 0, NULL); /* Fixme: We should check for an invalid keyblock and cancel the secret key import in this case. */ diff --git a/g10/keylist.c b/g10/keylist.c index 37a26dc9a..7203d160b 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -1012,9 +1012,9 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, key_origin_string (uid->keyorg), mk_datestr (updatestr, sizeof updatestr, uid->keyupdate), - pk->updateurl? "url=":""); - if (pk->updateurl) - print_utf8_string (es_stdout, pk->updateurl); + uid->updateurl? "url=":""); + if (uid->updateurl) + print_utf8_string (es_stdout, uid->updateurl); es_putc ('\n', es_stdout); } diff --git a/g10/keyserver.c b/g10/keyserver.c index a84961e37..9586448fa 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -1747,7 +1747,7 @@ keyserver_get_chunk (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc, (opt.keyserver_options.import_options | IMPORT_NO_SECKEY), keyserver_retrieval_screener, &screenerarg, - 0 /* FIXME? */); + 0 /* FIXME? */, NULL); } es_fclose (datastream); xfree (source); @@ -1878,7 +1878,7 @@ keyserver_fetch (ctrl_t ctrl, strlist_t urilist, int origin) stats_handle = import_new_stats_handle(); import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL, opt.keyserver_options.import_options, - NULL, NULL, origin); + NULL, NULL, origin, NULL); import_print_stats (stats_handle); import_release_stats_handle (stats_handle); @@ -1945,7 +1945,7 @@ keyserver_import_cert (ctrl_t ctrl, const char *name, int dane_mode, if (!err) err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len, IMPORT_NO_SECKEY, - NULL, NULL, KEYORG_DANE); + NULL, NULL, KEYORG_DANE, NULL); restore_import_filter (save_filt); } } @@ -1954,7 +1954,7 @@ keyserver_import_cert (ctrl_t ctrl, const char *name, int dane_mode, err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len, (opt.keyserver_options.import_options | IMPORT_NO_SECKEY), - NULL, NULL, 0); + NULL, NULL, 0, NULL); } opt.no_armor=armor_status; @@ -2043,6 +2043,7 @@ keyserver_import_wkd (ctrl_t ctrl, const char *name, int quick, gpg_error_t err; char *mbox; estream_t key; + char *url = NULL; /* We want to work on the mbox. That is what dirmngr will do anyway * and we need the mbox for the import filter anyway. */ @@ -2055,7 +2056,7 @@ keyserver_import_wkd (ctrl_t ctrl, const char *name, int quick, return err; } - err = gpg_dirmngr_wkd_get (ctrl, mbox, quick, &key); + err = gpg_dirmngr_wkd_get (ctrl, mbox, quick, &key, &url); if (err) ; else if (key) @@ -2078,7 +2079,7 @@ keyserver_import_wkd (ctrl_t ctrl, const char *name, int quick, if (!err) err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len, IMPORT_NO_SECKEY, - NULL, NULL, KEYORG_WKD); + NULL, NULL, KEYORG_WKD, url); } @@ -2089,6 +2090,7 @@ keyserver_import_wkd (ctrl_t ctrl, const char *name, int quick, key = NULL; } + xfree (url); xfree (mbox); return err; } diff --git a/g10/main.h b/g10/main.h index e69ed9da7..87417ee37 100644 --- a/g10/main.h +++ b/g10/main.h @@ -352,13 +352,14 @@ void restore_import_filter (import_filter_t filt); gpg_error_t read_key_from_file (ctrl_t ctrl, const char *fname, kbnode_t *r_keyblock); void import_keys (ctrl_t ctrl, char **fnames, int nnames, - import_stats_t stats_hd, unsigned int options, int origin); + import_stats_t stats_hd, unsigned int options, + int origin, const char *url); int import_keys_es_stream (ctrl_t ctrl, estream_t fp, import_stats_t stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, - int origin); + int origin, const char *url); gpg_error_t import_old_secring (ctrl_t ctrl, const char *fname); import_stats_t import_new_stats_handle (void); void import_release_stats_handle (import_stats_t hd);