rdp.c revision 3c54da9293eeed70e4bd08fc90648c394305c072
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Protocol services - RDP layer
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
Copyright 2003-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/>.
*/
/*
* Oracle GPL Disclaimer: For the avoidance of doubt, except that if any license choice
* other than GPL or LGPL is available it will apply instead, Oracle elects to use only
* the General Public License version 2 (GPLv2) at this time for any software where
* a choice of GPL license versions is made available with the language indicating
* that GPLv2 or any later version may be used, or where a choice of which version
* of the GPL is applied is otherwise unspecified.
*/
#include <time.h>
#ifndef _WIN32
#include <errno.h>
#include <unistd.h>
#endif
#include "rdesktop.h"
#include "ssl.h"
#ifdef HAVE_ICONV
#ifdef HAVE_ICONV_H
#if defined(RT_OS_SOLARIS) && !defined(_XPG6)
# define VBOX_XPG6_TMP_DEF
# define _XPG6
#endif
#if defined(RT_OS_SOLARIS) && defined(__USE_LEGACY_PROTOTYPES__)
# define VBOX_LEGACY_PROTO_TMP_DEF
#endif
#include <iconv.h>
#if defined(VBOX_XPG6_TMP_DEF)
#endif
#if defined(VBOX_LEGACY_PROTO_TMP_DEF)
# define __USE_LEGACY_PROTOTYPES__
#endif
#ifndef ICONV_CONST
#define ICONV_CONST ""
#endif
#endif
#endif
extern uint16 g_mcs_userid;
extern char *g_username;
extern char g_codepage[16];
extern RD_BOOL g_bitmap_compression;
extern RD_BOOL g_encryption;
extern RD_BOOL g_desktop_save;
extern RD_BOOL g_polygon_ellipse_orders;
extern RD_BOOL g_use_rdp5;
extern uint16 g_server_rdp_version;
extern uint32 g_rdp5_performanceflags;
extern int g_server_depth;
extern int g_width;
extern int g_height;
extern RD_BOOL g_bitmap_cache;
extern RD_BOOL g_bitmap_cache_persist_enable;
extern RD_BOOL g_numlock_sync;
extern RD_BOOL g_pending_resize;
extern RDPCOMP g_mppc_dict;
/* Session Directory support */
extern RD_BOOL g_redirect;
extern char g_redirect_server[64];
extern char g_redirect_domain[16];
extern char g_redirect_password[64];
extern char *g_redirect_username;
extern char g_redirect_cookie[128];
extern uint32 g_redirect_flags;
/* END Session Directory support */
extern uint32 g_reconnect_logonid;
extern char g_reconnect_random[16];
extern RD_BOOL g_has_reconnect_random;
#if WITH_DEBUG
static uint32 g_packetno;
#endif
#ifdef HAVE_ICONV
#endif
/* Receive an RDP packet */
static STREAM
{
{
return NULL;
if (rdpver == 0xff)
{
*type = 0;
return rdp_s;
}
else if (rdpver != 3)
{
/* rdp5_process should move g_next_packet ok */
*type = 0;
return rdp_s;
}
g_next_packet = rdp_s->p;
}
else
{
rdp_s->p = g_next_packet;
}
/* 32k packets are really 8, keepalive fix */
if (length == 0x8000)
{
g_next_packet += 8;
*type = 0;
return rdp_s;
}
#if WITH_DEBUG
#endif /* */
g_next_packet += length;
return rdp_s;
}
/* Initialise an RDP data packet */
static STREAM
rdp_init_data(int maxlen)
{
STREAM s;
return s;
}
/* Send an RDP data packet */
static void
{
s_pop_layer(s, rdp_hdr);
out_uint16_le(s, length);
out_uint8(s, 0); /* pad */
out_uint8(s, data_pdu_type);
out_uint8(s, 0); /* compress_type */
out_uint16(s, 0); /* compress_len */
}
/* Output a string in Unicode */
void
{
#ifdef HAVE_ICONV
if (g_iconv_works)
{
{
{
warning("rdp_out_unistr: iconv_open[%s -> %s] fail %p\n",
return;
}
(size_t) - 1)
{
return;
}
pout = (char *) s->p;
}
{
return;
}
s->p += len + 2;
}
else
#endif
{
int i = 0, j = 0;
len += 2;
while (i < len)
{
s->p[i++] = string[j++];
s->p[i++] = 0;
}
s->p += len;
}
}
/* Input a string in Unicode
*
* Returns str_len of string
*/
int
{
#ifdef HAVE_ICONV
if (g_iconv_works)
{
{
{
warning("rdp_in_unistr: iconv_open[%s -> %s] fail %p\n",
}
}
{
{
warning("server sent an unexpectedly long string, truncating\n");
}
else
{
}
}
/* we must update the location of the current STREAM for future reads of s->p */
s->p += in_len;
*pout = 0;
}
else
#endif
{
int i = 0;
int rem = 0;
{
warning("server sent an unexpectedly long string, truncating\n");
}
while (i < len)
{
in_uint8s(s, 1);
}
return len;
}
}
/* Parse a logon info packet */
static void
{
char *ipaddr = tcp_get_address();
int packetlen = 0;
STREAM s;
{
DEBUG_RDP5(("Sending RDP4-style Logon packet\n"));
out_uint32(s, 0);
out_uint32_le(s, flags);
out_uint16_le(s, len_domain);
out_uint16_le(s, len_user);
out_uint16_le(s, len_program);
}
else
{
flags |= RDP_LOGON_BLOB;
DEBUG_RDP5(("Sending RDP5-style Logon packet\n"));
4 + /* flags */
2 + /* len_domain */
2 + /* len_user */
2 + /* len_program */
2 + /* len_directory */
len_user + (flags & RDP_LOGON_AUTO ? len_password : 0) + 0 + /* We have no 512 byte BLOB. Perhaps we must? */
(flags & RDP_LOGON_BLOB && !(flags & RDP_LOGON_AUTO) ? 2 : 0) + /* After the BLOB is a unknown int16. If there is a BLOB, that is. */
(0 < len_program ? len_program : 2) + (0 < len_directory ? len_directory : 2) + 2 + /* Unknown (2) */
2 + /* Client ip length */
len_ip + /* Client ip */
2 + /* DLL string length */
len_dll + /* DLL string */
2 + /* Unknown */
2 + /* Unknown */
64 + /* Time zone #0 */
2 + /* Unknown */
64 + /* Time zone #1 */
32; /* Unknown */
out_uint32(s, 0); /* Unknown */
out_uint32_le(s, flags);
out_uint16_le(s, len_domain);
out_uint16_le(s, len_user);
if (flags & RDP_LOGON_AUTO)
{
}
{
out_uint16_le(s, 0);
}
out_uint16_le(s, len_program);
if (0 < len_domain)
else
out_uint16_le(s, 0);
if (flags & RDP_LOGON_AUTO)
{
}
{
out_uint16_le(s, 0);
}
if (0 < len_program)
{
}
else
{
out_uint16_le(s, 0);
}
if (0 < len_directory)
{
}
else
{
out_uint16_le(s, 0);
}
/* TS_EXTENDED_INFO_PACKET */
/* TS_TIME_ZONE_INFORMATION */
out_uint32_le(s, tzone);
out_uint32_le(s, 0x0a0000);
out_uint32_le(s, 0x050000);
out_uint32_le(s, 3);
out_uint32_le(s, 0);
out_uint32_le(s, 0);
out_uint32_le(s, 0x30000);
out_uint32_le(s, 0x050000);
out_uint32_le(s, 2);
out_uint32(s, 0);
/* Rest of TS_EXTENDED_INFO_PACKET */
/* Client Auto-Reconnect */
{
/* ARC_CS_PRIVATE_PACKET */
}
else
{
out_uint16_le(s, 0); /* cbAutoReconnectLen */
}
}
s_mark_end(s);
}
/* Send a control PDU */
static void
{
STREAM s;
s = rdp_init_data(8);
out_uint16_le(s, action);
out_uint16(s, 0); /* userid */
out_uint32(s, 0); /* control id */
s_mark_end(s);
}
/* Send a synchronisation PDU */
static void
rdp_send_synchronise(void)
{
STREAM s;
s = rdp_init_data(4);
out_uint16_le(s, 1002);
s_mark_end(s);
}
/* Send a single input event */
void
{
STREAM s;
s = rdp_init_data(16);
out_uint16(s, 0); /* pad */
out_uint32_le(s, time);
out_uint16_le(s, param1);
out_uint16_le(s, param2);
s_mark_end(s);
}
/* Send a client window information PDU */
void
{
STREAM s;
static int current_status = 1;
if (current_status == status)
return;
s = rdp_init_data(12);
out_uint32_le(s, status);
switch (status)
{
case 0: /* shut the server up */
break;
case 1: /* receive data again */
out_uint32_le(s, 0); /* unknown */
out_uint16_le(s, g_width);
out_uint16_le(s, g_height);
break;
}
s_mark_end(s);
}
/* Send persistent bitmap cache enumeration PDU's */
static void
rdp_enum_bmpcache2(void)
{
STREAM s;
offset = 0;
{
flags = 0;
if (offset == 0)
flags |= PDU_FLAG_FIRST;
flags |= PDU_FLAG_LAST;
/* header */
out_uint32_le(s, 0);
out_uint16_le(s, count);
out_uint16_le(s, 0);
out_uint16_le(s, 0);
out_uint16_le(s, 0);
out_uint16_le(s, 0);
out_uint16_le(s, num_keys);
out_uint32_le(s, 0);
out_uint32_le(s, flags);
/* list */
s_mark_end(s);
rdp_send_data(s, 0x2b);
offset += 169;
}
}
/* Send an (empty) font information PDU */
static void
{
STREAM s;
s = rdp_init_data(8);
out_uint16(s, 0); /* number of fonts */
out_uint16_le(s, 0); /* pad? */
s_mark_end(s);
}
/* Output general capability set */
static void
{
out_uint16(s, 0); /* Pad */
out_uint16(s, 0); /* Compression types */
/* Pad, according to T.128. 0x40d seems to
trigger
the server to start sending RDP5 packets.
However, the value is 0x1d04 with W2KTSK and
NT4MS. Hmm.. Anyway, thankyou, Microsoft,
for sending such information in a padding
field.. */
out_uint16(s, 0); /* Update capability */
out_uint16(s, 0); /* Remote unshare capability */
out_uint16(s, 0); /* Compression level */
out_uint16(s, 0); /* Pad */
}
/* Output bitmap capability set */
static void
{
out_uint16(s, 0); /* Pad */
out_uint16(s, 0); /* Unknown */
out_uint16(s, 0); /* Pad */
}
/* Output order capability set */
static void
{
out_uint16(s, 0); /* Pad */
out_uint32(s, 0); /* Unknown */
}
/* Output bitmap cache capability set */
static void
{
int Bpp;
}
/* Output bitmap cache v2 capability set */
static void
{
/* max cell size for cache 0 is 16x16, 1 = 32x32, 2 = 64x64, etc */
if (pstcache_init(2))
{
}
else
{
}
}
/* Output control capability set */
static void
{
out_uint16(s, 0); /* Control capabilities */
out_uint16(s, 0); /* Remote detach */
}
/* Output activation capability set */
static void
{
out_uint16(s, 0); /* Help key */
out_uint16(s, 0); /* Help index key */
out_uint16(s, 0); /* Extended help key */
out_uint16(s, 0); /* Window activate */
}
/* Output pointer capability set */
static void
{
out_uint16(s, 0); /* Colour pointer */
}
/* Output new pointer capability set */
static void
{
}
/* Output share capability set */
static void
{
out_uint16(s, 0); /* userid */
out_uint16(s, 0); /* pad */
}
/* Output colour cache capability set */
static void
{
out_uint16(s, 0); /* pad */
}
/* Output brush cache capability set */
static void
{
}
0x01, 0x00, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
0xFE, 0x00, 0x04, 0x00, 0xFE, 0x00, 0x04, 0x00,
0xFE, 0x00, 0x08, 0x00, 0xFE, 0x00, 0x08, 0x00,
0xFE, 0x00, 0x10, 0x00, 0xFE, 0x00, 0x20, 0x00,
0xFE, 0x00, 0x40, 0x00, 0xFE, 0x00, 0x80, 0x00,
0xFE, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x08,
0x00, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00
};
/* Output unknown capability sets */
static void
{
out_uint16_le(s, id);
out_uint16_le(s, length);
}
#define RDP5_FLAG 0x0030
/* Send a confirm active PDU */
static void
rdp_send_confirm_active(void)
{
STREAM s;
4 /* w2k fix, sessionid */ ;
if (g_use_rdp5)
{
}
else
{
}
out_uint16_le(s, sizeof(RDP_SOURCE));
out_uint16_le(s, caplen);
if (g_use_rdp5)
{
}
else
{
}
s_mark_end(s);
}
/* Process a general capability set */
static void
{
in_uint8s(s, 10);
in_uint16_le(s, pad2octetsB);
if (!pad2octetsB)
g_use_rdp5 = False;
}
/* Process a bitmap capability set */
static void
{
in_uint16_le(s, depth);
in_uint8s(s, 6);
in_uint16_le(s, width);
in_uint16_le(s, height);
/*
* The server may limit depth and change the size of the desktop (for
* example when shadowing another session).
*/
if (g_server_depth != depth)
{
warning("Remote desktop does not support colour depth %d; falling back to %d\n",
}
{
}
}
/* Process server capabilities */
static void
{
int n;
start = s->p;
in_uint16_le(s, ncapsets);
for (n = 0; n < ncapsets; n++)
{
return;
in_uint16_le(s, capset_type);
switch (capset_type)
{
case RDP_CAPSET_GENERAL:
break;
case RDP_CAPSET_BITMAP:
break;
}
s->p = next;
}
}
/* Respond to a demand active PDU */
static void
{
if (g_use_rdp5)
{
rdp_send_fonts(3);
}
else
{
rdp_send_fonts(1);
rdp_send_fonts(2);
}
}
/* Process a colour pointer PDU */
static void
{
sint16 x, y;
in_uint16_le(s, cache_idx);
in_uint16_le(s, x);
in_uint16_le(s, y);
in_uint16_le(s, width);
in_uint16_le(s, height);
in_uint16_le(s, masklen);
in_uint16_le(s, datalen);
{
}
/* sometimes x or y is out of bounds */
x = MAX(x, 0);
y = MAX(y, 0);
}
/* Process a colour pointer PDU */
void
{
process_colour_pointer_common(s, 24);
}
/* Process a New Pointer PDU - these pointers have variable bit depth */
void
{
int xor_bpp;
in_uint16_le(s, xor_bpp);
}
/* Process a cached pointer PDU */
void
{
in_uint16_le(s, cache_idx);
}
/* Process a system pointer PDU */
void
{
switch (system_pointer_type)
{
case RDP_NULL_POINTER:
break;
default:
}
}
/* Process a pointer PDU */
static void
{
uint16 x, y;
in_uint16_le(s, message_type);
switch (message_type)
{
case RDP_POINTER_MOVE:
in_uint16_le(s, x);
in_uint16_le(s, y);
if (s_check(s))
ui_move_pointer(x, y);
break;
case RDP_POINTER_COLOR:
break;
case RDP_POINTER_CACHED:
break;
case RDP_POINTER_SYSTEM:
break;
case RDP_POINTER_NEW:
break;
default:
}
}
/* Process bitmap updates */
void
{
int i;
in_uint16_le(s, num_updates);
for (i = 0; i < num_updates; i++)
{
in_uint16_le(s, left);
in_uint16_le(s, top);
in_uint16_le(s, right);
in_uint16_le(s, bottom);
in_uint16_le(s, width);
in_uint16_le(s, height);
in_uint16_le(s, bpp);
in_uint16_le(s, compress);
in_uint16_le(s, bufsize);
DEBUG(("BITMAP_UPDATE(l=%d,t=%d,r=%d,b=%d,w=%d,h=%d,Bpp=%d,cmp=%d)\n",
if (!compress)
{
int y;
for (y = 0; y < height; y++)
{
}
continue;
}
if (compress & 0x400)
{
}
else
{
in_uint16_le(s, size);
}
{
}
else
{
DEBUG_RDP5(("Failed to decompress data\n"));
}
}
}
/* Process a palette update */
void
{
int i;
{
}
}
/* Process an update PDU */
static void
{
in_uint16_le(s, update_type);
switch (update_type)
{
case RDP_UPDATE_ORDERS:
in_uint16_le(s, count);
process_orders(s, count);
break;
case RDP_UPDATE_BITMAP:
break;
case RDP_UPDATE_PALETTE:
process_palette(s);
break;
case RDP_UPDATE_SYNCHRONIZE:
break;
default:
}
}
/* Process a Save Session Info PDU */
void
{
in_uint32_le(s, infotype);
if (infotype == INFOTYPE_LOGON_EXTENDED_INF)
{
{
/* TS_LOGON_INFO_FIELD */
/* ARC_SC_PRIVATE_PACKET */
in_uint32_le(s, len);
if (len != 28)
{
warning("Invalid length in Auto-Reconnect packet\n");
return;
}
in_uint32_le(s, version);
if (version != 1)
{
warning("Unsupported version of Auto-Reconnect packet\n");
return;
}
}
}
}
/* Process a disconnect PDU */
void
{
in_uint32_le(s, *ext_disc_reason);
DEBUG(("Received disconnect PDU\n"));
}
/* Process data PDU */
static RD_BOOL
{
in_uint16_le(s, len);
in_uint8(s, data_pdu_type);
in_uint16_le(s, clen);
clen -= 18;
if (ctype & RDP_MPPC_COMPRESSED)
{
if (len > RDP_MPPC_DICT_SIZE)
error("error decompressed packet size exceeds max\n");
error("error while decompressing packet\n");
/* len -= 18; */
/* allocate memory and copy the uncompressed data into the temporary stream */
s = ns;
}
switch (data_pdu_type)
{
case RDP_DATA_PDU_UPDATE:
break;
case RDP_DATA_PDU_CONTROL:
DEBUG(("Received Control PDU\n"));
break;
case RDP_DATA_PDU_SYNCHRONISE:
DEBUG(("Received Sync PDU\n"));
break;
case RDP_DATA_PDU_POINTER:
break;
case RDP_DATA_PDU_BELL:
ui_bell();
break;
case RDP_DATA_PDU_LOGON:
DEBUG(("Received Logon PDU\n"));
/* User logged on */
break;
case RDP_DATA_PDU_DISCONNECT:
/* We used to return true and disconnect immediately here, but
* Windows Vista sends a disconnect PDU with reason 0 when
* reconnecting to a disconnected session, and MSTSC doesn't
* drop the connection. I think we should just save the status.
*/
break;
default:
}
return False;
}
/* Process redirect PDU from Session Directory */
static RD_BOOL
{
/* these 2 bytes are unknown, seem to be zeros */
in_uint8s(s, 2);
/* read connection flags */
{
/* read length of ip string */
in_uint32_le(s, len);
/* read ip string */
}
{
/* read length of cookie string */
in_uint32_le(s, len);
/* read cookie string (plain ASCII) */
{
warning("Unexpectedly large redirection cookie\n");
}
else
{
}
g_redirect_cookie[len] = 0;
}
{
/* read length of username string */
in_uint32_le(s, len);
/* read username string */
}
{
/* read length of domain string */
in_uint32_le(s, len);
/* read domain string */
}
{
/* read length of password string */
in_uint32_le(s, len);
/* read password string */
}
{
warning("PDU_REDIRECT_DONT_STORE_USERNAME set\n");
}
{
warning("PDU_REDIRECT_USE_SMARTCARD set\n");
}
{
warning("PDU_REDIRECT_INFORMATIONAL set\n");
}
{
warning("PDU_REDIRECT_HAS_TARGET_FQDN set\n");
}
{
warning("PDU_REDIRECT_HAS_TARGET_NETBIOS set\n");
}
{
warning("PDU_REDIRECT_HAS_TARGET_IP_ARRAY set\n");
}
g_redirect = True;
return True;
}
/* Process incoming packets */
void
{
{
if (g_pending_resize)
{
return;
}
}
}
/* used in uiports and rdp_main_loop, processes the rdp packets waiting */
{
STREAM s;
while (cont)
{
if (s == NULL)
return False;
switch (type)
{
case RDP_PDU_DEMAND_ACTIVE:
*deactivated = False;
break;
case RDP_PDU_DEACTIVATE:
DEBUG(("RDP_PDU_DEACTIVATE\n"));
*deactivated = True;
break;
case RDP_PDU_REDIRECT:
return process_redirect_pdu(s);
break;
case RDP_PDU_DATA:
break;
case 0:
break;
default:
}
}
return True;
}
/* Establish a connection up to the RDP layer */
{
return False;
return True;
}
/* Called during redirection to reset the state to support redirection */
void
rdp_reset_state(void)
{
g_rdp_shareid = 0;
}
/* Disconnect from the RDP layer */
void
rdp_disconnect(void)
{
}