blob: afcca8626f706764a2a1d765985914f2baf3e9e8 [file] [log] [blame]
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
#include "syshead.h"
#include "common.h"
#include "misc.h"
#include "crypto.h"
#include "win32.h"
#include "socket.h"
#include "fdmisc.h"
#include "proxy.h"
#include "base64.h"
#include "httpdigest.h"
#include "ntlm.h"
#include "memdbg.h"
#include "forward.h"
#define UP_TYPE_PROXY "HTTP Proxy"
struct http_proxy_options *
init_http_proxy_options_once(struct http_proxy_options **hpo,
struct gc_arena *gc)
{
if (!*hpo)
{
ALLOC_OBJ_CLEAR_GC(*hpo, struct http_proxy_options, gc);
/* http proxy defaults */
(*hpo)->http_version = "1.0";
}
return *hpo;
}
/* cached proxy username/password */
static struct user_pass static_proxy_user_pass;
static bool
recv_line(socket_descriptor_t sd,
char *buf,
int len,
const int timeout_sec,
const bool verbose,
struct buffer *lookahead,
volatile int *signal_received)
{
struct buffer la;
int lastc = 0;
CLEAR(la);
if (lookahead)
{
la = *lookahead;
}
while (true)
{
int status;
ssize_t size;
fd_set reads;
struct timeval tv;
uint8_t c;
if (buf_defined(&la))
{
ASSERT(buf_init(&la, 0));
}
FD_ZERO(&reads);
openvpn_fd_set(sd, &reads);
tv.tv_sec = timeout_sec;
tv.tv_usec = 0;
status = select(sd + 1, &reads, NULL, NULL, &tv);
get_signal(signal_received);
if (*signal_received)
{
goto error;
}
/* timeout? */
if (status == 0)
{
if (verbose)
{
msg(D_LINK_ERRORS | M_ERRNO, "recv_line: TCP port read timeout expired");
}
goto error;
}
/* error */
if (status < 0)
{
if (verbose)
{
msg(D_LINK_ERRORS | M_ERRNO, "recv_line: TCP port read failed on select()");
}
goto error;
}
/* read single char */
size = recv(sd, &c, 1, MSG_NOSIGNAL);
/* error? */
if (size != 1)
{
if (verbose)
{
msg(D_LINK_ERRORS | M_ERRNO, "recv_line: TCP port read failed on recv()");
}
goto error;
}
#if 0
if (isprint(c))
{
msg(M_INFO, "PROXY: read '%c' (%d)", c, (int)c);
}
else
{
msg(M_INFO, "PROXY: read (%d)", (int)c);
}
#endif
/* store char in buffer */
if (len > 1)
{
*buf++ = c;
--len;
}
/* also store char in lookahead buffer */
if (buf_defined(&la))
{
buf_write_u8(&la, c);
if (!isprint(c) && !isspace(c)) /* not ascii? */
{
if (verbose)
{
msg(D_LINK_ERRORS | M_ERRNO, "recv_line: Non-ASCII character (%d) read on recv()", (int)c);
}
*lookahead = la;
return false;
}
}
/* end of line? */
if (lastc == '\r' && c == '\n')
{
break;
}
lastc = c;
}
/* append trailing null */
if (len > 0)
{
*buf++ = '\0';
}
return true;
error:
return false;
}
static bool
send_line(socket_descriptor_t sd,
const char *buf)
{
const ssize_t size = send(sd, buf, strlen(buf), MSG_NOSIGNAL);
if (size != (ssize_t) strlen(buf))
{
msg(D_LINK_ERRORS | M_ERRNO, "send_line: TCP port write failed on send()");
return false;
}
return true;
}
static bool
send_line_crlf(socket_descriptor_t sd,
const char *src)
{
bool ret;
struct buffer buf = alloc_buf(strlen(src) + 3);
ASSERT(buf_write(&buf, src, strlen(src)));
ASSERT(buf_write(&buf, "\r\n", 3));
ret = send_line(sd, BSTR(&buf));
free_buf(&buf);
return ret;
}
static bool
send_crlf(socket_descriptor_t sd)
{
return send_line_crlf(sd, "");
}
uint8_t *
make_base64_string2(const uint8_t *str, int src_len, struct gc_arena *gc)
{
uint8_t *ret = NULL;
char *b64out = NULL;
ASSERT(openvpn_base64_encode((const void *)str, src_len, &b64out) >= 0);
ret = (uint8_t *) string_alloc(b64out, gc);
free(b64out);
return ret;
}
uint8_t *
make_base64_string(const uint8_t *str, struct gc_arena *gc)
{
return make_base64_string2(str, strlen((const char *)str), gc);
}
static const char *
username_password_as_base64(const struct http_proxy_info *p,
struct gc_arena *gc)
{
struct buffer out = alloc_buf_gc(strlen(p->up.username) + strlen(p->up.password) + 2, gc);
ASSERT(strlen(p->up.username) > 0);
buf_printf(&out, "%s:%s", p->up.username, p->up.password);
return (const char *)make_base64_string((const uint8_t *)BSTR(&out), gc);
}
static void
clear_user_pass_http(void)
{
purge_user_pass(&static_proxy_user_pass, true);
}
static void
get_user_pass_http(struct http_proxy_info *p, const bool force)
{
/*
* in case of forced (re)load, make sure the static storage is set as
* undefined, otherwise get_user_pass() won't try to load any credential
*/
if (force)
{
clear_user_pass_http();
}
if (!static_proxy_user_pass.defined)
{
unsigned int flags = GET_USER_PASS_MANAGEMENT;
if (p->queried_creds)
{
flags |= GET_USER_PASS_PREVIOUS_CREDS_FAILED;
}
if (p->options.inline_creds)
{
flags |= GET_USER_PASS_INLINE_CREDS;
}
get_user_pass(&static_proxy_user_pass,
p->options.auth_file,
UP_TYPE_PROXY,
flags);
p->queried_creds = true;
p->up = static_proxy_user_pass;
}
}
#if 0
/* function only used in #if 0 debug statement */
static void
dump_residual(socket_descriptor_t sd,
int timeout,
volatile int *signal_received)
{
char buf[256];
while (true)
{
if (!recv_line(sd, buf, sizeof(buf), timeout, true, NULL, signal_received))
{
return;
}
chomp(buf);
msg(D_PROXY, "PROXY HEADER: '%s'", buf);
}
}
#endif
/*
* Extract the Proxy-Authenticate header from the stream.
* Consumes all headers.
*/
static int
get_proxy_authenticate(socket_descriptor_t sd,
int timeout,
char **data,
struct gc_arena *gc,
volatile int *signal_received)
{
char buf[256];
int ret = HTTP_AUTH_NONE;
while (true)
{
if (!recv_line(sd, buf, sizeof(buf), timeout, true, NULL, signal_received))
{
free(*data);
*data = NULL;
return HTTP_AUTH_NONE;
}
chomp(buf);
if (!strlen(buf))
{
return ret;
}
if (ret == HTTP_AUTH_NONE && !strncmp(buf, "Proxy-Authenticate: ", 20))
{
if (!strncmp(buf+20, "Basic ", 6))
{
msg(D_PROXY, "PROXY AUTH BASIC: '%s'", buf);
*data = string_alloc(buf+26, gc);
ret = HTTP_AUTH_BASIC;
}
#if PROXY_DIGEST_AUTH
else if (!strncmp(buf+20, "Digest ", 7))
{
msg(D_PROXY, "PROXY AUTH DIGEST: '%s'", buf);
*data = string_alloc(buf+27, gc);
ret = HTTP_AUTH_DIGEST;
}
#endif
#if NTLM
else if (!strncmp(buf+20, "NTLM", 4))
{
msg(D_PROXY, "PROXY AUTH NTLM: '%s'", buf);
*data = NULL;
ret = HTTP_AUTH_NTLM;
}
#endif
}
}
}
static void
store_proxy_authenticate(struct http_proxy_info *p, char *data)
{
if (p->proxy_authenticate)
{
free(p->proxy_authenticate);
}
p->proxy_authenticate = data;
}
/*
* Parse out key/value pairs from Proxy-Authenticate string.
* Return true on success, or false on parse failure.
*/
static bool
get_key_value(const char *str, /* source string */
char *key, /* key stored here */
char *value, /* value stored here */
int max_key_len,
int max_value_len,
const char **endptr) /* next search position */
{
int c;
bool starts_with_quote = false;
bool escape = false;
for (c = max_key_len-1; (*str && (*str != '=') && c--); )
{
*key++ = *str++;
}
*key = '\0';
if ('=' != *str++)
{
/* no key/value found */
return false;
}
if ('\"' == *str)
{
/* quoted string */
str++;
starts_with_quote = true;
}
for (c = max_value_len-1; *str && c--; str++)
{
switch (*str)
{
case '\\':
if (!escape)
{
/* possibly the start of an escaped quote */
escape = true;
*value++ = '\\'; /* even though this is an escape character, we still
* store it as-is in the target buffer */
continue;
}
break;
case ',':
if (!starts_with_quote)
{
/* this signals the end of the value if we didn't get a starting quote
* and then we do "sloppy" parsing */
c = 0; /* the end */
continue;
}
break;
case '\r':
case '\n':
/* end of string */
c = 0;
continue;
case '\"':
if (!escape && starts_with_quote)
{
/* end of string */
c = 0;
continue;
}
break;
}
escape = false;
*value++ = *str;
}
*value = '\0';
*endptr = str;
return true; /* success */
}
static char *
get_pa_var(const char *key, const char *pa, struct gc_arena *gc)
{
char k[64];
char v[256];
const char *content = pa;
while (true)
{
const int status = get_key_value(content, k, v, sizeof(k), sizeof(v), &content);
if (status)
{
if (!strcmp(key, k))
{
return string_alloc(v, gc);
}
}
else
{
return NULL;
}
/* advance to start of next key */
if (*content == ',')
{
++content;
}
while (*content && isspace(*content))
{
++content;
}
}
}
struct http_proxy_info *
http_proxy_new(const struct http_proxy_options *o)
{
struct http_proxy_info *p;
if (!o || !o->server)
{
msg(M_FATAL, "HTTP_PROXY: server not specified");
}
ASSERT( o->port);
ALLOC_OBJ_CLEAR(p, struct http_proxy_info);
p->options = *o;
/* parse authentication method */
p->auth_method = HTTP_AUTH_NONE;
if (o->auth_method_string)
{
if (!strcmp(o->auth_method_string, "none"))
{
p->auth_method = HTTP_AUTH_NONE;
}
else if (!strcmp(o->auth_method_string, "basic"))
{
p->auth_method = HTTP_AUTH_BASIC;
}
#if NTLM
else if (!strcmp(o->auth_method_string, "ntlm"))
{
p->auth_method = HTTP_AUTH_NTLM;
}
else if (!strcmp(o->auth_method_string, "ntlm2"))
{
p->auth_method = HTTP_AUTH_NTLM2;
}
#endif
else
{
msg(M_FATAL, "ERROR: unknown HTTP authentication method: '%s'",
o->auth_method_string);
}
}
/* only basic and NTLM/NTLMv2 authentication supported so far */
if (p->auth_method == HTTP_AUTH_BASIC || p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2)
{
get_user_pass_http(p, true);
}
#if !NTLM
if (p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2)
{
msg(M_FATAL, "Sorry, this version of " PACKAGE_NAME " was built without NTLM Proxy support.");
}
#endif
p->defined = true;
return p;
}
void
http_proxy_close(struct http_proxy_info *hp)
{
free(hp);
}
static bool
add_proxy_headers(struct http_proxy_info *p,
socket_descriptor_t sd, /* already open to proxy */
const char *host, /* openvpn server remote */
const char *port /* openvpn server port */
)
{
char buf[512];
int i;
bool host_header_sent = false;
/*
* Send custom headers if provided
* If content is NULL the whole header is in name
* Also remember if we already sent a Host: header
*/
for (i = 0; i < MAX_CUSTOM_HTTP_HEADER && p->options.custom_headers[i].name; i++)
{
if (p->options.custom_headers[i].content)
{
openvpn_snprintf(buf, sizeof(buf), "%s: %s",
p->options.custom_headers[i].name,
p->options.custom_headers[i].content);
if (!strcasecmp(p->options.custom_headers[i].name, "Host"))
{
host_header_sent = true;
}
}
else
{
openvpn_snprintf(buf, sizeof(buf), "%s",
p->options.custom_headers[i].name);
if (!strncasecmp(p->options.custom_headers[i].name, "Host:", 5))
{
host_header_sent = true;
}
}
msg(D_PROXY, "Send to HTTP proxy: '%s'", buf);
if (!send_line_crlf(sd, buf))
{
return false;
}
}
if (!host_header_sent)
{
openvpn_snprintf(buf, sizeof(buf), "Host: %s", host);
msg(D_PROXY, "Send to HTTP proxy: '%s'", buf);
if (!send_line_crlf(sd, buf))
{
return false;
}
}
/* send User-Agent string if provided */
if (p->options.user_agent)
{
openvpn_snprintf(buf, sizeof(buf), "User-Agent: %s",
p->options.user_agent);
msg(D_PROXY, "Send to HTTP proxy: '%s'", buf);
if (!send_line_crlf(sd, buf))
{
return false;
}
}
return true;
}
bool
establish_http_proxy_passthru(struct http_proxy_info *p,
socket_descriptor_t sd, /* already open to proxy */
const char *host, /* openvpn server remote */
const char *port, /* openvpn server port */
struct event_timeout *server_poll_timeout,
struct buffer *lookahead,
volatile int *signal_received)
{
struct gc_arena gc = gc_new();
char buf[512];
char buf2[129];
char get[80];
int status;
int nparms;
bool ret = false;
bool processed = false;
/* get user/pass if not previously given */
if (p->auth_method == HTTP_AUTH_BASIC
|| p->auth_method == HTTP_AUTH_DIGEST
|| p->auth_method == HTTP_AUTH_NTLM)
{
get_user_pass_http(p, false);
}
/* are we being called again after getting the digest server nonce in the previous transaction? */
if (p->auth_method == HTTP_AUTH_DIGEST && p->proxy_authenticate)
{
nparms = 1;
status = 407;
}
else
{
/* format HTTP CONNECT message */
openvpn_snprintf(buf, sizeof(buf), "CONNECT %s:%s HTTP/%s",
host,
port,
p->options.http_version);
msg(D_PROXY, "Send to HTTP proxy: '%s'", buf);
/* send HTTP CONNECT message to proxy */
if (!send_line_crlf(sd, buf))
{
goto error;
}
if (!add_proxy_headers(p, sd, host, port))
{
goto error;
}
/* auth specified? */
switch (p->auth_method)
{
case HTTP_AUTH_NONE:
break;
case HTTP_AUTH_BASIC:
openvpn_snprintf(buf, sizeof(buf), "Proxy-Authorization: Basic %s",
username_password_as_base64(p, &gc));
msg(D_PROXY, "Attempting Basic Proxy-Authorization");
dmsg(D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf);
if (!send_line_crlf(sd, buf))
{
goto error;
}
break;
#if NTLM
case HTTP_AUTH_NTLM:
case HTTP_AUTH_NTLM2:
/* keep-alive connection */
openvpn_snprintf(buf, sizeof(buf), "Proxy-Connection: Keep-Alive");
if (!send_line_crlf(sd, buf))
{
goto error;
}
openvpn_snprintf(buf, sizeof(buf), "Proxy-Authorization: NTLM %s",
ntlm_phase_1(p, &gc));
msg(D_PROXY, "Attempting NTLM Proxy-Authorization phase 1");
dmsg(D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf);
if (!send_line_crlf(sd, buf))
{
goto error;
}
break;
#endif
default:
ASSERT(0);
}
/* send empty CR, LF */
if (!send_crlf(sd))
{
goto error;
}
/* receive reply from proxy */
if (!recv_line(sd, buf, sizeof(buf), get_server_poll_remaining_time(server_poll_timeout), true, NULL, signal_received))
{
goto error;
}
/* remove trailing CR, LF */
chomp(buf);
msg(D_PROXY, "HTTP proxy returned: '%s'", buf);
/* parse return string */
nparms = sscanf(buf, "%*s %d", &status);
}
/* check for a "407 Proxy Authentication Required" response */
while (nparms >= 1 && status == 407)
{
msg(D_PROXY, "Proxy requires authentication");
if (p->auth_method == HTTP_AUTH_BASIC && !processed)
{
processed = true;
}
else if ((p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2) && !processed) /* check for NTLM */
{
#if NTLM
/* look for the phase 2 response */
while (true)
{
if (!recv_line(sd, buf, sizeof(buf), get_server_poll_remaining_time(server_poll_timeout), true, NULL, signal_received))
{
goto error;
}
chomp(buf);
msg(D_PROXY, "HTTP proxy returned: '%s'", buf);
openvpn_snprintf(get, sizeof get, "%%*s NTLM %%%ds", (int) sizeof(buf2) - 1);
nparms = sscanf(buf, get, buf2);
buf2[128] = 0; /* we only need the beginning - ensure it's null terminated. */
/* check for "Proxy-Authenticate: NTLM TlRM..." */
if (nparms == 1)
{
/* parse buf2 */
msg(D_PROXY, "auth string: '%s'", buf2);
break;
}
}
/* if we are here then auth string was got */
msg(D_PROXY, "Received NTLM Proxy-Authorization phase 2 response");
/* receive and discard everything else */
while (recv_line(sd, NULL, 0, 2, true, NULL, signal_received))
{
}
/* now send the phase 3 reply */
/* format HTTP CONNECT message */
openvpn_snprintf(buf, sizeof(buf), "CONNECT %s:%s HTTP/%s",
host,
port,
p->options.http_version);
msg(D_PROXY, "Send to HTTP proxy: '%s'", buf);
/* send HTTP CONNECT message to proxy */
if (!send_line_crlf(sd, buf))
{
goto error;
}
/* keep-alive connection */
openvpn_snprintf(buf, sizeof(buf), "Proxy-Connection: Keep-Alive");
if (!send_line_crlf(sd, buf))
{
goto error;
}
/* send HOST etc, */
if (!add_proxy_headers(p, sd, host, port))
{
goto error;
}
msg(D_PROXY, "Attempting NTLM Proxy-Authorization phase 3");
{
const char *np3 = ntlm_phase_3(p, buf2, &gc);
if (!np3)
{
msg(D_PROXY, "NTLM Proxy-Authorization phase 3 failed: received corrupted data from proxy server");
goto error;
}
openvpn_snprintf(buf, sizeof(buf), "Proxy-Authorization: NTLM %s", np3);
}
msg(D_PROXY, "Send to HTTP proxy: '%s'", buf);
if (!send_line_crlf(sd, buf))
{
goto error;
}
/* ok so far... */
/* send empty CR, LF */
if (!send_crlf(sd))
{
goto error;
}
/* receive reply from proxy */
if (!recv_line(sd, buf, sizeof(buf), get_server_poll_remaining_time(server_poll_timeout), true, NULL, signal_received))
{
goto error;
}
/* remove trailing CR, LF */
chomp(buf);
msg(D_PROXY, "HTTP proxy returned: '%s'", buf);
/* parse return string */
nparms = sscanf(buf, "%*s %d", &status);
processed = true;
#endif /* if NTLM */
}
#if PROXY_DIGEST_AUTH
else if (p->auth_method == HTTP_AUTH_DIGEST && !processed)
{
char *pa = p->proxy_authenticate;
const int method = p->auth_method;
ASSERT(pa);
if (method == HTTP_AUTH_DIGEST)
{
const char *http_method = "CONNECT";
const char *nonce_count = "00000001";
const char *qop = "auth";
const char *username = p->up.username;
const char *password = p->up.password;
char *opaque_kv = "";
char uri[128];
uint8_t cnonce_raw[8];
uint8_t *cnonce;
HASHHEX session_key;
HASHHEX response;
const char *realm = get_pa_var("realm", pa, &gc);
const char *nonce = get_pa_var("nonce", pa, &gc);
const char *algor = get_pa_var("algorithm", pa, &gc);
const char *opaque = get_pa_var("opaque", pa, &gc);
if ( !realm || !nonce )
{
msg(D_LINK_ERRORS, "HTTP proxy: digest auth failed, malformed response "
"from server: realm= or nonce= missing" );
goto error;
}
/* generate a client nonce */
ASSERT(rand_bytes(cnonce_raw, sizeof(cnonce_raw)));
cnonce = make_base64_string2(cnonce_raw, sizeof(cnonce_raw), &gc);
/* build the digest response */
openvpn_snprintf(uri, sizeof(uri), "%s:%s",
host,
port);
if (opaque)
{
const int len = strlen(opaque)+16;
opaque_kv = gc_malloc(len, false, &gc);
openvpn_snprintf(opaque_kv, len, ", opaque=\"%s\"", opaque);
}
DigestCalcHA1(algor,
username,
realm,
password,
nonce,
(char *)cnonce,
session_key);
DigestCalcResponse(session_key,
nonce,
nonce_count,
(char *)cnonce,
qop,
http_method,
uri,
NULL,
response);
/* format HTTP CONNECT message */
openvpn_snprintf(buf, sizeof(buf), "%s %s HTTP/%s",
http_method,
uri,
p->options.http_version);
msg(D_PROXY, "Send to HTTP proxy: '%s'", buf);
/* send HTTP CONNECT message to proxy */
if (!send_line_crlf(sd, buf))
{
goto error;
}
/* send HOST etc, */
if (!add_proxy_headers(p, sd, host, port))
{
goto error;
}
/* send digest response */
openvpn_snprintf(buf, sizeof(buf), "Proxy-Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", qop=%s, nc=%s, cnonce=\"%s\", response=\"%s\"%s",
username,
realm,
nonce,
uri,
qop,
nonce_count,
cnonce,
response,
opaque_kv
);
msg(D_PROXY, "Send to HTTP proxy: '%s'", buf);
if (!send_line_crlf(sd, buf))
{
goto error;
}
if (!send_crlf(sd))
{
goto error;
}
/* receive reply from proxy */
if (!recv_line(sd, buf, sizeof(buf), get_server_poll_remaining_time(server_poll_timeout), true, NULL, signal_received))
{
goto error;
}
/* remove trailing CR, LF */
chomp(buf);
msg(D_PROXY, "HTTP proxy returned: '%s'", buf);
/* parse return string */
nparms = sscanf(buf, "%*s %d", &status);
processed = true;
}
else
{
msg(D_PROXY, "HTTP proxy: digest method not supported");
goto error;
}
}
#endif /* if PROXY_DIGEST_AUTH */
else if (p->options.auth_retry)
{
/* figure out what kind of authentication the proxy needs */
char *pa = NULL;
const int method = get_proxy_authenticate(sd,
get_server_poll_remaining_time(server_poll_timeout),
&pa,
NULL,
signal_received);
if (method != HTTP_AUTH_NONE)
{
if (pa)
{
msg(D_PROXY, "HTTP proxy authenticate '%s'", pa);
}
if (p->options.auth_retry == PAR_NCT && method == HTTP_AUTH_BASIC)
{
msg(D_PROXY, "HTTP proxy: support for basic auth and other cleartext proxy auth methods is disabled");
free(pa);
goto error;
}
p->auth_method = method;
store_proxy_authenticate(p, pa);
ret = true;
goto done;
}
else
{
msg(D_PROXY, "HTTP proxy: do not recognize the authentication method required by proxy");
free(pa);
goto error;
}
}
else
{
if (!processed)
{
msg(D_PROXY, "HTTP proxy: no support for proxy authentication method");
}
goto error;
}
/* clear state */
if (p->options.auth_retry)
{
clear_user_pass_http();
}
store_proxy_authenticate(p, NULL);
}
/* check return code, success = 200 */
if (nparms < 1 || status != 200)
{
msg(D_LINK_ERRORS, "HTTP proxy returned bad status");
#if 0
/* DEBUGGING -- show a multi-line HTTP error response */
dump_residual(sd, get_server_poll_remaining_time(server_poll_timeout), signal_received);
#endif
goto error;
}
/* SUCCESS */
/* receive line from proxy and discard */
if (!recv_line(sd, NULL, 0, get_server_poll_remaining_time(server_poll_timeout), true, NULL, signal_received))
{
goto error;
}
/*
* Toss out any extraneous chars, but don't throw away the
* start of the OpenVPN data stream (put it in lookahead).
*/
while (recv_line(sd, NULL, 0, 2, false, lookahead, signal_received))
{
}
/* reset queried_creds so that we don't think that the next creds request is due to an auth error */
p->queried_creds = false;
#if 0
if (lookahead && BLEN(lookahead))
{
msg(M_INFO, "HTTP PROXY: lookahead: %s", format_hex(BPTR(lookahead), BLEN(lookahead), 0));
}
#endif
done:
gc_free(&gc);
return ret;
error:
if (!*signal_received)
{
*signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- HTTP proxy error */
}
gc_free(&gc);
return ret;
}