diff options
author | jsorg71 <jsorg71> | 2005-11-03 02:01:50 +0000 |
---|---|---|
committer | jsorg71 <jsorg71> | 2005-11-03 02:01:50 +0000 |
commit | 523ea69fa574dbcbe9e261c8664c8e93f18eb09d (patch) | |
tree | 180fed20bd3e54ca7af1d0c02115769b54a0857b /rdp/rdp_sec.c | |
parent | 05ec6aa2d59efcf4c489bd3d7decd83be384157c (diff) | |
download | xrdp-proprietary-523ea69fa574dbcbe9e261c8664c8e93f18eb09d.tar.gz xrdp-proprietary-523ea69fa574dbcbe9e261c8664c8e93f18eb09d.zip |
early rdp module work
Diffstat (limited to 'rdp/rdp_sec.c')
-rw-r--r-- | rdp/rdp_sec.c | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/rdp/rdp_sec.c b/rdp/rdp_sec.c new file mode 100644 index 00000000..0bd727ee --- /dev/null +++ b/rdp/rdp_sec.c @@ -0,0 +1,651 @@ +/* + 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. + + xrdp: A Remote Desktop Protocol server. + Copyright (C) Jay Sorg 2005 + + librdp secure layer + +*/ + +#include "rdp.h" + +static char g_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 char g_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 }; + +/*****************************************************************************/ +struct rdp_sec* APP_CC +rdp_sec_create(struct rdp_rdp* owner) +{ + struct rdp_sec* self; + + self = (struct rdp_sec*)g_malloc(sizeof(struct rdp_sec), 1); + self->rdp_layer = owner; + make_stream(self->client_mcs_data); + init_stream(self->client_mcs_data, 8192); + make_stream(self->server_mcs_data); + init_stream(self->server_mcs_data, 8192); + self->mcs_layer = rdp_mcs_create(self, self->client_mcs_data, + self->server_mcs_data); + self->decrypt_rc4_info = g_rc4_info_create(); + self->encrypt_rc4_info = g_rc4_info_create(); + self->lic_layer = rdp_lic_create(self); + return self; +} + +/*****************************************************************************/ +void APP_CC +rdp_sec_delete(struct rdp_sec* self) +{ + if (self == 0) + { + return; + } + rdp_lic_delete(self->lic_layer); + rdp_mcs_delete(self->mcs_layer); + free_stream(self->client_mcs_data); + free_stream(self->server_mcs_data); + g_rc4_info_delete(self->decrypt_rc4_info); + g_rc4_info_delete(self->encrypt_rc4_info); + g_free(self); +} + +/*****************************************************************************/ +/* Reduce key entropy from 64 to 40 bits */ +static void APP_CC +rdp_sec_make_40bit(char* key) +{ + key[0] = 0xd1; + key[1] = 0x26; + key[2] = 0x9e; +} + +/*****************************************************************************/ +/* returns error */ +/* update an encryption key */ +static int APP_CC +rdp_sec_update(char* key, char* update_key, int key_len) +{ + char shasig[20]; + void* sha1_info; + void* md5_info; + void* rc4_info; + + sha1_info = g_sha1_info_create(); + md5_info = g_md5_info_create(); + rc4_info = g_rc4_info_create(); + g_sha1_clear(sha1_info); + g_sha1_transform(sha1_info, update_key, key_len); + g_sha1_transform(sha1_info, g_pad_54, 40); + g_sha1_transform(sha1_info, key, key_len); + g_sha1_complete(sha1_info, shasig); + g_md5_clear(md5_info); + g_md5_transform(md5_info, update_key, key_len); + g_md5_transform(md5_info, g_pad_92, 48); + g_md5_transform(md5_info, shasig, 20); + g_md5_complete(md5_info, key); + g_rc4_set_key(rc4_info, key, key_len); + g_rc4_crypt(rc4_info, key, key_len); + if (key_len == 8) + { + rdp_sec_make_40bit(key); + } + g_sha1_info_delete(sha1_info); + g_md5_info_delete(md5_info); + g_rc4_info_delete(rc4_info); + return 0; +} + +/*****************************************************************************/ +static void APP_CC +rdp_sec_decrypt(struct rdp_sec* self, char* data, int len) +{ + if (self->decrypt_use_count == 4096) + { + rdp_sec_update(self->decrypt_key, self->decrypt_update_key, + self->rc4_key_len); + g_rc4_set_key(self->decrypt_rc4_info, self->decrypt_key, + self->rc4_key_len); + self->decrypt_use_count = 0; + } + g_rc4_crypt(self->decrypt_rc4_info, data, len); + self->decrypt_use_count++; +} + +/*****************************************************************************/ +/* returns error */ +int APP_CC +rdp_sec_recv(struct rdp_sec* self, struct stream* s, int* chan) +{ + int flags; + + DEBUG((" in rdp_sec_recv\r\n")); + if (rdp_mcs_recv(self->mcs_layer, s, chan) != 0) + { + DEBUG((" error in rdp_sec_recv, rdp_mcs_recv failed\r\n")); + return 1; + } + in_uint32_le(s, flags); + DEBUG((" rdp_sec_recv flags %8.8x\r\n", flags)); + if (flags & SEC_ENCRYPT) /* 0x08 */ + { + in_uint8s(s, 8); /* signature */ + rdp_sec_decrypt(self, s->p, s->end - s->p); + } + if (flags & SEC_LICENCE_NEG) /* 0x80 */ + { + DEBUG((" in rdp_sec_recv, got SEC_LICENCE_NEG\r\n")); + rdp_lic_process(self->lic_layer, s); + *chan = 0; + } + DEBUG((" out rdp_sec_recv\r\n")); + return 0; +} + +/*****************************************************************************/ +/* prepare client mcs data to send in mcs layer */ +static void APP_CC +rdp_sec_out_mcs_data(struct rdp_sec* self) +{ + struct stream* s; + int hostlen; + int length; + + s = self->client_mcs_data; + init_stream(s, 512); + self->rdp_layer->mod->hostname[15] = 0; /* limit length to 15 */ + hostlen = 2 * g_strlen(self->rdp_layer->mod->hostname); + length = 158 + 76 + 12 + 4; + /* 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, 1); /* RDP version. 1 == RDP4, 4 == RDP5. */ + out_uint16_le(s, 8); + out_uint16_le(s, self->rdp_layer->mod->width); + out_uint16_le(s, self->rdp_layer->mod->height); + out_uint16_le(s, 0xca01); + out_uint16_le(s, 0xaa03); + out_uint32_le(s, self->rdp_layer->mod->keylayout); + out_uint32_le(s, 2600); /* Client build */ + /* Unicode name of client, padded to 32 bytes */ + rdp_rdp_out_unistr(s, self->rdp_layer->mod->hostname); + out_uint8s(s, 30 - hostlen); + out_uint32_le(s, 4); + out_uint32_le(s, 0); + out_uint32_le(s, 12); + out_uint8s(s, 64); /* reserved? 4 + 12 doublewords */ + out_uint16_le(s, 0xca01); /* color depth? */ + out_uint16_le(s, 1); + out_uint32_le(s, 0); + out_uint8(s, self->rdp_layer->mod->rdp_bpp); + 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, 9); + out_uint32_le(s, 0); + /* Client encryption settings */ + out_uint16_le(s, SEC_TAG_CLI_CRYPT); + out_uint16_le(s, 12); /* length */ + /* encryption supported, 128-bit supported */ + out_uint32_le(s, 0x3); + out_uint32_le(s, 0); /* Unknown */ + s_mark_end(s); +} + +/*****************************************************************************/ +/* Parse a public key structure */ +/* returns boolean */ +static int APP_CC +rdp_sec_parse_public_key(struct rdp_sec* self, struct stream* s, + char* modulus, char* exponent) +{ + int magic; + int modulus_len; + + in_uint32_le(s, magic); + if (magic != SEC_RSA_MAGIC) + { + return 0; + } + in_uint32_le(s, modulus_len); + if (modulus_len != SEC_MODULUS_SIZE + SEC_PADDING_SIZE) + { + return 0; + } + in_uint8s(s, 8); + in_uint8a(s, exponent, SEC_EXPONENT_SIZE); + in_uint8a(s, modulus, SEC_MODULUS_SIZE); + in_uint8s(s, SEC_PADDING_SIZE); + return s_check(s); +} + +/*****************************************************************************/ +/* Parse a crypto information structure */ +/* returns boolean */ +static int APP_CC +rdp_sec_parse_crypt_info(struct rdp_sec* self, struct stream* s, + char* modulus, char* exponent) +{ + int random_len; + int rsa_info_len; + int flags; + int tag; + int length; + char* next_tag; + char* end; + + in_uint32_le(s, self->rc4_key_size); /* 1 = 40-bit, 2 = 128-bit */ + in_uint32_le(s, self->crypt_level); /* 1 = low, 2 = medium, 3 = high */ + if (self->crypt_level == 0) /* no encryption */ + { + return 0; + } + in_uint32_le(s, random_len); + in_uint32_le(s, rsa_info_len); + if (random_len != SEC_RANDOM_SIZE) + { + return 0; + } + in_uint8a(s, self->server_random, random_len); + /* RSA info */ + end = s->p + rsa_info_len; + if (end > s->end) + { + return 0; + } + in_uint32_le(s, flags); /* 1 = RDP4-style, 0x80000002 = X.509 */ + if (flags & 1) + { + in_uint8s(s, 8); /* unknown */ + while (s->p < end) + { + in_uint16_le(s, tag); + in_uint16_le(s, length); + next_tag = s->p + length; + DEBUG((" rdp_sec_parse_crypt_info tag %d length %d\r\n", tag, length)); + switch (tag) + { + case SEC_TAG_PUBKEY: + if (!rdp_sec_parse_public_key(self, s, modulus, exponent)) + { + return 0; + } + break; + case SEC_TAG_KEYSIG: + break; + default: + break; + } + s->p = next_tag; + } + } + else + { + /* todo */ + return 0; + } + return s_check_end(s); +} + +/*****************************************************************************/ +static void APP_CC +rdp_sec_rsa_op(char* out, char* in, char* mod, char* exp) +{ + g_mod_exp(out, SEC_MODULUS_SIZE, /* 64 */ + in, SEC_RANDOM_SIZE, /* 32 */ + mod, SEC_MODULUS_SIZE, /* 64 */ + exp, SEC_EXPONENT_SIZE); /* 4 */ + //g_hexdump(out, SEC_MODULUS_SIZE); + //g_hexdump(in, SEC_RANDOM_SIZE); + //g_hexdump(mod, SEC_MODULUS_SIZE); + //g_hexdump(exp, SEC_EXPONENT_SIZE); +} + +/*****************************************************************************/ +void APP_CC +rdp_sec_hash_48(char* out, char* in, char* salt1, char* salt2, int salt) +{ + int i; + void* sha1_info; + void* md5_info; + char pad[4]; + char sha1_sig[20]; + char md5_sig[16]; + + sha1_info = g_sha1_info_create(); + md5_info = g_md5_info_create(); + for (i = 0; i < 3; i++) + { + g_memset(pad, salt + i, 4); + g_sha1_clear(sha1_info); + g_sha1_transform(sha1_info, pad, i + 1); + g_sha1_transform(sha1_info, in, 48); + g_sha1_transform(sha1_info, salt1, 32); + g_sha1_transform(sha1_info, salt2, 32); + g_sha1_complete(sha1_info, sha1_sig); + g_md5_clear(md5_info); + g_md5_transform(md5_info, in, 48); + g_md5_transform(md5_info, sha1_sig, 20); + g_md5_complete(md5_info, md5_sig); + g_memcpy(out + i * 16, md5_sig, 16); + } + g_sha1_info_delete(sha1_info); + g_md5_info_delete(md5_info); +} + +/*****************************************************************************/ +void APP_CC +rdp_sec_hash_16(char* out, char* in, char* salt1, char* salt2) +{ + void* md5_info; + + md5_info = g_md5_info_create(); + g_md5_clear(md5_info); + g_md5_transform(md5_info, in, 16); + g_md5_transform(md5_info, salt1, 32); + g_md5_transform(md5_info, salt2, 32); + g_md5_complete(md5_info, out); + g_md5_info_delete(md5_info); +} + +/*****************************************************************************/ +static int APP_CC +rdp_sec_generate_keys(struct rdp_sec* self) +{ + char session_key[48]; + char temp_hash[48]; + char input[48]; + + g_memcpy(input, self->client_random, 24); + g_memcpy(input + 24, self->server_random, 24); + rdp_sec_hash_48(temp_hash, input, self->client_random, + self->server_random, 65); + rdp_sec_hash_48(session_key, temp_hash, self->client_random, + self->server_random, 88); + g_memcpy(self->sign_key, session_key, 16); + rdp_sec_hash_16(self->decrypt_key, session_key + 16, self->client_random, + self->server_random); + rdp_sec_hash_16(self->encrypt_key, session_key + 32, self->client_random, + self->server_random); + DEBUG((" rdp_sec_generate_keys, rc4_key_size is %d\r\n", self->rc4_key_size)); + DEBUG((" rdp_sec_generate_keys, crypt_level is %d\r\n", self->crypt_level)); + if (self->rc4_key_size == 1) + { + rdp_sec_make_40bit(self->sign_key); + rdp_sec_make_40bit(self->encrypt_key); + rdp_sec_make_40bit(self->decrypt_key); + self->rc4_key_len = 8; + } + else + { + self->rc4_key_len = 16; + } + g_memcpy(self->decrypt_update_key, self->decrypt_key, 16); + g_memcpy(self->encrypt_update_key, self->encrypt_key, 16); + g_rc4_set_key(self->decrypt_rc4_info, self->decrypt_key, self->rc4_key_len); + g_rc4_set_key(self->encrypt_rc4_info, self->encrypt_key, self->rc4_key_len); + return 0; +} + +/*****************************************************************************/ +/* Process crypto information blob */ +static void APP_CC +rdp_sec_process_crypt_info(struct rdp_sec* self, struct stream* s) +{ + char modulus[64]; + char exponent[64]; + + g_memset(modulus, 0, sizeof(modulus)); + g_memset(exponent, 0, sizeof(exponent)); + if (!rdp_sec_parse_crypt_info(self, s, modulus, exponent)) + { + DEBUG((" error in rdp_sec_process_crypt_info\r\n")); + return; + } + /* Generate a client random, and determine encryption keys */ + g_random(self->client_random, 32); + rdp_sec_rsa_op(self->client_crypt_random, self->client_random, + modulus, exponent); + rdp_sec_generate_keys(self); +} + +/*****************************************************************************/ +/* Process connect response data blob */ +static void APP_CC +rdp_sec_process_mcs_data(struct rdp_sec* self) +{ + int tag; + int length; + int len; + char* next_tag; + struct stream* s; + + s = self->server_mcs_data; + s->p = s->data; + in_uint8s(s, 21); /* header (T.124 ConferenceCreateResponse) */ + in_uint8(s, len); + if (len & 0x80) + { + in_uint8(s, len); + } + while (s->p < s->end) + { + in_uint16_le(s, tag); + in_uint16_le(s, length); + DEBUG((" rdp_sec_process_mcs_data tag %d length %d\r\n", tag, length)); + if (length <= 4) + { + return; + } + next_tag = (s->p + length) - 4; + switch (tag) + { + case SEC_TAG_SRV_INFO: + //rdp_sec_process_srv_info(self, s); + break; + case SEC_TAG_SRV_CRYPT: + rdp_sec_process_crypt_info(self, s); + break; + case SEC_TAG_SRV_CHANNELS: + break; + default: + break; + } + s->p = next_tag; + } +} + +/*****************************************************************************/ +/* Transfer the client random to the server */ +/* returns error */ +static int APP_CC +rdp_sec_establish_key(struct rdp_sec* self) +{ + int length; + int flags; + struct stream* s; + + DEBUG((" sending client random\r\n")); + make_stream(s); + init_stream(s, 8192); + length = SEC_MODULUS_SIZE + SEC_PADDING_SIZE; + flags = SEC_CLIENT_RANDOM; + if (rdp_sec_init(self, s, flags) != 0) + { + free_stream(s); + return 1; + } + out_uint32_le(s, length); + out_uint8p(s, self->client_crypt_random, SEC_MODULUS_SIZE); + out_uint8s(s, SEC_PADDING_SIZE); + s_mark_end(s); + if (rdp_sec_send(self, s, flags) != 0) + { + free_stream(s); + return 1; + } + free_stream(s); + return 0; +} + +/*****************************************************************************/ +/* Establish a secure connection */ +int APP_CC +rdp_sec_connect(struct rdp_sec* self, char* ip, char* port) +{ + DEBUG((" in rdp_sec_connect\r\n")); + rdp_sec_out_mcs_data(self); + if (rdp_mcs_connect(self->mcs_layer, ip, port) != 0) + { + DEBUG((" out rdp_sec_connect error rdp_mcs_connect failed\r\n")); + return 1; + } + rdp_sec_process_mcs_data(self); + if (rdp_sec_establish_key(self) != 0) + { + DEBUG((" out rdp_sec_connect error rdp_sec_establish_key failed\r\n")); + return 1; + } + DEBUG((" out rdp_sec_connect\r\n")); + return 0; +} + +/*****************************************************************************/ +/* returns error */ +int APP_CC +rdp_sec_init(struct rdp_sec* self, struct stream* s, int flags) +{ + if (rdp_mcs_init(self->mcs_layer, s) != 0) + { + return 1; + } + if (flags & SEC_ENCRYPT) + { + s_push_layer(s, sec_hdr, 12); + } + else + { + s_push_layer(s, sec_hdr, 4); + } + return 0; +} + +/*****************************************************************************/ +/* Output a uint32 into a buffer (little-endian) */ +void APP_CC +rdp_sec_buf_out_uint32(char* buffer, int 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 APP_CC +rdp_sec_sign(char* signature, int siglen, char* session_key, int keylen, + char* data, int datalen) +{ + char shasig[20]; + char md5sig[16]; + char lenhdr[4]; + void* sha1_context; + void* md5_context; + + rdp_sec_buf_out_uint32(lenhdr, datalen); + sha1_context = g_sha1_info_create(); + g_sha1_clear(sha1_context); + g_sha1_transform(sha1_context, session_key, keylen); + g_sha1_transform(sha1_context, g_pad_54, 40); + g_sha1_transform(sha1_context, lenhdr, 4); + g_sha1_transform(sha1_context, data, datalen); + g_sha1_complete(sha1_context, shasig); + g_sha1_info_delete(sha1_context); + md5_context = g_md5_info_create(); + g_md5_clear(md5_context); + g_md5_transform(md5_context, session_key, keylen); + g_md5_transform(md5_context, g_pad_92, 48); + g_md5_transform(md5_context, shasig, 20); + g_md5_complete(md5_context, md5sig); + g_md5_info_delete(md5_context); + g_memcpy(signature, md5sig, siglen); +} + +/*****************************************************************************/ +/* Encrypt data using RC4 */ +static void APP_CC +rdp_sec_encrypt(struct rdp_sec* self, char* data, int length) +{ + if (self->encrypt_use_count == 4096) + { + rdp_sec_update(self->encrypt_key, self->encrypt_update_key, + self->rc4_key_len); + g_rc4_set_key(self->encrypt_rc4_info, self->encrypt_key, + self->rc4_key_len); + self->encrypt_use_count = 0; + } + g_rc4_crypt(self->encrypt_rc4_info, data, length); + self->encrypt_use_count++; +} + +/*****************************************************************************/ +/* returns error */ +int APP_CC +rdp_sec_send(struct rdp_sec* self, struct stream* s, int flags) +{ + int datalen; + + DEBUG((" in rdp_sec_send flags %8.8x\r\n", flags)); + s_pop_layer(s, sec_hdr); + out_uint32_le(s, flags); + if (flags & SEC_ENCRYPT) + { + datalen = (s->end - s->p) - 8; + rdp_sec_sign(s->p, 8, self->sign_key, self->rc4_key_len, s->p + 8, + datalen); + rdp_sec_encrypt(self, s->p + 8, datalen); + } + if (rdp_mcs_send(self->mcs_layer, s) != 0) + { + DEBUG((" out rdp_sec_send, rdp_mcs_send failed\r\n")); + return 1; + } + DEBUG((" out rdp_sec_send\r\n")); + return 0; +} |