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"
38 /* Parse an URI and store it in a new parsed URI item object which is
39 * returned at R_PARSEDURI (with its next set to NULL). On error an
40 * error code is returned an NULL stored at R_PARSEDITEM. */
42 ks_action_parse_uri (const char *uri, uri_item_t *r_parseduri)
54 return gpg_error (GPG_ERR_INV_URI);
56 item = xtrymalloc (sizeof *item + strlen (uri));
58 return gpg_error_from_syserror ();
61 item->parsed_uri = NULL;
62 strcpy (item->uri, uri);
65 if (!strncmp (uri, "ldap:", 5) && !(uri[5] == '/' && uri[6] == '/'))
67 /* Special ldap scheme given. This differs from a valid ldap
68 * scheme in that no double slash follows. We use
69 * http_parse_uri to put it as opaque value into parsed_uri. */
70 tmpstr = strconcat ("opaque:", uri+5, NULL);
72 err = gpg_error_from_syserror ();
74 err = http_parse_uri (&item->parsed_uri, tmpstr, 0);
76 else if ((s=strchr (uri, ':')) && !(s[1] == '/' && s[2] == '/'))
78 /* No valid scheme given. We use http_parse_uri to put the
79 * string as opaque value into parsed_uri. */
80 tmpstr = strconcat ("opaque:", uri, NULL);
82 err = gpg_error_from_syserror ();
84 err = http_parse_uri (&item->parsed_uri, tmpstr, 0);
86 else if (ldap_uri_p (uri))
89 /* Fixme: We should get rid of that parser and replace it with
90 * our generic (http) URI parser. */
92 /* If no port has been specified and the scheme ist ldaps we use
93 * our idea of the default port because the standard LDAP URL
94 * parser would use 636 here. This is because we redefined
95 * ldaps to mean starttls. */
96 #ifdef HAVE_W32_SYSTEM
97 if (!strcmp (uri, "ldap:///"))
101 if (!http_parse_uri (&item->parsed_uri,uri,HTTP_PARSE_NO_SCHEME_CHECK))
103 if (!item->parsed_uri->port
104 && !strcmp (item->parsed_uri->scheme, "ldaps"))
106 http_release_parsed_uri (item->parsed_uri);
107 item->parsed_uri = NULL;
110 err = ldap_parse_uri (&item->parsed_uri, uri);
111 if (!err && fixup == 1)
112 item->parsed_uri->ad_current = 1;
113 else if (!err && fixup == 2)
114 item->parsed_uri->port = 389;
117 #endif /* USE_LDAP */
119 err = http_parse_uri (&item->parsed_uri, uri, HTTP_PARSE_NO_SCHEME_CHECK);
131 /* Called by the engine's help functions to print the actual help. */
133 ks_print_help (ctrl_t ctrl, const char *text)
135 return dirmngr_status_help (ctrl, text);
139 /* Called by the engine's help functions to print the actual help. */
141 ks_printf_help (ctrl_t ctrl, const char *format, ...)
147 va_start (arg_ptr, format);
148 buf = es_vbsprintf (format, arg_ptr);
149 err = buf? 0 : gpg_error_from_syserror ();
152 err = dirmngr_status_help (ctrl, buf);
158 /* Run the help command for the engine responsible for URI. */
160 ks_action_help (ctrl_t ctrl, const char *url)
163 parsed_uri_t parsed_uri; /* The broken down URI. */
171 ks_print_help (ctrl, "Known schemata:\n");
177 if (!strncmp (url, "ldap:", 5) && !(url[5] == '/' && url[6] == '/'))
179 /* Special ldap scheme given. This differs from a valid
180 * ldap scheme in that no double slash follows. Use
181 * http_parse_uri to put it as opaque value into parsed_uri. */
182 tmpstr = strconcat ("opaque:", url+5, NULL);
184 err = gpg_error_from_syserror ();
187 err = http_parse_uri (&parsed_uri, tmpstr, 0);
191 else if ((s=strchr (url, ':')) && !(s[1] == '/' && s[2] == '/'))
193 /* No scheme given. Use http_parse_uri to put the string as
194 * opaque value into parsed_uri. */
195 tmpstr = strconcat ("opaque:", url, NULL);
197 err = gpg_error_from_syserror ();
200 err = http_parse_uri (&parsed_uri, tmpstr, 0);
204 else if (ldap_uri_p (url))
205 err = ldap_parse_uri (&parsed_uri, url);
209 err = http_parse_uri (&parsed_uri, url, HTTP_PARSE_NO_SCHEME_CHECK);
216 /* Call all engines to give them a chance to print a help string. */
217 err = ks_hkp_help (ctrl, parsed_uri);
219 err = ks_http_help (ctrl, parsed_uri);
221 err = ks_finger_help (ctrl, parsed_uri);
223 err = ks_kdns_help (ctrl, parsed_uri);
226 err = ks_ldap_help (ctrl, parsed_uri);
231 "(Use an URL for engine specific help.)");
233 http_release_parsed_uri (parsed_uri);
238 /* Resolve all host names. This is useful for looking at the status
239 of configured keyservers. */
241 ks_action_resolve (ctrl_t ctrl, uri_item_t keyservers)
247 for (uri = keyservers; !err && uri; uri = uri->next)
249 if (uri->parsed_uri->is_http)
252 err = ks_hkp_resolve (ctrl, uri->parsed_uri);
259 err = gpg_error (GPG_ERR_NO_KEYSERVER);
264 /* Search all configured keyservers for keys matching PATTERNS and
265 write the result to the provided output stream. */
267 ks_action_search (ctrl_t ctrl, uri_item_t keyservers,
268 strlist_t patterns, estream_t outfp)
277 return gpg_error (GPG_ERR_NO_USER_ID);
279 /* FIXME: We only take care of the first pattern. To fully support
280 multiple patterns we might either want to run several queries in
281 parallel and merge them. We also need to decide what to do with
282 errors - it might not be the best idea to ignore an error from
283 one server and silently continue with another server. For now we
284 stop at the first error, unless the server responds with '404 Not
285 Found', in which case we try the next server. */
286 for (uri = keyservers; !err && uri; uri = uri->next)
288 int is_http = uri->parsed_uri->is_http;
290 unsigned int http_status = 0;
292 is_ldap = (!strcmp (uri->parsed_uri->scheme, "ldap")
293 || !strcmp (uri->parsed_uri->scheme, "ldaps")
294 || !strcmp (uri->parsed_uri->scheme, "ldapi")
295 || uri->parsed_uri->opaque);
297 if (is_http || is_ldap)
302 err = ks_ldap_search (ctrl, uri->parsed_uri, patterns->d, &infp);
306 err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d,
307 &infp, &http_status);
310 if (err == gpg_error (GPG_ERR_NO_DATA)
311 && http_status == 404 /* not found */)
313 /* No record found. Clear error and try next server. */
320 err = copy_stream (infp, outfp);
329 err = gpg_error (GPG_ERR_NO_KEYSERVER);
330 else if (err == 0 && !any_results)
331 err = gpg_error (GPG_ERR_NO_DATA);
336 /* Get the requested keys (matching PATTERNS) using all configured
337 keyservers and write the result to the provided output stream. */
339 ks_action_get (ctrl_t ctrl, uri_item_t keyservers,
340 strlist_t patterns, unsigned int ks_get_flags,
341 gnupg_isotime_t newer, estream_t outfp)
344 gpg_error_t first_err = 0;
352 return gpg_error (GPG_ERR_NO_USER_ID);
354 /* FIXME: We only take care of the first keyserver. To fully
355 support multiple keyservers we need to track the result for each
356 pattern and use the next keyserver if one key was not found. The
357 keyservers might not all be fully synced thus it is not clear
358 whether the first keyserver has the freshest copy of the key.
359 Need to think about a better strategy. */
360 for (uri = keyservers; !err && uri; uri = uri->next)
362 int is_hkp_s = (strcmp (uri->parsed_uri->scheme, "hkp") == 0
363 || strcmp (uri->parsed_uri->scheme, "hkps") == 0);
364 int is_http_s = (strcmp (uri->parsed_uri->scheme, "http") == 0
365 || strcmp (uri->parsed_uri->scheme, "https") == 0);
368 if ((ks_get_flags & (KS_GET_FLAG_ONLY_LDAP|KS_GET_FLAG_ONLY_AD)))
369 is_hkp_s = is_http_s = 0;
372 is_ldap = (!strcmp (uri->parsed_uri->scheme, "ldap")
373 || !strcmp (uri->parsed_uri->scheme, "ldaps")
374 || !strcmp (uri->parsed_uri->scheme, "ldapi")
375 || uri->parsed_uri->opaque);
378 if (is_hkp_s || is_http_s || is_ldap)
381 for (sl = patterns; !err && sl; sl = sl->next)
385 err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, ks_get_flags,
390 err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
392 err = ks_http_fetch (ctrl, uri->parsed_uri->original,
393 KS_HTTP_FETCH_NOCACHE,
400 /* It is possible that a server does not carry a
401 key, thus we only save the error and continue
402 with the next pattern. FIXME: It is an open
403 question how to return such an error condition to
410 err = copy_stream (infp, outfp);
411 /* Reading from the keyserver should never fail, thus
412 return this error. */
421 break; /* Stop loop after a keyserver returned something. */
425 err = gpg_error (GPG_ERR_NO_KEYSERVER);
426 else if (!err && first_err && !any_data)
432 /* Retrieve keys from URL and write the result to the provided output
433 * stream OUTFP. If OUTFP is NULL the data is written to the bit
436 ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
440 parsed_uri_t parsed_uri; /* The broken down URI. */
443 return gpg_error (GPG_ERR_INV_URI);
445 err = http_parse_uri (&parsed_uri, url, HTTP_PARSE_NO_SCHEME_CHECK);
449 if (parsed_uri->is_http)
451 err = ks_http_fetch (ctrl, url, KS_HTTP_FETCH_NOCACHE, &infp);
454 err = copy_stream (infp, outfp);
458 else if (!parsed_uri->opaque)
460 err = gpg_error (GPG_ERR_INV_URI);
462 else if (!strcmp (parsed_uri->scheme, "finger"))
464 err = ks_finger_fetch (ctrl, parsed_uri, &infp);
467 err = copy_stream (infp, outfp);
471 else if (!strcmp (parsed_uri->scheme, "kdns"))
473 err = ks_kdns_fetch (ctrl, parsed_uri, &infp);
476 err = copy_stream (infp, outfp);
481 err = gpg_error (GPG_ERR_INV_URI);
483 http_release_parsed_uri (parsed_uri);
489 /* Send an OpenPGP key to all keyservers. The key in {DATA,DATALEN}
490 is expected to be in OpenPGP binary transport format. The metadata
491 in {INFO,INFOLEN} is in colon-separated format (concretely, it is
492 the output of 'gpg --list-keys --with-colons KEYID'). This function
493 may modify DATA and INFO. If this is a problem, then the caller
494 should create a copy. */
496 ks_action_put (ctrl_t ctrl, uri_item_t keyservers,
497 void *data, size_t datalen,
498 void *info, size_t infolen)
501 gpg_error_t first_err = 0;
508 for (uri = keyservers; !err && uri; uri = uri->next)
510 int is_http = uri->parsed_uri->is_http;
514 is_ldap = (!strcmp (uri->parsed_uri->scheme, "ldap")
515 || !strcmp (uri->parsed_uri->scheme, "ldaps")
516 || !strcmp (uri->parsed_uri->scheme, "ldapi")
517 || uri->parsed_uri->opaque);
520 if (is_http || is_ldap)
525 err = ks_ldap_put (ctrl, uri->parsed_uri, data, datalen,
530 err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
541 err = gpg_error (GPG_ERR_NO_KEYSERVER);
542 else if (!err && first_err)
549 /* Query the default LDAP server or the one given by URL using
550 * the filter expression FILTER. Write the result to OUTFP. */
552 ks_action_query (ctrl_t ctrl, const char *url, unsigned int ks_get_flags,
553 const char *filter, char **attrs,
554 gnupg_isotime_t newer, estream_t outfp)
558 estream_t infp = NULL;
559 uri_item_t puri; /* The broken down URI (only one item used). */
561 if (!url && (ks_get_flags & KS_GET_FLAG_ROOTDSE))
564 err = ks_action_parse_uri (url, &puri);
568 if ((ks_get_flags & KS_GET_FLAG_ROOTDSE))
570 /* Reset authentication for a serverless connection. */
571 puri->parsed_uri->ad_current = 0;
572 puri->parsed_uri->auth = NULL;
575 if (!strcmp (puri->parsed_uri->scheme, "ldap")
576 || !strcmp (puri->parsed_uri->scheme, "ldaps")
577 || !strcmp (puri->parsed_uri->scheme, "ldapi")
578 || puri->parsed_uri->opaque)
580 err = ks_ldap_query (ctrl, puri->parsed_uri, ks_get_flags, filter,
581 attrs, newer, &infp);
583 err = copy_stream (infp, outfp);
586 err = gpg_error (GPG_ERR_CONFIGURATION); /* No LDAP server known. */
589 release_uri_item_list (puri);
592 #else /* !USE_LDAP */
593 return gpg_error (GPG_ERR_NOT_IMPLEMENTED);