mirror of
https://github.com/FreeRDP/FreeRDP.git
synced 2024-12-04 07:14:03 +08:00
proxy: cleanup SOCKS support and add user/password support
This commit is contained in:
parent
1ba31551a6
commit
0f968b782c
@ -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)
|
||||
|
@ -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" },
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user