mirror of
https://github.com/morgan9e/FreeRDP
synced 2026-04-15 00:44:19 +09:00
Merge pull request #368 from pjd/crypto
Server-side Standard RDP Security support.
This commit is contained in:
@@ -145,6 +145,7 @@ typedef struct
|
||||
/* Certificates */
|
||||
|
||||
typedef struct rdp_certificate rdpCertificate;
|
||||
typedef struct rdp_key rdpKey;
|
||||
|
||||
struct rdp_CertBlob
|
||||
{
|
||||
@@ -296,7 +297,8 @@ struct rdp_settings
|
||||
boolean nla_security; /* 146 */
|
||||
boolean rdp_security; /* 147 */
|
||||
uint32 ntlm_version; /* 148 */
|
||||
uint32 paddingF[160 - 149]; /* 149 */
|
||||
boolean secure_checksum; /* 149 */
|
||||
uint32 paddingF[160 - 150]; /* 150 */
|
||||
|
||||
/* Session */
|
||||
boolean console_audio; /* 160 */
|
||||
@@ -349,7 +351,9 @@ struct rdp_settings
|
||||
rdpBlob* server_certificate; /* 267 */
|
||||
boolean ignore_certificate; /* 268 */
|
||||
rdpCertificate* server_cert; /* 269 */
|
||||
uint32 paddingL[280 - 270]; /* 270 */
|
||||
char* rdp_key_file; /* 270 */
|
||||
rdpKey* server_key; /* 271 */
|
||||
uint32 paddingL[280 - 272]; /* 272 */
|
||||
|
||||
/* Codecs */
|
||||
boolean rfx_codec; /* 280 */
|
||||
|
||||
@@ -1793,22 +1793,17 @@ boolean rdp_recv_demand_active(rdpRdp* rdp, STREAM* s)
|
||||
uint16 numberCapabilities;
|
||||
uint16 lengthSourceDescriptor;
|
||||
uint16 lengthCombinedCapabilities;
|
||||
uint32 securityHeader;
|
||||
uint16 securityFlags;
|
||||
|
||||
if (!rdp_read_header(rdp, s, &length, &channelId))
|
||||
return false;
|
||||
|
||||
if (rdp->settings->encryption)
|
||||
{
|
||||
stream_read_uint32(s, securityHeader);
|
||||
if (securityHeader & SEC_SECURE_CHECKSUM)
|
||||
rdp_read_security_header(s, &securityFlags);
|
||||
if (securityFlags & SEC_ENCRYPT)
|
||||
{
|
||||
printf("Error: TODO\n");
|
||||
return false;
|
||||
}
|
||||
if (securityHeader & SEC_ENCRYPT)
|
||||
{
|
||||
if (!rdp_decrypt(rdp, s, length - 4))
|
||||
if (!rdp_decrypt(rdp, s, length - 4, securityFlags))
|
||||
{
|
||||
printf("rdp_decrypt failed\n");
|
||||
return false;
|
||||
@@ -1927,9 +1922,24 @@ boolean rdp_recv_confirm_active(rdpRdp* rdp, STREAM* s)
|
||||
uint16 lengthSourceDescriptor;
|
||||
uint16 lengthCombinedCapabilities;
|
||||
uint16 numberCapabilities;
|
||||
uint16 securityFlags;
|
||||
|
||||
if (!rdp_read_header(rdp, s, &length, &channelId))
|
||||
return false;
|
||||
|
||||
if (rdp->settings->encryption)
|
||||
{
|
||||
rdp_read_security_header(s, &securityFlags);
|
||||
if (securityFlags & SEC_ENCRYPT)
|
||||
{
|
||||
if (!rdp_decrypt(rdp, s, length - 4, securityFlags))
|
||||
{
|
||||
printf("rdp_decrypt failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channelId != MCS_GLOBAL_CHANNEL_ID)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -17,6 +17,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
#include "certificate.h"
|
||||
|
||||
/**
|
||||
@@ -251,6 +258,7 @@ static boolean certificate_process_server_public_key(rdpCertificate* certificate
|
||||
modlen = keylen - 8;
|
||||
freerdp_blob_alloc(&(certificate->cert_info.modulus), modlen);
|
||||
stream_read(s, certificate->cert_info.modulus.data, modlen);
|
||||
/* 8 bytes of zero padding */
|
||||
stream_seek(s, 8);
|
||||
|
||||
return true;
|
||||
@@ -482,3 +490,71 @@ void certificate_free(rdpCertificate* certificate)
|
||||
}
|
||||
}
|
||||
|
||||
rdpKey* key_new(const char *keyfile)
|
||||
{
|
||||
rdpKey* key;
|
||||
RSA *rsa;
|
||||
FILE *fp;
|
||||
|
||||
key = (rdpKey*) xzalloc(sizeof(rdpKey));
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
|
||||
fp = fopen(keyfile, "r");
|
||||
if (fp == NULL) {
|
||||
printf("unable to load RSA key from %s: %s.", keyfile,
|
||||
strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
|
||||
if (rsa == NULL) {
|
||||
ERR_print_errors_fp(stdout);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
switch (RSA_check_key(rsa)) {
|
||||
case 0:
|
||||
RSA_free(rsa);
|
||||
printf("invalid RSA key in %s", keyfile);
|
||||
return NULL;
|
||||
case 1:
|
||||
/* Valid key. */
|
||||
break;
|
||||
default:
|
||||
ERR_print_errors_fp(stdout);
|
||||
RSA_free(rsa);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (BN_num_bytes(rsa->e) > 4) {
|
||||
RSA_free(rsa);
|
||||
printf("RSA public exponent too large in %s", keyfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
freerdp_blob_alloc(&key->modulus, BN_num_bytes(rsa->n));
|
||||
BN_bn2bin(rsa->n, key->modulus.data);
|
||||
crypto_reverse(key->modulus.data, key->modulus.length);
|
||||
freerdp_blob_alloc(&key->private_exponent, BN_num_bytes(rsa->d));
|
||||
BN_bn2bin(rsa->d, key->private_exponent.data);
|
||||
crypto_reverse(key->private_exponent.data, key->private_exponent.length);
|
||||
memset(key->exponent, 0, sizeof(key->exponent));
|
||||
BN_bn2bin(rsa->e, key->exponent + sizeof(key->exponent) - BN_num_bytes(rsa->e));
|
||||
crypto_reverse(key->exponent, sizeof(key->exponent));
|
||||
|
||||
RSA_free(rsa);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void key_free(rdpKey* key)
|
||||
{
|
||||
if (key != NULL)
|
||||
{
|
||||
freerdp_blob_free(&key->modulus);
|
||||
freerdp_blob_free(&key->private_exponent);
|
||||
xfree(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,13 @@
|
||||
#define BB_RSA_KEY_BLOB 6
|
||||
#define BB_RSA_SIGNATURE_BLOB 8
|
||||
|
||||
struct rdp_key
|
||||
{
|
||||
rdpBlob modulus;
|
||||
rdpBlob private_exponent;
|
||||
uint8 exponent[4];
|
||||
};
|
||||
|
||||
void certificate_read_x509_certificate(rdpCertBlob* cert, rdpCertInfo* info);
|
||||
|
||||
rdpX509CertChain* certificate_new_x509_certificate_chain(uint32 count);
|
||||
@@ -54,6 +61,9 @@ boolean certificate_read_server_certificate(rdpCertificate* certificate, uint8*
|
||||
rdpCertificate* certificate_new(void);
|
||||
void certificate_free(rdpCertificate* certificate);
|
||||
|
||||
rdpKey* key_new(const char *keyfile);
|
||||
void key_free(rdpKey* key);
|
||||
|
||||
#ifdef WITH_DEBUG_CERTIFICATE
|
||||
#define DEBUG_CERTIFICATE(fmt, ...) DEBUG_CLASS(CERTIFICATE, fmt, ## __VA_ARGS__)
|
||||
#else
|
||||
|
||||
@@ -182,7 +182,7 @@ boolean rdp_client_redirect(rdpRdp* rdp)
|
||||
|
||||
static boolean rdp_client_establish_keys(rdpRdp* rdp)
|
||||
{
|
||||
uint8 client_random[32];
|
||||
uint8 client_random[CLIENT_RANDOM_LENGTH];
|
||||
uint8 crypt_client_random[256 + 8];
|
||||
uint32 key_len;
|
||||
uint8* mod;
|
||||
@@ -198,15 +198,14 @@ static boolean rdp_client_establish_keys(rdpRdp* rdp)
|
||||
|
||||
/* encrypt client random */
|
||||
memset(crypt_client_random, 0, sizeof(crypt_client_random));
|
||||
memset(client_random, 0x5e, 32);
|
||||
crypto_nonce(client_random, 32);
|
||||
crypto_nonce(client_random, sizeof(client_random));
|
||||
key_len = rdp->settings->server_cert->cert_info.modulus.length;
|
||||
mod = rdp->settings->server_cert->cert_info.modulus.data;
|
||||
exp = rdp->settings->server_cert->cert_info.exponent;
|
||||
crypto_rsa_public_encrypt(client_random, 32, key_len, mod, exp, crypt_client_random);
|
||||
crypto_rsa_public_encrypt(client_random, sizeof(client_random), key_len, mod, exp, crypt_client_random);
|
||||
|
||||
/* send crypt client random to server */
|
||||
length = RDP_PACKET_HEADER_LENGTH + RDP_SECURITY_HEADER_LENGTH + 4 + key_len + 8;
|
||||
length = RDP_PACKET_HEADER_MAX_LENGTH + RDP_SECURITY_HEADER_LENGTH + 4 + key_len + 8;
|
||||
s = transport_send_stream_init(rdp->mcs->transport, length);
|
||||
rdp_write_header(rdp, s, length, MCS_GLOBAL_CHANNEL_ID);
|
||||
rdp_write_security_header(s, SEC_EXCHANGE_PKT);
|
||||
@@ -225,6 +224,75 @@ static boolean rdp_client_establish_keys(rdpRdp* rdp)
|
||||
}
|
||||
|
||||
rdp->do_crypt = true;
|
||||
if (rdp->settings->secure_checksum)
|
||||
rdp->do_secure_checksum = true;
|
||||
|
||||
if (rdp->settings->encryption_method == ENCRYPTION_METHOD_FIPS)
|
||||
{
|
||||
uint8 fips_ivec[8] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
|
||||
rdp->fips_encrypt = crypto_des3_encrypt_init(rdp->fips_encrypt_key, fips_ivec);
|
||||
rdp->fips_decrypt = crypto_des3_decrypt_init(rdp->fips_decrypt_key, fips_ivec);
|
||||
|
||||
rdp->fips_hmac = crypto_hmac_new();
|
||||
return true;
|
||||
}
|
||||
|
||||
rdp->rc4_decrypt_key = crypto_rc4_init(rdp->decrypt_key, rdp->rc4_key_len);
|
||||
rdp->rc4_encrypt_key = crypto_rc4_init(rdp->encrypt_key, rdp->rc4_key_len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean rdp_server_establish_keys(rdpRdp* rdp, STREAM* s)
|
||||
{
|
||||
uint8 client_random[64]; /* Should be only 32 after successfull decryption, but on failure might take up to 64 bytes. */
|
||||
uint8 crypt_client_random[256 + 8];
|
||||
uint32 rand_len, key_len;
|
||||
uint16 channel_id, length, sec_flags;
|
||||
uint8* mod;
|
||||
uint8* priv_exp;
|
||||
|
||||
if (rdp->settings->encryption == false)
|
||||
{
|
||||
/* No RDP Security. */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!rdp_read_header(rdp, s, &length, &channel_id))
|
||||
{
|
||||
printf("rdp_server_establish_keys: invalid RDP header\n");
|
||||
return false;
|
||||
}
|
||||
rdp_read_security_header(s, &sec_flags);
|
||||
if ((sec_flags & SEC_EXCHANGE_PKT) == 0)
|
||||
{
|
||||
printf("rdp_server_establish_keys: missing SEC_EXCHANGE_PKT in security header\n");
|
||||
return false;
|
||||
}
|
||||
stream_read_uint32(s, rand_len);
|
||||
key_len = rdp->settings->server_key->modulus.length;
|
||||
if (rand_len != key_len + 8)
|
||||
{
|
||||
printf("rdp_server_establish_keys: invalid encrypted client random length\n");
|
||||
return false;
|
||||
}
|
||||
memset(crypt_client_random, 0, sizeof(crypt_client_random));
|
||||
stream_read(s, crypt_client_random, rand_len);
|
||||
/* 8 zero bytes of padding */
|
||||
stream_seek(s, 8);
|
||||
mod = rdp->settings->server_key->modulus.data;
|
||||
priv_exp = rdp->settings->server_key->private_exponent.data;
|
||||
crypto_rsa_private_decrypt(crypt_client_random, rand_len - 8, key_len, mod, priv_exp, client_random);
|
||||
|
||||
/* now calculate encrypt / decrypt and update keys */
|
||||
if (!security_establish_keys(client_random, rdp))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rdp->do_crypt = true;
|
||||
if (rdp->settings->secure_checksum)
|
||||
rdp->do_secure_checksum = true;
|
||||
|
||||
if (rdp->settings->encryption_method == ENCRYPTION_METHOD_FIPS)
|
||||
{
|
||||
@@ -371,7 +439,7 @@ boolean rdp_client_connect_demand_active(rdpRdp* rdp, STREAM* s)
|
||||
if (!rdp_recv_demand_active(rdp, s))
|
||||
{
|
||||
stream_set_mark(s, mark);
|
||||
stream_seek(s, RDP_PACKET_HEADER_LENGTH);
|
||||
stream_seek(s, RDP_PACKET_HEADER_MAX_LENGTH);
|
||||
|
||||
if (rdp_recv_out_of_sequence_pdu(rdp, s) != true)
|
||||
return false;
|
||||
@@ -434,14 +502,11 @@ boolean rdp_server_accept_nego(rdpRdp* rdp, STREAM* s)
|
||||
|
||||
if (!nego_read_request(rdp->nego, s))
|
||||
return false;
|
||||
if (rdp->nego->requested_protocols == PROTOCOL_RDP)
|
||||
{
|
||||
printf("Standard RDP encryption is not supported.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
rdp->nego->selected_protocol = 0;
|
||||
|
||||
printf("Requested protocols:");
|
||||
if ((rdp->nego->requested_protocols | PROTOCOL_TLS))
|
||||
if ((rdp->nego->requested_protocols & PROTOCOL_TLS))
|
||||
{
|
||||
printf(" TLS");
|
||||
if (rdp->settings->tls_security)
|
||||
@@ -452,7 +517,7 @@ boolean rdp_server_accept_nego(rdpRdp* rdp, STREAM* s)
|
||||
else
|
||||
printf("(n)");
|
||||
}
|
||||
if ((rdp->nego->requested_protocols | PROTOCOL_NLA))
|
||||
if ((rdp->nego->requested_protocols & PROTOCOL_NLA))
|
||||
{
|
||||
printf(" NLA");
|
||||
if (rdp->settings->nla_security)
|
||||
@@ -463,6 +528,14 @@ boolean rdp_server_accept_nego(rdpRdp* rdp, STREAM* s)
|
||||
else
|
||||
printf("(n)");
|
||||
}
|
||||
printf(" RDP");
|
||||
if (rdp->settings->rdp_security && rdp->nego->selected_protocol == 0)
|
||||
{
|
||||
printf("(Y)");
|
||||
rdp->nego->selected_protocol = PROTOCOL_RDP;
|
||||
}
|
||||
else
|
||||
printf("(n)");
|
||||
printf("\n");
|
||||
|
||||
if (!nego_send_negotiation_response(rdp->nego))
|
||||
@@ -564,8 +637,20 @@ boolean rdp_server_accept_mcs_channel_join_request(rdpRdp* rdp, STREAM* s)
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean rdp_server_accept_client_keys(rdpRdp* rdp, STREAM* s)
|
||||
{
|
||||
|
||||
if (!rdp_server_establish_keys(rdp, s))
|
||||
return false;
|
||||
|
||||
rdp->state = CONNECTION_STATE_ESTABLISH_KEYS;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean rdp_server_accept_client_info(rdpRdp* rdp, STREAM* s)
|
||||
{
|
||||
|
||||
if (!rdp_recv_client_info(rdp, s))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ enum CONNECTION_STATE
|
||||
CONNECTION_STATE_MCS_ERECT_DOMAIN,
|
||||
CONNECTION_STATE_MCS_ATTACH_USER,
|
||||
CONNECTION_STATE_MCS_CHANNEL_JOIN,
|
||||
CONNECTION_STATE_ESTABLISH_KEYS,
|
||||
CONNECTION_STATE_LICENSE,
|
||||
CONNECTION_STATE_CAPABILITY,
|
||||
CONNECTION_STATE_FINALIZATION,
|
||||
@@ -59,6 +60,7 @@ boolean rdp_server_accept_mcs_connect_initial(rdpRdp* rdp, STREAM* s);
|
||||
boolean rdp_server_accept_mcs_erect_domain_request(rdpRdp* rdp, STREAM* s);
|
||||
boolean rdp_server_accept_mcs_attach_user_request(rdpRdp* rdp, STREAM* s);
|
||||
boolean rdp_server_accept_mcs_channel_join_request(rdpRdp* rdp, STREAM* s);
|
||||
boolean rdp_server_accept_client_keys(rdpRdp* rdp, STREAM* s);
|
||||
boolean rdp_server_accept_client_info(rdpRdp* rdp, STREAM* s);
|
||||
boolean rdp_server_accept_confirm_active(rdpRdp* rdp, STREAM* s);
|
||||
boolean rdp_server_reactivate(rdpRdp* rdp);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <freerdp/utils/stream.h>
|
||||
|
||||
#include "orders.h"
|
||||
#include "per.h"
|
||||
#include "update.h"
|
||||
#include "surface.h"
|
||||
|
||||
@@ -99,10 +100,7 @@ uint16 fastpath_read_header_rdp(rdpFastPath* fastpath, STREAM* s)
|
||||
{
|
||||
uint8 header;
|
||||
uint16 length;
|
||||
uint8 t;
|
||||
uint16 hs;
|
||||
|
||||
hs = 2;
|
||||
stream_read_uint8(s, header);
|
||||
|
||||
if (fastpath != NULL)
|
||||
@@ -111,18 +109,9 @@ uint16 fastpath_read_header_rdp(rdpFastPath* fastpath, STREAM* s)
|
||||
fastpath->numberEvents = (header & 0x3C) >> 2;
|
||||
}
|
||||
|
||||
stream_read_uint8(s, length); /* length1 */
|
||||
/* If most significant bit is not set, length2 is not presented. */
|
||||
if ((length & 0x80))
|
||||
{
|
||||
hs++;
|
||||
length &= 0x7F;
|
||||
length <<= 8;
|
||||
stream_read_uint8(s, t);
|
||||
length += t;
|
||||
}
|
||||
per_read_length(s, &length);
|
||||
|
||||
return length - hs;
|
||||
return length - stream_get_length(s);
|
||||
}
|
||||
|
||||
static void fastpath_recv_orders(rdpFastPath* fastpath, STREAM* s)
|
||||
@@ -485,19 +474,48 @@ boolean fastpath_recv_inputs(rdpFastPath* fastpath, STREAM* s)
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32 fastpath_get_sec_bytes(rdpRdp* rdp)
|
||||
{
|
||||
uint32 sec_bytes;
|
||||
|
||||
if (rdp->do_crypt)
|
||||
{
|
||||
sec_bytes = 8;
|
||||
if (rdp->settings->encryption_method == ENCRYPTION_METHOD_FIPS)
|
||||
sec_bytes += 4;
|
||||
}
|
||||
else
|
||||
sec_bytes = 0;
|
||||
return sec_bytes;
|
||||
}
|
||||
|
||||
STREAM* fastpath_input_pdu_init(rdpFastPath* fastpath, uint8 eventFlags, uint8 eventCode)
|
||||
{
|
||||
rdpRdp *rdp;
|
||||
STREAM* s;
|
||||
s = transport_send_stream_init(fastpath->rdp->transport, 127);
|
||||
stream_seek(s, 2); /* fpInputHeader and length1 */
|
||||
/* length2 is not necessary since input PDU should not exceed 127 bytes */
|
||||
|
||||
rdp = fastpath->rdp;
|
||||
|
||||
s = transport_send_stream_init(rdp->transport, 256);
|
||||
stream_seek(s, 3); /* fpInputHeader, length1 and length2 */
|
||||
if (rdp->do_crypt) {
|
||||
rdp->sec_flags |= SEC_ENCRYPT;
|
||||
if (rdp->do_secure_checksum)
|
||||
rdp->sec_flags |= SEC_SECURE_CHECKSUM;
|
||||
}
|
||||
stream_seek(s, fastpath_get_sec_bytes(rdp));
|
||||
stream_write_uint8(s, eventFlags | (eventCode << 5)); /* eventHeader (1 byte) */
|
||||
return s;
|
||||
}
|
||||
|
||||
boolean fastpath_send_input_pdu(rdpFastPath* fastpath, STREAM* s)
|
||||
{
|
||||
rdpRdp *rdp;
|
||||
uint16 length;
|
||||
uint8 eventHeader;
|
||||
int sec_bytes;
|
||||
|
||||
rdp = fastpath->rdp;
|
||||
|
||||
length = stream_get_length(s);
|
||||
if (length > 127)
|
||||
@@ -506,11 +524,39 @@ boolean fastpath_send_input_pdu(rdpFastPath* fastpath, STREAM* s)
|
||||
return false;
|
||||
}
|
||||
|
||||
stream_set_pos(s, 0);
|
||||
stream_write_uint8(s, (1 << 2));
|
||||
stream_write_uint8(s, length);
|
||||
eventHeader = FASTPATH_INPUT_ACTION_FASTPATH;
|
||||
eventHeader |= (1 << 2); /* numberEvents */
|
||||
if (rdp->sec_flags & SEC_ENCRYPT)
|
||||
eventHeader |= (FASTPATH_INPUT_ENCRYPTED << 6);
|
||||
if (rdp->sec_flags & SEC_SECURE_CHECKSUM)
|
||||
eventHeader |= (FASTPATH_INPUT_SECURE_CHECKSUM << 6);
|
||||
|
||||
stream_set_pos(s, length);
|
||||
stream_set_pos(s, 0);
|
||||
stream_write_uint8(s, eventHeader);
|
||||
sec_bytes = fastpath_get_sec_bytes(fastpath->rdp);
|
||||
/*
|
||||
* We always encode length in two bytes, eventhough we could use
|
||||
* only one byte if length <= 0x7F. It is just easier that way,
|
||||
* because we can leave room for fixed-length header, store all
|
||||
* the data first and then store the header.
|
||||
*/
|
||||
stream_write_uint16_be(s, 0x8000 | (length + sec_bytes));
|
||||
|
||||
if (sec_bytes > 0)
|
||||
{
|
||||
uint8* ptr;
|
||||
|
||||
ptr = stream_get_tail(s) + sec_bytes;
|
||||
if (rdp->sec_flags & SEC_SECURE_CHECKSUM)
|
||||
security_salted_mac_signature(rdp, ptr, length - 3, true, stream_get_tail(s));
|
||||
else
|
||||
security_mac_signature(rdp, ptr, length - 3, stream_get_tail(s));
|
||||
security_encrypt(ptr, length - 3, rdp);
|
||||
}
|
||||
|
||||
rdp->sec_flags = 0;
|
||||
|
||||
stream_set_pos(s, length + sec_bytes);
|
||||
if (transport_write(fastpath->rdp->transport, s) < 0)
|
||||
return false;
|
||||
|
||||
@@ -522,26 +568,33 @@ STREAM* fastpath_update_pdu_init(rdpFastPath* fastpath)
|
||||
STREAM* s;
|
||||
s = transport_send_stream_init(fastpath->rdp->transport, FASTPATH_MAX_PACKET_SIZE);
|
||||
stream_seek(s, 3); /* fpOutputHeader, length1 and length2 */
|
||||
stream_seek(s, fastpath_get_sec_bytes(fastpath->rdp));
|
||||
stream_seek(s, 3); /* updateHeader, size */
|
||||
return s;
|
||||
}
|
||||
|
||||
boolean fastpath_send_update_pdu(rdpFastPath* fastpath, uint8 updateCode, STREAM* s)
|
||||
{
|
||||
rdpRdp *rdp;
|
||||
uint8* bm;
|
||||
uint8* ptr;
|
||||
int fragment;
|
||||
int sec_bytes;
|
||||
uint16 length;
|
||||
boolean result;
|
||||
uint16 pduLength;
|
||||
uint16 maxLength;
|
||||
uint32 totalLength;
|
||||
uint8 fragmentation;
|
||||
uint8 header;
|
||||
STREAM* update;
|
||||
|
||||
result = true;
|
||||
|
||||
maxLength = FASTPATH_MAX_PACKET_SIZE - 6;
|
||||
totalLength = stream_get_length(s) - 6;
|
||||
rdp = fastpath->rdp;
|
||||
sec_bytes = fastpath_get_sec_bytes(rdp);
|
||||
maxLength = FASTPATH_MAX_PACKET_SIZE - 6 - sec_bytes;
|
||||
totalLength = stream_get_length(s) - 6 - sec_bytes;
|
||||
stream_set_pos(s, 0);
|
||||
update = stream_new(0);
|
||||
|
||||
@@ -549,7 +602,7 @@ boolean fastpath_send_update_pdu(rdpFastPath* fastpath, uint8 updateCode, STREAM
|
||||
{
|
||||
length = MIN(maxLength, totalLength);
|
||||
totalLength -= length;
|
||||
pduLength = length + 6;
|
||||
pduLength = length + 6 + sec_bytes;
|
||||
|
||||
if (totalLength == 0)
|
||||
fragmentation = (fragment == 0) ? FASTPATH_FRAGMENT_SINGLE : FASTPATH_FRAGMENT_LAST;
|
||||
@@ -557,14 +610,28 @@ boolean fastpath_send_update_pdu(rdpFastPath* fastpath, uint8 updateCode, STREAM
|
||||
fragmentation = (fragment == 0) ? FASTPATH_FRAGMENT_FIRST : FASTPATH_FRAGMENT_NEXT;
|
||||
|
||||
stream_get_mark(s, bm);
|
||||
stream_write_uint8(s, 0); /* fpOutputHeader (1 byte) */
|
||||
header = 0;
|
||||
if (sec_bytes > 0)
|
||||
header |= (FASTPATH_OUTPUT_ENCRYPTED << 6);
|
||||
stream_write_uint8(s, header); /* fpOutputHeader (1 byte) */
|
||||
stream_write_uint8(s, 0x80 | (pduLength >> 8)); /* length1 */
|
||||
stream_write_uint8(s, pduLength & 0xFF); /* length2 */
|
||||
if (sec_bytes > 0)
|
||||
stream_seek(s, sec_bytes);
|
||||
fastpath_write_update_header(s, updateCode, fragmentation, 0);
|
||||
stream_write_uint16(s, length);
|
||||
|
||||
stream_attach(update, bm, pduLength);
|
||||
stream_seek(update, pduLength);
|
||||
if (sec_bytes > 0)
|
||||
{
|
||||
ptr = bm + 3 + sec_bytes;
|
||||
if (rdp->sec_flags & SEC_SECURE_CHECKSUM)
|
||||
security_salted_mac_signature(rdp, ptr, length + 3, true, bm + 3);
|
||||
else
|
||||
security_mac_signature(rdp, ptr, length + 3, bm + 3);
|
||||
security_encrypt(ptr, length + 3, rdp);
|
||||
}
|
||||
if (transport_write(fastpath->rdp->transport, update) < 0)
|
||||
{
|
||||
stream_detach(update);
|
||||
@@ -573,8 +640,8 @@ boolean fastpath_send_update_pdu(rdpFastPath* fastpath, uint8 updateCode, STREAM
|
||||
}
|
||||
stream_detach(update);
|
||||
|
||||
/* Reserve 6 bytes for the next fragment header, if any. */
|
||||
stream_seek(s, length - 6);
|
||||
/* Reserve 6+sec_bytes bytes for the next fragment header, if any. */
|
||||
stream_seek(s, length - 6 - sec_bytes);
|
||||
}
|
||||
|
||||
stream_free(update);
|
||||
|
||||
@@ -25,12 +25,24 @@
|
||||
|
||||
typedef struct rdp_fastpath rdpFastPath;
|
||||
|
||||
enum FASTPATH_INPUT_ACTION_TYPE
|
||||
{
|
||||
FASTPATH_INPUT_ACTION_FASTPATH = 0x0,
|
||||
FASTPATH_INPUT_ACTION_X224 = 0x3
|
||||
};
|
||||
|
||||
enum FASTPATH_OUTPUT_ACTION_TYPE
|
||||
{
|
||||
FASTPATH_OUTPUT_ACTION_FASTPATH = 0x0,
|
||||
FASTPATH_OUTPUT_ACTION_X224 = 0x3
|
||||
};
|
||||
|
||||
enum FASTPATH_INPUT_ENCRYPTION_FLAGS
|
||||
{
|
||||
FASTPATH_INPUT_SECURE_CHECKSUM = 0x1,
|
||||
FASTPATH_INPUT_ENCRYPTED = 0x2
|
||||
};
|
||||
|
||||
enum FASTPATH_OUTPUT_ENCRYPTION_FLAGS
|
||||
{
|
||||
FASTPATH_OUTPUT_SECURE_CHECKSUM = 0x1,
|
||||
|
||||
@@ -407,6 +407,7 @@ boolean gcc_read_server_data_blocks(STREAM* s, rdpSettings* settings, int length
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("gcc_read_server_data_blocks: ignoring type=%hu\n", type);
|
||||
break;
|
||||
}
|
||||
offset += blockLength;
|
||||
@@ -740,10 +741,16 @@ boolean gcc_read_client_security_data(STREAM* s, rdpSettings *settings, uint16 b
|
||||
if (blockLength < 8)
|
||||
return false;
|
||||
|
||||
stream_read_uint32(s, settings->encryption_method); /* encryptionMethods */
|
||||
if (settings->encryption_method == 0)
|
||||
stream_read_uint32(s, settings->encryption_method); /* extEncryptionMethods */
|
||||
|
||||
if (settings->encryption)
|
||||
{
|
||||
stream_read_uint32(s, settings->encryption_method); /* encryptionMethods */
|
||||
if (settings->encryption_method == 0)
|
||||
stream_read_uint32(s, settings->encryption_method); /* extEncryptionMethods */
|
||||
}
|
||||
else
|
||||
{
|
||||
stream_seek(s, 8);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -825,16 +832,129 @@ boolean gcc_read_server_security_data(STREAM* s, rdpSettings *settings)
|
||||
return true;
|
||||
}
|
||||
|
||||
static const uint8 initial_signature[] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01
|
||||
};
|
||||
|
||||
void gcc_write_server_security_data(STREAM* s, rdpSettings *settings)
|
||||
{
|
||||
gcc_write_user_data_header(s, SC_SECURITY, 12);
|
||||
uint32 headerLen, serverRandomLen, serverCertLen, wPublicKeyBlobLen;
|
||||
uint8 signature[sizeof(initial_signature)];
|
||||
uint8 encryptedSignature[TSSK_KEY_LENGTH];
|
||||
uint8* sigData;
|
||||
int expLen, keyLen, sigDataLen;
|
||||
|
||||
stream_write_uint32(s, ENCRYPTION_METHOD_NONE); /* encryptionMethod */
|
||||
stream_write_uint32(s, ENCRYPTION_LEVEL_NONE); /* encryptionLevel */
|
||||
#if 0
|
||||
stream_write_uint32(s, 0); /* serverRandomLen */
|
||||
stream_write_uint32(s, 0); /* serverCertLen */
|
||||
#endif
|
||||
if (!settings->encryption) {
|
||||
settings->encryption_method = ENCRYPTION_METHOD_NONE;
|
||||
settings->encryption_level = ENCRYPTION_LEVEL_NONE;
|
||||
}
|
||||
else if ((settings->encryption_method & ENCRYPTION_METHOD_FIPS) != 0)
|
||||
{
|
||||
settings->encryption_method = ENCRYPTION_METHOD_FIPS;
|
||||
}
|
||||
else if ((settings->encryption_method & ENCRYPTION_METHOD_128BIT) != 0)
|
||||
{
|
||||
settings->encryption_method = ENCRYPTION_METHOD_128BIT;
|
||||
}
|
||||
else if ((settings->encryption_method & ENCRYPTION_METHOD_40BIT) != 0)
|
||||
{
|
||||
settings->encryption_method = ENCRYPTION_METHOD_40BIT;
|
||||
}
|
||||
|
||||
if (settings->encryption_method != ENCRYPTION_METHOD_NONE)
|
||||
settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE;
|
||||
|
||||
headerLen = 12;
|
||||
keyLen = 0;
|
||||
wPublicKeyBlobLen = 0;
|
||||
serverRandomLen = 0;
|
||||
serverCertLen = 0;
|
||||
|
||||
if (settings->encryption_method != ENCRYPTION_METHOD_NONE ||
|
||||
settings->encryption_level != ENCRYPTION_LEVEL_NONE) {
|
||||
serverRandomLen = 32;
|
||||
|
||||
keyLen = settings->server_key->modulus.length;
|
||||
expLen = sizeof(settings->server_key->exponent);
|
||||
wPublicKeyBlobLen = 4; /* magic (RSA1) */
|
||||
wPublicKeyBlobLen += 4; /* keylen */
|
||||
wPublicKeyBlobLen += 4; /* bitlen */
|
||||
wPublicKeyBlobLen += 4; /* datalen */
|
||||
wPublicKeyBlobLen += expLen;
|
||||
wPublicKeyBlobLen += keyLen;
|
||||
wPublicKeyBlobLen += 8; /* 8 bytes of zero padding */
|
||||
|
||||
serverCertLen = 4; /* dwVersion */
|
||||
serverCertLen += 4; /* dwSigAlgId */
|
||||
serverCertLen += 4; /* dwKeyAlgId */
|
||||
serverCertLen += 2; /* wPublicKeyBlobType */
|
||||
serverCertLen += 2; /* wPublicKeyBlobLen */
|
||||
serverCertLen += wPublicKeyBlobLen;
|
||||
serverCertLen += 2; /* wSignatureBlobType */
|
||||
serverCertLen += 2; /* wSignatureBlobLen */
|
||||
serverCertLen += sizeof(encryptedSignature); /* SignatureBlob */
|
||||
serverCertLen += 8; /* 8 bytes of zero padding */
|
||||
|
||||
headerLen += sizeof(serverRandomLen);
|
||||
headerLen += sizeof(serverCertLen);
|
||||
headerLen += serverRandomLen;
|
||||
headerLen += serverCertLen;
|
||||
}
|
||||
|
||||
gcc_write_user_data_header(s, SC_SECURITY, headerLen);
|
||||
|
||||
stream_write_uint32(s, settings->encryption_method); /* encryptionMethod */
|
||||
stream_write_uint32(s, settings->encryption_level); /* encryptionLevel */
|
||||
if (settings->encryption_method == ENCRYPTION_METHOD_NONE &&
|
||||
settings->encryption_level == ENCRYPTION_LEVEL_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream_write_uint32(s, serverRandomLen); /* serverRandomLen */
|
||||
stream_write_uint32(s, serverCertLen); /* serverCertLen */
|
||||
|
||||
freerdp_blob_alloc(settings->server_random, serverRandomLen);
|
||||
crypto_nonce(settings->server_random->data, serverRandomLen);
|
||||
stream_write(s, settings->server_random->data, serverRandomLen);
|
||||
|
||||
sigData = stream_get_tail(s);
|
||||
|
||||
stream_write_uint32(s, CERT_CHAIN_VERSION_1); /* dwVersion (4 bytes) */
|
||||
stream_write_uint32(s, SIGNATURE_ALG_RSA); /* dwSigAlgId */
|
||||
stream_write_uint32(s, KEY_EXCHANGE_ALG_RSA); /* dwKeyAlgId */
|
||||
stream_write_uint16(s, BB_RSA_KEY_BLOB); /* wPublicKeyBlobType */
|
||||
|
||||
stream_write_uint16(s, wPublicKeyBlobLen); /* wPublicKeyBlobLen */
|
||||
stream_write(s, "RSA1", 4); /* magic */
|
||||
stream_write_uint32(s, keyLen + 8); /* keylen */
|
||||
stream_write_uint32(s, keyLen * 8); /* bitlen */
|
||||
stream_write_uint32(s, keyLen - 1); /* datalen */
|
||||
|
||||
stream_write(s, settings->server_key->exponent, expLen);
|
||||
stream_write(s, settings->server_key->modulus.data, keyLen);
|
||||
stream_write_zero(s, 8);
|
||||
|
||||
sigDataLen = stream_get_tail(s) - sigData;
|
||||
|
||||
stream_write_uint16(s, BB_RSA_SIGNATURE_BLOB); /* wSignatureBlobType */
|
||||
stream_write_uint16(s, keyLen + 8); /* wSignatureBlobLen */
|
||||
|
||||
memcpy(signature, initial_signature, sizeof(initial_signature));
|
||||
CryptoMd5 md5Ctx;
|
||||
md5Ctx = crypto_md5_init();
|
||||
crypto_md5_update(md5Ctx, sigData, sigDataLen);
|
||||
crypto_md5_final(md5Ctx, signature);
|
||||
|
||||
crypto_rsa_private_encrypt(signature, sizeof(signature), TSSK_KEY_LENGTH, tssk_modulus, tssk_privateExponent, encryptedSignature);
|
||||
stream_write(s, encryptedSignature, sizeof(encryptedSignature));
|
||||
stream_write_zero(s, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -564,15 +564,32 @@ boolean rdp_recv_client_info(rdpRdp* rdp, STREAM* s)
|
||||
{
|
||||
uint16 length;
|
||||
uint16 channelId;
|
||||
uint16 sec_flags;
|
||||
uint16 securityFlags;
|
||||
|
||||
if (!rdp_read_header(rdp, s, &length, &channelId))
|
||||
return false;
|
||||
|
||||
rdp_read_security_header(s, &sec_flags);
|
||||
if ((sec_flags & SEC_INFO_PKT) == 0)
|
||||
rdp_read_security_header(s, &securityFlags);
|
||||
if ((securityFlags & SEC_INFO_PKT) == 0)
|
||||
return false;
|
||||
|
||||
if (rdp->settings->encryption)
|
||||
{
|
||||
if (securityFlags & SEC_REDIRECTION_PKT)
|
||||
{
|
||||
printf("Error: SEC_REDIRECTION_PKT unsupported\n");
|
||||
return false;
|
||||
}
|
||||
if (securityFlags & SEC_ENCRYPT)
|
||||
{
|
||||
if (!rdp_decrypt(rdp, s, length - 4, securityFlags))
|
||||
{
|
||||
printf("rdp_decrypt failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rdp_read_info_packet(s, rdp->settings);
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ STREAM* license_send_stream_init(rdpLicense* license)
|
||||
{
|
||||
STREAM* s;
|
||||
s = transport_send_stream_init(license->rdp->transport, 4096);
|
||||
stream_seek(s, LICENSE_PACKET_HEADER_LENGTH);
|
||||
stream_seek(s, LICENSE_PACKET_HEADER_MAX_LENGTH);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ boolean license_send(rdpLicense* license, STREAM* s, uint8 type)
|
||||
stream_set_pos(s, 0);
|
||||
|
||||
sec_flags = SEC_LICENSE_PKT;
|
||||
wMsgSize = length - LICENSE_PACKET_HEADER_LENGTH + 4;
|
||||
wMsgSize = length - LICENSE_PACKET_HEADER_MAX_LENGTH + 4;
|
||||
/**
|
||||
* Using EXTENDED_ERROR_MSG_SUPPORTED here would cause mstsc to crash when
|
||||
* running in server mode! This flag seems to be incorrectly documented.
|
||||
|
||||
@@ -44,8 +44,8 @@ typedef struct rdp_license rdpLicense;
|
||||
#define LICENSE_PKT_SC_MASK (LICENSE_REQUEST | PLATFORM_CHALLENGE | NEW_LICENSE | UPGRADE_LICENSE | ERROR_ALERT)
|
||||
#define LICENSE_PKT_MASK (LICENSE_PKT_CS_MASK | LICENSE_PKT_SC_MASK)
|
||||
|
||||
#define LICENSE_PREAMBLE_LENGTH 4
|
||||
#define LICENSE_PACKET_HEADER_LENGTH (RDP_PACKET_HEADER_LENGTH + RDP_SECURITY_HEADER_LENGTH + LICENSE_PREAMBLE_LENGTH)
|
||||
#define LICENSE_PREAMBLE_LENGTH 4
|
||||
#define LICENSE_PACKET_HEADER_MAX_LENGTH (RDP_PACKET_HEADER_MAX_LENGTH + RDP_SECURITY_HEADER_LENGTH + LICENSE_PREAMBLE_LENGTH)
|
||||
|
||||
/* Cryptographic Lengths */
|
||||
#define CLIENT_RANDOM_LENGTH 32
|
||||
|
||||
@@ -124,7 +124,7 @@ struct rdp_mcs
|
||||
};
|
||||
typedef struct rdp_mcs rdpMcs;
|
||||
|
||||
#define MCS_SEND_DATA_HEADER_LENGTH 8
|
||||
#define MCS_SEND_DATA_HEADER_MAX_LENGTH 8
|
||||
|
||||
#define MCS_TYPE_CONNECT_INITIAL 0x65
|
||||
#define MCS_TYPE_CONNECT_RESPONSE 0x66
|
||||
|
||||
@@ -521,8 +521,13 @@ void nego_process_negotiation_failure(rdpNego* nego, STREAM* s)
|
||||
boolean nego_send_negotiation_response(rdpNego* nego)
|
||||
{
|
||||
STREAM* s;
|
||||
rdpSettings* settings;
|
||||
int length;
|
||||
uint8 *bm, *em;
|
||||
boolean ret;
|
||||
|
||||
ret = true;
|
||||
settings = nego->transport->settings;
|
||||
|
||||
s = transport_send_stream_init(nego->transport, 256);
|
||||
length = TPDU_CONNECTION_CONFIRM_LENGTH;
|
||||
@@ -538,6 +543,20 @@ boolean nego_send_negotiation_response(rdpNego* nego)
|
||||
stream_write_uint32(s, nego->selected_protocol); /* selectedProtocol */
|
||||
length += 8;
|
||||
}
|
||||
else if (!settings->rdp_security)
|
||||
{
|
||||
stream_write_uint8(s, TYPE_RDP_NEG_FAILURE);
|
||||
stream_write_uint8(s, 0); /* flags */
|
||||
stream_write_uint16(s, 8); /* RDP_NEG_DATA length (8) */
|
||||
/*
|
||||
* TODO: Check for other possibilities,
|
||||
* like SSL_NOT_ALLOWED_BY_SERVER.
|
||||
*/
|
||||
printf("nego_send_negotiation_response: client supports only Standard RDP Security\n");
|
||||
stream_write_uint32(s, SSL_REQUIRED_BY_SERVER);
|
||||
length += 8;
|
||||
ret = false;
|
||||
}
|
||||
|
||||
stream_get_mark(s, em);
|
||||
stream_set_mark(s, bm);
|
||||
@@ -548,11 +567,42 @@ boolean nego_send_negotiation_response(rdpNego* nego)
|
||||
if (transport_write(nego->transport, s) < 0)
|
||||
return false;
|
||||
|
||||
/* update settings with negotiated protocol security */
|
||||
nego->transport->settings->requested_protocols = nego->requested_protocols;
|
||||
nego->transport->settings->selected_protocol = nego->selected_protocol;
|
||||
if (ret)
|
||||
{
|
||||
/* update settings with negotiated protocol security */
|
||||
settings->requested_protocols = nego->requested_protocols;
|
||||
settings->selected_protocol = nego->selected_protocol;
|
||||
|
||||
return true;
|
||||
if (settings->selected_protocol == PROTOCOL_RDP)
|
||||
{
|
||||
settings->tls_security = false;
|
||||
settings->nla_security = false;
|
||||
settings->rdp_security = true;
|
||||
settings->encryption = true;
|
||||
settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS;
|
||||
settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE;
|
||||
}
|
||||
else if (settings->selected_protocol == PROTOCOL_TLS)
|
||||
{
|
||||
settings->tls_security = true;
|
||||
settings->nla_security = false;
|
||||
settings->rdp_security = false;
|
||||
settings->encryption = false;
|
||||
settings->encryption_method = ENCRYPTION_METHOD_NONE;
|
||||
settings->encryption_level = ENCRYPTION_LEVEL_NONE;
|
||||
}
|
||||
else if (settings->selected_protocol == PROTOCOL_NLA)
|
||||
{
|
||||
settings->tls_security = true;
|
||||
settings->nla_security = true;
|
||||
settings->rdp_security = false;
|
||||
settings->encryption = false;
|
||||
settings->encryption_method = ENCRYPTION_METHOD_NONE;
|
||||
settings->encryption_level = ENCRYPTION_LEVEL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,10 @@ static boolean freerdp_peer_initialize(freerdp_peer* client)
|
||||
{
|
||||
client->context->rdp->settings->server_mode = true;
|
||||
client->context->rdp->state = CONNECTION_STATE_INITIAL;
|
||||
if (client->context->rdp->settings->rdp_key_file != NULL) {
|
||||
client->context->rdp->settings->server_key =
|
||||
key_new(client->context->rdp->settings->rdp_key_file);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -112,18 +116,35 @@ static boolean peer_recv_data_pdu(freerdp_peer* client, STREAM* s)
|
||||
|
||||
static boolean peer_recv_tpkt_pdu(freerdp_peer* client, STREAM* s)
|
||||
{
|
||||
rdpRdp *rdp;
|
||||
uint16 length;
|
||||
uint16 pduType;
|
||||
uint16 pduLength;
|
||||
uint16 pduSource;
|
||||
uint16 channelId;
|
||||
uint16 securityFlags;
|
||||
|
||||
if (!rdp_read_header(client->context->rdp, s, &length, &channelId))
|
||||
rdp = client->context->rdp;
|
||||
|
||||
if (!rdp_read_header(rdp, s, &length, &channelId))
|
||||
{
|
||||
printf("Incorrect RDP header.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rdp->settings->encryption)
|
||||
{
|
||||
rdp_read_security_header(s, &securityFlags);
|
||||
if (securityFlags & SEC_ENCRYPT)
|
||||
{
|
||||
if (!rdp_decrypt(rdp, s, length - 4, securityFlags))
|
||||
{
|
||||
printf("rdp_decrypt failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channelId != MCS_GLOBAL_CHANNEL_ID)
|
||||
{
|
||||
freerdp_channel_peer_process(client, s, channelId);
|
||||
@@ -169,7 +190,7 @@ static boolean peer_recv_fastpath_pdu(freerdp_peer* client, STREAM* s)
|
||||
|
||||
if (fastpath->encryptionFlags & FASTPATH_OUTPUT_ENCRYPTED)
|
||||
{
|
||||
rdp_decrypt(rdp, s, length);
|
||||
rdp_decrypt(rdp, s, length, (fastpath->encryptionFlags & FASTPATH_OUTPUT_SECURE_CHECKSUM) ? SEC_SECURE_CHECKSUM : 0);
|
||||
}
|
||||
|
||||
return fastpath_recv_inputs(fastpath, s);
|
||||
@@ -215,6 +236,15 @@ static boolean peer_recv_callback(rdpTransport* transport, STREAM* s, void* extr
|
||||
break;
|
||||
|
||||
case CONNECTION_STATE_MCS_CHANNEL_JOIN:
|
||||
if (client->context->rdp->settings->encryption) {
|
||||
if (!rdp_server_accept_client_keys(client->context->rdp, s))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
client->context->rdp->state = CONNECTION_STATE_ESTABLISH_KEYS;
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case CONNECTION_STATE_ESTABLISH_KEYS:
|
||||
if (!rdp_server_accept_client_info(client->context->rdp, s))
|
||||
return false;
|
||||
IFCALL(client->Capabilities, client);
|
||||
|
||||
@@ -105,7 +105,7 @@ boolean rdp_read_share_control_header(STREAM* s, uint16* length, uint16* type, u
|
||||
|
||||
void rdp_write_share_control_header(STREAM* s, uint16 length, uint16 type, uint16 channel_id)
|
||||
{
|
||||
length -= RDP_PACKET_HEADER_LENGTH;
|
||||
length -= RDP_PACKET_HEADER_MAX_LENGTH;
|
||||
|
||||
/* Share Control Header */
|
||||
stream_write_uint16(s, length); /* totalLength */
|
||||
@@ -113,7 +113,7 @@ void rdp_write_share_control_header(STREAM* s, uint16 length, uint16 type, uint1
|
||||
stream_write_uint16(s, channel_id); /* pduSource */
|
||||
}
|
||||
|
||||
boolean rdp_read_share_data_header(STREAM* s, uint16* length, uint8* type, uint32* share_id,
|
||||
boolean rdp_read_share_data_header(STREAM* s, uint16* length, uint8* type, uint32* share_id,
|
||||
uint8 *compressed_type, uint16 *compressed_len)
|
||||
{
|
||||
if (stream_get_left(s) < 12)
|
||||
@@ -126,7 +126,7 @@ boolean rdp_read_share_data_header(STREAM* s, uint16* length, uint8* type, uint3
|
||||
stream_read_uint16(s, *length); /* uncompressedLength (2 bytes) */
|
||||
stream_read_uint8(s, *type); /* pduType2, Data PDU Type (1 byte) */
|
||||
|
||||
if (*type & 0x80)
|
||||
if (*type & 0x80)
|
||||
{
|
||||
stream_read_uint8(s, *compressed_type); /* compressedType (1 byte) */
|
||||
stream_read_uint16(s, *compressed_len); /* compressedLength (2 bytes) */
|
||||
@@ -143,7 +143,7 @@ boolean rdp_read_share_data_header(STREAM* s, uint16* length, uint8* type, uint3
|
||||
|
||||
void rdp_write_share_data_header(STREAM* s, uint16 length, uint8 type, uint32 share_id)
|
||||
{
|
||||
length -= RDP_PACKET_HEADER_LENGTH;
|
||||
length -= RDP_PACKET_HEADER_MAX_LENGTH;
|
||||
length -= RDP_SHARE_CONTROL_HEADER_LENGTH;
|
||||
length -= RDP_SHARE_DATA_HEADER_LENGTH;
|
||||
|
||||
@@ -165,6 +165,8 @@ static int rdp_security_stream_init(rdpRdp* rdp, STREAM* s)
|
||||
if (rdp->settings->encryption_method == ENCRYPTION_METHOD_FIPS)
|
||||
stream_seek(s, 4);
|
||||
rdp->sec_flags |= SEC_ENCRYPT;
|
||||
if (rdp->do_secure_checksum)
|
||||
rdp->sec_flags |= SEC_SECURE_CHECKSUM;
|
||||
}
|
||||
else if (rdp->sec_flags != 0)
|
||||
{
|
||||
@@ -184,7 +186,7 @@ STREAM* rdp_send_stream_init(rdpRdp* rdp)
|
||||
STREAM* s;
|
||||
|
||||
s = transport_send_stream_init(rdp->transport, 2048);
|
||||
stream_seek(s, RDP_PACKET_HEADER_LENGTH);
|
||||
stream_seek(s, RDP_PACKET_HEADER_MAX_LENGTH);
|
||||
rdp_security_stream_init(rdp, s);
|
||||
|
||||
return s;
|
||||
@@ -194,7 +196,7 @@ STREAM* rdp_pdu_init(rdpRdp* rdp)
|
||||
{
|
||||
STREAM* s;
|
||||
s = transport_send_stream_init(rdp->transport, 2048);
|
||||
stream_seek(s, RDP_PACKET_HEADER_LENGTH);
|
||||
stream_seek(s, RDP_PACKET_HEADER_MAX_LENGTH);
|
||||
rdp_security_stream_init(rdp, s);
|
||||
stream_seek(s, RDP_SHARE_CONTROL_HEADER_LENGTH);
|
||||
return s;
|
||||
@@ -204,7 +206,7 @@ STREAM* rdp_data_pdu_init(rdpRdp* rdp)
|
||||
{
|
||||
STREAM* s;
|
||||
s = transport_send_stream_init(rdp->transport, 2048);
|
||||
stream_seek(s, RDP_PACKET_HEADER_LENGTH);
|
||||
stream_seek(s, RDP_PACKET_HEADER_MAX_LENGTH);
|
||||
rdp_security_stream_init(rdp, s);
|
||||
stream_seek(s, RDP_SHARE_CONTROL_HEADER_LENGTH);
|
||||
stream_seek(s, RDP_SHARE_DATA_HEADER_LENGTH);
|
||||
@@ -257,7 +259,7 @@ void rdp_write_header(rdpRdp* rdp, STREAM* s, uint16 length, uint16 channel_id)
|
||||
{
|
||||
int pad;
|
||||
|
||||
body_length = length - RDP_PACKET_HEADER_LENGTH - 16;
|
||||
body_length = length - RDP_PACKET_HEADER_MAX_LENGTH - 16;
|
||||
pad = 8 - (body_length % 8);
|
||||
if (pad != 8)
|
||||
length += pad;
|
||||
@@ -267,8 +269,13 @@ void rdp_write_header(rdpRdp* rdp, STREAM* s, uint16 length, uint16 channel_id)
|
||||
per_write_integer16(s, rdp->mcs->user_id, MCS_BASE_CHANNEL_ID); /* initiator */
|
||||
per_write_integer16(s, channel_id, 0); /* channelId */
|
||||
stream_write_uint8(s, 0x70); /* dataPriority + segmentation */
|
||||
|
||||
length = (length - RDP_PACKET_HEADER_LENGTH) | 0x8000;
|
||||
/*
|
||||
* We always encode length in two bytes, eventhough we could use
|
||||
* only one byte if length <= 0x7F. It is just easier that way,
|
||||
* because we can leave room for fixed-length header, store all
|
||||
* the data first and then store the header.
|
||||
*/
|
||||
length = (length - RDP_PACKET_HEADER_MAX_LENGTH) | 0x8000;
|
||||
stream_write_uint16_be(s, length); /* userData (OCTET_STRING) */
|
||||
}
|
||||
|
||||
@@ -312,7 +319,10 @@ static uint32 rdp_security_stream_out(rdpRdp* rdp, STREAM* s, int length)
|
||||
{
|
||||
data = s->p + 8;
|
||||
length = length - (data - s->data);
|
||||
security_mac_signature(rdp, data, length, s->p);
|
||||
if (sec_flags & SEC_SECURE_CHECKSUM)
|
||||
security_salted_mac_signature(rdp, data, length, true, s->p);
|
||||
else
|
||||
security_mac_signature(rdp, data, length, s->p);
|
||||
stream_seek(s, 8);
|
||||
security_encrypt(s->p, length, rdp);
|
||||
}
|
||||
@@ -371,7 +381,7 @@ boolean rdp_send(rdpRdp* rdp, STREAM* s, uint16 channel_id)
|
||||
|
||||
s->p = sec_hold;
|
||||
length += rdp_security_stream_out(rdp, s, length);
|
||||
|
||||
|
||||
stream_set_pos(s, length);
|
||||
if (transport_write(rdp->transport, s) < 0)
|
||||
return false;
|
||||
@@ -575,7 +585,7 @@ boolean rdp_recv_out_of_sequence_pdu(rdpRdp* rdp, STREAM* s)
|
||||
* @param length int
|
||||
*/
|
||||
|
||||
boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length)
|
||||
boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length, uint16 securityFlags)
|
||||
{
|
||||
uint8 cmac[8], wmac[8];
|
||||
|
||||
@@ -614,10 +624,21 @@ boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length)
|
||||
stream_read(s, wmac, sizeof(wmac));
|
||||
length -= sizeof(wmac);
|
||||
security_decrypt(s->p, length, rdp);
|
||||
security_mac_signature(rdp, s->p, length, cmac);
|
||||
if (memcmp(wmac, cmac, sizeof(wmac)) != 0) {
|
||||
printf("FATAL: invalid packet signature\n");
|
||||
return false;
|
||||
if (securityFlags & SEC_SECURE_CHECKSUM)
|
||||
security_salted_mac_signature(rdp, s->p, length, false, cmac);
|
||||
else
|
||||
security_mac_signature(rdp, s->p, length, cmac);
|
||||
if (memcmp(wmac, cmac, sizeof(wmac)) != 0)
|
||||
{
|
||||
printf("WARNING: invalid packet signature\n");
|
||||
/*
|
||||
* Because Standard RDP Security is totally broken,
|
||||
* and cannot protect against MITM, don't treat signature
|
||||
* verification failure as critical. This at least enables
|
||||
* us to work with broken RDP clients and servers that
|
||||
* generate invalid signatures.
|
||||
*/
|
||||
//return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -635,7 +656,7 @@ static boolean rdp_recv_tpkt_pdu(rdpRdp* rdp, STREAM* s)
|
||||
uint16 pduLength;
|
||||
uint16 pduSource;
|
||||
uint16 channelId;
|
||||
uint32 securityHeader;
|
||||
uint16 securityFlags;
|
||||
|
||||
if (!rdp_read_header(rdp, s, &length, &channelId))
|
||||
{
|
||||
@@ -645,21 +666,16 @@ static boolean rdp_recv_tpkt_pdu(rdpRdp* rdp, STREAM* s)
|
||||
|
||||
if (rdp->settings->encryption)
|
||||
{
|
||||
stream_read_uint32(s, securityHeader);
|
||||
if (securityHeader & SEC_SECURE_CHECKSUM)
|
||||
rdp_read_security_header(s, &securityFlags);
|
||||
if (securityFlags & (SEC_ENCRYPT|SEC_REDIRECTION_PKT))
|
||||
{
|
||||
printf("Error: TODO\n");
|
||||
return false;
|
||||
}
|
||||
if (securityHeader & (SEC_ENCRYPT|SEC_REDIRECTION_PKT))
|
||||
{
|
||||
if (!rdp_decrypt(rdp, s, length - 4))
|
||||
if (!rdp_decrypt(rdp, s, length - 4, securityFlags))
|
||||
{
|
||||
printf("rdp_decrypt failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (securityHeader & SEC_REDIRECTION_PKT)
|
||||
if (securityFlags & SEC_REDIRECTION_PKT)
|
||||
{
|
||||
/*
|
||||
* [MS-RDPBCGR] 2.2.13.2.1
|
||||
@@ -712,7 +728,7 @@ static boolean rdp_recv_fastpath_pdu(rdpRdp* rdp, STREAM* s)
|
||||
|
||||
fastpath = rdp->fastpath;
|
||||
length = fastpath_read_header_rdp(fastpath, s);
|
||||
|
||||
|
||||
if (length == 0 || length > stream_get_left(s))
|
||||
{
|
||||
printf("incorrect FastPath PDU header length %d\n", length);
|
||||
@@ -721,7 +737,7 @@ static boolean rdp_recv_fastpath_pdu(rdpRdp* rdp, STREAM* s)
|
||||
|
||||
if (fastpath->encryptionFlags & FASTPATH_OUTPUT_ENCRYPTED)
|
||||
{
|
||||
rdp_decrypt(rdp, s, length);
|
||||
rdp_decrypt(rdp, s, length, (fastpath->encryptionFlags & FASTPATH_OUTPUT_SECURE_CHECKSUM) ? SEC_SECURE_CHECKSUM : 0);
|
||||
}
|
||||
|
||||
return fastpath_recv_updates(rdp->fastpath, s);
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
#define RDP_SECURITY_HEADER_LENGTH 4
|
||||
#define RDP_SHARE_CONTROL_HEADER_LENGTH 6
|
||||
#define RDP_SHARE_DATA_HEADER_LENGTH 12
|
||||
#define RDP_PACKET_HEADER_LENGTH (TPDU_DATA_LENGTH + MCS_SEND_DATA_HEADER_LENGTH)
|
||||
#define RDP_PACKET_HEADER_MAX_LENGTH (TPDU_DATA_LENGTH + MCS_SEND_DATA_HEADER_MAX_LENGTH)
|
||||
|
||||
#define PDU_TYPE_DEMAND_ACTIVE 0x1
|
||||
#define PDU_TYPE_CONFIRM_ACTIVE 0x3
|
||||
@@ -143,6 +143,7 @@ struct rdp_rdp
|
||||
struct crypto_hmac_struct* fips_hmac;
|
||||
uint32 sec_flags;
|
||||
boolean do_crypt;
|
||||
boolean do_secure_checksum;
|
||||
uint8 sign_key[16];
|
||||
uint8 decrypt_key[16];
|
||||
uint8 encrypt_key[16];
|
||||
@@ -198,6 +199,6 @@ void rdp_free(rdpRdp* rdp);
|
||||
#define DEBUG_RDP(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length);
|
||||
boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length, uint16 securityFlags);
|
||||
|
||||
#endif /* __RDP_H */
|
||||
|
||||
@@ -264,7 +264,13 @@ void security_salted_mac_signature(rdpRdp *rdp, uint8* data, uint32 length, bool
|
||||
if (encryption)
|
||||
security_uint32_le(use_count_le, rdp->encrypt_use_count);
|
||||
else
|
||||
security_uint32_le(use_count_le, rdp->decrypt_use_count);
|
||||
{
|
||||
/*
|
||||
* We calculate checksum on plain text, so we must have already
|
||||
* decrypt it, which means decrypt_use_count is off by one.
|
||||
*/
|
||||
security_uint32_le(use_count_le, rdp->decrypt_use_count - 1);
|
||||
}
|
||||
|
||||
/* SHA1_Digest = SHA1(MACKeyN + pad1 + length + data) */
|
||||
sha1 = crypto_sha1_init();
|
||||
|
||||
@@ -56,6 +56,7 @@ rdpSettings* settings_new(void* instance)
|
||||
settings->kbd_fn_keys = 0;
|
||||
settings->kbd_layout = 0;
|
||||
settings->encryption = false;
|
||||
settings->secure_checksum = false;
|
||||
settings->port = 3389;
|
||||
settings->desktop_resize = true;
|
||||
|
||||
@@ -197,6 +198,7 @@ void settings_free(rdpSettings* settings)
|
||||
freerdp_blob_free(settings->server_certificate);
|
||||
xfree(settings->server_random);
|
||||
xfree(settings->server_certificate);
|
||||
xfree(settings->rdp_key_file);
|
||||
certificate_free(settings->server_cert);
|
||||
xfree(settings->client_auto_reconnect_cookie);
|
||||
xfree(settings->server_auto_reconnect_cookie);
|
||||
@@ -204,6 +206,7 @@ void settings_free(rdpSettings* settings)
|
||||
xfree(settings->bitmapCacheV2CellInfo);
|
||||
xfree(settings->glyphCache);
|
||||
xfree(settings->fragCache);
|
||||
key_free(settings->server_key);
|
||||
xfree(settings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv,
|
||||
" --ntlm: force NTLM authentication protocol version (1 or 2)\n"
|
||||
" --ignore-certificate: ignore verification of logon certificate\n"
|
||||
" --sec: force protocol security (rdp, tls or nla)\n"
|
||||
" --secure-checksum: use salted checksums with Standard RDP encryption\n"
|
||||
" --version: print version information\n"
|
||||
"\n", argv[0]);
|
||||
return FREERDP_ARGS_PARSE_HELP; //TODO: What is the correct return
|
||||
@@ -600,6 +601,10 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv,
|
||||
}
|
||||
num_extensions++;
|
||||
}
|
||||
else if (strcmp("--secure-checksum", argv[index]) == 0)
|
||||
{
|
||||
settings->secure_checksum = true;
|
||||
}
|
||||
else if (strcmp("--version", argv[index]) == 0)
|
||||
{
|
||||
printf("This is FreeRDP version %s\n", FREERDP_VERSION_FULL);
|
||||
|
||||
Reference in New Issue
Block a user