Imported Upstream version 2.1.0
[platform/upstream/gpg2.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  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27
28 #include "dirmngr.h"
29 #include "misc.h"
30 #include "ks-engine.h"
31 #include "ks-action.h"
32
33
34 /* Copy all data from IN to OUT.  */
35 static gpg_error_t
36 copy_stream (estream_t in, estream_t out)
37 {
38   char buffer[512];
39   size_t nread;
40
41   while (!es_read (in, buffer, sizeof buffer, &nread))
42     {
43       if (!nread)
44         return 0; /* EOF */
45       if (es_write (out, buffer, nread, NULL))
46         break;
47
48     }
49   return gpg_error_from_syserror ();
50 }
51
52
53 /* Called by the engine's help functions to print the actual help.  */
54 gpg_error_t
55 ks_print_help (ctrl_t ctrl, const char *text)
56 {
57   return dirmngr_status_help (ctrl, text);
58 }
59
60
61 /* Called by the engine's help functions to print the actual help.  */
62 gpg_error_t
63 ks_printf_help (ctrl_t ctrl, const char *format, ...)
64 {
65   va_list arg_ptr;
66   gpg_error_t err;
67   char *buf;
68
69   va_start (arg_ptr, format);
70   buf = es_vbsprintf (format, arg_ptr);
71   err = buf? 0 : gpg_error_from_syserror ();
72   va_end (arg_ptr);
73   if (!err)
74     err = dirmngr_status_help (ctrl, buf);
75   es_free (buf);
76   return err;
77 }
78
79
80 /* Run the help command for the engine responsible for URI.  */
81 gpg_error_t
82 ks_action_help (ctrl_t ctrl, const char *url)
83 {
84   gpg_error_t err;
85   parsed_uri_t parsed_uri;  /* The broken down URI.  */
86
87   if (!url || !*url)
88     {
89       ks_print_help (ctrl, "Known schemata:\n");
90       parsed_uri = NULL;
91     }
92   else
93     {
94       err = http_parse_uri (&parsed_uri, url, 1);
95       if (err)
96         return err;
97     }
98
99   /* Call all engines to give them a chance to print a help sting.  */
100   err = ks_hkp_help (ctrl, parsed_uri);
101   if (!err)
102     err = ks_http_help (ctrl, parsed_uri);
103   if (!err)
104     err = ks_finger_help (ctrl, parsed_uri);
105   if (!err)
106     err = ks_kdns_help (ctrl, parsed_uri);
107
108   if (!parsed_uri)
109     ks_print_help (ctrl,
110                    "(Use an URL for engine specific help.)");
111   else
112     http_release_parsed_uri (parsed_uri);
113   return err;
114 }
115
116
117 /* Resolve all host names.  This is useful for looking at the status
118    of configured keyservers.  */
119 gpg_error_t
120 ks_action_resolve (ctrl_t ctrl)
121 {
122   gpg_error_t err = 0;
123   int any_server = 0;
124   uri_item_t uri;
125
126   for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
127     {
128       if (uri->parsed_uri->is_http)
129         {
130           any_server = 1;
131           err = ks_hkp_resolve (ctrl, uri->parsed_uri);
132           if (err)
133             break;
134         }
135     }
136
137   if (!any_server)
138     err = gpg_error (GPG_ERR_NO_KEYSERVER);
139   return err;
140 }
141
142
143 /* Search all configured keyservers for keys matching PATTERNS and
144    write the result to the provided output stream.  */
145 gpg_error_t
146 ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
147 {
148   gpg_error_t err = 0;
149   int any_server = 0;
150   uri_item_t uri;
151   estream_t infp;
152
153   if (!patterns)
154     return gpg_error (GPG_ERR_NO_USER_ID);
155
156   /* FIXME: We only take care of the first pattern.  To fully support
157      multiple patterns we might either want to run several queries in
158      parallel and merge them.  We also need to decide what to do with
159      errors - it might not be the best idea to ignore an error from
160      one server and silently continue with another server.  For now we
161      stop at the first error. */
162   for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
163     {
164       if (uri->parsed_uri->is_http)
165         {
166           any_server = 1;
167           err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d, &infp);
168           if (!err)
169             {
170               err = copy_stream (infp, outfp);
171               es_fclose (infp);
172               break;
173             }
174         }
175     }
176
177   if (!any_server)
178     err = gpg_error (GPG_ERR_NO_KEYSERVER);
179   return err;
180 }
181
182
183 /* Get the requested keys (matching PATTERNS) using all configured
184    keyservers and write the result to the provided output stream.  */
185 gpg_error_t
186 ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
187 {
188   gpg_error_t err = 0;
189   gpg_error_t first_err = 0;
190   int any_server = 0;
191   int any_data = 0;
192   strlist_t sl;
193   uri_item_t uri;
194   estream_t infp;
195
196   if (!patterns)
197     return gpg_error (GPG_ERR_NO_USER_ID);
198
199   /* FIXME: We only take care of the first keyserver.  To fully
200      support multiple keyservers we need to track the result for each
201      pattern and use the next keyserver if one key was not found.  The
202      keyservers might not all be fully synced thus it is not clear
203      whether the first keyserver has the freshest copy of the key.
204      Need to think about a better strategy.  */
205   for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
206     {
207       if (uri->parsed_uri->is_http)
208         {
209           any_server = 1;
210           for (sl = patterns; !err && sl; sl = sl->next)
211             {
212               err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
213               if (err)
214                 {
215                   /* It is possible that a server does not carry a
216                      key, thus we only save the error and continue
217                      with the next pattern.  FIXME: It is an open
218                      question how to return such an error condition to
219                      the caller.  */
220                   first_err = err;
221                   err = 0;
222                 }
223               else
224                 {
225                   err = copy_stream (infp, outfp);
226                   /* Reading from the keyserver should never fail, thus
227                      return this error.  */
228                   if (!err)
229                     any_data = 1;
230                   es_fclose (infp);
231                   infp = NULL;
232                 }
233             }
234         }
235       if (any_data)
236         break; /* Stop loop after a keyserver returned something.  */
237     }
238
239   if (!any_server)
240     err = gpg_error (GPG_ERR_NO_KEYSERVER);
241   else if (!err && first_err && !any_data)
242     err = first_err;
243   return err;
244 }
245
246
247 /* Retrieve keys from URL and write the result to the provided output
248    stream OUTFP.  */
249 gpg_error_t
250 ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
251 {
252   gpg_error_t err = 0;
253   estream_t infp;
254   parsed_uri_t parsed_uri;  /* The broken down URI.  */
255
256   if (!url)
257     return gpg_error (GPG_ERR_INV_URI);
258
259   err = http_parse_uri (&parsed_uri, url, 1);
260   if (err)
261     return err;
262
263   if (parsed_uri->is_http)
264     {
265       err = ks_http_fetch (ctrl, url, &infp);
266       if (!err)
267         {
268           err = copy_stream (infp, outfp);
269           es_fclose (infp);
270         }
271     }
272   else if (!parsed_uri->opaque)
273     {
274       err = gpg_error (GPG_ERR_INV_URI);
275     }
276   else if (!strcmp (parsed_uri->scheme, "finger"))
277     {
278       err = ks_finger_fetch (ctrl, parsed_uri, &infp);
279       if (!err)
280         {
281           err = copy_stream (infp, outfp);
282           es_fclose (infp);
283         }
284     }
285   else if (!strcmp (parsed_uri->scheme, "kdns"))
286     {
287       err = ks_kdns_fetch (ctrl, parsed_uri, &infp);
288       if (!err)
289         {
290           err = copy_stream (infp, outfp);
291           es_fclose (infp);
292         }
293     }
294   else
295     err = gpg_error (GPG_ERR_INV_URI);
296
297   http_release_parsed_uri (parsed_uri);
298   return err;
299 }
300
301
302
303 /* Send an OpenPGP key to all keyservers.  The key in {DATA,DATALEN}
304    is expected in OpenPGP binary transport format.  */
305 gpg_error_t
306 ks_action_put (ctrl_t ctrl, const void *data, size_t datalen)
307 {
308   gpg_error_t err = 0;
309   gpg_error_t first_err = 0;
310   int any_server = 0;
311   uri_item_t uri;
312
313   for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
314     {
315       if (uri->parsed_uri->is_http)
316         {
317           any_server = 1;
318           err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
319           if (err)
320             {
321               first_err = err;
322               err = 0;
323             }
324         }
325     }
326
327   if (!any_server)
328     err = gpg_error (GPG_ERR_NO_KEYSERVER);
329   else if (!err && first_err)
330     err = first_err;
331   return err;
332 }