]> the.earth.li Git - onak.git/blob - keydb_hkp.c
Set the user agent to onak/<version> for HKP backend
[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 /**
39  *      initdb - Initialize the key database.
40  *
41  *      We initialize CURL here.
42  */
43 static void hkp_initdb(bool readonly)
44 {
45         curl_global_init(CURL_GLOBAL_DEFAULT);
46         curl = curl_easy_init();
47         if (curl == NULL) {
48                 logthing(LOGTHING_CRITICAL, "Could not initialize CURL.");
49                 exit(EXIT_FAILURE);
50         }
51         curl_easy_setopt(curl, CURLOPT_USERAGENT, "onak/" ONAK_VERSION);
52 }
53
54 /**
55  *      cleanupdb - De-initialize the key database.
56  *
57  *      We cleanup CURL here.
58  */
59 static void hkp_cleanupdb(void)
60 {
61         if (curl) {
62                 curl_easy_cleanup(curl);
63                 curl = NULL;
64         }
65         curl_global_cleanup();
66 }
67
68 /**
69  *      Receive data from a CURL request and process it into a buffer context.
70  */
71 static size_t hkp_curl_recv_data(void *buffer, size_t size, size_t nmemb,
72                 void *ctx)
73 {
74         buffer_putchar(ctx, nmemb * size, buffer);
75
76         return (nmemb * size);
77 }
78
79 /**
80  *      fetch_key - Given a keyid fetch the key from storage.
81  *      @keyid: The keyid to fetch.
82  *      @publickey: A pointer to a structure to return the key in.
83  *      @intrans: If we're already in a transaction.
84  *
85  *      We use the hex representation of the keyid as the filename to fetch the
86  *      key from. The key is stored in the file as a binary OpenPGP stream of
87  *      packets, so we can just use read_openpgp_stream() to read the packets
88  *      in and then parse_keys() to parse the packets into a publickey
89  *      structure.
90  */
91 static int hkp_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
92                 bool intrans)
93 {
94         struct openpgp_packet_list *packets = NULL;
95         char keyurl[1024];
96         CURLcode res;
97         struct buffer_ctx buf;
98
99         buf.offset = 0;
100         buf.size = 8192;
101         buf.buffer = malloc(8192);
102
103         snprintf(keyurl, sizeof(keyurl),
104                         "http://%s:11371/pks/lookup?op=get&search=0x%08" PRIX64,
105                         config.db_dir, keyid);
106
107         curl_easy_setopt(curl, CURLOPT_URL, keyurl);
108         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
109                         hkp_curl_recv_data);
110         curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
111         res = curl_easy_perform(curl);
112
113         if (res == 0) {
114                 buf.offset = 0;
115                 dearmor_openpgp_stream(buffer_fetchchar, &buf, &packets);
116                 parse_keys(packets, publickey);
117                 free_packet_list(packets);
118                 packets = NULL;
119         } else {
120                 logthing(LOGTHING_ERROR, "Couldn't find key: %s (%d)",
121                         curl_easy_strerror(res), res);
122         }
123
124         free(buf.buffer);
125         buf.offset = buf.size = 0;
126         buf.buffer = NULL;
127
128         return (res == 0) ? 1 : 0;
129 }
130
131 /**
132  *      fetch_key_text - Tries to find the keys that contain the supplied text.
133  *      @search: The text to search for.
134  *      @publickey: A pointer to a structure to return the key in.
135  *
136  *      This function searches for the supplied text and returns the keys that
137  *      contain it.
138  *
139  *      TODO: Write for flat file access. Some sort of grep?
140  */
141 static int hkp_fetch_key_text(const char *search,
142                 struct openpgp_publickey **publickey)
143 {
144         struct openpgp_packet_list *packets = NULL;
145         char keyurl[1024];
146         CURLcode res;
147         struct buffer_ctx buf;
148         int count = 0;
149
150         buf.offset = 0;
151         buf.size = 8192;
152         buf.buffer = malloc(8192);
153
154         snprintf(keyurl, sizeof(keyurl),
155                         "http://%s:11371/pks/lookup?op=get&search=%s",
156                         config.db_dir, search);
157
158         curl_easy_setopt(curl, CURLOPT_URL, keyurl);
159         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
160                         hkp_curl_recv_data);
161         curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
162         res = curl_easy_perform(curl);
163
164         if (res == 0) {
165                 buf.offset = 0;
166                 dearmor_openpgp_stream(buffer_fetchchar, &buf, &packets);
167                 count = parse_keys(packets, publickey);
168                 free_packet_list(packets);
169                 packets = NULL;
170         } else {
171                 logthing(LOGTHING_ERROR, "Couldn't find key: %s (%d)",
172                         curl_easy_strerror(res), res);
173         }
174
175         free(buf.buffer);
176         buf.offset = buf.size = 0;
177         buf.buffer = NULL;
178
179         return count;
180 }
181
182 /**
183  *      store_key - Takes a key and stores it.
184  *      @publickey: A pointer to the public key to store.
185  *      @intrans: If we're already in a transaction.
186  *      @update: If true the key exists and should be updated.
187  *
188  */
189 static int hkp_store_key(struct openpgp_publickey *publickey, bool intrans,
190                 bool update)
191 {
192         struct openpgp_packet_list *packets = NULL;
193         struct openpgp_packet_list *list_end = NULL;
194         char keyurl[1024];
195         CURLcode res;
196         struct buffer_ctx buf;
197         char *addform;
198
199         buf.offset = 0;
200         buf.size = 8192;
201         buf.buffer = malloc(8192);
202         buf.offset = snprintf(buf.buffer, buf.size, "keytextz");
203
204         flatten_publickey(publickey, &packets, &list_end);
205         armor_openpgp_stream(buffer_putchar, &buf, packets);
206         addform = curl_easy_escape(curl, buf.buffer, buf.offset);
207         addform[7] = '=';
208
209         snprintf(keyurl, sizeof(keyurl),
210                         "http://%s:11371/pks/add",
211                         config.db_dir);
212
213         curl_easy_setopt(curl, CURLOPT_URL, keyurl);
214         curl_easy_setopt(curl, CURLOPT_POSTFIELDS, addform);
215         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
216                         hkp_curl_recv_data);
217         buf.offset = 0;
218         curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
219         res = curl_easy_perform(curl);
220
221         if (res != 0) {
222                 logthing(LOGTHING_ERROR, "Couldn't send key: %s (%d)",
223                         curl_easy_strerror(res), res);
224         }
225
226         curl_free(addform);
227
228         /* TODO: buf has any response text we might want to parse. */
229         free(buf.buffer);
230         buf.offset = buf.size = 0;
231         buf.buffer = NULL;
232
233         return (res == 0) ? 1 : 0;
234 }
235
236 /**
237  *      delete_key - Given a keyid delete the key from storage.
238  *      @keyid: The keyid to delete.
239  *      @intrans: If we're already in a transaction.
240  *
241  *      No op for HKP.
242  */
243 static int hkp_delete_key(uint64_t keyid, bool intrans)
244 {
245         return -1;
246 }
247
248 /**
249  *      iterate_keys - call a function once for each key in the db.
250  *      @iterfunc: The function to call.
251  *      @ctx: A context pointer
252  *
253  *      Not applicable for HKP backend.
254  */
255 static int hkp_iterate_keys(void (*iterfunc)(void *ctx,
256                 struct openpgp_publickey *key), void *ctx)
257 {
258         return 0;
259 }
260
261 /**
262  *      starttrans - Start a transaction.
263  *
264  *      This is just a no-op for HKP access.
265  */
266 static bool hkp_starttrans(void)
267 {
268         return true;
269 }
270
271 /**
272  *      endtrans - End a transaction.
273  *
274  *      This is just a no-op for HKP access.
275  */
276 static void hkp_endtrans(void)
277 {
278         return;
279 }
280
281 /*
282  * Include the basic keydb routines.
283  */
284 #define NEED_KEYID2UID 1
285 #define NEED_GETKEYSIGS 1
286 #define NEED_GETFULLKEYID 1
287 #define NEED_UPDATEKEYS 1
288 #include "keydb.c"
289
290 struct dbfuncs keydb_hkp_funcs = {
291         .initdb                 = hkp_initdb,
292         .cleanupdb              = hkp_cleanupdb,
293         .starttrans             = hkp_starttrans,
294         .endtrans               = hkp_endtrans,
295         .fetch_key              = hkp_fetch_key,
296         .fetch_key_text         = hkp_fetch_key_text,
297         .store_key              = hkp_store_key,
298         .update_keys            = generic_update_keys,
299         .delete_key             = hkp_delete_key,
300         .getkeysigs             = generic_getkeysigs,
301         .cached_getkeysigs      = generic_cached_getkeysigs,
302         .keyid2uid              = generic_keyid2uid,
303         .getfullkeyid           = generic_getfullkeyid,
304         .iterate_keys           = hkp_iterate_keys,
305 };