]> the.earth.li Git - onak.git/blob - keydb_hkp.c
Fix issue with pre-seeding key database on Debian install
[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 struct onak_hkp_dbctx {
37         CURL *curl;
38         char hkpbase[1024];
39 };
40
41 static int hkp_parse_url(struct onak_hkp_dbctx *privctx, const char *url)
42 {
43         char proto[6], host[256];
44         unsigned int port;
45         int matched;
46         int ret = 1;
47
48         proto[0] = host[0] = 0;
49         port = 0;
50
51         matched = sscanf(url, "%5[a-z]://%256[a-zA-Z0-9.]:%u", proto, host,
52                         &port);
53         if (matched < 2) {
54                 proto[0] = 0;
55                 sscanf(url, "%256[a-zA-Z0-9.]:%u", host, &port);
56         }
57
58         if (host[0] == 0) {
59                 logthing(LOGTHING_CRITICAL, "Couldn't parse HKP host: %s",
60                         url);
61                 ret = 0;
62                 goto out;
63         }
64
65         if (proto[0] == 0 || !strcmp(proto, "hkp")) {
66                 if (port == 0) {
67                         port = 11371;
68                 }
69                 snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
70                         "http://%s:%u/pks", host, port);
71         } else if (!strcmp(proto, "hkps")) {
72                 if (port == 0) {
73                         port = 11372;
74                 }
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",
79                         proto);
80                 ret = 0;
81                 goto out;
82         } else if (port == 0) {
83                 snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
84                         "%s://%s/pks", proto, host);
85         } else {
86                 snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
87                         "%s://%s:%u/pks", proto, host, port);
88         }
89
90 out:
91         return ret;
92 }
93
94 /**
95  *      Receive data from a CURL request and process it into a buffer context.
96  */
97 static size_t hkp_curl_recv_data(void *buffer, size_t size, size_t nmemb,
98                 void *ctx)
99 {
100         buffer_putchar(ctx, nmemb * size, buffer);
101
102         return (nmemb * size);
103 }
104
105 static int hkp_fetch_key_url(struct onak_dbctx *dbctx,
106                 char *url,
107                 struct openpgp_publickey **publickey,
108                 bool intrans)
109 {
110         struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
111         struct openpgp_packet_list *packets = NULL;
112         CURLcode res;
113         struct buffer_ctx buf;
114         int count = 0;
115
116         buf.offset = 0;
117         buf.size = 8192;
118         buf.buffer = malloc(8192);
119
120         curl_easy_setopt(privctx->curl, CURLOPT_URL, url);
121         curl_easy_setopt(privctx->curl, CURLOPT_WRITEFUNCTION,
122                         hkp_curl_recv_data);
123         curl_easy_setopt(privctx->curl, CURLOPT_WRITEDATA, &buf);
124         res = curl_easy_perform(privctx->curl);
125
126         if (res == 0) {
127                 buf.offset = 0;
128                 dearmor_openpgp_stream(buffer_fetchchar, &buf, &packets);
129                 count = parse_keys(packets, publickey);
130                 free_packet_list(packets);
131                 packets = NULL;
132         } else {
133                 logthing(LOGTHING_ERROR, "Couldn't find key: %s (%d)",
134                         curl_easy_strerror(res), res);
135         }
136
137         free(buf.buffer);
138         buf.offset = buf.size = 0;
139         buf.buffer = NULL;
140
141         return count;
142 }
143
144 /**
145  *      hkp_fetch_key_id - Given a keyid fetch the key from HKP server.
146  */
147 static int hkp_fetch_key_id(struct onak_dbctx *dbctx,
148                 uint64_t keyid,
149                 struct openpgp_publickey **publickey,
150                 bool intrans)
151 {
152         struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
153         char keyurl[1024];
154
155         snprintf(keyurl, sizeof(keyurl),
156                         "%s/lookup?op=get&search=0x%08" PRIX64,
157                         privctx->hkpbase, keyid);
158
159         return (hkp_fetch_key_url(dbctx, keyurl, publickey, intrans));
160 }
161
162 /**
163  *      hkp_fetch_key_fp - Given a fingerprint fetch the key from HKP server.
164  */
165 static int hkp_fetch_key_fp(struct onak_dbctx *dbctx,
166                 struct openpgp_fingerprint *fingerprint,
167                 struct openpgp_publickey **publickey,
168                 bool intrans)
169 {
170         struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
171         char keyurl[1024];
172         int i, ofs;
173
174         if (fingerprint->length > MAX_FINGERPRINT_LEN) {
175                 return 0;
176         }
177
178         ofs = snprintf(keyurl, sizeof(keyurl),
179                         "%s/lookup?op=get&search=0x", privctx->hkpbase);
180
181         if ((ofs + fingerprint->length * 2 + 1)> sizeof(keyurl)) {
182                 return 0;
183         }
184
185         for (i = 0; i < fingerprint->length; i++) {
186                 ofs += sprintf(&keyurl[ofs], "%02X", fingerprint->fp[i]);
187         }
188
189         return (hkp_fetch_key_url(dbctx, keyurl, publickey, intrans));
190 }
191
192 /**
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.
196  *
197  *      This function searches for the supplied text and returns the keys that
198  *      contain it.
199  *
200  *      TODO: Write for flat file access. Some sort of grep?
201  */
202 static int hkp_fetch_key_text(struct onak_dbctx *dbctx,
203                 const char *search,
204                 struct openpgp_publickey **publickey)
205 {
206         struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
207         char keyurl[1024];
208
209         snprintf(keyurl, sizeof(keyurl),
210                         "%s/lookup?op=get&search=%s",
211                         privctx->hkpbase, search);
212
213         return (hkp_fetch_key_url(dbctx, keyurl, publickey, false));
214 }
215
216 /**
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.
221  *
222  */
223 static int hkp_store_key(struct onak_dbctx *dbctx,
224                 struct openpgp_publickey *publickey, bool intrans,
225                 bool update)
226 {
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;
230         char keyurl[1024];
231         CURLcode res;
232         struct buffer_ctx buf;
233         char *addform;
234
235         buf.offset = 0;
236         buf.size = 8192;
237         buf.buffer = malloc(8192);
238         buf.offset = snprintf(buf.buffer, buf.size, "keytextz");
239
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);
243         addform[7] = '=';
244
245         snprintf(keyurl, sizeof(keyurl), "%s/add", privctx->hkpbase);
246
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,
250                         hkp_curl_recv_data);
251         buf.offset = 0;
252         curl_easy_setopt(privctx->curl, CURLOPT_WRITEDATA, &buf);
253         res = curl_easy_perform(privctx->curl);
254
255         if (res != 0) {
256                 logthing(LOGTHING_ERROR, "Couldn't send key: %s (%d)",
257                         curl_easy_strerror(res), res);
258         }
259
260         curl_free(addform);
261
262         /* TODO: buf has any response text we might want to parse. */
263         free(buf.buffer);
264         buf.offset = buf.size = 0;
265         buf.buffer = NULL;
266
267         return (res == 0) ? 1 : 0;
268 }
269
270 /**
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.
274  *
275  *      No op for HKP.
276  */
277 static int hkp_delete_key(struct onak_dbctx *dbctx,
278                 uint64_t keyid, bool intrans)
279 {
280         return -1;
281 }
282
283 /**
284  *      iterate_keys - call a function once for each key in the db.
285  *      @iterfunc: The function to call.
286  *      @ctx: A context pointer
287  *
288  *      Not applicable for HKP backend.
289  */
290 static int hkp_iterate_keys(struct onak_dbctx *dbctx,
291                 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
292                 void *ctx)
293 {
294         return 0;
295 }
296
297 /**
298  *      starttrans - Start a transaction.
299  *
300  *      This is just a no-op for HKP access.
301  */
302 static bool hkp_starttrans(struct onak_dbctx *dbctx)
303 {
304         return true;
305 }
306
307 /**
308  *      endtrans - End a transaction.
309  *
310  *      This is just a no-op for HKP access.
311  */
312 static void hkp_endtrans(struct onak_dbctx *dbctx)
313 {
314         return;
315 }
316
317 /*
318  * Include the basic keydb routines.
319  */
320 #define NEED_KEYID2UID 1
321 #define NEED_GETKEYSIGS 1
322 #define NEED_GETFULLKEYID 1
323 #define NEED_UPDATEKEYS 1
324 #include "keydb.c"
325
326 /**
327  *      cleanupdb - De-initialize the key database.
328  *
329  *      We cleanup CURL here.
330  */
331 static void hkp_cleanupdb(struct onak_dbctx *dbctx)
332 {
333         struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
334
335         if (privctx->curl) {
336                 curl_easy_cleanup(privctx->curl);
337                 privctx->curl = NULL;
338         }
339         curl_global_cleanup();
340         free(privctx);
341         free(dbctx);
342 }
343
344 /**
345  *      initdb - Initialize the key database.
346  *
347  *      We initialize CURL here.
348  */
349 struct onak_dbctx *keydb_hkp_init(bool readonly)
350 {
351         struct onak_dbctx *dbctx;
352         struct onak_hkp_dbctx *privctx;
353         curl_version_info_data *curl_info;
354
355         dbctx = malloc(sizeof(struct onak_dbctx));
356         if (dbctx == NULL) {
357                 return NULL;
358         }
359
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;
375
376         if (!hkp_parse_url(privctx, config.db_dir)) {
377                 exit(EXIT_FAILURE);
378         }
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);
384                 dbctx = NULL;
385                 exit(EXIT_FAILURE);
386         }
387         curl_easy_setopt(privctx->curl, CURLOPT_USERAGENT,
388                 "onak/" ONAK_VERSION);
389
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",
395                                 privctx->hkpbase);
396                         hkp_cleanupdb(dbctx);
397                         dbctx = NULL;
398                         exit(EXIT_FAILURE);
399                 }
400         }
401
402         return dbctx;
403 }