]> the.earth.li Git - onak.git/blob - keydb_hkp.c
Skip signature hash verification for X509 signatures
[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  *      cleanupdb - De-initialize the key database.
95  *
96  *      We cleanup CURL here.
97  */
98 static void hkp_cleanupdb(void)
99 {
100         if (curl) {
101                 curl_easy_cleanup(curl);
102                 curl = NULL;
103         }
104         curl_global_cleanup();
105 }
106
107 /**
108  *      initdb - Initialize the key database.
109  *
110  *      We initialize CURL here.
111  */
112 static void hkp_initdb(bool readonly)
113 {
114         curl_version_info_data *curl_info;
115
116         if (!hkp_parse_url(config.db_dir)) {
117                 exit(EXIT_FAILURE);
118         }
119         curl_global_init(CURL_GLOBAL_DEFAULT);
120         curl = curl_easy_init();
121         if (curl == NULL) {
122                 logthing(LOGTHING_CRITICAL, "Could not initialize CURL.");
123                 exit(EXIT_FAILURE);
124         }
125         curl_easy_setopt(curl, CURLOPT_USERAGENT, "onak/" ONAK_VERSION);
126
127         if (strncmp(hkpbase, "https://", 8) == 0) {
128                 curl_info = curl_version_info(CURLVERSION_NOW);
129                 if (! (curl_info->features & CURL_VERSION_SSL)) {
130                         logthing(LOGTHING_CRITICAL,
131                                 "CURL lacks SSL support; cannot use HKP url: %s",
132                                 hkpbase);
133                         hkp_cleanupdb();
134                         exit(EXIT_FAILURE);
135                 }
136         }
137 }
138
139 /**
140  *      Receive data from a CURL request and process it into a buffer context.
141  */
142 static size_t hkp_curl_recv_data(void *buffer, size_t size, size_t nmemb,
143                 void *ctx)
144 {
145         buffer_putchar(ctx, nmemb * size, buffer);
146
147         return (nmemb * size);
148 }
149
150 static int hkp_fetch_key_url(char *url,
151                 struct openpgp_publickey **publickey,
152                 bool intrans)
153 {
154         struct openpgp_packet_list *packets = NULL;
155         CURLcode res;
156         struct buffer_ctx buf;
157         int count = 0;
158
159         buf.offset = 0;
160         buf.size = 8192;
161         buf.buffer = malloc(8192);
162
163         curl_easy_setopt(curl, CURLOPT_URL, url);
164         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
165                         hkp_curl_recv_data);
166         curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
167         res = curl_easy_perform(curl);
168
169         if (res == 0) {
170                 buf.offset = 0;
171                 dearmor_openpgp_stream(buffer_fetchchar, &buf, &packets);
172                 count = parse_keys(packets, publickey);
173                 free_packet_list(packets);
174                 packets = NULL;
175         } else {
176                 logthing(LOGTHING_ERROR, "Couldn't find key: %s (%d)",
177                         curl_easy_strerror(res), res);
178         }
179
180         free(buf.buffer);
181         buf.offset = buf.size = 0;
182         buf.buffer = NULL;
183
184         return count;
185 }
186
187 /**
188  *      hkp_fetch_key_id - Given a keyid fetch the key from HKP server.
189  */
190 static int hkp_fetch_key_id(uint64_t keyid,
191                 struct openpgp_publickey **publickey,
192                 bool intrans)
193 {
194         char keyurl[1024];
195
196         snprintf(keyurl, sizeof(keyurl),
197                         "%s/lookup?op=get&search=0x%08" PRIX64,
198                         hkpbase, keyid);
199
200         return (hkp_fetch_key_url(keyurl, publickey, intrans));
201 }
202
203 /**
204  *      hkp_fetch_key_fp - Given a fingerprint fetch the key from HKP server.
205  */
206 static int hkp_fetch_key_fp(uint8_t *fp, size_t fpsize,
207                 struct openpgp_publickey **publickey,
208                 bool intrans)
209 {
210         char keyurl[1024];
211         int i, ofs;
212
213         if (fpsize > MAX_FINGERPRINT_LEN) {
214                 return 0;
215         }
216
217         ofs = snprintf(keyurl, sizeof(keyurl),
218                         "%s/lookup?op=get&search=0x", hkpbase);
219
220         if ((ofs + fpsize * 2 + 1)> sizeof(keyurl)) {
221                 return 0;
222         }
223
224         for (i = 0; i < fpsize; i++) {
225                 ofs += sprintf(&keyurl[ofs], "%02X", fp[i]);
226         }
227
228         return (hkp_fetch_key_url(keyurl, publickey, intrans));
229 }
230
231 /**
232  *      fetch_key_text - Tries to find the keys that contain the supplied text.
233  *      @search: The text to search for.
234  *      @publickey: A pointer to a structure to return the key in.
235  *
236  *      This function searches for the supplied text and returns the keys that
237  *      contain it.
238  *
239  *      TODO: Write for flat file access. Some sort of grep?
240  */
241 static int hkp_fetch_key_text(const char *search,
242                 struct openpgp_publickey **publickey)
243 {
244         char keyurl[1024];
245
246         snprintf(keyurl, sizeof(keyurl),
247                         "%s/lookup?op=get&search=%s",
248                         hkpbase, search);
249
250         return (hkp_fetch_key_url(keyurl, publickey, false));
251 }
252
253 /**
254  *      store_key - Takes a key and stores it.
255  *      @publickey: A pointer to the public key to store.
256  *      @intrans: If we're already in a transaction.
257  *      @update: If true the key exists and should be updated.
258  *
259  */
260 static int hkp_store_key(struct openpgp_publickey *publickey, bool intrans,
261                 bool update)
262 {
263         struct openpgp_packet_list *packets = NULL;
264         struct openpgp_packet_list *list_end = NULL;
265         char keyurl[1024];
266         CURLcode res;
267         struct buffer_ctx buf;
268         char *addform;
269
270         buf.offset = 0;
271         buf.size = 8192;
272         buf.buffer = malloc(8192);
273         buf.offset = snprintf(buf.buffer, buf.size, "keytextz");
274
275         flatten_publickey(publickey, &packets, &list_end);
276         armor_openpgp_stream(buffer_putchar, &buf, packets);
277         addform = curl_easy_escape(curl, buf.buffer, buf.offset);
278         addform[7] = '=';
279
280         snprintf(keyurl, sizeof(keyurl), "%s/add", hkpbase);
281
282         curl_easy_setopt(curl, CURLOPT_URL, keyurl);
283         curl_easy_setopt(curl, CURLOPT_POSTFIELDS, addform);
284         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
285                         hkp_curl_recv_data);
286         buf.offset = 0;
287         curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
288         res = curl_easy_perform(curl);
289
290         if (res != 0) {
291                 logthing(LOGTHING_ERROR, "Couldn't send key: %s (%d)",
292                         curl_easy_strerror(res), res);
293         }
294
295         curl_free(addform);
296
297         /* TODO: buf has any response text we might want to parse. */
298         free(buf.buffer);
299         buf.offset = buf.size = 0;
300         buf.buffer = NULL;
301
302         return (res == 0) ? 1 : 0;
303 }
304
305 /**
306  *      delete_key - Given a keyid delete the key from storage.
307  *      @keyid: The keyid to delete.
308  *      @intrans: If we're already in a transaction.
309  *
310  *      No op for HKP.
311  */
312 static int hkp_delete_key(uint64_t keyid, bool intrans)
313 {
314         return -1;
315 }
316
317 /**
318  *      iterate_keys - call a function once for each key in the db.
319  *      @iterfunc: The function to call.
320  *      @ctx: A context pointer
321  *
322  *      Not applicable for HKP backend.
323  */
324 static int hkp_iterate_keys(void (*iterfunc)(void *ctx,
325                 struct openpgp_publickey *key), void *ctx)
326 {
327         return 0;
328 }
329
330 /**
331  *      starttrans - Start a transaction.
332  *
333  *      This is just a no-op for HKP access.
334  */
335 static bool hkp_starttrans(void)
336 {
337         return true;
338 }
339
340 /**
341  *      endtrans - End a transaction.
342  *
343  *      This is just a no-op for HKP access.
344  */
345 static void hkp_endtrans(void)
346 {
347         return;
348 }
349
350 /*
351  * Include the basic keydb routines.
352  */
353 #define NEED_KEYID2UID 1
354 #define NEED_GETKEYSIGS 1
355 #define NEED_GETFULLKEYID 1
356 #define NEED_UPDATEKEYS 1
357 #include "keydb.c"
358
359 struct dbfuncs keydb_hkp_funcs = {
360         .initdb                 = hkp_initdb,
361         .cleanupdb              = hkp_cleanupdb,
362         .starttrans             = hkp_starttrans,
363         .endtrans               = hkp_endtrans,
364         .fetch_key_id           = hkp_fetch_key_id,
365         .fetch_key_fp           = hkp_fetch_key_fp,
366         .fetch_key_text         = hkp_fetch_key_text,
367         .store_key              = hkp_store_key,
368         .update_keys            = generic_update_keys,
369         .delete_key             = hkp_delete_key,
370         .getkeysigs             = generic_getkeysigs,
371         .cached_getkeysigs      = generic_cached_getkeysigs,
372         .keyid2uid              = generic_keyid2uid,
373         .getfullkeyid           = generic_getfullkeyid,
374         .iterate_keys           = hkp_iterate_keys,
375 };