dirmngr: Cleanup the http module.

* configure.ac (NEED_NTBTLS_VERSION): Require at least 0.2.0 so that
we can remove a conditional compilation.

* dirmngr/http.c (struct proxy_info_s): New.
(release_proxy_info): New to keep proxy information in one object.
(send_request): Factor some code out to ...
(get_proxy_for_url): this,
(send_request_basic_checks): this,
(send_request_set_sni): this,
(run_ntbtls_handshake): this,
(run_gnutls_handshake): and this.
--

Note that this also removes some never used code.  For example the
NTBTLS handshake has code taken from GNUTLS which was never used due
to the different ways on how the certificates are checked.

The proxy code has been factored out to make to prepare further
authentication methods.  The proxy_info_t was introduced for the same
reason.

Tested against gnutls and ntbtls builds.  No proxy tests yet done,
because we need more sophisticated tests anyway.

GnuPG-bug-id: 5768
This commit is contained in:
Werner Koch 2023-09-18 17:37:42 +02:00
parent 2a2846959f
commit 845d5e61d8
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 448 additions and 316 deletions

View File

@ -67,7 +67,7 @@ NEED_KSBA_API=1
NEED_KSBA_VERSION=1.6.3 NEED_KSBA_VERSION=1.6.3
NEED_NTBTLS_API=1 NEED_NTBTLS_API=1
NEED_NTBTLS_VERSION=0.1.0 NEED_NTBTLS_VERSION=0.2.0
NEED_NPTH_API=1 NEED_NPTH_API=1
NEED_NPTH_VERSION=1.2 NEED_NPTH_VERSION=1.2

View File

@ -230,6 +230,14 @@ static es_cookie_io_functions_t simple_cookie_functions =
}; };
#endif #endif
/* An object to store information about a proxy. */
struct proxy_info_s
{
parsed_uri_t uri; /* The parsed proxy URL. */
int is_http_proxy; /* This is an http proxy. */
};
typedef struct proxy_info_s *proxy_info_t;
#if SIZEOF_UNSIGNED_LONG == 8 #if SIZEOF_UNSIGNED_LONG == 8
# define HTTP_SESSION_MAGIC 0x0068545470534553 /* "hTTpSES" */ # define HTTP_SESSION_MAGIC 0x0068545470534553 /* "hTTpSES" */
@ -317,13 +325,13 @@ struct http_context_s
static int opt_verbose; static int opt_verbose;
static int opt_debug; static int opt_debug;
/* The global callback for the verification function. */ /* The global callback for the verification function for GNUTLS. */
static gpg_error_t (*tls_callback) (http_t, http_session_t, int); static gpg_error_t (*tls_callback) (http_t, http_session_t, int);
/* The list of files with trusted CA certificates. */ /* The list of files with trusted CA certificates for GNUTLS. */
static strlist_t tls_ca_certlist; static strlist_t tls_ca_certlist;
/* The list of files with extra trusted CA certificates. */ /* The list of files with extra trusted CA certificates for GNUTLS. */
static strlist_t cfg_ca_certlist; static strlist_t cfg_ca_certlist;
/* The global callback for net activity. */ /* The global callback for net activity. */
@ -596,7 +604,7 @@ http_set_verbose (int verbose, int debug)
/* Register a non-standard global TLS callback function. If no /* Register a non-standard global TLS callback function. If no
verification is desired a callback needs to be registered which verification is desired a callback needs to be registered which
always returns NULL. */ always returns NULL. Only used for GNUTLS. */
void void
http_register_tls_callback (gpg_error_t (*cb)(http_t, http_session_t, int)) http_register_tls_callback (gpg_error_t (*cb)(http_t, http_session_t, int))
{ {
@ -607,7 +615,7 @@ http_register_tls_callback (gpg_error_t (*cb)(http_t, http_session_t, int))
/* Register a CA certificate for future use. The certificate is /* Register a CA certificate for future use. The certificate is
expected to be in FNAME. PEM format is assume if FNAME has a expected to be in FNAME. PEM format is assume if FNAME has a
suffix of ".pem". If FNAME is NULL the list of CA files is suffix of ".pem". If FNAME is NULL the list of CA files is
removed. */ removed. Only used for GNUTLS. */
void void
http_register_tls_ca (const char *fname) http_register_tls_ca (const char *fname)
{ {
@ -636,7 +644,8 @@ http_register_tls_ca (const char *fname)
* expected to be in FNAME. PEM format is assume if FNAME has a * expected to be in FNAME. PEM format is assume if FNAME has a
* suffix of ".pem". If FNAME is NULL the list of CA files is * suffix of ".pem". If FNAME is NULL the list of CA files is
* removed. This is a variant of http_register_tls_ca which puts the * removed. This is a variant of http_register_tls_ca which puts the
* certificate into a separate list enabled using HTTP_FLAG_TRUST_CFG. */ * certificate into a separate list enabled using HTTP_FLAG_TRUST_CFG.
* Only used for GNUTLS. */
void void
http_register_cfg_ca (const char *fname) http_register_cfg_ca (const char *fname)
{ {
@ -786,6 +795,8 @@ http_session_new (http_session_t *r_session,
int add_system_cas = !!(flags & HTTP_FLAG_TRUST_SYS); int add_system_cas = !!(flags & HTTP_FLAG_TRUST_SYS);
int is_hkps_pool; int is_hkps_pool;
(void)intended_hostname;
rc = gnutls_certificate_allocate_credentials (&sess->certcred); rc = gnutls_certificate_allocate_credentials (&sess->certcred);
if (rc < 0) if (rc < 0)
{ {
@ -1789,34 +1800,114 @@ is_hostname_port (const char *string)
} }
/* /* Free the PROXY object. */
* Send a HTTP request to the server static void
* Returns 0 if the request was successful release_proxy_info (proxy_info_t proxy)
*/ {
if (!proxy)
return;
http_release_parsed_uri (proxy->uri);
xfree (proxy);
}
/* Return the proxy to be used for the URL or host specified in HD.
* If OVERRIDE_PROXY is not NULL and not empty, this proxy will be
* used instead of any configured or dynamically determined proxy. If
* the function runs into an error an error code is returned and NULL
* is stored at R_PROXY. If the fucntion was successful and a proxy
* is to be used, information on the procy is stored at R_PROXY; if no
* proxy shall be used R_PROXY is set to NULL. Caller should always
* use release_proxy_info on the value stored at R_PROXY. */
static gpg_error_t static gpg_error_t
send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth, get_proxy_for_url (http_t hd, const char *override_proxy, proxy_info_t *r_proxy)
const char *proxy, const char *srvtag, unsigned int timeout,
strlist_t headers)
{ {
gpg_error_t err; gpg_error_t err;
const char *server; const char *proxystr, *s;
char *request, *p; proxy_info_t proxy;
unsigned short port;
const char *http_proxy = NULL; r_proxy = NULL;
char *proxy_authstr = NULL;
char *authstr = NULL; if (override_proxy && *override_proxy)
assuan_fd_t sock; proxystr = override_proxy;
int have_http_proxy = 0; else if (!(hd->flags & HTTP_FLAG_TRY_PROXY))
return 0; /* --honor-http-proxy not active */
else if ((s = getenv (HTTP_PROXY_ENV)) && *s)
proxystr = s;
#ifdef HAVE_W32_SYSTEM
#endif
else
return 0; /* No proxy known. */
proxy = xtrycalloc (1, sizeof *proxy);
if (!proxy)
{
err = gpg_error_from_syserror ();
log_error ("error allocating memory for proxy\n");
return err;
}
err = parse_uri (&proxy->uri, proxystr, 0, 0);
if (gpg_err_code (err) == GPG_ERR_INV_URI
&& is_hostname_port (proxystr))
{
/* Retry assuming a "hostname:port" string. */
char *tmpname = strconcat ("http://", proxystr, NULL);
if (!tmpname)
err = gpg_error_from_syserror ();
else if (!parse_uri (&proxy->uri, tmpname, 0, 0))
err = 0;
xfree (tmpname);
}
if (!err)
{
/* Get rid of the escapes in the authstring. */
if (proxy->uri->auth)
remove_escapes (proxy->uri->auth);
if (!strcmp (proxy->uri->scheme, "http"))
proxy->is_http_proxy = 1;
else if (!strcmp (proxy->uri->scheme, "socks4")
|| !strcmp (proxy->uri->scheme, "socks5h"))
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
else
err = gpg_error (GPG_ERR_INV_URI);
if (err)
{
log_error ("invalid HTTP proxy (%s): %s\n",
proxystr, gpg_strerror (err));
err = gpg_err_make (default_errsource, GPG_ERR_CONFIGURATION);
}
else if (opt_verbose)
log_info ("using '%s' to proxy '%s'\n",
proxystr, hd->uri? hd->uri->original : NULL);
}
if (err)
xfree (proxy);
else
*r_proxy = proxy;
return err;
}
/* Some checks done by send_request. */
static gpg_error_t
send_request_basic_checks (http_t hd)
{
int mode;
if (hd->uri->use_tls && !hd->session) if (hd->uri->use_tls && !hd->session)
{ {
log_error ("TLS requested but no session object provided\n"); log_error ("TLS requested but no session object provided\n");
return gpg_err_make (default_errsource, GPG_ERR_INTERNAL); return gpg_error (GPG_ERR_INTERNAL);
} }
if (hd->uri->use_tls && !hd->session->tls_session) if (hd->uri->use_tls && !hd->session->tls_session)
{ {
log_error ("TLS requested but no TLS context available\n"); log_error ("TLS requested but no TLS context available\n");
return gpg_err_make (default_errsource, GPG_ERR_INTERNAL); return gpg_error (GPG_ERR_INTERNAL);
} }
if (opt_debug) if (opt_debug)
log_debug ("Using TLS library: %s %s\n", log_debug ("Using TLS library: %s %s\n",
@ -1827,37 +1918,35 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
#endif /*HTTP_USE_GNUTLS*/ #endif /*HTTP_USE_GNUTLS*/
); );
if ((hd->flags & HTTP_FLAG_FORCE_TOR)) if ((hd->flags & HTTP_FLAG_FORCE_TOR)
&& (assuan_sock_get_flag (ASSUAN_INVALID_FD, "tor-mode", &mode) || !mode))
{ {
int mode; log_error ("Tor support is not available\n");
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
if (assuan_sock_get_flag (ASSUAN_INVALID_FD, "tor-mode", &mode) || !mode)
{
log_error ("Tor support is not available\n");
return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED);
}
/* Non-blocking connects do not work with our Tor proxy because
* we can't continue the Socks protocol after the EINPROGRESS.
* Disable the timeout to use a blocking connect. */
timeout = 0;
} }
server = *hd->uri->host ? hd->uri->host : "localhost"; return 0;
port = hd->uri->port ? hd->uri->port : 80; }
/* Helper for send_request to set the servername. */
static gpg_error_t
send_request_set_sni (http_t hd, const char *name)
{
gpg_error_t err = 0;
# if HTTP_USE_GNUTLS
int rc;
# endif
/* Try to use SNI. */ /* Try to use SNI. */
if (hd->uri->use_tls) if (hd->uri->use_tls)
{ {
#if HTTP_USE_GNUTLS
int rc;
#endif
xfree (hd->session->servername); xfree (hd->session->servername);
hd->session->servername = xtrystrdup (httphost? httphost : server); hd->session->servername = xtrystrdup (name);
if (!hd->session->servername) if (!hd->session->servername)
{ {
err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); err = gpg_error_from_syserror ();
return err; goto leave;
} }
#if HTTP_USE_NTBTLS #if HTTP_USE_NTBTLS
@ -1866,7 +1955,7 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
if (err) if (err)
{ {
log_info ("ntbtls_set_hostname failed: %s\n", gpg_strerror (err)); log_info ("ntbtls_set_hostname failed: %s\n", gpg_strerror (err));
return err; goto leave;
} }
#elif HTTP_USE_GNUTLS #elif HTTP_USE_GNUTLS
rc = gnutls_server_name_set (hd->session->tls_session, rc = gnutls_server_name_set (hd->session->tls_session,
@ -1878,175 +1967,21 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
#endif /*HTTP_USE_GNUTLS*/ #endif /*HTTP_USE_GNUTLS*/
} }
if ( (proxy && *proxy) leave:
|| ( (hd->flags & HTTP_FLAG_TRY_PROXY) return err;
&& (http_proxy = getenv (HTTP_PROXY_ENV)) }
&& *http_proxy ))
{
parsed_uri_t uri;
if (proxy)
http_proxy = proxy;
err = parse_uri (&uri, http_proxy, 0, 0);
if (gpg_err_code (err) == GPG_ERR_INV_URI
&& is_hostname_port (http_proxy))
{
/* Retry assuming a "hostname:port" string. */
char *tmpname = strconcat ("http://", http_proxy, NULL);
if (tmpname && !parse_uri (&uri, tmpname, 0, 0))
err = 0;
xfree (tmpname);
}
if (err)
;
else if (!strcmp (uri->scheme, "http"))
have_http_proxy = 1;
else if (!strcmp (uri->scheme, "socks4")
|| !strcmp (uri->scheme, "socks5h"))
err = gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED);
else
err = gpg_err_make (default_errsource, GPG_ERR_INV_URI);
if (err)
{
log_error ("invalid HTTP proxy (%s): %s\n",
http_proxy, gpg_strerror (err));
return gpg_err_make (default_errsource, GPG_ERR_CONFIGURATION);
}
if (uri->auth)
{
remove_escapes (uri->auth);
proxy_authstr = make_header_line ("Proxy-Authorization: Basic ",
"\r\n",
uri->auth, strlen(uri->auth));
if (!proxy_authstr)
{
err = gpg_err_make (default_errsource,
gpg_err_code_from_syserror ());
http_release_parsed_uri (uri);
return err;
}
}
err = connect_server (ctrl,
*uri->host ? uri->host : "localhost",
uri->port ? uri->port : 80,
hd->flags, NULL, timeout, &sock);
http_release_parsed_uri (uri);
}
else
{
err = connect_server (ctrl,
server, port, hd->flags, srvtag, timeout, &sock);
}
if (err)
{
xfree (proxy_authstr);
return err;
}
hd->sock = my_socket_new (sock);
if (!hd->sock)
{
xfree (proxy_authstr);
return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
}
if (have_http_proxy && hd->uri->use_tls)
{
int saved_flags;
cookie_t cookie;
/* Try to use the CONNECT method to proxy our TLS stream. */
request = es_bsprintf
("CONNECT %s:%hu HTTP/1.0\r\nHost: %s:%hu\r\n%s",
httphost ? httphost : server,
port,
httphost ? httphost : server,
port,
proxy_authstr ? proxy_authstr : "");
xfree (proxy_authstr);
proxy_authstr = NULL;
if (! request)
return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
log_debug_string (request, "http.c:request:");
cookie = xtrycalloc (1, sizeof *cookie);
if (! cookie)
{
err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
xfree (request);
return err;
}
cookie->sock = my_socket_ref (hd->sock);
hd->write_cookie = cookie;
hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
if (! hd->fp_write)
{
err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
my_socket_unref (cookie->sock, NULL, NULL);
xfree (cookie);
xfree (request);
hd->write_cookie = NULL;
return err;
}
else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
xfree (request);
request = NULL;
/* Make sure http_wait_response doesn't close the stream. */
saved_flags = hd->flags;
hd->flags &= ~HTTP_FLAG_SHUTDOWN;
/* Get the response. */
err = http_wait_response (hd);
/* Restore flags, destroy stream. */
hd->flags = saved_flags;
es_fclose (hd->fp_read);
hd->fp_read = NULL;
hd->read_cookie = NULL;
/* Reset state. */
hd->in_data = 0;
if (err)
return err;
if (hd->status_code != 200)
{
request = es_bsprintf
("CONNECT %s:%hu",
httphost ? httphost : server,
port);
log_error (_("error accessing '%s': http status %u\n"),
request ? request : "out of core",
http_get_status_code (hd));
xfree (request);
return gpg_error (GPG_ERR_NO_DATA);
}
/* We are done with the proxy, the code below will establish a
* TLS session and talk directly to the target server. */
http_proxy = NULL;
}
/* Run the NTBTLS handshake if needed. */
#if HTTP_USE_NTBTLS #if HTTP_USE_NTBTLS
static gpg_error_t
run_ntbtls_handshake (http_t hd)
{
gpg_error_t err;
estream_t in, out;
if (hd->uri->use_tls) if (hd->uri->use_tls)
{ {
estream_t in, out;
my_socket_ref (hd->sock); my_socket_ref (hd->sock);
/* Until we support send/recv in estream under Windows we need /* Until we support send/recv in estream under Windows we need
@ -2060,8 +1995,7 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
if (!in) if (!in)
{ {
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
xfree (proxy_authstr); goto leave;
return err;
} }
# ifdef HAVE_W32_SYSTEM # ifdef HAVE_W32_SYSTEM
@ -2074,8 +2008,7 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
{ {
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
es_fclose (in); es_fclose (in);
xfree (proxy_authstr); goto leave;
return err;
} }
err = ntbtls_set_transport (hd->session->tls_session, in, out); err = ntbtls_set_transport (hd->session->tls_session, in, out);
@ -2083,8 +2016,9 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
{ {
log_info ("TLS set_transport failed: %s <%s>\n", log_info ("TLS set_transport failed: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err)); gpg_strerror (err), gpg_strsource (err));
xfree (proxy_authstr); es_fclose (in);
return err; es_fclose (out);
goto leave;
} }
if (hd->session->verify_cb) if (hd->session->verify_cb)
@ -2095,71 +2029,62 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
{ {
log_error ("ntbtls_set_verify_cb failed: %s\n", log_error ("ntbtls_set_verify_cb failed: %s\n",
gpg_strerror (err)); gpg_strerror (err));
xfree (proxy_authstr); goto leave;
return err;
} }
} }
while ((err = ntbtls_handshake (hd->session->tls_session))) while ((err = ntbtls_handshake (hd->session->tls_session)))
{ {
#if NTBTLS_VERSION_NUMBER >= 0x000200
unsigned int tlevel, ttype; unsigned int tlevel, ttype;
const char *s = ntbtls_get_last_alert (hd->session->tls_session, const char *s;
&tlevel, &ttype);
s = ntbtls_get_last_alert (hd->session->tls_session, &tlevel, &ttype);
if (s) if (s)
log_info ("TLS alert: %s (%u.%u)\n", s, tlevel, ttype); log_info ("TLS alert: %s (%u.%u)\n", s, tlevel, ttype);
#endif
switch (err) switch (err)
{ {
default: default:
log_info ("TLS handshake failed: %s <%s>\n", log_info ("TLS handshake failed: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err)); gpg_strerror (err), gpg_strsource (err));
xfree (proxy_authstr); goto leave;
return err;
} }
} }
hd->session->verify.done = 0; hd->session->verify.done = 0;
/* Try the available verify callbacks until one returns success /* Note that in contrast to GNUTLS NTBTLS uses a registered
* or a real error. Note that NTBTLS does the verification * callback to run the verification as part of the handshake. */
* during the handshake via */ err = 0;
err = 0; /* Fixme check that the CB has been called. */ /* FIXME: We could check that the CB has been called and if not
* error out with this warning:
* if (err)
* {
* log_info ("TLS connection authentication failed: %s <%s>\n",
* gpg_strerror (err), gpg_strsource (err));
* goto leave;
* }
*/
}
else
err = 0;
if (hd->session->verify_cb leave:
&& gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR return err;
&& gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED) }
err = hd->session->verify_cb (hd->session->verify_cb_value, #endif /*HTTP_USE_NTBTLS*/
hd, hd->session,
(hd->flags | hd->session->flags),
hd->session->tls_session);
if (tls_callback
&& gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR
&& gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
err = tls_callback (hd, hd->session, 0);
if (gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR /* Run the GNUTLS handshake if needed. */
&& gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED) #if HTTP_USE_GNUTLS
err = http_verify_server_credentials (hd->session); static gpg_error_t
run_gnutls_handshake (http_t hd, const char *server)
if (err) {
{ gpg_error_t err;
log_info ("TLS connection authentication failed: %s <%s>\n", int rc;
gpg_strerror (err), gpg_strsource (err));
xfree (proxy_authstr);
return err;
}
}
#elif HTTP_USE_GNUTLS
if (hd->uri->use_tls) if (hd->uri->use_tls)
{ {
int rc;
my_socket_ref (hd->sock); my_socket_ref (hd->sock);
gnutls_transport_set_ptr (hd->session->tls_session, hd->sock); gnutls_transport_set_ptr (hd->session->tls_session, hd->sock);
gnutls_transport_set_pull_function (hd->session->tls_session, gnutls_transport_set_pull_function (hd->session->tls_session,
@ -2195,8 +2120,8 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
} }
else else
log_info ("TLS handshake failed: %s\n", gnutls_strerror (rc)); log_info ("TLS handshake failed: %s\n", gnutls_strerror (rc));
xfree (proxy_authstr); err = gpg_error (GPG_ERR_NETWORK);
return gpg_err_make (default_errsource, GPG_ERR_NETWORK); goto leave;
} }
hd->session->verify.done = 0; hd->session->verify.done = 0;
@ -2208,13 +2133,195 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
{ {
log_info ("TLS connection authentication failed: %s\n", log_info ("TLS connection authentication failed: %s\n",
gpg_strerror (err)); gpg_strerror (err));
xfree (proxy_authstr); goto leave;
return err;
} }
} }
else
err =0;
leave:
return err;
}
#endif /*HTTP_USE_GNUTLS*/ #endif /*HTTP_USE_GNUTLS*/
/*
* Send a HTTP request to the server
* Returns 0 if the request was successful
*/
static gpg_error_t
send_request (ctrl_t ctrl,
http_t hd, const char *httphost, const char *auth,
const char *override_proxy,
const char *srvtag, unsigned int timeout,
strlist_t headers)
{
gpg_error_t err;
const char *server;
char *request = NULL;
char *relpath = NULL;
unsigned short port;
int use_http_proxy = 0;
char *proxy_authstr = NULL;
char *authstr = NULL;
assuan_fd_t sock;
proxy_info_t proxy = NULL;
cookie_t cookie = NULL;
cookie_t cookie2 = NULL;
err = send_request_basic_checks (hd);
if (err)
goto leave;
if ((hd->flags & HTTP_FLAG_FORCE_TOR))
{
/* Non-blocking connects do not work with our Tor proxy because
* we can't continue the Socks protocol after the EINPROGRESS.
* Disable the timeout to use a blocking connect. */
timeout = 0;
}
server = *hd->uri->host ? hd->uri->host : "localhost";
port = hd->uri->port ? hd->uri->port : 80;
if ((err = send_request_set_sni (hd, httphost? httphost : server)))
goto leave;
if ((err = get_proxy_for_url (hd, override_proxy, &proxy)))
goto leave;
if (proxy && proxy->is_http_proxy)
{
use_http_proxy = 1; /* We want to use a proxy for the conenction. */
err = connect_server (ctrl,
*proxy->uri->host ? proxy->uri->host : "localhost",
proxy->uri->port ? proxy->uri->port : 80,
hd->flags, NULL, timeout, &sock);
}
else
{
err = connect_server (ctrl,
server, port, hd->flags, srvtag, timeout, &sock);
}
if (err)
goto leave;
hd->sock = my_socket_new (sock);
if (!hd->sock)
{
err = gpg_error_from_syserror ();
goto leave;
}
#if USE_TLS
if (use_http_proxy && hd->uri->use_tls)
{
int saved_flags;
log_assert (!proxy_authstr);
if (proxy->uri->auth
&& !(proxy_authstr = make_header_line ("Proxy-Authorization: Basic ",
"\r\n",
proxy->uri->auth,
strlen (proxy->uri->auth))))
{
err = gpg_error_from_syserror ();
goto leave;
}
/* Try to use the CONNECT method to proxy our TLS stream. */
xfree (request);
request = es_bsprintf
("CONNECT %s:%hu HTTP/1.0\r\nHost: %s:%hu\r\n%s",
httphost ? httphost : server,
port,
httphost ? httphost : server,
port,
proxy_authstr ? proxy_authstr : "");
if (!request)
{
err = gpg_error_from_syserror ();
goto leave;
}
if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
log_debug_string (request, "http.c:request:");
cookie = xtrycalloc (1, sizeof *cookie);
if (!cookie)
{
err = gpg_error_from_syserror ();
goto leave;
}
cookie->sock = my_socket_ref (hd->sock);
hd->write_cookie = cookie;
hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
if (! hd->fp_write)
{
err = gpg_error_from_syserror ();
goto leave;
}
if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
{
err = gpg_error_from_syserror ();
goto leave;
}
/* Make sure http_wait_response doesn't close the stream. */
saved_flags = hd->flags;
hd->flags &= ~HTTP_FLAG_SHUTDOWN;
/* Get the response and set hd->fp_read */
err = http_wait_response (hd);
/* Restore flags, destroy stream. */
hd->flags = saved_flags;
es_fclose (hd->fp_read);
hd->fp_read = NULL;
hd->read_cookie = NULL;
/* Reset state. */
hd->in_data = 0;
if (err)
goto leave;
if (hd->status_code != 200)
{
xfree (request);
request = es_bsprintf
("CONNECT %s:%hu",
httphost ? httphost : server,
port);
log_error (_("error accessing '%s': http status %u\n"),
request ? request : "out of core",
http_get_status_code (hd));
err = gpg_error (GPG_ERR_NO_DATA);
goto leave;
}
/* We are done with the proxy, the code below will establish a
* TLS session and talk directly to the target server. Thus we
* clear the flag to indicate this. */
use_http_proxy = 0;
}
#endif /* USE_TLS */
#if HTTP_USE_NTBTLS
err = run_ntbtls_handshake (hd);
#elif HTTP_USE_GNUTLS
err = run_gnutls_handshake (hd, server);
#else
err = 0;
#endif
if (err)
goto leave;
if (auth || hd->uri->auth) if (auth || hd->uri->auth)
{ {
char *myauth; char *myauth;
@ -2224,9 +2331,8 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
myauth = xtrystrdup (auth); myauth = xtrystrdup (auth);
if (!myauth) if (!myauth)
{ {
xfree (proxy_authstr); err = gpg_error_from_syserror ();
return gpg_err_make (default_errsource, goto leave;
gpg_err_code_from_syserror ());
} }
remove_escapes (myauth); remove_escapes (myauth);
} }
@ -2238,27 +2344,38 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
authstr = make_header_line ("Authorization: Basic ", "\r\n", authstr = make_header_line ("Authorization: Basic ", "\r\n",
myauth, strlen (myauth)); myauth, strlen (myauth));
if (auth) if (auth) /* (Was allocated.) */
xfree (myauth); xfree (myauth);
if (!authstr) if (!authstr)
{ {
xfree (proxy_authstr); err = gpg_error_from_syserror ();
return gpg_err_make (default_errsource, goto leave;
gpg_err_code_from_syserror ());
} }
} }
p = build_rel_path (hd->uri); relpath = build_rel_path (hd->uri);
if (!p) if (!relpath)
{ {
xfree (authstr); err = gpg_error_from_syserror ();
xfree (proxy_authstr); goto leave;
return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
} }
if (http_proxy && *http_proxy) if (use_http_proxy)
{ {
xfree (proxy_authstr);
proxy_authstr = NULL;
if (proxy->uri->auth
&& !(proxy_authstr = make_header_line ("Proxy-Authorization: Basic ",
"\r\n",
proxy->uri->auth,
strlen (proxy->uri->auth))))
{
err = gpg_error_from_syserror ();
goto leave;
}
xfree (request);
request = es_bsprintf request = es_bsprintf
("%s %s://%s:%hu%s%s HTTP/1.0\r\n%s%s", ("%s %s://%s:%hu%s%s HTTP/1.0\r\n%s%s",
hd->req_type == HTTP_REQ_GET ? "GET" : hd->req_type == HTTP_REQ_GET ? "GET" :
@ -2266,9 +2383,14 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS", hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS",
hd->uri->use_tls? "https" : "http", hd->uri->use_tls? "https" : "http",
httphost? httphost : server, httphost? httphost : server,
port, *p == '/' ? "" : "/", p, port, *relpath == '/' ? "" : "/", relpath,
authstr ? authstr : "", authstr ? authstr : "",
proxy_authstr ? proxy_authstr : ""); proxy_authstr ? proxy_authstr : "");
if (!request)
{
err = gpg_error_from_syserror ();
goto leave;
}
} }
else else
{ {
@ -2279,23 +2401,21 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
else else
snprintf (portstr, sizeof portstr, ":%u", port); snprintf (portstr, sizeof portstr, ":%u", port);
xfree (request);
request = es_bsprintf request = es_bsprintf
("%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s", ("%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
hd->req_type == HTTP_REQ_GET ? "GET" : hd->req_type == HTTP_REQ_GET ? "GET" :
hd->req_type == HTTP_REQ_HEAD ? "HEAD" : hd->req_type == HTTP_REQ_HEAD ? "HEAD" :
hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS", hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS",
*p == '/' ? "" : "/", p, *relpath == '/' ? "" : "/", relpath,
httphost? httphost : server, httphost? httphost : server,
portstr, portstr,
authstr? authstr:""); authstr? authstr:"");
} if (!request)
xfree (p); {
if (!request) err = gpg_error_from_syserror ();
{ goto leave;
err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); }
xfree (authstr);
xfree (proxy_authstr);
return err;
} }
if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP)) if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
@ -2304,53 +2424,63 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
/* First setup estream so that we can write even the first line /* First setup estream so that we can write even the first line
using estream. This is also required for the sake of gnutls. */ using estream. This is also required for the sake of gnutls. */
{ {
cookie_t cookie; cookie2 = xtrycalloc (1, sizeof *cookie);
if (!cookie2)
cookie = xtrycalloc (1, sizeof *cookie);
if (!cookie)
{ {
err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); err = gpg_error_from_syserror ();
goto leave; goto leave;
} }
cookie->sock = my_socket_ref (hd->sock); cookie2->sock = my_socket_ref (hd->sock);
hd->write_cookie = cookie; hd->write_cookie = cookie2;
cookie->use_tls = hd->uri->use_tls; cookie2->use_tls = hd->uri->use_tls;
cookie->session = http_session_ref (hd->session); cookie2->session = http_session_ref (hd->session);
hd->fp_write = es_fopencookie (cookie, "w", cookie_functions); hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
if (!hd->fp_write) if (!hd->fp_write)
{ {
err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); err = gpg_error_from_syserror ();
my_socket_unref (cookie->sock, NULL, NULL); goto leave;
xfree (cookie); }
hd->write_cookie = NULL; if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
{
err = gpg_error_from_syserror ();
goto leave;
} }
else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
else
err = 0;
if (!err) for (;headers; headers=headers->next)
{ {
for (;headers; headers=headers->next) if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
{ log_debug_string (headers->d, "http.c:request-header:");
if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP)) if ((es_fputs (headers->d, hd->fp_write) || es_fflush (hd->fp_write))
log_debug_string (headers->d, "http.c:request-header:"); || (es_fputs("\r\n",hd->fp_write) || es_fflush(hd->fp_write)))
if ((es_fputs (headers->d, hd->fp_write) || es_fflush (hd->fp_write)) {
|| (es_fputs("\r\n",hd->fp_write) || es_fflush(hd->fp_write))) err = gpg_error_from_syserror ();
{ goto leave;
err = gpg_err_make (default_errsource, }
gpg_err_code_from_syserror ()); }
break;
}
}
}
} }
leave: leave:
if (cookie2)
{
my_socket_unref (cookie2->sock, NULL, NULL);
if (hd)
hd->write_cookie = NULL;
xfree (cookie2);
}
if (cookie)
{
my_socket_unref (cookie->sock, NULL, NULL);
if (hd)
hd->write_cookie = NULL;
xfree (cookie);
}
es_free (request); es_free (request);
xfree (authstr); xfree (authstr);
xfree (proxy_authstr); xfree (proxy_authstr);
xfree (relpath);
release_proxy_info (proxy);
return err; return err;
} }
@ -3447,7 +3577,7 @@ cookie_close (void *cookie)
/* Verify the credentials of the server. Returns 0 on success and /* Verify the credentials of the server. Returns 0 on success and
store the result in the session object. */ store the result in the session object. Only used by GNUTLS. */
gpg_error_t gpg_error_t
http_verify_server_credentials (http_session_t sess) http_verify_server_credentials (http_session_t sess)
{ {

View File

@ -132,9 +132,11 @@ typedef gpg_error_t (*http_verify_cb_t) (void *opaque,
void http_set_verbose (int verbose, int debug); void http_set_verbose (int verbose, int debug);
/* The next three functions are only used with GNUTLS. */
void http_register_tls_callback (gpg_error_t (*cb)(http_t,http_session_t,int)); void http_register_tls_callback (gpg_error_t (*cb)(http_t,http_session_t,int));
void http_register_tls_ca (const char *fname); void http_register_tls_ca (const char *fname);
void http_register_cfg_ca (const char *fname); void http_register_cfg_ca (const char *fname);
void http_register_netactivity_cb (void (*cb)(void)); void http_register_netactivity_cb (void (*cb)(void));