]> the.earth.li Git - onak.git/blob - keydb_hkp.c
Enhance HKP backend URL parsing
[onak.git] / keydb_hkp.c
1 /*
2  * keydb_hkp.c - Routines to store and fetch keys from another keyserver.
3  *
4  * Copyright 2013 Jonathan McDowell <noodles@earth.li>
5  *
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.
9  *
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
13  * more details.
14  *
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.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <curl/curl.h>
25
26 #include "armor.h"
27 #include "charfuncs.h"
28 #include "keydb.h"
29 #include "keystructs.h"
30 #include "log.h"
31 #include "mem.h"
32 #include "onak-conf.h"
33 #include "parsekey.h"
34 #include "version.h"
35
36 static CURL *curl = NULL;
37
38 static char hkpbase[1024];
39
40 static int hkp_parse_url(const char *url)
41 {
42         char proto[6], host[256];
43         unsigned int port;
44         int matched;
45         int ret = 1;
46
47         proto[0] = host[0] = 0;
48         port = 0;
49
50         matched = sscanf(url, "%5[a-z]://%256[a-zA-Z0-9.]:%u", proto, host,
51                         &port);
52         if (matched < 2) {
53                 proto[0] = 0;
54                 matched = sscanf(url, "%256[a-zA-Z0-9.]:%u", host, &port);
55         }
56
57         if (host[0] == 0) {
58                 logthing(LOGTHING_CRITICAL, "Couldn't parse HKP host: %s",
59                         url);
60                 ret = 0;
61                 goto out;
62         }
63
64         if (proto[0] == 0 || !strcmp(proto, "hkp")) {
65                 if (port == 0) {
66                         port = 11371;
67                 }
68                 snprintf(hkpbase, sizeof(hkpbase),
69                         "http://%s:%u/pks", host, port);
70         } else if (!strcmp(proto, "hkps")) {
71                 if (port == 0) {
72                         port = 11372;
73                 }
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",
78                         proto);
79                 ret = 0;
80                 goto out;
81         } else if (port == 0) {
82                 snprintf(hkpbase, sizeof(hkpbase),
83                         "%s://%s/pks", proto, host);
84         } else {
85                 snprintf(hkpbase, sizeof(hkpbase),
86                         "%s://%s:%u/pks", proto, host, port);
87         }
88
89 out:
90         return ret;
91 }
92
93 /**
94  *      initdb - Initialize the key database.
95  *
96  *      We initialize CURL here.
97  */
98 static void hkp_initdb(bool readonly)
99 {
100         if (!hkp_parse_url(config.db_dir)) {
101                 exit(EXIT_FAILURE);
102         }
103         curl_global_init(CURL_GLOBAL_DEFAULT);
104         curl = curl_easy_init();
105         if (curl == NULL) {
106                 logthing(LOGTHING_CRITICAL, "Could not initialize CURL.");
107                 exit(EXIT_FAILURE);
108         }
109         curl_easy_setopt(curl, CURLOPT_USERAGENT, "onak/" ONAK_VERSION);
110 }
111
112 /**
113  *      cleanupdb - De-initialize the key database.
114  *
115  *      We cleanup CURL here.
116  */
117 static void hkp_cleanupdb(void)
118 {
119         if (curl) {
120                 curl_easy_cleanup(curl);
121                 curl = NULL;
122         }
123         curl_global_cleanup();
124 }
125
126 /**
127  *      Receive data from a CURL request and process it into a buffer context.
128  */
129 static size_t hkp_curl_recv_data(void *buffer, size_t size, size_t nmemb,
130                 void *ctx)
131 {
132         buffer_putchar(ctx, nmemb * size, buffer);
133
134         return (nmemb * size);
135 }
136
137 /**
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.
142  *
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
147  *      structure.
148  */
149 static int hkp_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
150                 bool intrans)
151 {
152         struct openpgp_packet_list *packets = NULL;
153         char keyurl[1024];
154         CURLcode res;
155         struct buffer_ctx buf;
156
157         buf.offset = 0;
158         buf.size = 8192;
159         buf.buffer = malloc(8192);
160
161         snprintf(keyurl, sizeof(keyurl),
162                         "%s/lookup?op=get&search=0x%08" PRIX64,
163                         hkpbase, keyid);
164
165         curl_easy_setopt(curl, CURLOPT_URL, keyurl);
166         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
167                         hkp_curl_recv_data);
168         curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
169         res = curl_easy_perform(curl);
170
171         if (res == 0) {
172                 buf.offset = 0;
173                 dearmor_openpgp_stream(buffer_fetchchar, &buf, &packets);
174                 parse_keys(packets, publickey);
175                 free_packet_list(packets);
176                 packets = NULL;
177         } else {
178                 logthing(LOGTHING_ERROR, "Couldn't find key: %s (%d)",
179                         curl_easy_strerror(res), res);
180         }
181
182         free(buf.buffer);
183         buf.offset = buf.size = 0;
184         buf.buffer = NULL;
185
186         return (res == 0) ? 1 : 0;
187 }
188
189 /**
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.
193  *
194  *      This function searches for the supplied text and returns the keys that
195  *      contain it.
196  *
197  *      TODO: Write for flat file access. Some sort of grep?
198  */
199 static int hkp_fetch_key_text(const char *search,
200                 struct openpgp_publickey **publickey)
201 {
202         struct openpgp_packet_list *packets = NULL;
203         char keyurl[1024];
204         CURLcode res;
205         struct buffer_ctx buf;
206         int count = 0;
207
208         buf.offset = 0;
209         buf.size = 8192;
210         buf.buffer = malloc(8192);
211
212         snprintf(keyurl, sizeof(keyurl),
213                         "%s/lookup?op=get&search=%s",
214                         hkpbase, search);
215
216         curl_easy_setopt(curl, CURLOPT_URL, keyurl);
217         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
218                         hkp_curl_recv_data);
219         curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
220         res = curl_easy_perform(curl);
221
222         if (res == 0) {
223                 buf.offset = 0;
224                 dearmor_openpgp_stream(buffer_fetchchar, &buf, &packets);
225                 count = parse_keys(packets, publickey);
226                 free_packet_list(packets);
227                 packets = NULL;
228         } else {
229                 logthing(LOGTHING_ERROR, "Couldn't find key: %s (%d)",
230                         curl_easy_strerror(res), res);
231         }
232
233         free(buf.buffer);
234         buf.offset = buf.size = 0;
235         buf.buffer = NULL;
236
237         return count;
238 }
239
240 /**
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.
245  *
246  */
247 static int hkp_store_key(struct openpgp_publickey *publickey, bool intrans,
248                 bool update)
249 {
250         struct openpgp_packet_list *packets = NULL;
251         struct openpgp_packet_list *list_end = NULL;
252         char keyurl[1024];
253         CURLcode res;
254         struct buffer_ctx buf;
255         char *addform;
256
257         buf.offset = 0;
258         buf.size = 8192;
259         buf.buffer = malloc(8192);
260         buf.offset = snprintf(buf.buffer, buf.size, "keytextz");
261
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);
265         addform[7] = '=';
266
267         snprintf(keyurl, sizeof(keyurl), "%s/add", hkpbase);
268
269         curl_easy_setopt(curl, CURLOPT_URL, keyurl);
270         curl_easy_setopt(curl, CURLOPT_POSTFIELDS, addform);
271         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
272                         hkp_curl_recv_data);
273         buf.offset = 0;
274         curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
275         res = curl_easy_perform(curl);
276
277         if (res != 0) {
278                 logthing(LOGTHING_ERROR, "Couldn't send key: %s (%d)",
279                         curl_easy_strerror(res), res);
280         }
281
282         curl_free(addform);
283
284         /* TODO: buf has any response text we might want to parse. */
285         free(buf.buffer);
286         buf.offset = buf.size = 0;
287         buf.buffer = NULL;
288
289         return (res == 0) ? 1 : 0;
290 }
291
292 /**
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.
296  *
297  *      No op for HKP.
298  */
299 static int hkp_delete_key(uint64_t keyid, bool intrans)
300 {
301         return -1;
302 }
303
304 /**
305  *      iterate_keys - call a function once for each key in the db.
306  *      @iterfunc: The function to call.
307  *      @ctx: A context pointer
308  *
309  *      Not applicable for HKP backend.
310  */
311 static int hkp_iterate_keys(void (*iterfunc)(void *ctx,
312                 struct openpgp_publickey *key), void *ctx)
313 {
314         return 0;
315 }
316
317 /**
318  *      starttrans - Start a transaction.
319  *
320  *      This is just a no-op for HKP access.
321  */
322 static bool hkp_starttrans(void)
323 {
324         return true;
325 }
326
327 /**
328  *      endtrans - End a transaction.
329  *
330  *      This is just a no-op for HKP access.
331  */
332 static void hkp_endtrans(void)
333 {
334         return;
335 }
336
337 /*
338  * Include the basic keydb routines.
339  */
340 #define NEED_KEYID2UID 1
341 #define NEED_GETKEYSIGS 1
342 #define NEED_GETFULLKEYID 1
343 #define NEED_UPDATEKEYS 1
344 #include "keydb.c"
345
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,
361 };