dirmngr: Allow redirection from https to http for CRLs
[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 <https://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   int any_results = 0;
148   uri_item_t uri;
149   estream_t infp;
150
151   if (!patterns)
152     return gpg_error (GPG_ERR_NO_USER_ID);
153
154   /* FIXME: We only take care of the first pattern.  To fully support
155      multiple patterns we might either want to run several queries in
156      parallel and merge them.  We also need to decide what to do with
157      errors - it might not be the best idea to ignore an error from
158      one server and silently continue with another server.  For now we
159      stop at the first error, unless the server responds with '404 Not
160      Found', in which case we try the next server.  */
161   for (uri = keyservers; !err && uri; uri = uri->next)
162     {
163       int is_http = uri->parsed_uri->is_http;
164       int is_ldap = 0;
165       unsigned int http_status = 0;
166 #if USE_LDAP
167       is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
168                  || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
169                  || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
170 #endif
171       if (is_http || is_ldap)
172         {
173           any_server = 1;
174 #if USE_LDAP
175           if (is_ldap)
176             err = ks_ldap_search (ctrl, uri->parsed_uri, patterns->d, &infp);
177           else
178 #endif
179             {
180               err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d,
181                                    &infp, &http_status);
182             }
183
184           if (err == gpg_error (GPG_ERR_NO_DATA)
185               && http_status == 404 /* not found */)
186             {
187               /* No record found.  Clear error and try next server.  */
188               err = 0;
189               continue;
190             }
191
192           if (!err)
193             {
194               err = copy_stream (infp, outfp);
195               es_fclose (infp);
196               any_results = 1;
197               break;
198             }
199         }
200     }
201
202   if (!any_server)
203     err = gpg_error (GPG_ERR_NO_KEYSERVER);
204   else if (err == 0 && !any_results)
205     err = gpg_error (GPG_ERR_NO_DATA);
206   return err;
207 }
208
209
210 /* Get the requested keys (matching PATTERNS) using all configured
211    keyservers and write the result to the provided output stream.  */
212 gpg_error_t
213 ks_action_get (ctrl_t ctrl, uri_item_t keyservers,
214                strlist_t patterns, estream_t outfp)
215 {
216   gpg_error_t err = 0;
217   gpg_error_t first_err = 0;
218   int any_server = 0;
219   int any_data = 0;
220   strlist_t sl;
221   uri_item_t uri;
222   estream_t infp;
223
224   if (!patterns)
225     return gpg_error (GPG_ERR_NO_USER_ID);
226
227   /* FIXME: We only take care of the first keyserver.  To fully
228      support multiple keyservers we need to track the result for each
229      pattern and use the next keyserver if one key was not found.  The
230      keyservers might not all be fully synced thus it is not clear
231      whether the first keyserver has the freshest copy of the key.
232      Need to think about a better strategy.  */
233   for (uri = keyservers; !err && uri; uri = uri->next)
234     {
235       int is_hkp_s = (strcmp (uri->parsed_uri->scheme, "hkp") == 0
236                       || strcmp (uri->parsed_uri->scheme, "hkps") == 0);
237       int is_http_s = (strcmp (uri->parsed_uri->scheme, "http") == 0
238                        || strcmp (uri->parsed_uri->scheme, "https") == 0);
239       int is_ldap = 0;
240
241 #if USE_LDAP
242       is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
243                  || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
244                  || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
245 #endif
246
247       if (is_hkp_s || is_http_s || is_ldap)
248         {
249           any_server = 1;
250           for (sl = patterns; !err && sl; sl = sl->next)
251             {
252 #if USE_LDAP
253               if (is_ldap)
254                 err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, &infp);
255               else
256 #endif
257               if (is_hkp_s)
258                 err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
259               else if (is_http_s)
260                 err = ks_http_fetch (ctrl, uri->parsed_uri->original,
261                                      KS_HTTP_FETCH_NOCACHE,
262                                      &infp);
263               else
264                 BUG ();
265
266               if (err)
267                 {
268                   /* It is possible that a server does not carry a
269                      key, thus we only save the error and continue
270                      with the next pattern.  FIXME: It is an open
271                      question how to return such an error condition to
272                      the caller.  */
273                   first_err = err;
274                   err = 0;
275                 }
276               else
277                 {
278                   err = copy_stream (infp, outfp);
279                   /* Reading from the keyserver should never fail, thus
280                      return this error.  */
281                   if (!err)
282                     any_data = 1;
283                   es_fclose (infp);
284                   infp = NULL;
285                 }
286             }
287         }
288       if (any_data)
289         break; /* Stop loop after a keyserver returned something.  */
290     }
291
292   if (!any_server)
293     err = gpg_error (GPG_ERR_NO_KEYSERVER);
294   else if (!err && first_err && !any_data)
295     err = first_err;
296   return err;
297 }
298
299
300 /* Retrieve keys from URL and write the result to the provided output
301  * stream OUTFP.  If OUTFP is NULL the data is written to the bit
302  * bucket. */
303 gpg_error_t
304 ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
305 {
306   gpg_error_t err = 0;
307   estream_t infp;
308   parsed_uri_t parsed_uri;  /* The broken down URI.  */
309
310   if (!url)
311     return gpg_error (GPG_ERR_INV_URI);
312
313   err = http_parse_uri (&parsed_uri, url, 1);
314   if (err)
315     return err;
316
317   if (parsed_uri->is_http)
318     {
319       err = ks_http_fetch (ctrl, url, KS_HTTP_FETCH_NOCACHE, &infp);
320       if (!err)
321         {
322           err = copy_stream (infp, outfp);
323           es_fclose (infp);
324         }
325     }
326   else if (!parsed_uri->opaque)
327     {
328       err = gpg_error (GPG_ERR_INV_URI);
329     }
330   else if (!strcmp (parsed_uri->scheme, "finger"))
331     {
332       err = ks_finger_fetch (ctrl, parsed_uri, &infp);
333       if (!err)
334         {
335           err = copy_stream (infp, outfp);
336           es_fclose (infp);
337         }
338     }
339   else if (!strcmp (parsed_uri->scheme, "kdns"))
340     {
341       err = ks_kdns_fetch (ctrl, parsed_uri, &infp);
342       if (!err)
343         {
344           err = copy_stream (infp, outfp);
345           es_fclose (infp);
346         }
347     }
348   else
349     err = gpg_error (GPG_ERR_INV_URI);
350
351   http_release_parsed_uri (parsed_uri);
352   return err;
353 }
354
355
356
357 /* Send an OpenPGP key to all keyservers.  The key in {DATA,DATALEN}
358    is expected to be in OpenPGP binary transport format.  The metadata
359    in {INFO,INFOLEN} is in colon-separated format (concretely, it is
360    the output of 'for x in keys sigs; do gpg --list-$x --with-colons
361    KEYID; done'.  This function may modify DATA and INFO.  If this is
362    a problem, then the caller should create a copy.  */
363 gpg_error_t
364 ks_action_put (ctrl_t ctrl, uri_item_t keyservers,
365                void *data, size_t datalen,
366                void *info, size_t infolen)
367 {
368   gpg_error_t err = 0;
369   gpg_error_t first_err = 0;
370   int any_server = 0;
371   uri_item_t uri;
372
373   (void) info;
374   (void) infolen;
375
376   for (uri = keyservers; !err && uri; uri = uri->next)
377     {
378       int is_http = uri->parsed_uri->is_http;
379       int is_ldap = 0;
380
381 #if USE_LDAP
382       is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
383                 || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
384                 || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
385 #endif
386
387       if (is_http || is_ldap)
388         {
389           any_server = 1;
390 #if USE_LDAP
391           if (is_ldap)
392             err = ks_ldap_put (ctrl, uri->parsed_uri, data, datalen,
393                                info, infolen);
394           else
395 #endif
396             {
397               err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
398             }
399           if (err)
400             {
401               first_err = err;
402               err = 0;
403             }
404         }
405     }
406
407   if (!any_server)
408     err = gpg_error (GPG_ERR_NO_KEYSERVER);
409   else if (!err && first_err)
410     err = first_err;
411   return err;
412 }