2 * keydb_hkp.c - Routines to store and fetch keys from another keyserver.
4 * Copyright 2013 Jonathan McDowell <noodles@earth.li>
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 51
17 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include <curl/curl.h>
27 #include "charfuncs.h"
29 #include "keystructs.h"
32 #include "onak-conf.h"
36 struct onak_hkp_dbctx {
41 static int hkp_parse_url(struct onak_hkp_dbctx *privctx, const char *url)
43 char proto[6], host[256];
48 proto[0] = host[0] = 0;
51 matched = sscanf(url, "%5[a-z]://%256[a-zA-Z0-9.]:%u", proto, host,
55 sscanf(url, "%256[a-zA-Z0-9.]:%u", host, &port);
59 logthing(LOGTHING_CRITICAL, "Couldn't parse HKP host: %s",
65 if (proto[0] == 0 || !strcmp(proto, "hkp")) {
69 snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
70 "http://%s:%u/pks", host, port);
71 } else if (!strcmp(proto, "hkps")) {
75 snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
76 "https://%s:%u/pks", host, port);
77 } else if (strcmp(proto, "http") && strcmp(proto, "https")) {
78 logthing(LOGTHING_CRITICAL, "Unknown HKP protocol: %s",
82 } else if (port == 0) {
83 snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
84 "%s://%s/pks", proto, host);
86 snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
87 "%s://%s:%u/pks", proto, host, port);
95 * Receive data from a CURL request and process it into a buffer context.
97 static size_t hkp_curl_recv_data(void *buffer, size_t size, size_t nmemb,
100 buffer_putchar(ctx, nmemb * size, buffer);
102 return (nmemb * size);
105 static int hkp_fetch_key_url(struct onak_dbctx *dbctx,
107 struct openpgp_publickey **publickey,
110 struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
111 struct openpgp_packet_list *packets = NULL;
113 struct buffer_ctx buf;
118 buf.buffer = malloc(8192);
120 curl_easy_setopt(privctx->curl, CURLOPT_URL, url);
121 curl_easy_setopt(privctx->curl, CURLOPT_WRITEFUNCTION,
123 curl_easy_setopt(privctx->curl, CURLOPT_WRITEDATA, &buf);
124 res = curl_easy_perform(privctx->curl);
128 dearmor_openpgp_stream(buffer_fetchchar, &buf, &packets);
129 count = parse_keys(packets, publickey);
130 free_packet_list(packets);
133 logthing(LOGTHING_ERROR, "Couldn't find key: %s (%d)",
134 curl_easy_strerror(res), res);
138 buf.offset = buf.size = 0;
145 * hkp_fetch_key_id - Given a keyid fetch the key from HKP server.
147 static int hkp_fetch_key_id(struct onak_dbctx *dbctx,
149 struct openpgp_publickey **publickey,
152 struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
155 snprintf(keyurl, sizeof(keyurl),
156 "%s/lookup?op=get&search=0x%08" PRIX64,
157 privctx->hkpbase, keyid);
159 return (hkp_fetch_key_url(dbctx, keyurl, publickey, intrans));
163 * hkp_fetch_key_fp - Given a fingerprint fetch the key from HKP server.
165 static int hkp_fetch_key_fp(struct onak_dbctx *dbctx,
166 struct openpgp_fingerprint *fingerprint,
167 struct openpgp_publickey **publickey,
170 struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
174 if (fingerprint->length > MAX_FINGERPRINT_LEN) {
178 ofs = snprintf(keyurl, sizeof(keyurl),
179 "%s/lookup?op=get&search=0x", privctx->hkpbase);
181 if ((ofs + fingerprint->length * 2 + 1)> sizeof(keyurl)) {
185 for (i = 0; i < fingerprint->length; i++) {
186 ofs += sprintf(&keyurl[ofs], "%02X", fingerprint->fp[i]);
189 return (hkp_fetch_key_url(dbctx, keyurl, publickey, intrans));
193 * fetch_key_text - Tries to find the keys that contain the supplied text.
194 * @search: The text to search for.
195 * @publickey: A pointer to a structure to return the key in.
197 * This function searches for the supplied text and returns the keys that
200 * TODO: Write for flat file access. Some sort of grep?
202 static int hkp_fetch_key_text(struct onak_dbctx *dbctx,
204 struct openpgp_publickey **publickey)
206 struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
209 snprintf(keyurl, sizeof(keyurl),
210 "%s/lookup?op=get&search=%s",
211 privctx->hkpbase, search);
213 return (hkp_fetch_key_url(dbctx, keyurl, publickey, false));
217 * store_key - Takes a key and stores it.
218 * @publickey: A pointer to the public key to store.
219 * @intrans: If we're already in a transaction.
220 * @update: If true the key exists and should be updated.
223 static int hkp_store_key(struct onak_dbctx *dbctx,
224 struct openpgp_publickey *publickey, bool intrans,
227 struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
228 struct openpgp_packet_list *packets = NULL;
229 struct openpgp_packet_list *list_end = NULL;
232 struct buffer_ctx buf;
237 buf.buffer = malloc(8192);
238 buf.offset = snprintf(buf.buffer, buf.size, "keytextz");
240 flatten_publickey(publickey, &packets, &list_end);
241 armor_openpgp_stream(buffer_putchar, &buf, packets);
242 addform = curl_easy_escape(privctx->curl, buf.buffer, buf.offset);
245 snprintf(keyurl, sizeof(keyurl), "%s/add", privctx->hkpbase);
247 curl_easy_setopt(privctx->curl, CURLOPT_URL, keyurl);
248 curl_easy_setopt(privctx->curl, CURLOPT_POSTFIELDS, addform);
249 curl_easy_setopt(privctx->curl, CURLOPT_WRITEFUNCTION,
252 curl_easy_setopt(privctx->curl, CURLOPT_WRITEDATA, &buf);
253 res = curl_easy_perform(privctx->curl);
256 logthing(LOGTHING_ERROR, "Couldn't send key: %s (%d)",
257 curl_easy_strerror(res), res);
262 /* TODO: buf has any response text we might want to parse. */
264 buf.offset = buf.size = 0;
267 return (res == 0) ? 1 : 0;
271 * delete_key - Given a keyid delete the key from storage.
272 * @keyid: The keyid to delete.
273 * @intrans: If we're already in a transaction.
277 static int hkp_delete_key(struct onak_dbctx *dbctx,
278 uint64_t keyid, bool intrans)
284 * iterate_keys - call a function once for each key in the db.
285 * @iterfunc: The function to call.
286 * @ctx: A context pointer
288 * Not applicable for HKP backend.
290 static int hkp_iterate_keys(struct onak_dbctx *dbctx,
291 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
298 * starttrans - Start a transaction.
300 * This is just a no-op for HKP access.
302 static bool hkp_starttrans(struct onak_dbctx *dbctx)
308 * endtrans - End a transaction.
310 * This is just a no-op for HKP access.
312 static void hkp_endtrans(struct onak_dbctx *dbctx)
318 * Include the basic keydb routines.
320 #define NEED_KEYID2UID 1
321 #define NEED_GETKEYSIGS 1
322 #define NEED_GETFULLKEYID 1
323 #define NEED_UPDATEKEYS 1
327 * cleanupdb - De-initialize the key database.
329 * We cleanup CURL here.
331 static void hkp_cleanupdb(struct onak_dbctx *dbctx)
333 struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
336 curl_easy_cleanup(privctx->curl);
337 privctx->curl = NULL;
339 curl_global_cleanup();
345 * initdb - Initialize the key database.
347 * We initialize CURL here.
349 struct onak_dbctx *keydb_hkp_init(bool readonly)
351 struct onak_dbctx *dbctx;
352 struct onak_hkp_dbctx *privctx;
353 curl_version_info_data *curl_info;
355 dbctx = malloc(sizeof(struct onak_dbctx));
360 dbctx->priv = privctx = malloc(sizeof(*privctx));
361 dbctx->cleanupdb = hkp_cleanupdb;
362 dbctx->starttrans = hkp_starttrans;
363 dbctx->endtrans = hkp_endtrans;
364 dbctx->fetch_key_id = hkp_fetch_key_id;
365 dbctx->fetch_key_fp = hkp_fetch_key_fp;
366 dbctx->fetch_key_text = hkp_fetch_key_text;
367 dbctx->store_key = hkp_store_key;
368 dbctx->update_keys = generic_update_keys;
369 dbctx->delete_key = hkp_delete_key;
370 dbctx->getkeysigs = generic_getkeysigs;
371 dbctx->cached_getkeysigs = generic_cached_getkeysigs;
372 dbctx->keyid2uid = generic_keyid2uid;
373 dbctx->getfullkeyid = generic_getfullkeyid;
374 dbctx->iterate_keys = hkp_iterate_keys;
376 if (!hkp_parse_url(privctx, config.db_dir)) {
379 curl_global_init(CURL_GLOBAL_DEFAULT);
380 privctx->curl = curl_easy_init();
381 if (privctx->curl == NULL) {
382 logthing(LOGTHING_CRITICAL, "Could not initialize CURL.");
383 hkp_cleanupdb(dbctx);
387 curl_easy_setopt(privctx->curl, CURLOPT_USERAGENT,
388 "onak/" ONAK_VERSION);
390 if (strncmp(privctx->hkpbase, "https://", 8) == 0) {
391 curl_info = curl_version_info(CURLVERSION_NOW);
392 if (! (curl_info->features & CURL_VERSION_SSL)) {
393 logthing(LOGTHING_CRITICAL,
394 "CURL lacks SSL support; cannot use HKP url: %s",
396 hkp_cleanupdb(dbctx);