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
6 * This file is part of GnuPG.
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.
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.
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/>.
31 #include "ks-engine.h"
32 #include "ks-action.h"
34 # include "ldap-parse-uri.h"
37 /* Called by the engine's help functions to print the actual help. */
39 ks_print_help (ctrl_t ctrl, const char *text)
41 return dirmngr_status_help (ctrl, text);
45 /* Called by the engine's help functions to print the actual help. */
47 ks_printf_help (ctrl_t ctrl, const char *format, ...)
53 va_start (arg_ptr, format);
54 buf = es_vbsprintf (format, arg_ptr);
55 err = buf? 0 : gpg_error_from_syserror ();
58 err = dirmngr_status_help (ctrl, buf);
64 /* Run the help command for the engine responsible for URI. */
66 ks_action_help (ctrl_t ctrl, const char *url)
69 parsed_uri_t parsed_uri; /* The broken down URI. */
73 ks_print_help (ctrl, "Known schemata:\n");
80 err = ldap_parse_uri (&parsed_uri, url);
84 err = http_parse_uri (&parsed_uri, url, 1);
91 /* Call all engines to give them a chance to print a help sting. */
92 err = ks_hkp_help (ctrl, parsed_uri);
94 err = ks_http_help (ctrl, parsed_uri);
96 err = ks_finger_help (ctrl, parsed_uri);
98 err = ks_kdns_help (ctrl, parsed_uri);
101 err = ks_ldap_help (ctrl, parsed_uri);
106 "(Use an URL for engine specific help.)");
108 http_release_parsed_uri (parsed_uri);
113 /* Resolve all host names. This is useful for looking at the status
114 of configured keyservers. */
116 ks_action_resolve (ctrl_t ctrl, uri_item_t keyservers)
122 for (uri = keyservers; !err && uri; uri = uri->next)
124 if (uri->parsed_uri->is_http)
127 err = ks_hkp_resolve (ctrl, uri->parsed_uri);
134 err = gpg_error (GPG_ERR_NO_KEYSERVER);
139 /* Search all configured keyservers for keys matching PATTERNS and
140 write the result to the provided output stream. */
142 ks_action_search (ctrl_t ctrl, uri_item_t keyservers,
143 strlist_t patterns, estream_t outfp)
152 return gpg_error (GPG_ERR_NO_USER_ID);
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)
163 int is_http = uri->parsed_uri->is_http;
165 unsigned int http_status = 0;
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);
171 if (is_http || is_ldap)
176 err = ks_ldap_search (ctrl, uri->parsed_uri, patterns->d, &infp);
180 err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d,
181 &infp, &http_status);
184 if (err == gpg_error (GPG_ERR_NO_DATA)
185 && http_status == 404 /* not found */)
187 /* No record found. Clear error and try next server. */
194 err = copy_stream (infp, outfp);
203 err = gpg_error (GPG_ERR_NO_KEYSERVER);
204 else if (err == 0 && !any_results)
205 err = gpg_error (GPG_ERR_NO_DATA);
210 /* Get the requested keys (matching PATTERNS) using all configured
211 keyservers and write the result to the provided output stream. */
213 ks_action_get (ctrl_t ctrl, uri_item_t keyservers,
214 strlist_t patterns, estream_t outfp)
217 gpg_error_t first_err = 0;
225 return gpg_error (GPG_ERR_NO_USER_ID);
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)
235 int is_http = uri->parsed_uri->is_http;
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);
244 if (is_http || is_ldap)
247 for (sl = patterns; !err && sl; sl = sl->next)
251 err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, &infp);
255 err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
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
270 err = copy_stream (infp, outfp);
271 /* Reading from the keyserver should never fail, thus
272 return this error. */
281 break; /* Stop loop after a keyserver returned something. */
285 err = gpg_error (GPG_ERR_NO_KEYSERVER);
286 else if (!err && first_err && !any_data)
292 /* Retrieve keys from URL and write the result to the provided output
295 ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
299 parsed_uri_t parsed_uri; /* The broken down URI. */
302 return gpg_error (GPG_ERR_INV_URI);
304 err = http_parse_uri (&parsed_uri, url, 1);
308 if (parsed_uri->is_http)
310 err = ks_http_fetch (ctrl, url, &infp);
313 err = copy_stream (infp, outfp);
317 else if (!parsed_uri->opaque)
319 err = gpg_error (GPG_ERR_INV_URI);
321 else if (!strcmp (parsed_uri->scheme, "finger"))
323 err = ks_finger_fetch (ctrl, parsed_uri, &infp);
326 err = copy_stream (infp, outfp);
330 else if (!strcmp (parsed_uri->scheme, "kdns"))
332 err = ks_kdns_fetch (ctrl, parsed_uri, &infp);
335 err = copy_stream (infp, outfp);
340 err = gpg_error (GPG_ERR_INV_URI);
342 http_release_parsed_uri (parsed_uri);
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. */
355 ks_action_put (ctrl_t ctrl, uri_item_t keyservers,
356 void *data, size_t datalen,
357 void *info, size_t infolen)
360 gpg_error_t first_err = 0;
367 for (uri = keyservers; !err && uri; uri = uri->next)
369 int is_http = uri->parsed_uri->is_http;
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);
378 if (is_http || is_ldap)
383 err = ks_ldap_put (ctrl, uri->parsed_uri, data, datalen,
388 err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
399 err = gpg_error (GPG_ERR_NO_KEYSERVER);
400 else if (!err && first_err)