diff --git a/client/common/cmdline.c b/client/common/cmdline.c index 9fa8fe48f..19ea41010 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -140,6 +141,8 @@ COMMAND_LINE_ARGUMENT_A args[] = { "help", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "?", "print help" }, { "play-rfx", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Replay rfx pcap file" }, { "auth-only", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Authenticate only." }, + { "reconnect-cookie", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Pass base64 reconnect cookie to the connection" }, + { "print-reconnect-cookie", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Print base64 reconnect cookie after connecting" }, { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } }; @@ -1669,6 +1672,27 @@ int freerdp_client_parse_command_line_arguments(int argc, char** argv, rdpSettin { settings->AuthenticationOnly = arg->Value ? TRUE : FALSE; } + CommandLineSwitchCase(arg, "reconnect-cookie") + { + BYTE *base64; + int length; + crypto_base64_decode((BYTE *) (arg->Value), + (int) strlen(arg->Value), &base64, &length); + if ((base64 != NULL) && (length == sizeof(ARC_SC_PRIVATE_PACKET))) + { + memcpy(settings->ServerAutoReconnectCookie, base64, length); + free(base64); + } + else + { + fprintf(stderr, "reconnect-cookie: invalid base64 '%s'\n", + arg->Value); + } + } + CommandLineSwitchCase(arg, "print-reconnect-cookie") + { + settings->PrintReconnectCookie = arg->Value ? TRUE : FALSE; + } CommandLineSwitchDefault(arg) { } diff --git a/include/freerdp/crypto/crypto.h b/include/freerdp/crypto/crypto.h index e9130dbef..6a82556d3 100644 --- a/include/freerdp/crypto/crypto.h +++ b/include/freerdp/crypto/crypto.h @@ -106,6 +106,7 @@ typedef struct crypto_hmac_struct* CryptoHmac; FREERDP_API CryptoHmac crypto_hmac_new(void); FREERDP_API void crypto_hmac_sha1_init(CryptoHmac hmac, const BYTE *data, UINT32 length); +FREERDP_API void crypto_hmac_md5_init(CryptoHmac hmac, const BYTE *data, UINT32 length); FREERDP_API void crypto_hmac_update(CryptoHmac hmac, const BYTE *data, UINT32 length); FREERDP_API void crypto_hmac_final(CryptoHmac hmac, BYTE *out_data, UINT32 length); FREERDP_API void crypto_hmac_free(CryptoHmac hmac); diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 02697579d..61a0d355e 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -795,7 +795,8 @@ struct rdp_settings ALIGN64 DWORD ServerRandomLength; /* 197 */ ALIGN64 BYTE* ServerCertificate; /* 198 */ ALIGN64 DWORD ServerCertificateLength; /* 199 */ - UINT64 padding0256[256 - 200]; /* 200 */ + ALIGN64 BYTE* ClientRandom; /* 200 */ + UINT64 padding0256[256 - 201]; /* 201 */ /* Client Network Data */ ALIGN64 UINT32 ChannelCount; /* 256 */ @@ -870,7 +871,8 @@ struct rdp_settings ALIGN64 UINT32 AutoReconnectMaxRetries; /* 833 */ ALIGN64 ARC_CS_PRIVATE_PACKET* ClientAutoReconnectCookie; /* 834 */ ALIGN64 ARC_SC_PRIVATE_PACKET* ServerAutoReconnectCookie; /* 835 */ - UINT64 padding0896[896 - 835]; /* 835 */ + ALIGN64 BOOL PrintReconnectCookie; /* 836 */ + UINT64 padding0896[896 - 837]; /* 837 */ /* Client Info (Time Zone) */ ALIGN64 TIME_ZONE_INFO* ClientTimeZone; /* 896 */ diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c index 0a081ad47..2e2de425f 100644 --- a/libfreerdp/core/connection.c +++ b/libfreerdp/core/connection.c @@ -380,7 +380,6 @@ static BOOL rdp_client_establish_keys(rdpRdp* rdp) UINT32 length; UINT32 key_len; BYTE crypt_client_random[256 + 8]; - BYTE client_random[CLIENT_RANDOM_LENGTH]; if (!rdp->settings->DisableEncryption) { @@ -389,12 +388,15 @@ static BOOL rdp_client_establish_keys(rdpRdp* rdp) } /* encrypt client random */ + if (rdp->settings->ClientRandom) free(rdp->settings->ClientRandom); + rdp->settings->ClientRandom = malloc(CLIENT_RANDOM_LENGTH); + if (rdp->settings->ClientRandom == NULL) return FALSE; ZeroMemory(crypt_client_random, sizeof(crypt_client_random)); - crypto_nonce(client_random, sizeof(client_random)); + crypto_nonce(rdp->settings->ClientRandom, CLIENT_RANDOM_LENGTH); key_len = rdp->settings->RdpServerCertificate->cert_info.ModulusLength; mod = rdp->settings->RdpServerCertificate->cert_info.Modulus; exp = rdp->settings->RdpServerCertificate->cert_info.exponent; - crypto_rsa_public_encrypt(client_random, sizeof(client_random), key_len, mod, exp, crypt_client_random); + crypto_rsa_public_encrypt(rdp->settings->ClientRandom, CLIENT_RANDOM_LENGTH, key_len, mod, exp, crypt_client_random); /* send crypt client random to server */ length = RDP_PACKET_HEADER_MAX_LENGTH + RDP_SECURITY_HEADER_LENGTH + 4 + key_len + 8; @@ -416,7 +418,7 @@ static BOOL rdp_client_establish_keys(rdpRdp* rdp) Stream_Free(s, TRUE); /* now calculate encrypt / decrypt and update keys */ - if (!security_establish_keys(client_random, rdp)) + if (!security_establish_keys(rdp->settings->ClientRandom, rdp)) { return FALSE; } diff --git a/libfreerdp/core/info.c b/libfreerdp/core/info.c index 92d6dd331..d87d81653 100644 --- a/libfreerdp/core/info.c +++ b/libfreerdp/core/info.c @@ -22,6 +22,8 @@ #endif #include +#include +#include #include "timezone.h" @@ -60,6 +62,14 @@ BOOL rdp_read_server_auto_reconnect_cookie(wStream* s, rdpSettings* settings) Stream_Read_UINT32(s, autoReconnectCookie->version); /* version (4 bytes) */ Stream_Read_UINT32(s, autoReconnectCookie->logonId); /* LogonId (4 bytes) */ Stream_Read(s, autoReconnectCookie->arcRandomBits, 16); /* arcRandomBits (16 bytes) */ + if ((settings->PrintReconnectCookie) && (autoReconnectCookie->cbLen > 0)) + { + char *base64; + base64 = crypto_base64_encode((BYTE *) autoReconnectCookie, + sizeof(ARC_SC_PRIVATE_PACKET)); + fprintf(stderr, "Reconnect-cookie: %s\n", base64); + free(base64); + } return TRUE; } @@ -188,7 +198,7 @@ void rdp_write_extended_info_packet(wStream* s, rdpSettings* settings) cbClientDir = ConvertToUnicode(CP_UTF8, 0, settings->ClientDir, -1, &clientDir, 0) * 2; - cbAutoReconnectLen = (int) settings->ClientAutoReconnectCookie->cbLen; + cbAutoReconnectLen = (int) settings->ServerAutoReconnectCookie->cbLen; Stream_Write_UINT16(s, clientAddressFamily); /* clientAddressFamily */ @@ -214,7 +224,41 @@ void rdp_write_extended_info_packet(wStream* s, rdpSettings* settings) Stream_Write_UINT16(s, cbAutoReconnectLen); /* cbAutoReconnectLen */ if (cbAutoReconnectLen > 0) + { + CryptoHmac hmac; + ARC_SC_PRIVATE_PACKET* serverCookie; + ARC_CS_PRIVATE_PACKET* clientCookie; + + printf("Sending auto reconnect\n"); + serverCookie = settings->ServerAutoReconnectCookie; + clientCookie = settings->ClientAutoReconnectCookie; + + clientCookie->cbLen = serverCookie->cbLen; + clientCookie->version = serverCookie->version; + clientCookie->logonId = serverCookie->logonId; + + hmac = crypto_hmac_new(); + crypto_hmac_md5_init(hmac, serverCookie->arcRandomBits, 16); + if (settings->SelectedProtocol == PROTOCOL_RDP) + { + crypto_hmac_update(hmac, (BYTE *) (settings->ClientRandom), 32); + } + else + { + /* Anthony Tong's version had 16 zeroes here; I'm not sure why. + * I do know that 16 did not reconnect correctly vs Win2008RDVH, + * and 32 did. + */ + const BYTE zeros[32] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; + crypto_hmac_update(hmac, zeros, 32); + } + crypto_hmac_final(hmac, clientCookie->securityVerifier, 16); + rdp_write_client_auto_reconnect_cookie(s, settings); /* autoReconnectCookie */ + /* mark as used */ + settings->ServerAutoReconnectCookie->cbLen = 0; + } /* reserved1 (2 bytes) */ /* reserved2 (2 bytes) */ diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index ea719aae3..5241c8f3e 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -450,6 +450,7 @@ void freerdp_settings_free(rdpSettings* settings) free(settings->ClientHostname); free(settings->ClientProductId); free(settings->ServerRandom); + if (settings->ClientRandom) free(settings->ClientRandom); free(settings->ServerCertificate); free(settings->RdpKeyFile); certificate_free(settings->RdpServerCertificate); diff --git a/libfreerdp/crypto/crypto.c b/libfreerdp/crypto/crypto.c index 64c42e91e..2445187a0 100644 --- a/libfreerdp/crypto/crypto.c +++ b/libfreerdp/crypto/crypto.c @@ -132,6 +132,11 @@ void crypto_hmac_sha1_init(CryptoHmac hmac, const BYTE* data, UINT32 length) HMAC_Init_ex(&hmac->hmac_ctx, data, length, EVP_sha1(), NULL); } +void crypto_hmac_md5_init(CryptoHmac hmac, const BYTE* data, UINT32 length) +{ + HMAC_Init_ex(&hmac->hmac_ctx, data, length, EVP_md5(), NULL); +} + void crypto_hmac_update(CryptoHmac hmac, const BYTE* data, UINT32 length) { HMAC_Update(&hmac->hmac_ctx, data, length);