dirmngr: Add http proxy support for keyservers.
[gnupg.git] / dirmngr / ks-action.c
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 ks_print_help (ctrl_t ctrl, const char *text)
40 {
41   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 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   va_start (arg_ptr, format);
54   buf = es_vbsprintf (format, arg_ptr);
55   err = buf? 0 : gpg_error_from_syserror ();
56   va_end (arg_ptr);
57   if (!err)
58     err = dirmngr_status_help (ctrl, buf);
59   es_free (buf);
60   return err;
61 }
62
63
64 /* Run the help command for the engine responsible for URI.  */
65 gpg_error_t
66 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   if (!url || !*url)
72     {
73       ks_print_help (ctrl, "Known schemata:\n");
74       parsed_uri = NULL;
75     }
76   else
77     {
78 #if USE_LDAP
79       if (ldap_uri_p (url))
80         err = ldap_parse_uri (&parsed_uri, url);
81       else
82 #endif
83         {
84           err = http_parse_uri (&parsed_uri, url, 1);
85         }
86
87       if (err)
88         return err;
89     }
90
91   /* Call all engines to give them a chance to print a help sting.  */
92   err = ks_hkp_help (ctrl, parsed_uri);
93   if (!err)
94     err = ks_http_help (ctrl, parsed_uri);
95   if (!err)
96     err = ks_finger_help (ctrl, parsed_uri);
97   if (!err)
98     err = ks_kdns_help (ctrl, parsed_uri);
99 #if USE_LDAP
100   if (!err)
101     err = ks_ldap_help (ctrl, parsed_uri);
102 #endif
103
104   if (!parsed_uri)
105     ks_print_help (ctrl,
106                    "(Use an URL for engine specific help.)");
107   else
108     http_release_parsed_uri (parsed_uri);
109   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 ks_action_resolve (ctrl_t ctrl, uri_item_t keyservers)
117 {
118   gpg_error_t err = 0;
119   int any_server = 0;
120   uri_item_t uri;
121
122   for (uri = keyservers; !err && uri; uri = uri->next)
123     {
124       if (uri->parsed_uri->is_http)
125         {
126           any_server = 1;
127           err = ks_hkp_resolve (ctrl, uri->parsed_uri);
128           if (err)
129             break;
130         }
131     }
132
133   if (!any_server)
134     err = gpg_error (GPG_ERR_NO_KEYSERVER);
135   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 ks_action_search (ctrl_t ctrl, uri_item_t keyservers,
143                   strlist_t patterns, estream_t outfp)
144 {
145   gpg_error_t err = 0;
146   int any_server = 0;
147   uri_item_t uri;
148   estream_t infp;
149
150   if (!patterns)
151     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   for (uri = keyservers; !err && uri; uri = uri->next)
160     {
161       int is_http = uri->parsed_uri->is_http;
162       int is_ldap = 0;
163 #if USE_LDAP
164       is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
165                  || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
166                  || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
167 #endif
168       if (is_http || is_ldap)
169         {
170           any_server = 1;
171 #if USE_LDAP
172           if (is_ldap)
173             err = ks_ldap_search (ctrl, uri->parsed_uri, patterns->d, &infp);
174           else
175 #endif
176             {
177               err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d, &infp);
178             }
179
180           if (!err)
181             {
182               err = copy_stream (infp, outfp);
183               es_fclose (infp);
184               break;
185             }
186         }
187     }
188
189   if (!any_server)
190     err = gpg_error (GPG_ERR_NO_KEYSERVER);
191   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 ks_action_get (ctrl_t ctrl, uri_item_t keyservers,
199                strlist_t patterns, estream_t outfp)
200 {
201   gpg_error_t err = 0;
202   gpg_error_t first_err = 0;
203   int any_server = 0;
204   int any_data = 0;
205   strlist_t sl;
206   uri_item_t uri;
207   estream_t infp;
208
209   if (!patterns)
210     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   for (uri = keyservers; !err && uri; uri = uri->next)
219     {
220       int is_http = uri->parsed_uri->is_http;
221       int is_ldap = 0;
222
223 #if USE_LDAP
224       is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
225                  || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
226                  || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
227 #endif
228
229       if (is_http || is_ldap)
230         {
231           any_server = 1;
232           for (sl = patterns; !err && sl; sl = sl->next)
233             {
234 #if USE_LDAP
235               if (is_ldap)
236                 err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, &infp);
237               else
238 #endif
239                 {
240                   err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
241                 }
242
243               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                   first_err = err;
251                   err = 0;
252                 }
253               else
254                 {
255                   err = copy_stream (infp, outfp);
256                   /* Reading from the keyserver should never fail, thus
257                      return this error.  */
258                   if (!err)
259                     any_data = 1;
260                   es_fclose (infp);
261                   infp = NULL;
262                 }
263             }
264         }
265       if (any_data)
266         break; /* Stop loop after a keyserver returned something.  */
267     }
268
269   if (!any_server)
270     err = gpg_error (GPG_ERR_NO_KEYSERVER);
271   else if (!err && first_err && !any_data)
272     err = first_err;
273   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 ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
281 {
282   gpg_error_t err = 0;
283   estream_t infp;
284   parsed_uri_t parsed_uri;  /* The broken down URI.  */
285
286   if (!url)
287     return gpg_error (GPG_ERR_INV_URI);
288
289   err = http_parse_uri (&parsed_uri, url, 1);
290   if (err)
291     return err;
292
293   if (parsed_uri->is_http)
294     {
295       err = ks_http_fetch (ctrl, url, &infp);
296       if (!err)
297         {
298           err = copy_stream (infp, outfp);
299           es_fclose (infp);
300         }
301     }
302   else if (!parsed_uri->opaque)
303     {
304       err = gpg_error (GPG_ERR_INV_URI);
305     }
306   else if (!strcmp (parsed_uri->scheme, "finger"))
307     {
308       err = ks_finger_fetch (ctrl, parsed_uri, &infp);
309       if (!err)
310         {
311           err = copy_stream (infp, outfp);
312           es_fclose (infp);
313         }
314     }
315   else if (!strcmp (parsed_uri->scheme, "kdns"))
316     {
317       err = ks_kdns_fetch (ctrl, parsed_uri, &infp);
318       if (!err)
319         {
320           err = copy_stream (infp, outfp);
321           es_fclose (infp);
322         }
323     }
324   else
325     err = gpg_error (GPG_ERR_INV_URI);
326
327   http_release_parsed_uri (parsed_uri);
328   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 ks_action_put (ctrl_t ctrl, uri_item_t keyservers,
341                void *data, size_t datalen,
342                void *info, size_t infolen)
343 {
344   gpg_error_t err = 0;
345   gpg_error_t first_err = 0;
346   int any_server = 0;
347   uri_item_t uri;
348
349   (void) info;
350   (void) infolen;
351
352   for (uri = keyservers; !err && uri; uri = uri->next)
353     {
354       int is_http = uri->parsed_uri->is_http;
355       int is_ldap = 0;
356
357 #if USE_LDAP
358       is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
359                 || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
360                 || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
361 #endif
362
363       if (is_http || is_ldap)
364         {
365           any_server = 1;
366 #if USE_LDAP
367           if (is_ldap)
368             err = ks_ldap_put (ctrl, uri->parsed_uri, data, datalen,
369                                info, infolen);
370           else
371 #endif
372             {
373               err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
374             }
375           if (err)
376             {
377               first_err = err;
378               err = 0;
379             }
380         }
381     }
382
383   if (!any_server)
384     err = gpg_error (GPG_ERR_NO_KEYSERVER);
385   else if (!err && first_err)
386     err = first_err;
387   return err;
388 }