6fbf66fa |
/*
* 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.
* |
49979459 |
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
6fbf66fa |
*
* 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.
* |
caa54ac3 |
* 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. |
6fbf66fa |
*/
|
c110b289 |
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
|
6fbf66fa |
#include "syshead.h"
#include "common.h"
#include "misc.h" |
b27dc04c |
#include "crypto.h" |
6fbf66fa |
#include "win32.h"
#include "socket.h"
#include "fdmisc.h"
#include "proxy.h" |
f214bb21 |
#include "base64.h" |
b27dc04c |
#include "httpdigest.h" |
6fbf66fa |
#include "ntlm.h"
#include "memdbg.h" |
f2134b7b |
#include "forward.h" |
6fbf66fa |
|
6624b877 |
#define UP_TYPE_PROXY "HTTP Proxy"
|
af1bf85a |
struct http_proxy_options * |
81d882d5 |
init_http_proxy_options_once(struct http_proxy_options **hpo,
struct gc_arena *gc) |
af1bf85a |
{ |
81d882d5 |
if (!*hpo) |
af1bf85a |
{ |
81d882d5 |
ALLOC_OBJ_CLEAR_GC(*hpo, struct http_proxy_options, gc);
/* http proxy defaults */
(*hpo)->http_version = "1.0"; |
af1bf85a |
} |
81d882d5 |
return *hpo; |
af1bf85a |
}
|
6fbf66fa |
/* cached proxy username/password */
static struct user_pass static_proxy_user_pass;
static bool |
81d882d5 |
recv_line(socket_descriptor_t sd,
char *buf,
int len,
const int timeout_sec,
const bool verbose,
struct buffer *lookahead,
volatile int *signal_received) |
6fbf66fa |
{ |
81d882d5 |
struct buffer la;
int lastc = 0; |
6fbf66fa |
|
81d882d5 |
CLEAR(la);
if (lookahead)
{
la = *lookahead;
} |
6fbf66fa |
|
81d882d5 |
while (true) |
6fbf66fa |
{ |
81d882d5 |
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;
} |
6fbf66fa |
#if 0 |
81d882d5 |
if (isprint(c))
{
msg(M_INFO, "PROXY: read '%c' (%d)", c, (int)c);
}
else
{
msg(M_INFO, "PROXY: read (%d)", (int)c);
} |
6fbf66fa |
#endif
|
81d882d5 |
/* 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; |
6fbf66fa |
}
|
81d882d5 |
/* append trailing null */
if (len > 0)
{
*buf++ = '\0';
} |
6fbf66fa |
|
81d882d5 |
return true; |
6fbf66fa |
|
81d882d5 |
error:
return false; |
6fbf66fa |
}
static bool |
81d882d5 |
send_line(socket_descriptor_t sd,
const char *buf) |
6fbf66fa |
{ |
81d882d5 |
const ssize_t size = send(sd, buf, strlen(buf), MSG_NOSIGNAL);
if (size != (ssize_t) strlen(buf)) |
6fbf66fa |
{ |
81d882d5 |
msg(D_LINK_ERRORS | M_ERRNO, "send_line: TCP port write failed on send()");
return false; |
6fbf66fa |
} |
81d882d5 |
return true; |
6fbf66fa |
}
static bool |
81d882d5 |
send_line_crlf(socket_descriptor_t sd,
const char *src) |
6fbf66fa |
{ |
81d882d5 |
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; |
6fbf66fa |
}
static bool |
81d882d5 |
send_crlf(socket_descriptor_t sd) |
6fbf66fa |
{ |
81d882d5 |
return send_line_crlf(sd, ""); |
6fbf66fa |
}
uint8_t * |
81d882d5 |
make_base64_string2(const uint8_t *str, int src_len, struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
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; |
6fbf66fa |
}
uint8_t * |
81d882d5 |
make_base64_string(const uint8_t *str, struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
return make_base64_string2(str, strlen((const char *)str), gc); |
6fbf66fa |
}
static const char * |
81d882d5 |
username_password_as_base64(const struct http_proxy_info *p,
struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
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); |
6fbf66fa |
}
|
f214bb21 |
static void |
86b58ceb |
clear_user_pass_http(void)
{
purge_user_pass(&static_proxy_user_pass, true);
}
static void |
81d882d5 |
get_user_pass_http(struct http_proxy_info *p, const bool force) |
f214bb21 |
{ |
86b58ceb |
/*
* 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) |
f214bb21 |
{ |
81d882d5 |
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; |
f214bb21 |
}
} |
b27dc04c |
|
f2134b7b |
#if 0
/* function only used in #if 0 debug statement */ |
b27dc04c |
static void |
81d882d5 |
dump_residual(socket_descriptor_t sd,
int timeout,
volatile int *signal_received) |
b27dc04c |
{ |
81d882d5 |
char buf[256];
while (true) |
b27dc04c |
{ |
81d882d5 |
if (!recv_line(sd, buf, sizeof(buf), timeout, true, NULL, signal_received))
{
return;
}
chomp(buf);
msg(D_PROXY, "PROXY HEADER: '%s'", buf); |
b27dc04c |
}
} |
f2134b7b |
#endif |
b27dc04c |
/*
* Extract the Proxy-Authenticate header from the stream.
* Consumes all headers.
*/
static int |
81d882d5 |
get_proxy_authenticate(socket_descriptor_t sd,
int timeout,
char **data,
volatile int *signal_received) |
b27dc04c |
{ |
81d882d5 |
char buf[256];
int ret = HTTP_AUTH_NONE;
while (true) |
b27dc04c |
{ |
81d882d5 |
if (!recv_line(sd, buf, sizeof(buf), timeout, true, NULL, signal_received))
{ |
8d606cd3 |
free(*data); |
81d882d5 |
*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); |
2dca268a |
*data = string_alloc(buf+26, NULL); |
81d882d5 |
ret = HTTP_AUTH_BASIC;
} |
b27dc04c |
#if PROXY_DIGEST_AUTH |
81d882d5 |
else if (!strncmp(buf+20, "Digest ", 7))
{
msg(D_PROXY, "PROXY AUTH DIGEST: '%s'", buf); |
2dca268a |
*data = string_alloc(buf+27, NULL); |
81d882d5 |
ret = HTTP_AUTH_DIGEST;
} |
b27dc04c |
#endif
#if NTLM |
81d882d5 |
else if (!strncmp(buf+20, "NTLM", 4))
{
msg(D_PROXY, "PROXY AUTH HTLM: '%s'", buf);
*data = NULL;
ret = HTTP_AUTH_NTLM;
} |
b27dc04c |
#endif |
81d882d5 |
} |
b27dc04c |
}
}
static void |
81d882d5 |
store_proxy_authenticate(struct http_proxy_info *p, char *data) |
b27dc04c |
{ |
81d882d5 |
if (p->proxy_authenticate)
{
free(p->proxy_authenticate);
}
p->proxy_authenticate = data; |
b27dc04c |
}
/*
* 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 */ |
81d882d5 |
char *key, /* key stored here */
char *value, /* value stored here */
int max_key_len,
int max_value_len,
const char **endptr) /* next search position */ |
b27dc04c |
{ |
81d882d5 |
int c;
bool starts_with_quote = false;
bool escape = false; |
b27dc04c |
|
81d882d5 |
for (c = max_key_len-1; (*str && (*str != '=') && c--); ) |
4cd4899e |
{ |
81d882d5 |
*key++ = *str++; |
4cd4899e |
} |
81d882d5 |
*key = '\0'; |
b27dc04c |
|
81d882d5 |
if ('=' != *str++)
{
/* no key/value found */
return false;
} |
b27dc04c |
|
81d882d5 |
if ('\"' == *str) |
b27dc04c |
{ |
81d882d5 |
/* quoted string */
str++;
starts_with_quote = true; |
b27dc04c |
}
|
81d882d5 |
for (c = max_value_len-1; *str && c--; str++) |
b27dc04c |
{ |
81d882d5 |
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; |
b27dc04c |
} |
81d882d5 |
*value = '\0'; |
b27dc04c |
|
81d882d5 |
*endptr = str; |
b27dc04c |
|
81d882d5 |
return true; /* success */ |
b27dc04c |
}
static char * |
81d882d5 |
get_pa_var(const char *key, const char *pa, struct gc_arena *gc) |
b27dc04c |
{ |
81d882d5 |
char k[64];
char v[256];
const char *content = pa; |
b27dc04c |
|
81d882d5 |
while (true) |
b27dc04c |
{ |
81d882d5 |
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)) |
4cd4899e |
{ |
81d882d5 |
++content; |
4cd4899e |
} |
b27dc04c |
}
} |
f214bb21 |
|
6fbf66fa |
struct http_proxy_info * |
81d882d5 |
http_proxy_new(const struct http_proxy_options *o) |
6fbf66fa |
{ |
81d882d5 |
struct http_proxy_info *p; |
f214bb21 |
|
81d882d5 |
if (!o || !o->server)
{
msg(M_FATAL, "HTTP_PROXY: server not specified");
} |
6fbf66fa |
|
81d882d5 |
ASSERT( o->port); |
6fbf66fa |
|
81d882d5 |
ALLOC_OBJ_CLEAR(p, struct http_proxy_info);
p->options = *o; |
6fbf66fa |
|
81d882d5 |
/* parse authentication method */
p->auth_method = HTTP_AUTH_NONE;
if (o->auth_method_string) |
6fbf66fa |
{ |
81d882d5 |
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;
} |
b27dc04c |
#if NTLM |
81d882d5 |
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;
} |
b27dc04c |
#endif |
81d882d5 |
else
{
msg(M_FATAL, "ERROR: unknown HTTP authentication method: '%s'",
o->auth_method_string);
} |
6fbf66fa |
}
|
81d882d5 |
/* 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) |
6fbf66fa |
{ |
81d882d5 |
get_user_pass_http(p, true); |
6fbf66fa |
}
#if !NTLM |
81d882d5 |
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.");
} |
6fbf66fa |
#endif
|
81d882d5 |
p->defined = true;
return p; |
6fbf66fa |
}
|
4e9a51d7 |
void |
81d882d5 |
http_proxy_close(struct http_proxy_info *hp) |
4e9a51d7 |
{ |
81d882d5 |
free(hp); |
4e9a51d7 |
}
|
72bcdfdc |
static bool |
81d882d5 |
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 */
) |
d0cb816c |
{ |
81d882d5 |
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++) |
d0cb816c |
{ |
81d882d5 |
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;
} |
d0cb816c |
}
|
81d882d5 |
if (!host_header_sent) |
d0cb816c |
{ |
81d882d5 |
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;
} |
d0cb816c |
}
|
81d882d5 |
/* send User-Agent string if provided */
if (p->options.user_agent) |
d0cb816c |
{ |
81d882d5 |
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;
} |
d0cb816c |
}
|
81d882d5 |
return true; |
d0cb816c |
}
bool |
81d882d5 |
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) |
6fbf66fa |
{ |
81d882d5 |
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) |
6fbf66fa |
{ |
81d882d5 |
nparms = 1;
status = 407; |
6fbf66fa |
} |
81d882d5 |
else |
6fbf66fa |
{ |
81d882d5 |
/* 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; |
6fbf66fa |
#if NTLM |
81d882d5 |
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; |
6fbf66fa |
#endif
|
81d882d5 |
default:
ASSERT(0);
} |
6fbf66fa |
|
81d882d5 |
/* send empty CR, LF */
if (!send_crlf(sd))
{
goto error;
} |
6fbf66fa |
|
81d882d5 |
/* 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;
} |
6fbf66fa |
|
81d882d5 |
/* remove trailing CR, LF */
chomp(buf); |
6fbf66fa |
|
81d882d5 |
msg(D_PROXY, "HTTP proxy returned: '%s'", buf); |
6fbf66fa |
|
81d882d5 |
/* parse return string */
nparms = sscanf(buf, "%*s %d", &status); |
b27dc04c |
} |
6fbf66fa |
|
81d882d5 |
/* check for a "407 Proxy Authentication Required" response */
while (nparms >= 1 && status == 407) |
6fbf66fa |
{ |
81d882d5 |
msg(D_PROXY, "Proxy requires authentication"); |
6fbf66fa |
|
81d882d5 |
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 */ |
6fbf66fa |
{
#if NTLM |
81d882d5 |
/* look for the phase 2 response */ |
6fbf66fa |
|
81d882d5 |
while (true) |
6fbf66fa |
{ |
81d882d5 |
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); |
6fbf66fa |
|
81d882d5 |
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. */ |
6fbf66fa |
|
81d882d5 |
/* check for "Proxy-Authenticate: NTLM TlRM..." */
if (nparms == 1) |
6fbf66fa |
{ |
81d882d5 |
/* parse buf2 */
msg(D_PROXY, "auth string: '%s'", buf2);
break; |
6fbf66fa |
}
} |
81d882d5 |
/* if we are here then auth string was got */
msg(D_PROXY, "Received NTLM Proxy-Authorization phase 2 response"); |
6fbf66fa |
|
81d882d5 |
/* receive and discard everything else */
while (recv_line(sd, NULL, 0, 2, true, NULL, signal_received)) |
4cd4899e |
{
} |
6fbf66fa |
|
81d882d5 |
/* now send the phase 3 reply */ |
6fbf66fa |
|
81d882d5 |
/* format HTTP CONNECT message */
openvpn_snprintf(buf, sizeof(buf), "CONNECT %s:%s HTTP/%s",
host,
port,
p->options.http_version); |
6fbf66fa |
|
81d882d5 |
msg(D_PROXY, "Send to HTTP proxy: '%s'", buf); |
6fbf66fa |
|
81d882d5 |
/* send HTTP CONNECT message to proxy */
if (!send_line_crlf(sd, buf))
{
goto error;
} |
6fbf66fa |
|
81d882d5 |
/* keep-alive connection */
openvpn_snprintf(buf, sizeof(buf), "Proxy-Connection: Keep-Alive");
if (!send_line_crlf(sd, buf))
{
goto error;
} |
1bda73a7 |
|
81d882d5 |
/* send HOST etc, */
if (!add_proxy_headers(p, sd, host, port))
{
goto error;
} |
6fbf66fa |
|
81d882d5 |
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;
} |
6fbf66fa |
|
81d882d5 |
/* 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;
} |
6fbf66fa |
|
81d882d5 |
/* remove trailing CR, LF */
chomp(buf); |
6fbf66fa |
|
81d882d5 |
msg(D_PROXY, "HTTP proxy returned: '%s'", buf);
/* parse return string */
nparms = sscanf(buf, "%*s %d", &status);
processed = true;
#endif /* if NTLM */
} |
b27dc04c |
#if PROXY_DIGEST_AUTH |
81d882d5 |
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);
|
14865773 |
if ( !realm || !nonce )
{
msg(D_LINK_ERRORS, "HTTP proxy: digest auth failed, malformed response "
"from server: realm= or nonce= missing" );
goto error;
}
|
81d882d5 |
/* 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,
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"); |
8d606cd3 |
free(pa); |
81d882d5 |
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); |
b27dc04c |
} |
6fbf66fa |
|
81d882d5 |
/* check return code, success = 200 */
if (nparms < 1 || status != 200) |
6fbf66fa |
{ |
81d882d5 |
msg(D_LINK_ERRORS, "HTTP proxy returned bad status"); |
f214bb21 |
#if 0 |
81d882d5 |
/* DEBUGGING -- show a multi-line HTTP error response */
dump_residual(sd, get_server_poll_remaining_time(server_poll_timeout), signal_received); |
6fbf66fa |
#endif |
81d882d5 |
goto error; |
6fbf66fa |
}
|
81d882d5 |
/* SUCCESS */ |
3cf6c932 |
|
81d882d5 |
/* 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;
} |
6fbf66fa |
|
81d882d5 |
/*
* 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)) |
4cd4899e |
{
} |
6fbf66fa |
|
81d882d5 |
/* reset queried_creds so that we don't think that the next creds request is due to an auth error */
p->queried_creds = false; |
3cf6c932 |
|
6fbf66fa |
#if 0 |
81d882d5 |
if (lookahead && BLEN(lookahead))
{
msg(M_INFO, "HTTP PROXY: lookahead: %s", format_hex(BPTR(lookahead), BLEN(lookahead), 0));
} |
6fbf66fa |
#endif
|
81d882d5 |
done:
gc_free(&gc);
return ret; |
6fbf66fa |
|
81d882d5 |
error:
if (!*signal_received)
{
*signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- HTTP proxy error */
}
gc_free(&gc);
return ret; |
6fbf66fa |
} |