dirmngr: Better encapsulate the keyservers variable.
[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 #include "ldap-parse-uri.h"
34
35 /* Called by the engine's help functions to print the actual help.  */
36 gpg_error_t
37 ks_print_help (ctrl_t ctrl, const char *text)
38 {
39   return dirmngr_status_help (ctrl, text);
40 }
41
42
43 /* Called by the engine's help functions to print the actual help.  */
44 gpg_error_t
45 ks_printf_help (ctrl_t ctrl, const char *format, ...)
46 {
47   va_list arg_ptr;
48   gpg_error_t err;
49   char *buf;
50
51   va_start (arg_ptr, format);
52   buf = es_vbsprintf (format, arg_ptr);
53   err = buf? 0 : gpg_error_from_syserror ();
54   va_end (arg_ptr);
55   if (!err)
56     err = dirmngr_status_help (ctrl, buf);
57   es_free (buf);
58   return err;
59 }
60
61
62 /* Run the help command for the engine responsible for URI.  */
63 gpg_error_t
64 ks_action_help (ctrl_t ctrl, const char *url)
65 {
66   gpg_error_t err;
67   parsed_uri_t parsed_uri;  /* The broken down URI.  */
68
69   if (!url || !*url)
70     {
71       ks_print_help (ctrl, "Known schemata:\n");
72       parsed_uri = NULL;
73     }
74   else
75     {
76       if (ldap_uri_p (url))
77         err = ldap_parse_uri (&parsed_uri, url);
78       else
79         err = http_parse_uri (&parsed_uri, url, 1);
80
81       if (err)
82         return err;
83     }
84
85   /* Call all engines to give them a chance to print a help sting.  */
86   err = ks_hkp_help (ctrl, parsed_uri);
87   if (!err)
88     err = ks_http_help (ctrl, parsed_uri);
89   if (!err)
90     err = ks_finger_help (ctrl, parsed_uri);
91   if (!err)
92     err = ks_kdns_help (ctrl, parsed_uri);
93   if (!err)
94     err = ks_ldap_help (ctrl, parsed_uri);
95
96   if (!parsed_uri)
97     ks_print_help (ctrl,
98                    "(Use an URL for engine specific help.)");
99   else
100     http_release_parsed_uri (parsed_uri);
101   return err;
102 }
103
104
105 /* Resolve all host names.  This is useful for looking at the status
106    of configured keyservers.  */
107 gpg_error_t
108 ks_action_resolve (ctrl_t ctrl, uri_item_t keyservers)
109 {
110   gpg_error_t err = 0;
111   int any_server = 0;
112   uri_item_t uri;
113
114   for (uri = keyservers; !err && uri; uri = uri->next)
115     {
116       if (uri->parsed_uri->is_http)
117         {
118           any_server = 1;
119           err = ks_hkp_resolve (ctrl, uri->parsed_uri);
120           if (err)
121             break;
122         }
123     }
124
125   if (!any_server)
126     err = gpg_error (GPG_ERR_NO_KEYSERVER);
127   return err;
128 }
129
130
131 /* Search all configured keyservers for keys matching PATTERNS and
132    write the result to the provided output stream.  */
133 gpg_error_t
134 ks_action_search (ctrl_t ctrl, uri_item_t keyservers,
135                   strlist_t patterns, estream_t outfp)
136 {
137   gpg_error_t err = 0;
138   int any_server = 0;
139   uri_item_t uri;
140   estream_t infp;
141
142   if (!patterns)
143     return gpg_error (GPG_ERR_NO_USER_ID);
144
145   /* FIXME: We only take care of the first pattern.  To fully support
146      multiple patterns we might either want to run several queries in
147      parallel and merge them.  We also need to decide what to do with
148      errors - it might not be the best idea to ignore an error from
149      one server and silently continue with another server.  For now we
150      stop at the first error. */
151   for (uri = keyservers; !err && uri; uri = uri->next)
152     {
153       int is_http = uri->parsed_uri->is_http;
154       int is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
155                      || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
156                      || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
157       if (is_http || is_ldap)
158         {
159           any_server = 1;
160           if (is_http)
161             err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d, &infp);
162           else if (is_ldap)
163             err = ks_ldap_search (ctrl, uri->parsed_uri, patterns->d, &infp);
164
165           if (!err)
166             {
167               err = copy_stream (infp, outfp);
168               es_fclose (infp);
169               break;
170             }
171         }
172     }
173
174   if (!any_server)
175     err = gpg_error (GPG_ERR_NO_KEYSERVER);
176   return err;
177 }
178
179
180 /* Get the requested keys (matching PATTERNS) using all configured
181    keyservers and write the result to the provided output stream.  */
182 gpg_error_t
183 ks_action_get (ctrl_t ctrl, uri_item_t keyservers,
184                strlist_t patterns, estream_t outfp)
185 {
186   gpg_error_t err = 0;
187   gpg_error_t first_err = 0;
188   int any_server = 0;
189   int any_data = 0;
190   strlist_t sl;
191   uri_item_t uri;
192   estream_t infp;
193
194   if (!patterns)
195     return gpg_error (GPG_ERR_NO_USER_ID);
196
197   /* FIXME: We only take care of the first keyserver.  To fully
198      support multiple keyservers we need to track the result for each
199      pattern and use the next keyserver if one key was not found.  The
200      keyservers might not all be fully synced thus it is not clear
201      whether the first keyserver has the freshest copy of the key.
202      Need to think about a better strategy.  */
203   for (uri = keyservers; !err && uri; uri = uri->next)
204     {
205       int is_http = uri->parsed_uri->is_http;
206       int is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
207                      || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
208                      || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
209       if (is_http || is_ldap)
210         {
211           any_server = 1;
212           for (sl = patterns; !err && sl; sl = sl->next)
213             {
214               if (is_http)
215                 err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
216               else
217                 err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, &infp);
218
219               if (err)
220                 {
221                   /* It is possible that a server does not carry a
222                      key, thus we only save the error and continue
223                      with the next pattern.  FIXME: It is an open
224                      question how to return such an error condition to
225                      the caller.  */
226                   first_err = err;
227                   err = 0;
228                 }
229               else
230                 {
231                   err = copy_stream (infp, outfp);
232                   /* Reading from the keyserver should never fail, thus
233                      return this error.  */
234                   if (!err)
235                     any_data = 1;
236                   es_fclose (infp);
237                   infp = NULL;
238                 }
239             }
240         }
241       if (any_data)
242         break; /* Stop loop after a keyserver returned something.  */
243     }
244
245   if (!any_server)
246     err = gpg_error (GPG_ERR_NO_KEYSERVER);
247   else if (!err && first_err && !any_data)
248     err = first_err;
249   return err;
250 }
251
252
253 /* Retrieve keys from URL and write the result to the provided output
254    stream OUTFP.  */
255 gpg_error_t
256 ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
257 {
258   gpg_error_t err = 0;
259   estream_t infp;
260   parsed_uri_t parsed_uri;  /* The broken down URI.  */
261
262   if (!url)
263     return gpg_error (GPG_ERR_INV_URI);
264
265   err = http_parse_uri (&parsed_uri, url, 1);
266   if (err)
267     return err;
268
269   if (parsed_uri->is_http)
270     {
271       err = ks_http_fetch (ctrl, url, &infp);
272       if (!err)
273         {
274           err = copy_stream (infp, outfp);
275           es_fclose (infp);
276         }
277     }
278   else if (!parsed_uri->opaque)
279     {
280       err = gpg_error (GPG_ERR_INV_URI);
281     }
282   else if (!strcmp (parsed_uri->scheme, "finger"))
283     {
284       err = ks_finger_fetch (ctrl, parsed_uri, &infp);
285       if (!err)
286         {
287           err = copy_stream (infp, outfp);
288           es_fclose (infp);
289         }
290     }
291   else if (!strcmp (parsed_uri->scheme, "kdns"))
292     {
293       err = ks_kdns_fetch (ctrl, parsed_uri, &infp);
294       if (!err)
295         {
296           err = copy_stream (infp, outfp);
297           es_fclose (infp);
298         }
299     }
300   else
301     err = gpg_error (GPG_ERR_INV_URI);
302
303   http_release_parsed_uri (parsed_uri);
304   return err;
305 }
306
307
308
309 /* Send an OpenPGP key to all keyservers.  The key in {DATA,DATALEN}
310    is expected to be in OpenPGP binary transport format.  The metadata
311    in {INFO,INFOLEN} is in colon-separated format (concretely, it is
312    the output of 'for x in keys sigs; do gpg --list-$x --with-colons
313    KEYID; done'.  This function may modify DATA and INFO.  If this is
314    a problem, then the caller should create a copy.  */
315 gpg_error_t
316 ks_action_put (ctrl_t ctrl, uri_item_t keyservers,
317                void *data, size_t datalen,
318                void *info, size_t infolen)
319 {
320   gpg_error_t err = 0;
321   gpg_error_t first_err = 0;
322   int any_server = 0;
323   uri_item_t uri;
324
325   for (uri = keyservers; !err && uri; uri = uri->next)
326     {
327       int is_http = uri->parsed_uri->is_http;
328       int is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
329                      || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
330                      || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
331
332       if (is_http || is_ldap)
333         {
334           any_server = 1;
335           if (is_http)
336             err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
337           else
338             err = ks_ldap_put (ctrl, uri->parsed_uri, data, datalen,
339                                info, infolen);
340
341           if (err)
342             {
343               first_err = err;
344               err = 0;
345             }
346         }
347     }
348
349   if (!any_server)
350     err = gpg_error (GPG_ERR_NO_KEYSERVER);
351   else if (!err && first_err)
352     err = first_err;
353   return err;
354 }