mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-23 10:29:58 +01:00
4a4bb874f6
* dirmngr/http.h (parsed_uri_s): Add fields off_host and off_path. (http_redir_info_t): New. * dirmngr/http.c (do_parse_uri): Set new fields. (same_host_p): New. (http_prepare_redirect): New. * dirmngr/t-http-basic.c: New test. * dirmngr/ks-engine-hkp.c (send_request): Use http_prepare_redirect instead of the open code. * dirmngr/ks-engine-http.c (ks_http_fetch): Ditto. -- With this change a http query will not follow a redirect unless the Location header gives the same host. If the host is different only the host and port is taken from the Location header and the original path and query parts are kept. Signed-off-by: Werner Koch <wk@gnupg.org> (cherry picked from commit fa1b1eaa4241ff3f0634c8bdf8591cbc7c464144)
498 lines
13 KiB
C
498 lines
13 KiB
C
/* t-http.c
|
|
* Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006, 2009, 2010,
|
|
* 2011 Free Software Foundation, Inc.
|
|
* Copyright (C) 2014 Werner Koch
|
|
*
|
|
* This file is part of GnuPG.
|
|
*
|
|
* This file is free software; you can redistribute it and/or modify
|
|
* it under the terms of either
|
|
*
|
|
* - the GNU Lesser General Public License as published by the Free
|
|
* Software Foundation; either version 3 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* or
|
|
*
|
|
* - the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* or both in parallel, as here.
|
|
*
|
|
* This file is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <assuan.h>
|
|
|
|
#include "../common/util.h"
|
|
#include "../common/logging.h"
|
|
#include "dns-stuff.h"
|
|
#include "http.h"
|
|
|
|
#include <ksba.h>
|
|
#if HTTP_USE_NTBTLS
|
|
# include <ntbtls.h>
|
|
#elif HTTP_USE_GNUTLS
|
|
# include <gnutls/gnutls.h> /* For init, logging, and deinit. */
|
|
#endif /*HTTP_USE_GNUTLS*/
|
|
|
|
#define PGM "t-http"
|
|
|
|
static int verbose;
|
|
static int debug;
|
|
static int no_verify;
|
|
|
|
/* static void */
|
|
/* read_dh_params (const char *fname) */
|
|
/* { */
|
|
/* gpg_error_t err; */
|
|
/* int rc; */
|
|
/* FILE *fp; */
|
|
/* struct stat st; */
|
|
/* char *buf; */
|
|
/* size_t buflen; */
|
|
/* gnutls_datum_t datum; */
|
|
|
|
/* fp = fopen (fname, "rb"); */
|
|
/* if (!fp) */
|
|
/* { */
|
|
/* err = gpg_error_from_syserror (); */
|
|
/* log_fatal ("can't open '%s': %s\n", fname, gpg_strerror (err)); */
|
|
/* } */
|
|
|
|
/* if (fstat (fileno(fp), &st)) */
|
|
/* { */
|
|
/* err = gpg_error_from_syserror (); */
|
|
/* log_fatal ("can't stat '%s': %s\n", fname, gpg_strerror (err)); */
|
|
/* } */
|
|
|
|
/* buflen = st.st_size; */
|
|
/* buf = xmalloc (buflen+1); */
|
|
/* if (fread (buf, buflen, 1, fp) != 1) */
|
|
/* { */
|
|
/* err = gpg_error_from_syserror (); */
|
|
/* log_fatal ("error reading '%s': %s\n", fname, gpg_strerror (err)); */
|
|
/* } */
|
|
/* fclose (fp); */
|
|
|
|
/* datum.size = buflen; */
|
|
/* datum.data = buf; */
|
|
|
|
/* rc = gnutls_dh_params_import_pkcs3 (dh_params, &datum, GNUTLS_X509_FMT_PEM); */
|
|
/* if (rc < 0) */
|
|
/* log_fatal ("gnutls_dh_param_import failed: %s\n", gnutls_strerror (rc)); */
|
|
|
|
/* xfree (buf); */
|
|
/* } */
|
|
|
|
|
|
|
|
#if HTTP_USE_GNUTLS
|
|
static gpg_error_t
|
|
verify_callback (http_t hd, http_session_t session, int reserved)
|
|
{
|
|
(void)hd;
|
|
(void)reserved;
|
|
return no_verify? 0 : http_verify_server_credentials (session);
|
|
}
|
|
#endif
|
|
|
|
#if HTTP_USE_GNUTLS
|
|
static void
|
|
my_gnutls_log (int level, const char *text)
|
|
{
|
|
fprintf (stderr, "gnutls:L%d: %s", level, text);
|
|
}
|
|
#endif
|
|
|
|
#if HTTP_USE_NTBTLS
|
|
static gpg_error_t
|
|
my_http_tls_verify_cb (void *opaque,
|
|
http_t http,
|
|
http_session_t session,
|
|
unsigned int http_flags,
|
|
void *tls_context)
|
|
{
|
|
gpg_error_t err;
|
|
int idx;
|
|
ksba_cert_t cert;
|
|
ksba_cert_t hostcert = NULL;
|
|
|
|
(void)opaque;
|
|
(void)http;
|
|
(void)session;
|
|
(void)http_flags;
|
|
|
|
/* Get the peer's certs fron ntbtls. */
|
|
for (idx = 0;
|
|
(cert = ntbtls_x509_get_peer_cert (tls_context, idx)); idx++)
|
|
{
|
|
if (!idx)
|
|
{
|
|
log_info ("Received host certificate\n");
|
|
hostcert = cert;
|
|
}
|
|
else
|
|
{
|
|
|
|
log_info ("Received additional certificate\n");
|
|
ksba_cert_release (cert);
|
|
}
|
|
}
|
|
if (!idx)
|
|
{
|
|
err = gpg_error (GPG_ERR_MISSING_CERT);
|
|
goto leave;
|
|
}
|
|
|
|
err = 0;
|
|
|
|
leave:
|
|
ksba_cert_release (hostcert);
|
|
log_info ("my_http_tls_verify_cb returns: %s\n", gpg_strerror (err));
|
|
return err;
|
|
}
|
|
#endif /*HTTP_USE_NTBTLS*/
|
|
|
|
|
|
|
|
/* Prepend FNAME with the srcdir environment variable's value and
|
|
return an allocated filename. */
|
|
static char *
|
|
prepend_srcdir (const char *fname)
|
|
{
|
|
static const char *srcdir;
|
|
char *result;
|
|
|
|
if (!srcdir && !(srcdir = getenv ("srcdir")))
|
|
srcdir = ".";
|
|
|
|
result = xmalloc (strlen (srcdir) + 1 + strlen (fname) + 1);
|
|
strcpy (result, srcdir);
|
|
strcat (result, "/");
|
|
strcat (result, fname);
|
|
return result;
|
|
}
|
|
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
int last_argc = -1;
|
|
gpg_error_t err;
|
|
int rc; parsed_uri_t uri;
|
|
uri_tuple_t r;
|
|
http_t hd;
|
|
int c;
|
|
unsigned int my_http_flags = 0;
|
|
int no_out = 0;
|
|
int tls_dbg = 0;
|
|
int no_crl = 0;
|
|
const char *cafile = NULL;
|
|
http_session_t session = NULL;
|
|
unsigned int timeout = 0;
|
|
|
|
gpgrt_init ();
|
|
log_set_prefix (PGM, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID);
|
|
if (argc)
|
|
{ argc--; argv++; }
|
|
while (argc && last_argc != argc )
|
|
{
|
|
last_argc = argc;
|
|
if (!strcmp (*argv, "--"))
|
|
{
|
|
argc--; argv++;
|
|
break;
|
|
}
|
|
else if (!strcmp (*argv, "--help"))
|
|
{
|
|
fputs ("usage: " PGM " URL\n"
|
|
"Options:\n"
|
|
" --verbose print timings etc.\n"
|
|
" --debug flyswatter\n"
|
|
" --tls-debug N use TLS debug level N\n"
|
|
" --cacert FNAME expect CA certificate in file FNAME\n"
|
|
" --timeout MS timeout for connect in MS\n"
|
|
" --no-verify do not verify the certificate\n"
|
|
" --force-tls use HTTP_FLAG_FORCE_TLS\n"
|
|
" --force-tor use HTTP_FLAG_FORCE_TOR\n"
|
|
" --no-out do not print the content\n"
|
|
" --no-crl do not consuilt a CRL\n",
|
|
stdout);
|
|
exit (0);
|
|
}
|
|
else if (!strcmp (*argv, "--verbose"))
|
|
{
|
|
verbose++;
|
|
argc--; argv++;
|
|
}
|
|
else if (!strcmp (*argv, "--debug"))
|
|
{
|
|
verbose += 2;
|
|
debug++;
|
|
argc--; argv++;
|
|
}
|
|
else if (!strcmp (*argv, "--tls-debug"))
|
|
{
|
|
argc--; argv++;
|
|
if (argc)
|
|
{
|
|
tls_dbg = atoi (*argv);
|
|
argc--; argv++;
|
|
}
|
|
}
|
|
else if (!strcmp (*argv, "--cacert"))
|
|
{
|
|
argc--; argv++;
|
|
if (argc)
|
|
{
|
|
cafile = *argv;
|
|
argc--; argv++;
|
|
}
|
|
}
|
|
else if (!strcmp (*argv, "--timeout"))
|
|
{
|
|
argc--; argv++;
|
|
if (argc)
|
|
{
|
|
timeout = strtoul (*argv, NULL, 10);
|
|
argc--; argv++;
|
|
}
|
|
}
|
|
else if (!strcmp (*argv, "--no-verify"))
|
|
{
|
|
no_verify = 1;
|
|
argc--; argv++;
|
|
}
|
|
else if (!strcmp (*argv, "--force-tls"))
|
|
{
|
|
my_http_flags |= HTTP_FLAG_FORCE_TLS;
|
|
argc--; argv++;
|
|
}
|
|
else if (!strcmp (*argv, "--force-tor"))
|
|
{
|
|
my_http_flags |= HTTP_FLAG_FORCE_TOR;
|
|
argc--; argv++;
|
|
}
|
|
else if (!strcmp (*argv, "--no-out"))
|
|
{
|
|
no_out = 1;
|
|
argc--; argv++;
|
|
}
|
|
else if (!strcmp (*argv, "--no-crl"))
|
|
{
|
|
no_crl = 1;
|
|
argc--; argv++;
|
|
}
|
|
else if (!strncmp (*argv, "--", 2))
|
|
{
|
|
fprintf (stderr, PGM ": unknown option '%s'\n", *argv);
|
|
exit (1);
|
|
}
|
|
}
|
|
if (argc != 1)
|
|
{
|
|
fprintf (stderr, PGM ": no or too many URLS given\n");
|
|
exit (1);
|
|
}
|
|
|
|
if (!cafile)
|
|
cafile = prepend_srcdir ("tls-ca.pem");
|
|
|
|
if (verbose)
|
|
my_http_flags |= HTTP_FLAG_LOG_RESP;
|
|
|
|
if (verbose || debug)
|
|
http_set_verbose (verbose, debug);
|
|
|
|
/* http.c makes use of the assuan socket wrapper. */
|
|
assuan_sock_init ();
|
|
|
|
if ((my_http_flags & HTTP_FLAG_FORCE_TOR))
|
|
{
|
|
enable_dns_tormode (1);
|
|
if (assuan_sock_set_flag (ASSUAN_INVALID_FD, "tor-mode", 1))
|
|
{
|
|
log_error ("error enabling Tor mode: %s\n", strerror (errno));
|
|
log_info ("(is your Libassuan recent enough?)\n");
|
|
}
|
|
}
|
|
|
|
#if HTTP_USE_NTBTLS
|
|
log_info ("new session.\n");
|
|
err = http_session_new (&session, NULL,
|
|
((no_crl? HTTP_FLAG_NO_CRL : 0)
|
|
| HTTP_FLAG_TRUST_DEF),
|
|
my_http_tls_verify_cb, NULL);
|
|
if (err)
|
|
log_error ("http_session_new failed: %s\n", gpg_strerror (err));
|
|
ntbtls_set_debug (tls_dbg, NULL, NULL);
|
|
|
|
#elif HTTP_USE_GNUTLS
|
|
|
|
rc = gnutls_global_init ();
|
|
if (rc)
|
|
log_error ("gnutls_global_init failed: %s\n", gnutls_strerror (rc));
|
|
|
|
http_register_tls_callback (verify_callback);
|
|
http_register_tls_ca (cafile);
|
|
|
|
err = http_session_new (&session, NULL,
|
|
((no_crl? HTTP_FLAG_NO_CRL : 0)
|
|
| HTTP_FLAG_TRUST_DEF),
|
|
NULL, NULL);
|
|
if (err)
|
|
log_error ("http_session_new failed: %s\n", gpg_strerror (err));
|
|
|
|
/* rc = gnutls_dh_params_init(&dh_params); */
|
|
/* if (rc) */
|
|
/* log_error ("gnutls_dh_params_init failed: %s\n", gnutls_strerror (rc)); */
|
|
/* read_dh_params ("dh_param.pem"); */
|
|
|
|
/* rc = gnutls_certificate_set_x509_trust_file */
|
|
/* (certcred, "ca.pem", GNUTLS_X509_FMT_PEM); */
|
|
/* if (rc) */
|
|
/* log_error ("gnutls_certificate_set_x509_trust_file failed: %s\n", */
|
|
/* gnutls_strerror (rc)); */
|
|
|
|
/* gnutls_certificate_set_dh_params (certcred, dh_params); */
|
|
|
|
gnutls_global_set_log_function (my_gnutls_log);
|
|
if (tls_dbg)
|
|
gnutls_global_set_log_level (tls_dbg);
|
|
|
|
#else
|
|
(void)err;
|
|
(void)tls_dbg;
|
|
(void)no_crl;
|
|
#endif /*HTTP_USE_GNUTLS*/
|
|
|
|
rc = http_parse_uri (&uri, *argv, 1);
|
|
if (rc)
|
|
{
|
|
log_error ("'%s': %s\n", *argv, gpg_strerror (rc));
|
|
return 1;
|
|
}
|
|
|
|
printf ("Scheme: %s\n", uri->scheme);
|
|
if (uri->opaque)
|
|
printf ("Value : %s\n", uri->path);
|
|
else
|
|
{
|
|
printf ("Auth : %s\n", uri->auth? uri->auth:"[none]");
|
|
printf ("Host : %s (off=%hu)\n", uri->host, uri->off_host);
|
|
printf ("Port : %u\n", uri->port);
|
|
printf ("Path : %s (off=%hu)\n", uri->path, uri->off_path);
|
|
for (r = uri->params; r; r = r->next)
|
|
{
|
|
printf ("Params: %s", r->name);
|
|
if (!r->no_value)
|
|
{
|
|
printf ("=%s", r->value);
|
|
if (strlen (r->value) != r->valuelen)
|
|
printf (" [real length=%d]", (int) r->valuelen);
|
|
}
|
|
putchar ('\n');
|
|
}
|
|
for (r = uri->query; r; r = r->next)
|
|
{
|
|
printf ("Query : %s", r->name);
|
|
if (!r->no_value)
|
|
{
|
|
printf ("=%s", r->value);
|
|
if (strlen (r->value) != r->valuelen)
|
|
printf (" [real length=%d]", (int) r->valuelen);
|
|
}
|
|
putchar ('\n');
|
|
}
|
|
printf ("Flags :%s%s%s%s\n",
|
|
uri->is_http? " http":"",
|
|
uri->opaque? " opaque":"",
|
|
uri->v6lit? " v6lit":"",
|
|
uri->onion? " onion":"");
|
|
printf ("TLS : %s\n",
|
|
uri->use_tls? "yes":
|
|
(my_http_flags&HTTP_FLAG_FORCE_TLS)? "forced" : "no");
|
|
printf ("Tor : %s\n",
|
|
(my_http_flags&HTTP_FLAG_FORCE_TOR)? "yes" : "no");
|
|
|
|
}
|
|
fflush (stdout);
|
|
http_release_parsed_uri (uri);
|
|
uri = NULL;
|
|
|
|
if (session)
|
|
http_session_set_timeout (session, timeout);
|
|
|
|
rc = http_open_document (&hd, *argv, NULL, my_http_flags,
|
|
NULL, session, NULL, NULL);
|
|
if (rc)
|
|
{
|
|
log_error ("can't get '%s': %s\n", *argv, gpg_strerror (rc));
|
|
return 1;
|
|
}
|
|
log_info ("open_http_document succeeded; status=%u\n",
|
|
http_get_status_code (hd));
|
|
|
|
{
|
|
const char **names;
|
|
int i;
|
|
|
|
names = http_get_header_names (hd);
|
|
if (!names)
|
|
log_fatal ("http_get_header_names failed: %s\n",
|
|
gpg_strerror (gpg_error_from_syserror ()));
|
|
for (i = 0; names[i]; i++)
|
|
printf ("HDR: %s: %s\n", names[i], http_get_header (hd, names[i]));
|
|
xfree (names);
|
|
}
|
|
fflush (stdout);
|
|
|
|
switch (http_get_status_code (hd))
|
|
{
|
|
case 200:
|
|
case 400:
|
|
case 401:
|
|
case 403:
|
|
case 404:
|
|
{
|
|
unsigned long count = 0;
|
|
while ((c = es_getc (http_get_read_ptr (hd))) != EOF)
|
|
{
|
|
count++;
|
|
if (!no_out)
|
|
putchar (c);
|
|
}
|
|
log_info ("Received bytes: %lu\n", count);
|
|
}
|
|
break;
|
|
case 301:
|
|
case 302:
|
|
case 307:
|
|
log_info ("Redirected to: %s\n", http_get_header (hd, "Location"));
|
|
break;
|
|
}
|
|
http_close (hd, 0);
|
|
|
|
http_session_release (session);
|
|
#ifdef HTTP_USE_GNUTLS
|
|
gnutls_global_deinit ();
|
|
#endif /*HTTP_USE_GNUTLS*/
|
|
|
|
return 0;
|
|
}
|