secure.c revision a180a41bba1d50822df23fff0099e90b86638b89
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Protocol services - RDP encryption and licensing
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
Copyright 2005-2011 Peter Astrand <astrand@cendio.se> for Cendio AB
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "rdesktop.h"
#include "ssl.h"
extern char g_hostname[16];
extern int g_width;
extern int g_height;
extern unsigned int g_keylayout;
extern int g_keyboard_type;
extern int g_keyboard_subtype;
extern int g_keyboard_functionkeys;
extern RD_BOOL g_encryption;
extern RD_BOOL g_licence_issued;
extern RD_BOOL g_licence_error_result;
extern RDP_VERSION g_rdp_version;
extern RD_BOOL g_console_session;
extern uint32 g_redirect_session_id;
extern int g_server_depth;
extern VCHANNEL g_channels[];
extern unsigned int g_num_channels;
static int g_rc4_key_len;
static RDSSL_RC4 g_rc4_decrypt_key;
static RDSSL_RC4 g_rc4_encrypt_key;
static uint32 g_server_public_key_len;
/* These values must be available to reset state - Session Directory */
static int g_sec_encrypt_use_count = 0;
static int g_sec_decrypt_use_count = 0;
/*
* I believe this is based on SSLv3 with the following differences:
* 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
*/
/*
* 48-byte transformation used to generate master secret (6.1) and key material (6.2.2).
* Both SHA1 and MD5 algorithms are used.
*/
void
{
int i;
for (i = 0; i < 3; i++)
{
}
}
/*
* 16-byte transformation used to generate export keys (6.2.2).
*/
void
{
}
/*
* 16-byte sha1 hash
*/
void
{
}
/* create string from hash */
void
{
int k;
{
}
}
/* Reduce key entropy from 64 to 40 bits */
static void
{
key[0] = 0xd1;
}
/* Generate encryption keys given client and server randoms */
static void
{
/* Construct pre-master secret */
/* Generate master secret and then key material */
/* First 16 bytes of key material is MAC secret */
/* Generate export keys from next two blocks of 16 bytes */
if (rc4_key_size == 1)
{
DEBUG(("40-bit encryption enabled\n"));
g_rc4_key_len = 8;
}
else
{
g_rc4_key_len = 16;
}
/* Save initial RC4 keys as update keys */
/* Initialise RC4 state arrays */
}
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
};
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
{
}
/* Generate a MAC hash (5.2.3.1), using a combination of SHA1 and MD5 */
void
{
}
/* Update an encryption key */
static void
{
if (g_rc4_key_len == 8)
}
/* Encrypt data using RC4 */
static void
{
if (g_sec_encrypt_use_count == 4096)
{
}
}
/* Decrypt data using RC4 */
void
{
if (g_sec_decrypt_use_count == 4096)
{
}
}
/* Perform an RSA public key encryption operation */
static void
{
}
/* Initialise secure transport packet */
{
int hdrlen;
STREAM s;
if (!g_licence_issued && !g_licence_error_result)
else
return s;
}
/* Transmit secure transport packet over specified channel */
void
{
int datalen;
#ifdef WITH_SCARD
#endif
s_pop_layer(s, sec_hdr);
out_uint32_le(s, flags);
if (flags & SEC_ENCRYPT)
{
flags &= ~SEC_ENCRYPT;
#if WITH_DEBUG
DEBUG(("Sending encrypted packet:\n"));
#endif
}
#ifdef WITH_SCARD
#endif
}
/* Transmit secure transport packet */
void
{
}
/* Transfer the client random to the server */
static void
sec_establish_key(void)
{
STREAM s;
out_uint32_le(s, length);
s_mark_end(s);
}
/* Output connect initial data blob */
static void
{
unsigned int i;
if (g_num_channels > 0)
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, 16);
out_uint8(s, 0);
out_uint16_le(s, 0xc001);
out_uint8(s, 0);
/* Client information */
out_uint16_le(s, (g_rdp_version >= RDP_V5) ? 4 : 1); /* RDP version. 1 == RDP4, 4 >= RDP5 to RDP8 */
out_uint16_le(s, 8);
out_uint16_le(s, g_width);
out_uint16_le(s, g_height);
out_uint16_le(s, 0xca01);
out_uint16_le(s, 0xaa03);
out_uint32_le(s, g_keylayout);
/* Unicode name of client, padded to 32 bytes */
/* See
out_uint16_le(s, 1);
out_uint32(s, 0);
out_uint8(s, g_server_depth);
out_uint16_le(s, 0x0700);
out_uint8(s, 0);
out_uint32_le(s, 1);
out_uint8s(s, 64);
/* Write a Client Cluster Data (TS_UD_CS_CLUSTER) */
uint32 cluster_flags = 0;
if (g_console_session || g_redirect_session_id != 0)
/* Client encryption settings */
out_uint32(s, 0); /* Unknown */
if (g_num_channels > 0)
{
for (i = 0; i < g_num_channels; i++)
{
}
}
s_mark_end(s);
}
/* Parse a public key structure */
static RD_BOOL
{
in_uint32_le(s, magic);
if (magic != SEC_RSA_MAGIC)
{
return False;
}
in_uint32_le(s, modulus_len);
{
return False;
}
return s_check(s);
}
/* Parse a public signature structure */
static RD_BOOL
{
if (len != 72)
{
return True;
}
}
/* Parse a crypto information structure */
static RD_BOOL
{
if (crypt_level == 0)
{
/* no encryption */
return False;
}
in_uint32_le(s, random_len);
in_uint32_le(s, rsa_info_len);
if (random_len != SEC_RANDOM_SIZE)
{
return False;
}
/* RSA info */
end = s->p + rsa_info_len;
return False;
if (flags & 1)
{
DEBUG_RDP5(("We're going for the RDP4-style encryption\n"));
while (s->p < end)
{
in_uint16_le(s, tag);
in_uint16_le(s, length);
switch (tag)
{
case SEC_TAG_PUBKEY:
return False;
DEBUG_RDP5(("Got Public key, RDP4-style\n"));
break;
case SEC_TAG_KEYSIG:
return False;
break;
default:
}
s->p = next_tag;
}
}
else
{
DEBUG_RDP5(("We're going for the RDP5-style encryption\n"));
if (certcount < 2)
{
error("Server didn't send enough X509 certificates\n");
return False;
}
{ /* ignore all the certificates between the root and the signing CA */
in_uint32_le(s, ignorelen);
if (ignorecert == NULL)
{ /* XXX: error out? */
DEBUG_RDP5(("got a bad cert: this will probably screw up the rest of the communication\n"));
}
#ifdef WITH_DEBUG_RDP5
#endif
}
/* Do da funky X.509 stuffy
"How did I find out about this? I looked up and saw a
bright light and when I came to I had a scar on my forehead
and knew about X.500"
- Peter Gutman in a early version of
*/
in_uint32_le(s, cacert_len);
in_uint8s(s, cacert_len);
{
error("Couldn't load CA Certificate from server\n");
return False;
}
in_uint32_le(s, cert_len);
if (NULL == server_cert)
{
error("Couldn't load Certificate from server\n");
return False;
}
{
error("Security error CA Certificate invalid\n");
return False;
}
if (NULL == server_public_key)
{
DEBUG_RDP5(("Didn't parse X509 correctly\n"));
return False;
}
if ((g_server_public_key_len < SEC_MODULUS_SIZE) ||
{
error("Bad server public key size (%u bits)\n",
g_server_public_key_len * 8);
return False;
}
modulus, SEC_MAX_MODULUS_SIZE) != 0)
{
error("Problem extracting RSA exponent, modulus");
return False;
}
return True; /* There's some garbage here we don't care about */
}
return s_check_end(s);
}
/* Process crypto information blob */
static void
{
{
DEBUG(("Failed to parse crypt info\n"));
return;
}
DEBUG(("Generating client random\n"));
}
/* Process SRV_INFO, find RDP version supported by server */
static void
{
if (1 == g_server_rdp_version)
{
g_server_depth = 8;
}
}
/* Process connect response data blob */
void
{
if (len & 0x80)
while (s->p < s->end)
{
in_uint16_le(s, tag);
in_uint16_le(s, length);
if (length <= 4)
return;
switch (tag)
{
case SEC_TAG_SRV_INFO:
break;
case SEC_TAG_SRV_CRYPT:
break;
case SEC_TAG_SRV_CHANNELS:
/* FIXME: We should parse this information and
use it to map RDP5 channels to MCS
channels */
break;
default:
}
s->p = next_tag;
}
}
/* Receive secure transport packet */
{
STREAM s;
{
{
if (*rdpver != 3)
{
if (*rdpver & 0x80)
{
sec_decrypt(s->p, s->end - s->p);
}
return s;
}
}
{
in_uint32_le(s, sec_flags);
if (g_encryption)
{
if (sec_flags & SEC_ENCRYPT)
{
sec_decrypt(s->p, s->end - s->p);
}
if (sec_flags & SEC_LICENCE_NEG)
{
licence_process(s);
continue;
}
{
sec_decrypt(s->p, s->end - s->p);
/* Check for a redirect packet, starts with 00 04 */
if (s->p[0] == 0 && s->p[1] == 4)
{
/* for some reason the PDU and the length seem to be swapped.
This isn't good, but we're going to do a byte for byte
swap. So the first foure value appear as: 00 04 XX YY,
where XX YY is the little endian length. We're going to
use 04 00 as the PDU type, so after our swap this will look
like: XX YY 04 00 */
swapbyte = s->p[0];
s->p[0] = s->p[2];
s->p[2] = swapbyte;
swapbyte = s->p[1];
s->p[1] = s->p[3];
s->p[3] = swapbyte;
swapbyte = s->p[2];
s->p[2] = s->p[3];
s->p[3] = swapbyte;
}
#ifdef WITH_DEBUG
/* warning! this debug statement will show passwords in the clear! */
#endif
}
}
else
{
{
licence_process(s);
continue;
}
s->p -= 4;
}
}
if (channel != MCS_GLOBAL_CHANNEL)
{
channel_process(s, channel);
*rdpver = 0xff;
return s;
}
return s;
}
return NULL;
}
/* Establish a secure connection */
{
/* Start a MCS connect sequence */
return False;
/* We exchange some RDP data during the MCS-Connect */
/* finialize the MCS connect sequence */
if (!mcs_connect_finalize(&mcs_data))
return False;
/* sec_process_mcs_data(&mcs_data); */
if (g_encryption)
return True;
}
/* Disconnect a connection */
void
sec_disconnect(void)
{
}
/* reset the state of the sec layer */
void
sec_reset_state(void)
{
g_server_rdp_version = 0;
g_licence_issued = 0;
}