Fix usage of ARGPARSE_OPTS.
[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.  */
300 gpg_error_t
301 ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
302 {
303   gpg_error_t err = 0;
304   estream_t infp;
305   parsed_uri_t parsed_uri;  /* The broken down URI.  */
306
307   if (!url)
308     return gpg_error (GPG_ERR_INV_URI);
309
310   err = http_parse_uri (&parsed_uri, url, 1);
311   if (err)
312     return err;
313
314   if (parsed_uri->is_http)
315     {
316       err = ks_http_fetch (ctrl, url, &infp);
317       if (!err)
318         {
319           err = copy_stream (infp, outfp);
320           es_fclose (infp);
321         }
322     }
323   else if (!parsed_uri->opaque)
324     {
325       err = gpg_error (GPG_ERR_INV_URI);
326     }
327   else if (!strcmp (parsed_uri->scheme, "finger"))
328     {
329       err = ks_finger_fetch (ctrl, parsed_uri, &infp);
330       if (!err)
331         {
332           err = copy_stream (infp, outfp);
333           es_fclose (infp);
334         }
335     }
336   else if (!strcmp (parsed_uri->scheme, "kdns"))
337     {
338       err = ks_kdns_fetch (ctrl, parsed_uri, &infp);
339       if (!err)
340         {
341           err = copy_stream (infp, outfp);
342           es_fclose (infp);
343         }
344     }
345   else
346     err = gpg_error (GPG_ERR_INV_URI);
347
348   http_release_parsed_uri (parsed_uri);
349   return err;
350 }
351
352
353
354 /* Send an OpenPGP key to all keyservers.  The key in {DATA,DATALEN}
355    is expected to be in OpenPGP binary transport format.  The metadata
356    in {INFO,INFOLEN} is in colon-separated format (concretely, it is
357    the output of 'for x in keys sigs; do gpg --list-$x --with-colons
358    KEYID; done'.  This function may modify DATA and INFO.  If this is
359    a problem, then the caller should create a copy.  */
360 gpg_error_t
361 ks_action_put (ctrl_t ctrl, uri_item_t keyservers,
362                void *data, size_t datalen,
363                void *info, size_t infolen)
364 {
365   gpg_error_t err = 0;
366   gpg_error_t first_err = 0;
367   int any_server = 0;
368   uri_item_t uri;
369
370   (void) info;
371   (void) infolen;
372
373   for (uri = keyservers; !err && uri; uri = uri->next)
374     {
375       int is_http = uri->parsed_uri->is_http;
376       int is_ldap = 0;
377
378 #if USE_LDAP
379       is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0
380                 || strcmp (uri->parsed_uri->scheme, "ldaps") == 0
381                 || strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
382 #endif
383
384       if (is_http || is_ldap)
385         {
386           any_server = 1;
387 #if USE_LDAP
388           if (is_ldap)
389             err = ks_ldap_put (ctrl, uri->parsed_uri, data, datalen,
390                                info, infolen);
391           else
392 #endif
393             {
394               err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
395             }
396           if (err)
397             {
398               first_err = err;
399               err = 0;
400             }
401         }
402     }
403
404   if (!any_server)
405     err = gpg_error (GPG_ERR_NO_KEYSERVER);
406   else if (!err && first_err)
407     err = first_err;
408   return err;
409 }