Line data Source code
1 : /* ks-action.c - OpenPGP keyserver actions
2 : * Copyright (C) 2011 Free Software Foundation, Inc.
3 : * Copyright (C) 2011, 2014 Werner Koch
4 : * Copyright (C) 2015 g10 Code GmbH
5 : *
6 : * This file is part of GnuPG.
7 : *
8 : * GnuPG is free software; you can redistribute it and/or modify
9 : * it under the terms of the GNU General Public License as published by
10 : * the Free Software Foundation; either version 3 of the License, or
11 : * (at your option) any later version.
12 : *
13 : * GnuPG is distributed in the hope that it will be useful,
14 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : * GNU General Public License for more details.
17 : *
18 : * You should have received a copy of the GNU General Public License
19 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include <config.h>
23 :
24 : #include <stdio.h>
25 : #include <stdlib.h>
26 : #include <string.h>
27 : #include <assert.h>
28 :
29 : #include "dirmngr.h"
30 : #include "misc.h"
31 : #include "ks-engine.h"
32 : #include "ks-action.h"
33 : #if USE_LDAP
34 : # include "ldap-parse-uri.h"
35 : #endif
36 :
37 : /* Called by the engine's help functions to print the actual help. */
38 : gpg_error_t
39 0 : ks_print_help (ctrl_t ctrl, const char *text)
40 : {
41 0 : return dirmngr_status_help (ctrl, text);
42 : }
43 :
44 :
45 : /* Called by the engine's help functions to print the actual help. */
46 : gpg_error_t
47 0 : ks_printf_help (ctrl_t ctrl, const char *format, ...)
48 : {
49 : va_list arg_ptr;
50 : gpg_error_t err;
51 : char *buf;
52 :
53 0 : va_start (arg_ptr, format);
54 0 : buf = es_vbsprintf (format, arg_ptr);
55 0 : err = buf? 0 : gpg_error_from_syserror ();
56 0 : va_end (arg_ptr);
57 0 : if (!err)
58 0 : err = dirmngr_status_help (ctrl, buf);
59 0 : es_free (buf);
60 0 : return err;
61 : }
62 :
63 :
64 : /* Run the help command for the engine responsible for URI. */
65 : gpg_error_t
66 0 : ks_action_help (ctrl_t ctrl, const char *url)
67 : {
68 : gpg_error_t err;
69 : parsed_uri_t parsed_uri; /* The broken down URI. */
70 :
71 0 : if (!url || !*url)
72 : {
73 0 : ks_print_help (ctrl, "Known schemata:\n");
74 0 : parsed_uri = NULL;
75 : }
76 : else
77 : {
78 : #if USE_LDAP
79 0 : if (ldap_uri_p (url))
80 0 : err = ldap_parse_uri (&parsed_uri, url);
81 : else
82 : #endif
83 : {
84 0 : err = http_parse_uri (&parsed_uri, url, 1);
85 : }
86 :
87 0 : if (err)
88 0 : return err;
89 : }
90 :
91 : /* Call all engines to give them a chance to print a help sting. */
92 0 : err = ks_hkp_help (ctrl, parsed_uri);
93 0 : if (!err)
94 0 : err = ks_http_help (ctrl, parsed_uri);
95 0 : if (!err)
96 0 : err = ks_finger_help (ctrl, parsed_uri);
97 0 : if (!err)
98 0 : err = ks_kdns_help (ctrl, parsed_uri);
99 : #if USE_LDAP
100 0 : if (!err)
101 0 : err = ks_ldap_help (ctrl, parsed_uri);
102 : #endif
103 :
104 0 : if (!parsed_uri)
105 0 : ks_print_help (ctrl,
106 : "(Use an URL for engine specific help.)");
107 : else
108 0 : http_release_parsed_uri (parsed_uri);
109 0 : return err;
110 : }
111 :
112 :
113 : /* Resolve all host names. This is useful for looking at the status
114 : of configured keyservers. */
115 : gpg_error_t
116 0 : ks_action_resolve (ctrl_t ctrl, uri_item_t keyservers)
117 : {
118 0 : gpg_error_t err = 0;
119 0 : int any_server = 0;
120 : uri_item_t uri;
121 :
122 0 : for (uri = keyservers; !err && uri; uri = uri->next)
123 : {
124 0 : if (uri->parsed_uri->is_http)
125 : {
126 0 : any_server = 1;
127 0 : err = ks_hkp_resolve (ctrl, uri->parsed_uri);
128 0 : if (err)
129 0 : break;
130 : }
131 : }
132 :
133 0 : if (!any_server)
134 0 : err = gpg_error (GPG_ERR_NO_KEYSERVER);
135 0 : return err;
136 : }
137 :
138 :
139 : /* Search all configured keyservers for keys matching PATTERNS and
140 : write the result to the provided output stream. */
141 : gpg_error_t
142 0 : ks_action_search (ctrl_t ctrl, uri_item_t keyservers,
143 : strlist_t patterns, estream_t outfp)
144 : {
145 0 : gpg_error_t err = 0;
146 0 : int any_server = 0;
147 : uri_item_t uri;
148 : estream_t infp;
149 :
150 0 : if (!patterns)
151 0 : return gpg_error (GPG_ERR_NO_USER_ID);
152 :
153 : /* FIXME: We only take care of the first pattern. To fully support
154 : multiple patterns we might either want to run several queries in
155 : parallel and merge them. We also need to decide what to do with
156 : errors - it might not be the best idea to ignore an error from
157 : one server and silently continue with another server. For now we
158 : stop at the first error. */
159 0 : for (uri = keyservers; !err && uri; uri = uri->next)
160 : {
161 0 : int is_http = uri->parsed_uri->is_http;
162 0 : int is_ldap = 0;
163 : #if USE_LDAP
164 0 : is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
165 0 : || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
166 0 : || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
167 : #endif
168 0 : if (is_http || is_ldap)
169 : {
170 0 : any_server = 1;
171 : #if USE_LDAP
172 0 : if (is_ldap)
173 0 : err = ks_ldap_search (ctrl, uri->parsed_uri, patterns->d, &infp);
174 : else
175 : #endif
176 : {
177 0 : err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d, &infp);
178 : }
179 :
180 0 : if (!err)
181 : {
182 0 : err = copy_stream (infp, outfp);
183 0 : es_fclose (infp);
184 0 : break;
185 : }
186 : }
187 : }
188 :
189 0 : if (!any_server)
190 0 : err = gpg_error (GPG_ERR_NO_KEYSERVER);
191 0 : return err;
192 : }
193 :
194 :
195 : /* Get the requested keys (matching PATTERNS) using all configured
196 : keyservers and write the result to the provided output stream. */
197 : gpg_error_t
198 0 : ks_action_get (ctrl_t ctrl, uri_item_t keyservers,
199 : strlist_t patterns, estream_t outfp)
200 : {
201 0 : gpg_error_t err = 0;
202 0 : gpg_error_t first_err = 0;
203 0 : int any_server = 0;
204 0 : int any_data = 0;
205 : strlist_t sl;
206 : uri_item_t uri;
207 : estream_t infp;
208 :
209 0 : if (!patterns)
210 0 : return gpg_error (GPG_ERR_NO_USER_ID);
211 :
212 : /* FIXME: We only take care of the first keyserver. To fully
213 : support multiple keyservers we need to track the result for each
214 : pattern and use the next keyserver if one key was not found. The
215 : keyservers might not all be fully synced thus it is not clear
216 : whether the first keyserver has the freshest copy of the key.
217 : Need to think about a better strategy. */
218 0 : for (uri = keyservers; !err && uri; uri = uri->next)
219 : {
220 0 : int is_http = uri->parsed_uri->is_http;
221 0 : int is_ldap = 0;
222 :
223 : #if USE_LDAP
224 0 : is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
225 0 : || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
226 0 : || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
227 : #endif
228 :
229 0 : if (is_http || is_ldap)
230 : {
231 0 : any_server = 1;
232 0 : for (sl = patterns; !err && sl; sl = sl->next)
233 : {
234 : #if USE_LDAP
235 0 : if (is_ldap)
236 0 : err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, &infp);
237 : else
238 : #endif
239 : {
240 0 : err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
241 : }
242 :
243 0 : if (err)
244 : {
245 : /* It is possible that a server does not carry a
246 : key, thus we only save the error and continue
247 : with the next pattern. FIXME: It is an open
248 : question how to return such an error condition to
249 : the caller. */
250 0 : first_err = err;
251 0 : err = 0;
252 : }
253 : else
254 : {
255 0 : err = copy_stream (infp, outfp);
256 : /* Reading from the keyserver should never fail, thus
257 : return this error. */
258 0 : if (!err)
259 0 : any_data = 1;
260 0 : es_fclose (infp);
261 0 : infp = NULL;
262 : }
263 : }
264 : }
265 0 : if (any_data)
266 0 : break; /* Stop loop after a keyserver returned something. */
267 : }
268 :
269 0 : if (!any_server)
270 0 : err = gpg_error (GPG_ERR_NO_KEYSERVER);
271 0 : else if (!err && first_err && !any_data)
272 0 : err = first_err;
273 0 : return err;
274 : }
275 :
276 :
277 : /* Retrieve keys from URL and write the result to the provided output
278 : stream OUTFP. */
279 : gpg_error_t
280 0 : ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
281 : {
282 0 : gpg_error_t err = 0;
283 : estream_t infp;
284 : parsed_uri_t parsed_uri; /* The broken down URI. */
285 :
286 0 : if (!url)
287 0 : return gpg_error (GPG_ERR_INV_URI);
288 :
289 0 : err = http_parse_uri (&parsed_uri, url, 1);
290 0 : if (err)
291 0 : return err;
292 :
293 0 : if (parsed_uri->is_http)
294 : {
295 0 : err = ks_http_fetch (ctrl, url, &infp);
296 0 : if (!err)
297 : {
298 0 : err = copy_stream (infp, outfp);
299 0 : es_fclose (infp);
300 : }
301 : }
302 0 : else if (!parsed_uri->opaque)
303 : {
304 0 : err = gpg_error (GPG_ERR_INV_URI);
305 : }
306 0 : else if (!strcmp (parsed_uri->scheme, "finger"))
307 : {
308 0 : err = ks_finger_fetch (ctrl, parsed_uri, &infp);
309 0 : if (!err)
310 : {
311 0 : err = copy_stream (infp, outfp);
312 0 : es_fclose (infp);
313 : }
314 : }
315 0 : else if (!strcmp (parsed_uri->scheme, "kdns"))
316 : {
317 0 : err = ks_kdns_fetch (ctrl, parsed_uri, &infp);
318 0 : if (!err)
319 : {
320 0 : err = copy_stream (infp, outfp);
321 0 : es_fclose (infp);
322 : }
323 : }
324 : else
325 0 : err = gpg_error (GPG_ERR_INV_URI);
326 :
327 0 : http_release_parsed_uri (parsed_uri);
328 0 : return err;
329 : }
330 :
331 :
332 :
333 : /* Send an OpenPGP key to all keyservers. The key in {DATA,DATALEN}
334 : is expected to be in OpenPGP binary transport format. The metadata
335 : in {INFO,INFOLEN} is in colon-separated format (concretely, it is
336 : the output of 'for x in keys sigs; do gpg --list-$x --with-colons
337 : KEYID; done'. This function may modify DATA and INFO. If this is
338 : a problem, then the caller should create a copy. */
339 : gpg_error_t
340 0 : ks_action_put (ctrl_t ctrl, uri_item_t keyservers,
341 : void *data, size_t datalen,
342 : void *info, size_t infolen)
343 : {
344 0 : gpg_error_t err = 0;
345 0 : gpg_error_t first_err = 0;
346 0 : int any_server = 0;
347 : uri_item_t uri;
348 :
349 : (void) info;
350 : (void) infolen;
351 :
352 0 : for (uri = keyservers; !err && uri; uri = uri->next)
353 : {
354 0 : int is_http = uri->parsed_uri->is_http;
355 0 : int is_ldap = 0;
356 :
357 : #if USE_LDAP
358 0 : is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
359 0 : || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
360 0 : || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
361 : #endif
362 :
363 0 : if (is_http || is_ldap)
364 : {
365 0 : any_server = 1;
366 : #if USE_LDAP
367 0 : if (is_ldap)
368 0 : err = ks_ldap_put (ctrl, uri->parsed_uri, data, datalen,
369 : info, infolen);
370 : else
371 : #endif
372 : {
373 0 : err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
374 : }
375 0 : if (err)
376 : {
377 0 : first_err = err;
378 0 : err = 0;
379 : }
380 : }
381 : }
382 :
383 0 : if (!any_server)
384 0 : err = gpg_error (GPG_ERR_NO_KEYSERVER);
385 0 : else if (!err && first_err)
386 0 : err = first_err;
387 0 : return err;
388 : }
|