dirmngr: Check for WKD support at session end
[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, &infp);
261               else
262                 BUG ();
263
264               if (err)
265                 {
266                   /* It is possible that a server does not carry a
267                      key, thus we only save the error and continue
268                      with the next pattern.  FIXME: It is an open
269                      question how to return such an error condition to
270                      the caller.  */
271                   first_err = err;
272                   err = 0;
273                 }
274               else
275                 {
276                   err = copy_stream (infp, outfp);
277                   /* Reading from the keyserver should never fail, thus
278                      return this error.  */
279                   if (!err)
280                     any_data = 1;
281                   es_fclose (infp);
282                   infp = NULL;
283                 }
284             }
285         }
286       if (any_data)
287         break; /* Stop loop after a keyserver returned something.  */
288     }
289
290   if (!any_server)
291     err = gpg_error (GPG_ERR_NO_KEYSERVER);
292   else if (!err && first_err && !any_data)
293     err = first_err;
294   return err;
295 }
296
297
298 /* Retrieve keys from URL and write the result to the provided output
299  * stream OUTFP.  If OUTFP is NULL the data is written to the bit
300  * bucket. */
301 gpg_error_t
302 ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
303 {
304   gpg_error_t err = 0;
305   estream_t infp;
306   parsed_uri_t parsed_uri;  /* The broken down URI.  */
307
308   if (!url)
309     return gpg_error (GPG_ERR_INV_URI);
310
311   err = http_parse_uri (&parsed_uri, url, 1);
312   if (err)
313     return err;
314
315   if (parsed_uri->is_http)
316     {
317       err = ks_http_fetch (ctrl, url, &infp);
318       if (!err)
319         {
320           err = copy_stream (infp, outfp);
321           es_fclose (infp);
322         }
323     }
324   else if (!parsed_uri->opaque)
325     {
326       err = gpg_error (GPG_ERR_INV_URI);
327     }
328   else if (!strcmp (parsed_uri->scheme, "finger"))
329     {
330       err = ks_finger_fetch (ctrl, parsed_uri, &infp);
331       if (!err)
332         {
333           err = copy_stream (infp, outfp);
334           es_fclose (infp);
335         }
336     }
337   else if (!strcmp (parsed_uri->scheme, "kdns"))
338     {
339       err = ks_kdns_fetch (ctrl, parsed_uri, &infp);
340       if (!err)
341         {
342           err = copy_stream (infp, outfp);
343           es_fclose (infp);
344         }
345     }
346   else
347     err = gpg_error (GPG_ERR_INV_URI);
348
349   http_release_parsed_uri (parsed_uri);
350   return err;
351 }
352
353
354
355 /* Send an OpenPGP key to all keyservers.  The key in {DATA,DATALEN}
356    is expected to be in OpenPGP binary transport format.  The metadata
357    in {INFO,INFOLEN} is in colon-separated format (concretely, it is
358    the output of 'for x in keys sigs; do gpg --list-$x --with-colons
359    KEYID; done'.  This function may modify DATA and INFO.  If this is
360    a problem, then the caller should create a copy.  */
361 gpg_error_t
362 ks_action_put (ctrl_t ctrl, uri_item_t keyservers,
363                void *data, size_t datalen,
364                void *info, size_t infolen)
365 {
366   gpg_error_t err = 0;
367   gpg_error_t first_err = 0;
368   int any_server = 0;
369   uri_item_t uri;
370
371   (void) info;
372   (void) infolen;
373
374   for (uri = keyservers; !err && uri; uri = uri->next)
375     {
376       int is_http = uri->parsed_uri->is_http;
377       int is_ldap = 0;
378
379 #if USE_LDAP
380       is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
381                 || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
382                 || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
383 #endif
384
385       if (is_http || is_ldap)
386         {
387           any_server = 1;
388 #if USE_LDAP
389           if (is_ldap)
390             err = ks_ldap_put (ctrl, uri->parsed_uri, data, datalen,
391                                info, infolen);
392           else
393 #endif
394             {
395               err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
396             }
397           if (err)
398             {
399               first_err = err;
400               err = 0;
401             }
402         }
403     }
404
405   if (!any_server)
406     err = gpg_error (GPG_ERR_NO_KEYSERVER);
407   else if (!err && first_err)
408     err = first_err;
409   return err;
410 }