📄 secure.c
字号:
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Protocol services - RDP encryption and licensing
Copyright (C) Matthew Chapman 1999-2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "rdesktop.h"
#include <string.h>
// TODO: remove dependency on OpenSSL
#include <openssl/rc4.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <openssl/bn.h>
#include <openssl/x509v3.h>
/*
* I believe this is based on SSLv3 with the following differences:
* MAC algorithm (5.2.3.1) uses only 32-bit length in place of seq_num/type/length fields
* MAC algorithm uses SHA1 and MD5 for the two hash functions instead of one or other
* key_block algorithm (6.2.2) uses 'X', 'YY', 'ZZZ' instead of 'A', 'BB', 'CCC'
* key_block partitioning is different (16 bytes each: MAC secret, decrypt key, encrypt key)
* encryption/decryption keys updated every 4096 packets
* See http://wp.netscape.com/eng/ssl3/draft302.txt
*/
/*
* 48-byte transformation used to generate master secret (6.1) and key material (6.2.2).
* Both SHA1 and MD5 algorithms are used.
*/
void
sec_hash_48(uint8 * out, uint8 * in, uint8 * salt1, uint8 * salt2, uint8 salt)
{
uint8 shasig[20];
uint8 pad[4];
SHA_CTX sha;
MD5_CTX md5;
int i;
for (i = 0; i < 3; i++)
{
memset(pad, salt + i, i + 1);
SHA1_Init(&sha);
SHA1_Update(&sha, pad, i + 1);
SHA1_Update(&sha, in, 48);
SHA1_Update(&sha, salt1, 32);
SHA1_Update(&sha, salt2, 32);
SHA1_Final(shasig, &sha);
MD5_Init(&md5);
MD5_Update(&md5, in, 48);
MD5_Update(&md5, shasig, 20);
MD5_Final(&out[i * 16], &md5);
}
}
/*
* 16-byte transformation used to generate export keys (6.2.2).
*/
void
sec_hash_16(uint8 * out, uint8 * in, uint8 * salt1, uint8 * salt2)
{
MD5_CTX md5;
MD5_Init(&md5);
MD5_Update(&md5, in, 16);
MD5_Update(&md5, salt1, 32);
MD5_Update(&md5, salt2, 32);
MD5_Final(out, &md5);
}
/* Reduce key entropy from 64 to 40 bits */
static void
sec_make_40bit(uint8 * key)
{
key[0] = 0xd1;
key[1] = 0x26;
key[2] = 0x9e;
}
/* Generate encryption keys given client and server randoms */
static void
sec_generate_keys(RDPCLIENT * This, uint8 * client_random, uint8 * server_random, int rc4_key_size)
{
uint8 pre_master_secret[48];
uint8 master_secret[48];
uint8 key_block[48];
/* Construct pre-master secret */
memcpy(pre_master_secret, client_random, 24);
memcpy(pre_master_secret + 24, server_random, 24);
/* Generate master secret and then key material */
sec_hash_48(master_secret, pre_master_secret, client_random, server_random, 'A');
sec_hash_48(key_block, master_secret, client_random, server_random, 'X');
/* First 16 bytes of key material is MAC secret */
memcpy(This->secure.sign_key, key_block, 16);
/* Generate export keys from next two blocks of 16 bytes */
sec_hash_16(This->secure.decrypt_key, &key_block[16], client_random, server_random);
sec_hash_16(This->secure.encrypt_key, &key_block[32], client_random, server_random);
if (rc4_key_size == 1)
{
DEBUG(("40-bit encryption enabled\n"));
sec_make_40bit(This->secure.sign_key);
sec_make_40bit(This->secure.decrypt_key);
sec_make_40bit(This->secure.encrypt_key);
This->secure.rc4_key_len = 8;
}
else
{
DEBUG(("rc_4_key_size == %d, 128-bit encryption enabled\n", rc4_key_size));
This->secure.rc4_key_len = 16;
}
/* Save initial RC4 keys as update keys */
memcpy(This->secure.decrypt_update_key, This->secure.decrypt_key, 16);
memcpy(This->secure.encrypt_update_key, This->secure.encrypt_key, 16);
/* Initialise RC4 state arrays */
RC4_set_key(&This->secure.rc4_decrypt_key, This->secure.rc4_key_len, This->secure.decrypt_key);
RC4_set_key(&This->secure.rc4_encrypt_key, This->secure.rc4_key_len, This->secure.encrypt_key);
}
static const uint8 pad_54[40] = {
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54
};
static const uint8 pad_92[48] = {
92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
92, 92, 92, 92, 92, 92, 92,
92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
92, 92, 92, 92, 92, 92, 92
};
/* Output a uint32 into a buffer (little-endian) */
void
buf_out_uint32(uint8 * buffer, uint32 value)
{
buffer[0] = (value) & 0xff;
buffer[1] = (value >> 8) & 0xff;
buffer[2] = (value >> 16) & 0xff;
buffer[3] = (value >> 24) & 0xff;
}
/* Generate a MAC hash (5.2.3.1), using a combination of SHA1 and MD5 */
void
sec_sign(uint8 * signature, int siglen, uint8 * session_key, int keylen, uint8 * data, int datalen)
{
uint8 shasig[20];
uint8 md5sig[16];
uint8 lenhdr[4];
SHA_CTX sha;
MD5_CTX md5;
buf_out_uint32(lenhdr, datalen);
SHA1_Init(&sha);
SHA1_Update(&sha, session_key, keylen);
SHA1_Update(&sha, pad_54, 40);
SHA1_Update(&sha, lenhdr, 4);
SHA1_Update(&sha, data, datalen);
SHA1_Final(shasig, &sha);
MD5_Init(&md5);
MD5_Update(&md5, session_key, keylen);
MD5_Update(&md5, pad_92, 48);
MD5_Update(&md5, shasig, 20);
MD5_Final(md5sig, &md5);
memcpy(signature, md5sig, siglen);
}
/* Update an encryption key */
static void
sec_update(RDPCLIENT * This, uint8 * key, uint8 * update_key)
{
uint8 shasig[20];
SHA_CTX sha;
MD5_CTX md5;
RC4_KEY update;
SHA1_Init(&sha);
SHA1_Update(&sha, update_key, This->secure.rc4_key_len);
SHA1_Update(&sha, pad_54, 40);
SHA1_Update(&sha, key, This->secure.rc4_key_len);
SHA1_Final(shasig, &sha);
MD5_Init(&md5);
MD5_Update(&md5, update_key, This->secure.rc4_key_len);
MD5_Update(&md5, pad_92, 48);
MD5_Update(&md5, shasig, 20);
MD5_Final(key, &md5);
RC4_set_key(&update, This->secure.rc4_key_len, key);
RC4(&update, This->secure.rc4_key_len, key, key);
if (This->secure.rc4_key_len == 8)
sec_make_40bit(key);
}
/* Encrypt data using RC4 */
static void
sec_encrypt(RDPCLIENT * This, uint8 * data, int length)
{
if (This->secure.encrypt_use_count == 4096)
{
sec_update(This, This->secure.encrypt_key, This->secure.encrypt_update_key);
RC4_set_key(&This->secure.rc4_encrypt_key, This->secure.rc4_key_len, This->secure.encrypt_key);
This->secure.encrypt_use_count = 0;
}
RC4(&This->secure.rc4_encrypt_key, length, data, data);
This->secure.encrypt_use_count++;
}
/* Decrypt data using RC4 */
void
sec_decrypt(RDPCLIENT * This, uint8 * data, int length)
{
if (This->secure.decrypt_use_count == 4096)
{
sec_update(This, This->secure.decrypt_key, This->secure.decrypt_update_key);
RC4_set_key(&This->secure.rc4_decrypt_key, This->secure.rc4_key_len, This->secure.decrypt_key);
This->secure.decrypt_use_count = 0;
}
RC4(&This->secure.rc4_decrypt_key, length, data, data);
This->secure.decrypt_use_count++;
}
static void
reverse(uint8 * p, int len)
{
int i, j;
uint8 temp;
for (i = 0, j = len - 1; i < j; i++, j--)
{
temp = p[i];
p[i] = p[j];
p[j] = temp;
}
}
/* Perform an RSA public key encryption operation */
static void
sec_rsa_encrypt(uint8 * out, uint8 * in, int len, uint32 modulus_size, uint8 * modulus,
uint8 * exponent)
{
BN_CTX *ctx;
BIGNUM mod, exp, x, y;
uint8 inr[SEC_MAX_MODULUS_SIZE];
int outlen;
reverse(modulus, modulus_size);
reverse(exponent, SEC_EXPONENT_SIZE);
memcpy(inr, in, len);
reverse(inr, len);
ctx = BN_CTX_new();
BN_init(&mod);
BN_init(&exp);
BN_init(&x);
BN_init(&y);
BN_bin2bn(modulus, modulus_size, &mod);
BN_bin2bn(exponent, SEC_EXPONENT_SIZE, &exp);
BN_bin2bn(inr, len, &x);
BN_mod_exp(&y, &x, &exp, &mod, ctx);
outlen = BN_bn2bin(&y, out);
reverse(out, outlen);
if ((uint32)outlen < modulus_size)
memset(out + outlen, 0, modulus_size - outlen);
BN_free(&y);
BN_clear_free(&x);
BN_free(&exp);
BN_free(&mod);
BN_CTX_free(ctx);
}
/* Initialise secure transport packet */
STREAM
sec_init(RDPCLIENT * This, uint32 flags, int maxlen)
{
int hdrlen;
STREAM s;
if (!This->licence_issued)
hdrlen = (flags & SEC_ENCRYPT) ? 12 : 4;
else
hdrlen = (flags & SEC_ENCRYPT) ? 12 : 0;
s = mcs_init(This, maxlen + hdrlen);
if(s == NULL)
return s;
s_push_layer(s, sec_hdr, hdrlen);
return s;
}
/* Transmit secure transport packet over specified channel */
// !!! we need a lock here !!!
BOOL
sec_send_to_channel(RDPCLIENT * This, STREAM s, uint32 flags, uint16 channel)
{
int datalen;
s_pop_layer(s, sec_hdr);
if (!This->licence_issued || (flags & SEC_ENCRYPT))
out_uint32_le(s, flags);
if (flags & SEC_ENCRYPT)
{
flags &= ~SEC_ENCRYPT;
datalen = (int)(s->end - s->p - 8);
#if WITH_DEBUG
DEBUG(("Sending encrypted packet:\n"));
hexdump(s->p + 8, datalen);
#endif
sec_sign(s->p, 8, This->secure.sign_key, This->secure.rc4_key_len, s->p + 8, datalen);
sec_encrypt(This, s->p + 8, datalen);
}
return mcs_send_to_channel(This, s, channel);
}
/* Transmit secure transport packet */
BOOL
sec_send(RDPCLIENT * This, STREAM s, uint32 flags)
{
return sec_send_to_channel(This, s, flags, MCS_GLOBAL_CHANNEL);
}
/* Transfer the client random to the server */
static void
sec_establish_key(RDPCLIENT * This)
{
uint32 length = This->secure.server_public_key_len + SEC_PADDING_SIZE;
uint32 flags = SEC_CLIENT_RANDOM;
STREAM s;
s = sec_init(This, flags, length + 4);
out_uint32_le(s, length);
out_uint8p(s, This->secure.crypted_random, This->secure.server_public_key_len);
out_uint8s(s, SEC_PADDING_SIZE);
s_mark_end(s);
sec_send(This, s, flags);
}
/* Output connect initial data blob */
static void
sec_out_mcs_data(RDPCLIENT * This, STREAM s, wchar_t * hostname)
{
int hostlen = 2 * (int)wcslen(hostname);
int length = 158 + 76 + 12 + 4;
unsigned int i;
if (This->num_channels > 0)
length += This->num_channels * 12 + 8;
if (hostlen > 30)
hostlen = 30;
/* Generic Conference Control (T.124) ConferenceCreateRequest */
out_uint16_be(s, 5);
out_uint16_be(s, 0x14);
out_uint8(s, 0x7c);
out_uint16_be(s, 1);
out_uint16_be(s, (length | 0x8000)); /* remaining length */
out_uint16_be(s, 8); /* length? */
out_uint16_be(s, 16);
out_uint8(s, 0);
out_uint16_le(s, 0xc001);
out_uint8(s, 0);
out_uint32_le(s, 0x61637544); /* OEM ID: "Duca", as in Ducati. */
out_uint16_be(s, ((length - 14) | 0x8000)); /* remaining length */
/* Client information */
out_uint16_le(s, SEC_TAG_CLI_INFO);
out_uint16_le(s, 212); /* length */
out_uint16_le(s, This->use_rdp5 ? 4 : 1); /* RDP version. 1 == RDP4, 4 == RDP5. */
out_uint16_le(s, 8);
out_uint16_le(s, This->width);
out_uint16_le(s, This->height);
out_uint16_le(s, 0xca01);
out_uint16_le(s, 0xaa03);
out_uint32_le(s, This->keylayout);
out_uint32_le(s, 2600); /* Client build. We are now 2600 compatible :-) */
/* Unicode name of client, padded to 32 bytes */
rdp_out_unistr(This, s, hostname, hostlen);
out_uint8s(s, 30 - hostlen);
/* See
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceddk40/html/cxtsksupportingremotedesktopprotocol.asp */
out_uint32_le(s, This->keyboard_type);
out_uint32_le(s, This->keyboard_subtype);
out_uint32_le(s, This->keyboard_functionkeys);
out_uint8s(s, 64); /* reserved? 4 + 12 doublewords */
out_uint16_le(s, 0xca01); /* colour depth? */
out_uint16_le(s, 1);
out_uint32(s, 0);
out_uint8(s, This->server_depth);
out_uint16_le(s, 0x0700);
out_uint8(s, 0);
out_uint32_le(s, 1);
out_uint8s(s, 64); /* End of client info */
out_uint16_le(s, SEC_TAG_CLI_4);
out_uint16_le(s, 12);
out_uint32_le(s, This->console_session ? 0xb : 9);
out_uint32(s, 0);
/* Client encryption settings */
out_uint16_le(s, SEC_TAG_CLI_CRYPT);
out_uint16_le(s, 12); /* length */
out_uint32_le(s, This->encryption ? 0x3 : 0); /* encryption supported, 128-bit supported */
out_uint32(s, 0); /* Unknown */
DEBUG_RDP5(("This->num_channels is %d\n", This->num_channels));
if (This->num_channels > 0)
{
out_uint16_le(s, SEC_TAG_CLI_CHANNELS);
out_uint16_le(s, This->num_channels * 12 + 8); /* length */
out_uint32_le(s, This->num_channels); /* number of virtual channels */
for (i = 0; i < This->num_channels; i++)
{
DEBUG_RDP5(("Requesting channel %s\n", This->channels[i].name));
out_uint8a(s, This->channel_defs[i].name, 8);
out_uint32_be(s, This->channel_defs[i].options);
}
}
s_mark_end(s);
}
/* Parse a public key structure */
static BOOL
sec_parse_public_key(RDPCLIENT * This, STREAM s, uint8 ** modulus, uint8 ** exponent)
{
uint32 magic, modulus_len;
in_uint32_le(s, magic);
if (magic != SEC_RSA_MAGIC)
{
error("RSA magic 0x%x\n", magic);
return False;
}
in_uint32_le(s, modulus_len);
modulus_len -= SEC_PADDING_SIZE;
if ((modulus_len < 64) || (modulus_len > SEC_MAX_MODULUS_SIZE))
{
error("Bad server public key size (%u bits)\n", modulus_len * 8);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -