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