proxy: cleanup SOCKS support and add user/password support

This commit is contained in:
David Fort 2018-04-29 21:25:28 +02:00
parent 1ba31551a6
commit 0f968b782c
8 changed files with 226 additions and 85 deletions

View File

@ -1318,6 +1318,8 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
allowUnknown);
settings->ProxyHostname = NULL;
settings->ProxyUsername = NULL;
settings->ProxyPassword = NULL;
if (compatibility)
{
@ -1821,11 +1823,14 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
}
CommandLineSwitchCase(arg, "proxy")
{
/* initial value */
settings->ProxyType = PROXY_TYPE_HTTP;
/* initial value */
settings->ProxyType = PROXY_TYPE_HTTP;
if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
{
char *atPtr;
/* value is [scheme://][user:password@]hostname:port */
p = strstr(arg->Value, "://");
if (p)
@ -1849,6 +1854,43 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
arg->Value = p + 3;
}
/* arg->Value is now [user:password@]hostname:port */
atPtr = strrchr(arg->Value, '@');
if (atPtr)
{
/* got a login / password,
* atPtr
* v
* [user:password@]hostname:port
* ^
* colonPtr
*/
char *colonPtr = strchr(arg->Value, ':');
if (!colonPtr || (colonPtr > atPtr))
{
WLog_ERR(TAG, "invalid syntax for proxy, expected syntax is user:password@host:port");
return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
}
*colonPtr = '\0';
settings->ProxyUsername = _strdup(arg->Value);
if (!settings->ProxyUsername)
{
WLog_ERR(TAG, "unable to allocate proxy username");
return COMMAND_LINE_ERROR_MEMORY;
}
*atPtr = '\0';
settings->ProxyPassword = _strdup(colonPtr + 1);
if (!settings->ProxyPassword)
{
WLog_ERR(TAG, "unable to allocate proxy password");
return COMMAND_LINE_ERROR_MEMORY;
}
arg->Value = atPtr + 1;
}
p = strchr(arg->Value, ':');
if (p)

View File

@ -137,7 +137,7 @@ static COMMAND_LINE_ARGUMENT_A args[] =
{ "port", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL, "Server port" },
{ "print-reconnect-cookie", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Print base64 reconnect cookie after connecting" },
{ "printer", COMMAND_LINE_VALUE_OPTIONAL, "<name>[,<driver>]", NULL, NULL, -1, NULL, "Redirect printer device" },
{ "proxy", COMMAND_LINE_VALUE_REQUIRED, "[<proto>://]<host>:<port>", NULL, NULL, -1, NULL, "Proxy settings: override env.var (see also environment variable below).\n\tProtocol \"socks5\" should be given explicitly where \"http\" is default.\n\tNote: socks proxy is not supported by env. variable" },
{ "proxy", COMMAND_LINE_VALUE_REQUIRED, "[<proto>://][<user>:<password>@]<host>:<port>", NULL, NULL, -1, NULL, "Proxy settings: override env.var (see also environment variable below).\n\tProtocol \"socks5\" should be given explicitly where \"http\" is default.\n\tNote: socks proxy is not supported by env. variable" },
{ "pth", COMMAND_LINE_VALUE_REQUIRED, "<password-hash>", NULL, NULL, -1, "pass-the-hash", "Pass the hash (restricted admin mode)" },
{ "pwidth", COMMAND_LINE_VALUE_REQUIRED, "<width>", NULL, NULL, -1, NULL, "Physical width of display (in millimeters)" },
{ "reconnect-cookie", COMMAND_LINE_VALUE_REQUIRED, "<base64-cookie>", NULL, NULL, -1, NULL, "Pass base64 reconnect cookie to the connection" },

View File

@ -1165,7 +1165,9 @@ struct rdp_settings
ALIGN64 UINT32 ProxyType; /* 2015 */
ALIGN64 char* ProxyHostname; /* 2016 */
ALIGN64 UINT16 ProxyPort; /* 2017 */
UINT64 padding2112[2112 - 2018]; /* 2018 */
ALIGN64 char* ProxyUsername; /* 2018 */
ALIGN64 char* ProxyPassword; /* 2019 */
UINT64 padding2112[2112 - 2020]; /* 2020 */
/**
* RemoteApp

View File

@ -681,7 +681,8 @@ static BOOL rdg_tls_connect(rdpRdg* rdg, rdpTls* tls, const char* peerAddress, i
rdpSettings* settings = rdg->settings;
const char* peerHostname = settings->GatewayHostname;
UINT16 peerPort = settings->GatewayPort;
BOOL isProxyConnection = proxy_prepare(settings, &peerHostname, &peerPort, TRUE);
const char *proxyUsername, *proxyPassword;
BOOL isProxyConnection = proxy_prepare(settings, &peerHostname, &peerPort, &proxyUsername, &proxyPassword);
sockfd = freerdp_tcp_connect(rdg->context, settings,
peerAddress ? peerAddress : peerHostname,
@ -714,7 +715,7 @@ static BOOL rdg_tls_connect(rdpRdg* rdg, rdpTls* tls, const char* peerAddress, i
if (isProxyConnection)
{
if (!proxy_connect(settings, bufferedBio, settings->GatewayHostname, settings->GatewayPort))
if (!proxy_connect(settings, bufferedBio, proxyUsername, proxyPassword, settings->GatewayHostname, settings->GatewayPort))
return FALSE;
}

View File

@ -734,7 +734,8 @@ static int rpc_channel_tls_connect(RpcChannel* channel, int timeout)
rdpSettings* settings = context->settings;
const char* peerHostname = settings->GatewayHostname;
UINT16 peerPort = settings->GatewayPort;
BOOL isProxyConnection = proxy_prepare(settings, &peerHostname, &peerPort, TRUE);
const char *proxyUsername = settings->ProxyUsername, *proxyPassword = settings->ProxyPassword;
BOOL isProxyConnection = proxy_prepare(settings, &peerHostname, &peerPort, &proxyUsername, &proxyPassword);
sockfd = freerdp_tcp_connect(context, settings, peerHostname,
peerPort, timeout);
@ -759,7 +760,7 @@ static int rpc_channel_tls_connect(RpcChannel* channel, int timeout)
if (isProxyConnection)
{
if (!proxy_connect(settings, bufferedBio, settings->GatewayHostname, settings->GatewayPort))
if (!proxy_connect(settings, bufferedBio, proxyUsername, proxyPassword, settings->GatewayHostname, settings->GatewayPort))
return -1;
}

View File

@ -29,12 +29,50 @@
#define CRLF "\r\n"
#define TAG FREERDP_TAG("core.proxy")
/* SOCKS Proxy auth methods by rfc1928 */
enum
{
AUTH_M_NO_AUTH = 0,
AUTH_M_GSSAPI = 1,
AUTH_M_USR_PASS = 2
};
enum
{
SOCKS_CMD_CONNECT = 1,
SOCKS_CMD_BIND = 2,
SOCKS_CMD_UDP_ASSOCIATE = 3
};
enum
{
SOCKS_ADDR_IPV4 = 1,
SOCKS_ADDR_FQDN = 3,
SOCKS_ADDR_IPV6 = 4,
};
/* CONN REQ replies in enum. order */
static const char *rplstat[] =
{
"succeeded",
"general SOCKS server failure",
"connection not allowed by ruleset",
"Network unreachable",
"Host unreachable",
"Connection refused",
"TTL expired",
"Command not supported",
"Address type not supported"
};
static BOOL http_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port);
static BOOL socks_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port);
static BOOL socks_proxy_connect(BIO* bufferedBio, const char *proxyUsername, const char *proxyPassword, const char* hostname, UINT16 port);
void proxy_read_environment(rdpSettings* settings, char* envname);
BOOL proxy_prepare(rdpSettings* settings, const char** lpPeerHostname, UINT16* lpPeerPort,
BOOL isHTTPS)
const char** lpProxyUsername, const char** lpProxyPassword)
{
/* For TSGateway, find the system HTTPS proxy automatically */
if (!settings->ProxyType)
@ -47,6 +85,8 @@ BOOL proxy_prepare(rdpSettings* settings, const char** lpPeerHostname, UINT16* l
{
*lpPeerHostname = settings->ProxyHostname;
*lpPeerPort = settings->ProxyPort;
*lpProxyUsername = settings->ProxyUsername;
*lpProxyPassword = settings->ProxyPassword;
return TRUE;
}
@ -151,7 +191,8 @@ BOOL proxy_parse_uri(rdpSettings* settings, const char* uri)
return TRUE;
}
BOOL proxy_connect(rdpSettings* settings, BIO* bufferedBio, const char* hostname, UINT16 port)
BOOL proxy_connect(rdpSettings* settings, BIO* bufferedBio, const char *proxyUsername, const char *proxyPassword,
const char* hostname, UINT16 port)
{
switch (settings->ProxyType)
{
@ -162,7 +203,7 @@ BOOL proxy_connect(rdpSettings* settings, BIO* bufferedBio, const char* hostname
return http_proxy_connect(bufferedBio, hostname, port);
case PROXY_TYPE_SOCKS:
return socks_proxy_connect(bufferedBio, hostname, port);
return socks_proxy_connect(bufferedBio, proxyUsername, proxyPassword, hostname, port);
default:
WLog_ERR(TAG, "Invalid internal proxy configuration");
@ -259,111 +300,160 @@ static BOOL http_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 po
return TRUE;
}
static int recv_socks_reply(BIO* bufferedBio, BYTE * buf, int len, char* reason)
static int recv_socks_reply(BIO* bufferedBio, BYTE* buf, int len, char* reason, BYTE checkVer)
{
int status;
ZeroMemory(buf, len);
for(;;) {
status = BIO_read(bufferedBio, buf, len);
if (status > 0) break;
else if (status < 0)
{
/* Error? */
if (BIO_should_retry(bufferedBio))
{
USleep(100);
continue;
}
for(;;)
{
status = BIO_read(bufferedBio, buf, len);
if (status > 0)
break;
WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (Status %d)", reason, status);
return -1;
}
else if (status == 0)
{
/* Error? */
WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)", reason);
return -1;
}
if (status < 0)
{
/* Error? */
if (BIO_should_retry(bufferedBio))
{
USleep(100);
continue;
}
WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (Status %d)", reason, status);
return -1;
}
if (status == 0)
{
/* Error? */
WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)", reason);
return -1;
}
}
if (buf[0] != 5)
if (status < 2)
{
WLog_ERR(TAG, "SOCKS Proxy version is not 5 (%s)", reason);
return -1;
WLog_ERR(TAG, "SOCKS Proxy reply packet too short (%s)", reason);
return -1;
}
if (buf[0] != checkVer)
{
WLog_ERR(TAG, "SOCKS Proxy version is not 5 (%s)", reason);
return -1;
}
return status;
}
/* SOCKS Proxy auth methods by rfc1928 */
#define AUTH_M_NO_AUTH 0
#define AUTH_M_GSSAPI 1
#define AUTH_M_USR_PASS 2
static BOOL socks_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port)
static BOOL socks_proxy_connect(BIO* bufferedBio, const char *proxyUsername, const char *proxyPassword,
const char* hostname, UINT16 port)
{
int status;
BYTE buf[280], hostnlen = strlen(hostname) & 0xff;
/* CONN REQ replies in enum. order */
static const char *rplstat[] = {
"succeeded",
"general SOCKS server failure",
"connection not allowed by ruleset",
"Network unreachable",
"Host unreachable",
"Connection refused",
"TTL expired",
"Command not supported",
"Address type not supported"
};
int nauthMethods = 1, writeLen = 3;
BYTE buf[3 + 255 + 255]; /* biggest packet is user/pass auth */
size_t hostnlen = strnlen(hostname, 255);
if (proxyUsername && proxyPassword)
{
nauthMethods++;
writeLen++;
}
/* select auth. method */
memset(buf, '\0', sizeof(buf));
buf[0] = 5; /* SOCKS version */
buf[1] = 1; /* #of methods offered */
buf[1] = nauthMethods; /* #of methods offered */
buf[2] = AUTH_M_NO_AUTH;
status = BIO_write(bufferedBio, buf, 3);
if (status != 3)
if (nauthMethods > 1)
buf[3] = AUTH_M_USR_PASS;
status = BIO_write(bufferedBio, buf, writeLen);
if (status != writeLen)
{
WLog_ERR(TAG, "SOCKS proxy: failed to write AUTH METHOD request");
return FALSE;
}
status = recv_socks_reply(bufferedBio, buf, sizeof(buf), "AUTH REQ");
if (status < 0) return FALSE;
status = recv_socks_reply(bufferedBio, buf, 2, "AUTH REQ", 5);
if (status <= 0)
return FALSE;
if (buf[1] != AUTH_M_NO_AUTH)
switch(buf[1])
{
WLog_ERR(TAG, "SOCKS Proxy: (NO AUTH) method was not selected by proxy");
return FALSE;
case AUTH_M_NO_AUTH:
WLog_DBG(TAG, "SOCKS Proxy: (NO AUTH) method was selected");
break;
case AUTH_M_USR_PASS:
{
int usernameLen = strnlen(proxyUsername, 255);
int userpassLen = strnlen(proxyPassword, 255);
BYTE *ptr;
if (nauthMethods < 2)
{
WLog_ERR(TAG, "SOCKS Proxy: USER/PASS method was not proposed to server");
return FALSE;
}
/* user/password v1 method */
ptr = buf + 2;
buf[0] = 1;
buf[1] = usernameLen;
memcpy(ptr, proxyUsername, usernameLen);
ptr += usernameLen;
*ptr = userpassLen;
ptr++;
memcpy(ptr, proxyPassword, userpassLen);
status = BIO_write(bufferedBio, buf, 3 + usernameLen + userpassLen);
if (status != 3 + usernameLen + userpassLen)
{
WLog_ERR(TAG, "SOCKS Proxy: error writing user/password request");
return FALSE;
}
status = recv_socks_reply(bufferedBio, buf, 2, "AUTH REQ", 1);
if (status < 2)
return FALSE;
if (buf[1] != 0x00)
{
WLog_ERR(TAG, "SOCKS Proxy: invalid user/password");
return FALSE;
}
break;
}
default:
WLog_ERR(TAG, "SOCKS Proxy: unknown method 0x%x was selected by proxy", buf[1]);
return FALSE;
}
/* CONN request */
memset(buf, '\0', sizeof(buf));
buf[0] = 5; /* SOCKS version */
buf[1] = 1; /* command = connect */
/* 3rd octet is reserved x00 */
buf[3] = 3; /* addr.type = FQDN */
buf[1] = SOCKS_CMD_CONNECT; /* command */
buf[2] = 0; /* 3rd octet is reserved x00 */
buf[3] = SOCKS_ADDR_FQDN; /* addr.type */
buf[4] = hostnlen; /* DST.ADDR */
memcpy(buf +5, hostname, hostnlen);
memcpy(buf + 5, hostname, hostnlen);
/* follows DST.PORT in netw. format */
buf[hostnlen +5] = 0xff & (port >> 8);
buf[hostnlen +6] = 0xff & port;
status = BIO_write(bufferedBio, buf, hostnlen +7);
if (status != (hostnlen +7))
buf[hostnlen + 5] = (port >> 8) & 0xff;
buf[hostnlen + 6] = port & 0xff;
status = BIO_write(bufferedBio, buf, hostnlen + 7);
if (status != (hostnlen + 7))
{
WLog_ERR(TAG, "SOCKS proxy: failed to write CONN REQ");
return FALSE;
}
status = recv_socks_reply(bufferedBio, buf, sizeof(buf), "CONN REQ");
if (status < 0) return FALSE;
status = recv_socks_reply(bufferedBio, buf, sizeof(buf), "CONN REQ", 5);
if (status < 4)
return FALSE;
if (buf[1] == 0)
{
WLog_INFO(TAG, "Successfully connected to %s:%d", hostname, port);
return TRUE;
WLog_INFO(TAG, "Successfully connected to %s:%d", hostname, port);
return TRUE;
}
if (buf[1] > 0 && buf[1] < 9)

View File

@ -24,8 +24,9 @@
#include <openssl/bio.h>
BOOL proxy_prepare(rdpSettings* settings, const char** lpPeerHostname, UINT16* lpPeerPort,
BOOL isHTTPS);
const char **lpProxyUsername, const char **lpProxyPassword);
BOOL proxy_parse_uri(rdpSettings* settings, const char* uri);
BOOL proxy_connect(rdpSettings* settings, BIO* bio, const char* hostname, UINT16 port);
BOOL proxy_connect(rdpSettings* settings, BIO* bio, const char *proxyUsername, const char *proxyPassword,
const char* hostname, UINT16 port);
#endif /* FREERDP_LIB_CORE_HTTP_PROXY_H */

View File

@ -406,11 +406,13 @@ BOOL transport_connect(rdpTransport* transport, const char* hostname,
else
{
UINT16 peerPort;
const char *peerHostname;
BOOL isProxyConnection = proxy_prepare(settings, &peerHostname, &peerPort, TRUE);
const char *proxyHostname, *proxyUsername, *proxyPassword;
BOOL isProxyConnection = proxy_prepare(settings, &proxyHostname, &peerPort,
&proxyUsername, &proxyPassword);
if (isProxyConnection)
sockfd = freerdp_tcp_connect(context, settings, peerHostname, peerPort, timeout);
sockfd = freerdp_tcp_connect(context, settings, proxyHostname, peerPort, timeout);
else
sockfd = freerdp_tcp_connect(context, settings, hostname, port, timeout);
@ -421,8 +423,10 @@ BOOL transport_connect(rdpTransport* transport, const char* hostname,
return FALSE;
if (isProxyConnection)
if (!proxy_connect(settings, transport->frontBio, hostname, port))
return FALSE;
{
if (!proxy_connect(settings, transport->frontBio, proxyUsername, proxyPassword, hostname, port))
return FALSE;
}
status = TRUE;
}