]> the.earth.li Git - onak.git/blob - keydb_hkp.c
Add ability to drop overly large packets
[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, see <https://www.gnu.org/licenses/>.
17  */
18
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.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 struct onak_hkp_dbctx {
37         struct onak_db_config *config; /* Our DB config info */
38         CURL *curl;
39         char hkpbase[512];
40 };
41
42 static int hkp_parse_url(struct onak_hkp_dbctx *privctx, const char *url)
43 {
44         char proto[6], host[256];
45         unsigned int port;
46         int matched;
47         int ret = 1;
48
49         proto[0] = host[0] = 0;
50         port = 0;
51
52         matched = sscanf(url, "%5[a-z]://%256[a-zA-Z0-9.-]:%u", proto, host,
53                         &port);
54         if (matched < 2) {
55                 proto[0] = 0;
56                 sscanf(url, "%256[a-zA-Z0-9.-]:%u", host, &port);
57         }
58
59         if (host[0] == 0) {
60                 logthing(LOGTHING_CRITICAL, "Couldn't parse HKP host: %s",
61                         url);
62                 ret = 0;
63                 goto out;
64         }
65
66         if (proto[0] == 0 || !strcmp(proto, "hkp")) {
67                 if (port == 0) {
68                         port = 11371;
69                 }
70                 snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
71                         "http://%s:%u/pks", host, port);
72         } else if (!strcmp(proto, "hkps")) {
73                 if (port == 0) {
74                         port = 11372;
75                 }
76                 snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
77                         "https://%s:%u/pks", host, port);
78         } else if (strcmp(proto, "http") && strcmp(proto, "https")) {
79                 logthing(LOGTHING_CRITICAL, "Unknown HKP protocol: %s",
80                         proto);
81                 ret = 0;
82                 goto out;
83         } else if (port == 0) {
84                 snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
85                         "%s://%s/pks", proto, host);
86         } else {
87                 snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
88                         "%s://%s:%u/pks", proto, host, port);
89         }
90
91 out:
92         return ret;
93 }
94
95 /**
96  *      Receive data from a CURL request and process it into a buffer context.
97  */
98 static size_t hkp_curl_recv_data(void *buffer, size_t size, size_t nmemb,
99                 void *ctx)
100 {
101         buffer_putchar(ctx, nmemb * size, buffer);
102
103         return (nmemb * size);
104 }
105
106 static int hkp_fetch_key_url(struct onak_dbctx *dbctx,
107                 char *url,
108                 struct openpgp_publickey **publickey,
109                 bool intrans)
110 {
111         struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
112         struct openpgp_packet_list *packets = NULL;
113         CURLcode res;
114         struct buffer_ctx buf;
115         int count = 0;
116
117         buf.offset = 0;
118         buf.size = 8192;
119         buf.buffer = malloc(8192);
120
121         curl_easy_setopt(privctx->curl, CURLOPT_URL, url);
122         curl_easy_setopt(privctx->curl, CURLOPT_WRITEFUNCTION,
123                         hkp_curl_recv_data);
124         curl_easy_setopt(privctx->curl, CURLOPT_WRITEDATA, &buf);
125         res = curl_easy_perform(privctx->curl);
126
127         if (res == 0) {
128                 buf.offset = 0;
129                 dearmor_openpgp_stream(buffer_fetchchar, &buf, &packets);
130                 count = parse_keys(packets, publickey);
131                 free_packet_list(packets);
132                 packets = NULL;
133         } else {
134                 logthing(LOGTHING_ERROR, "Couldn't find key: %s (%d)",
135                         curl_easy_strerror(res), res);
136         }
137
138         free(buf.buffer);
139         buf.offset = buf.size = 0;
140         buf.buffer = NULL;
141
142         return count;
143 }
144
145 /**
146  *      hkp_fetch_key_id - Given a keyid fetch the key from HKP server.
147  */
148 static int hkp_fetch_key_id(struct onak_dbctx *dbctx,
149                 uint64_t keyid,
150                 struct openpgp_publickey **publickey,
151                 bool intrans)
152 {
153         struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
154         char keyurl[1024];
155
156         snprintf(keyurl, sizeof(keyurl),
157                         "%s/lookup?op=get&search=0x%08" PRIX64,
158                         privctx->hkpbase, keyid);
159
160         return (hkp_fetch_key_url(dbctx, keyurl, publickey, intrans));
161 }
162
163 /**
164  *      hkp_fetch_key_fp - Given a fingerprint fetch the key from HKP server.
165  */
166 static int hkp_fetch_key_fp(struct onak_dbctx *dbctx,
167                 struct openpgp_fingerprint *fingerprint,
168                 struct openpgp_publickey **publickey,
169                 bool intrans)
170 {
171         struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
172         char keyurl[1024];
173         int i, ofs;
174
175         if (fingerprint->length > MAX_FINGERPRINT_LEN) {
176                 return 0;
177         }
178
179         ofs = snprintf(keyurl, sizeof(keyurl),
180                         "%s/lookup?op=get&search=0x", privctx->hkpbase);
181
182         if ((ofs + fingerprint->length * 2 + 1)> sizeof(keyurl)) {
183                 return 0;
184         }
185
186         for (i = 0; i < fingerprint->length; i++) {
187                 ofs += sprintf(&keyurl[ofs], "%02X", fingerprint->fp[i]);
188         }
189
190         return (hkp_fetch_key_url(dbctx, keyurl, publickey, intrans));
191 }
192
193 /**
194  *      fetch_key_text - Tries to find the keys that contain the supplied text.
195  *      @search: The text to search for.
196  *      @publickey: A pointer to a structure to return the key in.
197  *
198  *      This function searches for the supplied text and returns the keys that
199  *      contain it.
200  *
201  *      TODO: Write for flat file access. Some sort of grep?
202  */
203 static int hkp_fetch_key_text(struct onak_dbctx *dbctx,
204                 const char *search,
205                 struct openpgp_publickey **publickey)
206 {
207         struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
208         char keyurl[1024];
209
210         snprintf(keyurl, sizeof(keyurl),
211                         "%s/lookup?op=get&search=%s",
212                         privctx->hkpbase, search);
213
214         return (hkp_fetch_key_url(dbctx, keyurl, publickey, false));
215 }
216
217 /**
218  *      store_key - Takes a key and stores it.
219  *      @publickey: A pointer to the public key to store.
220  *      @intrans: If we're already in a transaction.
221  *      @update: If true the key exists and should be updated.
222  *
223  */
224 static int hkp_store_key(struct onak_dbctx *dbctx,
225                 struct openpgp_publickey *publickey, bool intrans,
226                 bool update)
227 {
228         struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
229         struct openpgp_packet_list *packets = NULL;
230         struct openpgp_packet_list *list_end = NULL;
231         char keyurl[1024];
232         CURLcode res;
233         struct buffer_ctx buf;
234         char *addform;
235
236         buf.offset = 0;
237         buf.size = 8192;
238         buf.buffer = malloc(8192);
239         buf.offset = snprintf(buf.buffer, buf.size, "keytextz");
240
241         flatten_publickey(publickey, &packets, &list_end);
242         armor_openpgp_stream(buffer_putchar, &buf, packets);
243         addform = curl_easy_escape(privctx->curl, buf.buffer, buf.offset);
244         addform[7] = '=';
245
246         snprintf(keyurl, sizeof(keyurl), "%s/add", privctx->hkpbase);
247
248         curl_easy_setopt(privctx->curl, CURLOPT_URL, keyurl);
249         curl_easy_setopt(privctx->curl, CURLOPT_POSTFIELDS, addform);
250         curl_easy_setopt(privctx->curl, CURLOPT_WRITEFUNCTION,
251                         hkp_curl_recv_data);
252         buf.offset = 0;
253         curl_easy_setopt(privctx->curl, CURLOPT_WRITEDATA, &buf);
254         res = curl_easy_perform(privctx->curl);
255
256         if (res != 0) {
257                 logthing(LOGTHING_ERROR, "Couldn't send key: %s (%d)",
258                         curl_easy_strerror(res), res);
259         }
260
261         curl_free(addform);
262
263         /* TODO: buf has any response text we might want to parse. */
264         free(buf.buffer);
265         buf.offset = buf.size = 0;
266         buf.buffer = NULL;
267
268         return (res == 0) ? 1 : 0;
269 }
270
271 /**
272  *      delete_key - Given a keyid delete the key from storage.
273  *      @keyid: The keyid to delete.
274  *      @intrans: If we're already in a transaction.
275  *
276  *      No op for HKP.
277  */
278 static int hkp_delete_key(struct onak_dbctx *dbctx,
279                 uint64_t keyid, bool intrans)
280 {
281         return -1;
282 }
283
284 /**
285  *      iterate_keys - call a function once for each key in the db.
286  *      @iterfunc: The function to call.
287  *      @ctx: A context pointer
288  *
289  *      Not applicable for HKP backend.
290  */
291 static int hkp_iterate_keys(struct onak_dbctx *dbctx,
292                 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
293                 void *ctx)
294 {
295         return 0;
296 }
297
298 /**
299  *      starttrans - Start a transaction.
300  *
301  *      This is just a no-op for HKP access.
302  */
303 static bool hkp_starttrans(struct onak_dbctx *dbctx)
304 {
305         return true;
306 }
307
308 /**
309  *      endtrans - End a transaction.
310  *
311  *      This is just a no-op for HKP access.
312  */
313 static void hkp_endtrans(struct onak_dbctx *dbctx)
314 {
315         return;
316 }
317
318 /*
319  * Include the basic keydb routines.
320  */
321 #define NEED_KEYID2UID 1
322 #define NEED_GETKEYSIGS 1
323 #define NEED_GETFULLKEYID 1
324 #define NEED_UPDATEKEYS 1
325 #include "keydb.c"
326
327 /**
328  *      cleanupdb - De-initialize the key database.
329  *
330  *      We cleanup CURL here.
331  */
332 static void hkp_cleanupdb(struct onak_dbctx *dbctx)
333 {
334         struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
335
336         if (privctx->curl) {
337                 curl_easy_cleanup(privctx->curl);
338                 privctx->curl = NULL;
339         }
340         curl_global_cleanup();
341         free(privctx);
342         free(dbctx);
343 }
344
345 /**
346  *      initdb - Initialize the key database.
347  *
348  *      We initialize CURL here.
349  */
350 struct onak_dbctx *keydb_hkp_init(struct onak_db_config *dbcfg, bool readonly)
351 {
352         struct onak_dbctx *dbctx;
353         struct onak_hkp_dbctx *privctx;
354         curl_version_info_data *curl_info;
355
356         dbctx = malloc(sizeof(struct onak_dbctx));
357         if (dbctx == NULL) {
358                 return NULL;
359         }
360
361         dbctx->config = dbcfg;
362         dbctx->priv = privctx = malloc(sizeof(*privctx));
363         dbctx->cleanupdb                = hkp_cleanupdb;
364         dbctx->starttrans               = hkp_starttrans;
365         dbctx->endtrans                 = hkp_endtrans;
366         dbctx->fetch_key_id             = hkp_fetch_key_id;
367         dbctx->fetch_key_fp             = hkp_fetch_key_fp;
368         dbctx->fetch_key_text           = hkp_fetch_key_text;
369         dbctx->store_key                = hkp_store_key;
370         dbctx->update_keys              = generic_update_keys;
371         dbctx->delete_key               = hkp_delete_key;
372         dbctx->getkeysigs               = generic_getkeysigs;
373         dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
374         dbctx->keyid2uid                = generic_keyid2uid;
375         dbctx->getfullkeyid             = generic_getfullkeyid;
376         dbctx->iterate_keys             = hkp_iterate_keys;
377
378         if (!hkp_parse_url(privctx, dbcfg->location)) {
379                 exit(EXIT_FAILURE);
380         }
381         logthing(LOGTHING_INFO, "Using %s as HKP forwarding URL.",
382                 privctx->hkpbase);
383         curl_global_init(CURL_GLOBAL_DEFAULT);
384         privctx->curl = curl_easy_init();
385         if (privctx->curl == NULL) {
386                 logthing(LOGTHING_CRITICAL, "Could not initialize CURL.");
387                 hkp_cleanupdb(dbctx);
388                 dbctx = NULL;
389                 exit(EXIT_FAILURE);
390         }
391         curl_easy_setopt(privctx->curl, CURLOPT_USERAGENT,
392                 "onak/" ONAK_VERSION);
393
394         if (strncmp(privctx->hkpbase, "https://", 8) == 0) {
395                 curl_info = curl_version_info(CURLVERSION_NOW);
396                 if (! (curl_info->features & CURL_VERSION_SSL)) {
397                         logthing(LOGTHING_CRITICAL,
398                                 "CURL lacks SSL support; cannot use HKP url: %s",
399                                 privctx->hkpbase);
400                         hkp_cleanupdb(dbctx);
401                         dbctx = NULL;
402                         exit(EXIT_FAILURE);
403                 }
404         }
405
406         return dbctx;
407 }