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 static CURL *curl = NULL;
38 static char hkpbase[1024];
40 static int hkp_parse_url(const char *url)
42 char proto[6], host[256];
47 proto[0] = host[0] = 0;
50 matched = sscanf(url, "%5[a-z]://%256[a-zA-Z0-9.]:%u", proto, host,
54 matched = sscanf(url, "%256[a-zA-Z0-9.]:%u", host, &port);
58 logthing(LOGTHING_CRITICAL, "Couldn't parse HKP host: %s",
64 if (proto[0] == 0 || !strcmp(proto, "hkp")) {
68 snprintf(hkpbase, sizeof(hkpbase),
69 "http://%s:%u/pks", host, port);
70 } else if (!strcmp(proto, "hkps")) {
74 snprintf(hkpbase, sizeof(hkpbase),
75 "https://%s:%u/pks", host, port);
76 } else if (strcmp(proto, "http") && strcmp(proto, "https")) {
77 logthing(LOGTHING_CRITICAL, "Unknown HKP protocol: %s",
81 } else if (port == 0) {
82 snprintf(hkpbase, sizeof(hkpbase),
83 "%s://%s/pks", proto, host);
85 snprintf(hkpbase, sizeof(hkpbase),
86 "%s://%s:%u/pks", proto, host, port);
94 * initdb - Initialize the key database.
96 * We initialize CURL here.
98 static void hkp_initdb(bool readonly)
100 if (!hkp_parse_url(config.db_dir)) {
103 curl_global_init(CURL_GLOBAL_DEFAULT);
104 curl = curl_easy_init();
106 logthing(LOGTHING_CRITICAL, "Could not initialize CURL.");
109 curl_easy_setopt(curl, CURLOPT_USERAGENT, "onak/" ONAK_VERSION);
113 * cleanupdb - De-initialize the key database.
115 * We cleanup CURL here.
117 static void hkp_cleanupdb(void)
120 curl_easy_cleanup(curl);
123 curl_global_cleanup();
127 * Receive data from a CURL request and process it into a buffer context.
129 static size_t hkp_curl_recv_data(void *buffer, size_t size, size_t nmemb,
132 buffer_putchar(ctx, nmemb * size, buffer);
134 return (nmemb * size);
138 * fetch_key - Given a keyid fetch the key from storage.
139 * @keyid: The keyid to fetch.
140 * @publickey: A pointer to a structure to return the key in.
141 * @intrans: If we're already in a transaction.
143 * We use the hex representation of the keyid as the filename to fetch the
144 * key from. The key is stored in the file as a binary OpenPGP stream of
145 * packets, so we can just use read_openpgp_stream() to read the packets
146 * in and then parse_keys() to parse the packets into a publickey
149 static int hkp_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
152 struct openpgp_packet_list *packets = NULL;
155 struct buffer_ctx buf;
159 buf.buffer = malloc(8192);
161 snprintf(keyurl, sizeof(keyurl),
162 "%s/lookup?op=get&search=0x%08" PRIX64,
165 curl_easy_setopt(curl, CURLOPT_URL, keyurl);
166 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
168 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
169 res = curl_easy_perform(curl);
173 dearmor_openpgp_stream(buffer_fetchchar, &buf, &packets);
174 parse_keys(packets, publickey);
175 free_packet_list(packets);
178 logthing(LOGTHING_ERROR, "Couldn't find key: %s (%d)",
179 curl_easy_strerror(res), res);
183 buf.offset = buf.size = 0;
186 return (res == 0) ? 1 : 0;
190 * fetch_key_text - Tries to find the keys that contain the supplied text.
191 * @search: The text to search for.
192 * @publickey: A pointer to a structure to return the key in.
194 * This function searches for the supplied text and returns the keys that
197 * TODO: Write for flat file access. Some sort of grep?
199 static int hkp_fetch_key_text(const char *search,
200 struct openpgp_publickey **publickey)
202 struct openpgp_packet_list *packets = NULL;
205 struct buffer_ctx buf;
210 buf.buffer = malloc(8192);
212 snprintf(keyurl, sizeof(keyurl),
213 "%s/lookup?op=get&search=%s",
216 curl_easy_setopt(curl, CURLOPT_URL, keyurl);
217 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
219 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
220 res = curl_easy_perform(curl);
224 dearmor_openpgp_stream(buffer_fetchchar, &buf, &packets);
225 count = parse_keys(packets, publickey);
226 free_packet_list(packets);
229 logthing(LOGTHING_ERROR, "Couldn't find key: %s (%d)",
230 curl_easy_strerror(res), res);
234 buf.offset = buf.size = 0;
241 * store_key - Takes a key and stores it.
242 * @publickey: A pointer to the public key to store.
243 * @intrans: If we're already in a transaction.
244 * @update: If true the key exists and should be updated.
247 static int hkp_store_key(struct openpgp_publickey *publickey, bool intrans,
250 struct openpgp_packet_list *packets = NULL;
251 struct openpgp_packet_list *list_end = NULL;
254 struct buffer_ctx buf;
259 buf.buffer = malloc(8192);
260 buf.offset = snprintf(buf.buffer, buf.size, "keytextz");
262 flatten_publickey(publickey, &packets, &list_end);
263 armor_openpgp_stream(buffer_putchar, &buf, packets);
264 addform = curl_easy_escape(curl, buf.buffer, buf.offset);
267 snprintf(keyurl, sizeof(keyurl), "%s/add", hkpbase);
269 curl_easy_setopt(curl, CURLOPT_URL, keyurl);
270 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, addform);
271 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
274 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
275 res = curl_easy_perform(curl);
278 logthing(LOGTHING_ERROR, "Couldn't send key: %s (%d)",
279 curl_easy_strerror(res), res);
284 /* TODO: buf has any response text we might want to parse. */
286 buf.offset = buf.size = 0;
289 return (res == 0) ? 1 : 0;
293 * delete_key - Given a keyid delete the key from storage.
294 * @keyid: The keyid to delete.
295 * @intrans: If we're already in a transaction.
299 static int hkp_delete_key(uint64_t keyid, bool intrans)
305 * iterate_keys - call a function once for each key in the db.
306 * @iterfunc: The function to call.
307 * @ctx: A context pointer
309 * Not applicable for HKP backend.
311 static int hkp_iterate_keys(void (*iterfunc)(void *ctx,
312 struct openpgp_publickey *key), void *ctx)
318 * starttrans - Start a transaction.
320 * This is just a no-op for HKP access.
322 static bool hkp_starttrans(void)
328 * endtrans - End a transaction.
330 * This is just a no-op for HKP access.
332 static void hkp_endtrans(void)
338 * Include the basic keydb routines.
340 #define NEED_KEYID2UID 1
341 #define NEED_GETKEYSIGS 1
342 #define NEED_GETFULLKEYID 1
343 #define NEED_UPDATEKEYS 1
346 struct dbfuncs keydb_hkp_funcs = {
347 .initdb = hkp_initdb,
348 .cleanupdb = hkp_cleanupdb,
349 .starttrans = hkp_starttrans,
350 .endtrans = hkp_endtrans,
351 .fetch_key = hkp_fetch_key,
352 .fetch_key_text = hkp_fetch_key_text,
353 .store_key = hkp_store_key,
354 .update_keys = generic_update_keys,
355 .delete_key = hkp_delete_key,
356 .getkeysigs = generic_getkeysigs,
357 .cached_getkeysigs = generic_cached_getkeysigs,
358 .keyid2uid = generic_keyid2uid,
359 .getfullkeyid = generic_getfullkeyid,
360 .iterate_keys = hkp_iterate_keys,