8fdc1ba216
Windows 10 RDS is quite relaxed about missing channel join PDUs, whereas we have to adhere quite tightly to the specification to make sure we get a TLS "Client hello" where appropriate. This makes us incompatible with older RDP clients. For example, the Wyse sx0 thin client does not send a channel join PDU for the user channel. Older, non-TLS versions of xrdp supported these devices. This commit re-implements the xrdp v0.6.1 behaviour for non-TLS connections only, allowing system administrators to use these devices on trusted networks. These devices are in any case too old to establish a modern TLS connection.
1546 lines
50 KiB
C
1546 lines
50 KiB
C
/**
|
|
* xrdp: A Remote Desktop Protocol server.
|
|
*
|
|
* Copyright (C) Jay Sorg 2004-2014
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* mcs layer which implements the Multipoint Communication Service protocol as
|
|
* specified in [ITU-T T.125]
|
|
*/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include <config_ac.h>
|
|
#endif
|
|
|
|
#include "libxrdp.h"
|
|
#include "ms-rdpbcgr.h"
|
|
#include "log.h"
|
|
|
|
/* Forward references */
|
|
static int
|
|
handle_non_tls_client_channel_join_requests(struct xrdp_mcs *self,
|
|
struct stream *s, int *appid);
|
|
|
|
/*****************************************************************************/
|
|
struct xrdp_mcs *
|
|
xrdp_mcs_create(struct xrdp_sec *owner, struct trans *trans,
|
|
struct stream *client_mcs_data,
|
|
struct stream *server_mcs_data)
|
|
{
|
|
struct xrdp_mcs *self;
|
|
|
|
self = (struct xrdp_mcs *)g_malloc(sizeof(struct xrdp_mcs), 1);
|
|
self->sec_layer = owner;
|
|
self->userid = 1;
|
|
self->chanid = 1001;
|
|
self->client_mcs_data = client_mcs_data;
|
|
self->server_mcs_data = server_mcs_data;
|
|
self->iso_layer = xrdp_iso_create(self, trans);
|
|
self->channel_list = list_create();
|
|
|
|
return self;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
void
|
|
xrdp_mcs_delete(struct xrdp_mcs *self)
|
|
{
|
|
struct mcs_channel_item *channel_item;
|
|
int index;
|
|
int count;
|
|
|
|
if (self == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* here we have to free the channel items and anything in them */
|
|
count = self->channel_list->count;
|
|
|
|
for (index = count - 1; index >= 0; index--)
|
|
{
|
|
channel_item = (struct mcs_channel_item *)
|
|
list_get_item(self->channel_list, index);
|
|
g_free(channel_item);
|
|
}
|
|
|
|
list_delete(self->channel_list);
|
|
|
|
xrdp_iso_delete(self->iso_layer);
|
|
/* make sure we get null pointer exception if struct is used again. */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "xrdp_mcs_delete processed");
|
|
g_memset(self, 0, sizeof(struct xrdp_mcs)) ;
|
|
g_free(self);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Send an [ITU-T T.125] DomainMCSPDU message with type ChannelJoinConfirm */
|
|
/* returns error = 1 ok = 0 */
|
|
static int
|
|
xrdp_mcs_send_cjcf(struct xrdp_mcs *self, int userid, int chanid)
|
|
{
|
|
struct stream *s;
|
|
|
|
make_stream(s);
|
|
init_stream(s, 8192);
|
|
|
|
if (xrdp_iso_init(self->iso_layer, s) != 0)
|
|
{
|
|
free_stream(s);
|
|
LOG(LOG_LEVEL_ERROR, "xrdp_mcs_send_cjcf: xrdp_iso_init failed");
|
|
return 1;
|
|
}
|
|
|
|
/* The DomainMCSPDU choice index is a 6-bit int with the next bit as the
|
|
bit field of the two optional fields in the struct (channelId, nonStandard)
|
|
*/
|
|
out_uint8(s, (MCS_CJCF << 2) | 0x02); /* DomainMCSPDU choice index,
|
|
channelId field is present,
|
|
nonStandard field is not present */
|
|
out_uint8(s, 0); /* result choice index 0 = rt-successful */
|
|
out_uint16_be(s, userid); /* initiator */
|
|
out_uint16_be(s, chanid); /* requested */
|
|
out_uint16_be(s, chanid); /* channelId (OPTIONAL) */
|
|
s_mark_end(s);
|
|
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Sending [ITU-T T.125] ChannelJoinConfirm "
|
|
"result SUCCESS, initiator %d, requested %d, "
|
|
"channelId %d", userid, chanid, chanid);
|
|
if (xrdp_iso_send(self->iso_layer, s) != 0)
|
|
{
|
|
free_stream(s);
|
|
LOG(LOG_LEVEL_ERROR, "Sening [ITU-T T.125] ChannelJoinConfirm failed");
|
|
return 1;
|
|
}
|
|
|
|
free_stream(s);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Reads the header of a DomainMCSPDU and gets the appid
|
|
*
|
|
* @return 0 for success */
|
|
static int
|
|
get_domain_mcs_pdu_header(struct xrdp_mcs *self, struct stream *s, int *appid)
|
|
{
|
|
int opcode;
|
|
if (xrdp_iso_recv(self->iso_layer, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "xrdp_mcs_recv: xrdp_iso_recv failed");
|
|
return 1;
|
|
}
|
|
|
|
if (!s_check_rem_and_log(s, 1, "Parsing [ITU-T T.125] DomainMCSPDU"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/* The DomainMCSPDU choice index is a 6-bit int with the 2 least
|
|
significant bits of the byte as padding */
|
|
in_uint8(s, opcode);
|
|
*appid = opcode >> 2; /* 2-bit padding */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE,
|
|
"Received [ITU-T T.125] DomainMCSPDU choice index %d", *appid);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* Processes an [ITU-T T.125] DomainMCSPDU message.
|
|
*
|
|
* Note: DomainMCSPDU messages use the ALIGNED BASIC-PER (Packed Encoding Rules)
|
|
* from [ITU-T X.691].
|
|
*
|
|
* returns error
|
|
*/
|
|
int
|
|
xrdp_mcs_recv(struct xrdp_mcs *self, struct stream *s, int *chan)
|
|
{
|
|
int appid;
|
|
int len;
|
|
|
|
if (get_domain_mcs_pdu_header(self, s, &appid) != 0)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (self->expecting_channel_join_requests)
|
|
{
|
|
if (handle_non_tls_client_channel_join_requests(self, s, &appid) != 0)
|
|
{
|
|
return 1;
|
|
}
|
|
LOG(LOG_LEVEL_DEBUG, "[MCS Connection Sequence] completed");
|
|
self->expecting_channel_join_requests = 0;
|
|
}
|
|
|
|
if (appid == MCS_DPUM) /* Disconnect Provider Ultimatum */
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_TRACE,
|
|
"Received [ITU-T T.125] DisconnectProviderUltimatum");
|
|
LOG(LOG_LEVEL_DEBUG, "Recieved disconnection request");
|
|
return 1;
|
|
}
|
|
|
|
|
|
if (appid != MCS_SDRQ)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Received [ITU-T T.125] DomainMCSPDU "
|
|
"choice index %d is unknown. Expected the DomainMCSPDU to "
|
|
"contain the type SendDataRequest with index %d",
|
|
appid, MCS_SDRQ);
|
|
return 1;
|
|
}
|
|
|
|
if (!s_check_rem_and_log(s, 6, "Parsing [ITU-T T.125] SendDataRequest"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
in_uint8s(s, 2); /* initiator */
|
|
in_uint16_be(s, *chan); /* channelId */
|
|
in_uint8s(s, 1); /* dataPriority (4-bits), segmentation (2-bits), padding (2-bits) */
|
|
in_uint8(s, len); /* userData Length (byte 1) */
|
|
|
|
if ((len & 0xC0) == 0x80)
|
|
{
|
|
/* From [ITU-T X.691] 11.9.3.7
|
|
encoding a length determinant if "n" is greater than 127 and
|
|
less than 16K, then n is encoded using 2 bytes.
|
|
The first byte will have the two highest order bits set to 1 and 0
|
|
(ie. len & 0xC0 == 0x80) and the length is encoded as remaining 14 bits of
|
|
the two bytes (ie. len & 0x3fff). */
|
|
if (!s_check_rem_and_log(s, 1, "Parsing [ITU-T T.125] SendDataRequest userData Length"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint8s(s, 1); /* userData Length (byte 2) */
|
|
}
|
|
else if ((len & 0xC0) == 0xC0)
|
|
{
|
|
/* From [ITU-T X.691] 11.9.3.8
|
|
encoding a length determinant if "n" is greater than 16K,
|
|
then the list of items is fragmented with the length of the first
|
|
fragment encoded using 1 byte. The two highest order bits are set
|
|
to 1 and 1 (ie. len & 0xC0 == 0xC0) and the remaining 6 bits contain
|
|
a multiplyer for 16K (ie. n = (len & 0x3f) * 0x3f)
|
|
*/
|
|
LOG(LOG_LEVEL_ERROR, "[ITU-T T.125] SendDataRequest with length greater "
|
|
"than 16K is not supported. len 0x%2.2x", len);
|
|
return 1;
|
|
}
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [ITU-T T.125] SendDataRequest "
|
|
"initiator (ignored), channelId %d, dataPriority (ignored), "
|
|
"segmentation (ignored), userData Length (ignored)", *chan);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* Parse the identifier and length of a [ITU-T X.690] BER (Basic Encoding Rules)
|
|
* structure header.
|
|
*
|
|
* @param self
|
|
* @param s [in] - the stream to read from
|
|
* @param tag_val [in] - the expected tag value
|
|
* @param len [out] - the length of the structure
|
|
* @returns error
|
|
*/
|
|
static int
|
|
xrdp_mcs_ber_parse_header(struct xrdp_mcs *self, struct stream *s,
|
|
int tag_val, int *len)
|
|
{
|
|
int tag;
|
|
int l;
|
|
int i;
|
|
|
|
if (tag_val > 0xff)
|
|
{
|
|
if (!s_check_rem_and_log(s, 2, "Parsing [ITU-T X.690] Identifier"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint16_be(s, tag);
|
|
}
|
|
else
|
|
{
|
|
if (!s_check_rem_and_log(s, 1, "Parsing [ITU-T X.690] Identifier"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint8(s, tag);
|
|
}
|
|
|
|
if (tag != tag_val)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Parsed [ITU-T X.690] Identifier: "
|
|
"expected 0x%4.4x, actual 0x%4.4x", tag_val, tag);
|
|
return 1;
|
|
}
|
|
|
|
if (!s_check_rem_and_log(s, 1, "Parsing [ITU-T X.690] Length"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
in_uint8(s, l);
|
|
|
|
if (l & 0x80)
|
|
{
|
|
l = l & ~0x80;
|
|
*len = 0;
|
|
|
|
while (l > 0)
|
|
{
|
|
if (!s_check_rem_and_log(s, 1, "Parsing [ITU-T X.690] Length"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint8(s, i);
|
|
*len = (*len << 8) | i;
|
|
l--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*len = l;
|
|
}
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Parsed BER header [ITU-T X.690] "
|
|
"Identifier 0x%4.4x, Length %d", tag, *len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Parses a [ITU-T T.125] DomainParameters structure encoded using BER */
|
|
/* returns error */
|
|
static int
|
|
xrdp_mcs_parse_domain_params(struct xrdp_mcs *self, struct stream *s)
|
|
{
|
|
int len;
|
|
|
|
if (xrdp_mcs_ber_parse_header(self, s, MCS_TAG_DOMAIN_PARAMS, &len) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] DomainParameters failed");
|
|
return 1;
|
|
}
|
|
|
|
if (len < 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] DomainParameters length field is "
|
|
"invalid. Expected >= 0, actual %d", len);
|
|
return 1;
|
|
}
|
|
if (!s_check_rem_and_log(s, len, "Parsing [ITU-T T.125] DomainParameters"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
in_uint8s(s, len); /* skip all fields */
|
|
|
|
return !s_check_rem_and_log(s, 0, "Parsing [ITU-T T.125] DomainParameters");
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Process a [ITU-T T.125] Connect-Initial message encoded using BER */
|
|
/* returns error */
|
|
static int
|
|
xrdp_mcs_recv_connect_initial(struct xrdp_mcs *self)
|
|
{
|
|
int len;
|
|
struct stream *s;
|
|
|
|
s = libxrdp_force_read(self->iso_layer->trans);
|
|
if (s == 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Processing [ITU-T T.125] Connect-Initial failed");
|
|
return 1;
|
|
}
|
|
|
|
if (xrdp_iso_recv(self->iso_layer, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Processing [ITU-T T.125] Connect-Initial failed");
|
|
return 1;
|
|
}
|
|
|
|
if (xrdp_mcs_ber_parse_header(self, s, MCS_CONNECT_INITIAL, &len) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] Connect-Initial failed");
|
|
return 1;
|
|
}
|
|
|
|
if (xrdp_mcs_ber_parse_header(self, s, BER_TAG_OCTET_STRING, &len) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] Connect-Initial callingDomainSelector failed");
|
|
return 1;
|
|
}
|
|
|
|
if (len < 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] Connect-Initial callingDomainSelector length field is "
|
|
"invalid. Expected >= 0, actual %d", len);
|
|
return 1;
|
|
}
|
|
if (!s_check_rem_and_log(s, len, "Parsing [ITU-T T.125] Connect-Initial callingDomainSelector"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
in_uint8s(s, len); /* [ITU-T T.125] Connect-Initial callingDomainSelector */
|
|
|
|
if (xrdp_mcs_ber_parse_header(self, s, BER_TAG_OCTET_STRING, &len) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] Connect-Initial calledDomainSelector failed");
|
|
return 1;
|
|
}
|
|
if (len < 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] Connect-Initial calledDomainSelector length field is "
|
|
"invalid. Expected >= 0, actual %d", len);
|
|
return 1;
|
|
}
|
|
if (!s_check_rem_and_log(s, len, "Parsing [ITU-T T.125] Connect-Initial calledDomainSelector"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
in_uint8s(s, len); /* [ITU-T T.125] Connect-Initial calledDomainSelector */
|
|
|
|
if (xrdp_mcs_ber_parse_header(self, s, BER_TAG_BOOLEAN, &len) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] Connect-Initial upwardFlag failed");
|
|
return 1;
|
|
}
|
|
if (len < 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] Connect-Initial upwardFlag length field is "
|
|
"invalid. Expected >= 0, actual %d", len);
|
|
return 1;
|
|
}
|
|
if (!s_check_rem_and_log(s, len, "Parsing [ITU-T T.125] Connect-Initial upwardFlag"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
in_uint8s(s, len); /* [ITU-T T.125] Connect-Initial upwardFlag */
|
|
|
|
/* [ITU-T T.125] Connect-Initial targetParameters */
|
|
if (xrdp_mcs_parse_domain_params(self, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] Connect-Initial targetParameters failed");
|
|
return 1;
|
|
}
|
|
|
|
/* [ITU-T T.125] Connect-Initial minimumParameters */
|
|
if (xrdp_mcs_parse_domain_params(self, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] Connect-Initial minimumParameters failed");
|
|
return 1;
|
|
}
|
|
|
|
/* [ITU-T T.125] Connect-Initial maximumParameters */
|
|
if (xrdp_mcs_parse_domain_params(self, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] Connect-Initial maximumParameters failed");
|
|
return 1;
|
|
}
|
|
|
|
if (xrdp_mcs_ber_parse_header(self, s, BER_TAG_OCTET_STRING, &len) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] Connect-Initial userData failed");
|
|
return 1;
|
|
}
|
|
/* mcs userData can not be zero length */
|
|
if ((len <= 0) || (len > 16 * 1024))
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [ITU-T T.125] Connect-Initial userData: invalid length. "
|
|
"Expected min 1, max %d; Actual %d", 16 * 1024, len);
|
|
return 1;
|
|
}
|
|
if (!s_check_rem_and_log(s, len, "Parsing [ITU-T T.125] Connect-Initial userData"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received header [ITU-T T.125] Connect-Initial "
|
|
"callingDomainSelector (ignored), calledDomainSelector (ignored), "
|
|
"upwardFlag (ignored), targetParameters (ignored), "
|
|
"minimumParameters (ignored), maximumParameters (ignored), "
|
|
"userData (copied to client_mcs_data)");
|
|
/* make a copy of client mcs data */
|
|
init_stream(self->client_mcs_data, len);
|
|
out_uint8a(self->client_mcs_data, s->p, len); /* [ITU-T T.125] Connect-Initial userData */
|
|
in_uint8s(s, len);
|
|
s_mark_end(self->client_mcs_data);
|
|
|
|
if (!s_check_end_and_log(s, "MCS protocol error [ITU-T T.125] Connect-Initial"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Processes a [ITU-T T.25] DomainMCSPDU with type ErectDomainRequest
|
|
*
|
|
* Note: a parsing example can be found in [MS-RDPBCGR] 4.1.5
|
|
*
|
|
* returns error */
|
|
static int
|
|
xrdp_mcs_recv_edrq(struct xrdp_mcs *self)
|
|
{
|
|
int opcode;
|
|
struct stream *s;
|
|
|
|
s = libxrdp_force_read(self->iso_layer->trans);
|
|
if (s == 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Processing [ITU-T T.125] ErectDomainRequest failed");
|
|
return 1;
|
|
}
|
|
|
|
if (xrdp_iso_recv(self->iso_layer, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Processing [ITU-T T.125] ErectDomainRequest failed");
|
|
return 1;
|
|
}
|
|
|
|
if (!s_check_rem_and_log(s, 1, "Parsing [ITU-T T.125] DomainMCSPDU"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/* The DomainMCSPDU choice index is a 6-bit int with the next bit as the
|
|
bit field of the optional field in the struct
|
|
*/
|
|
in_uint8(s, opcode);
|
|
|
|
if ((opcode >> 2) != MCS_EDRQ)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Parsed [ITU-T T.125] DomainMCSPDU choice index "
|
|
"expected %d, received %d", MCS_EDRQ, (opcode >> 2));
|
|
return 1;
|
|
}
|
|
|
|
if (!s_check_rem_and_log(s, 4, "Parsing [ITU-T T.125] ErectDomainRequest"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
in_uint8s(s, 2); /* subHeight */
|
|
in_uint8s(s, 2); /* subInterval */
|
|
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [ITU-T T.125] DomainMCSPDU "
|
|
"choice index %d (ErectDomainRequest)", (opcode >> 2));
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [ITU-T T.125] ErectDomainRequest "
|
|
"subHeight (ignored), subInterval (ignored), "
|
|
"nonStandard (%s)",
|
|
(opcode & 2) ? "present" : "not present");
|
|
|
|
/*
|
|
* [MS-RDPBCGR] 2.2.1.5 says that the mcsEDrq field is 5 bytes (which have
|
|
* already been read into the opcode and previous fields), so the
|
|
* nonStandard field should never be present.
|
|
*/
|
|
if (opcode & 2) /* ErectDomainRequest v3 nonStandard optional field is present? */
|
|
{
|
|
if (!s_check_rem_and_log(s, 2, "Parsing [ITU-T T.125] ErectDomainRequest nonStandard"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint16_be(s, self->userid); /* NonStandardParameter.key
|
|
NonStandardParameter.data */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [ITU-T T.125] DomainMCSPDU "
|
|
"choice index %d (ErectDomainRequest)", (opcode >> 2));
|
|
}
|
|
|
|
if (!s_check_end_and_log(s, "MCS protocol error [ITU-T T.125] ErectDomainRequest"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Processes a [ITU-T T.25] DomainMCSPDU with type AttachUserRequest
|
|
*
|
|
* Note: a parsing example can be found in [MS-RDPBCGR] 4.1.6
|
|
*
|
|
* returns error */
|
|
static int
|
|
xrdp_mcs_recv_aurq(struct xrdp_mcs *self)
|
|
{
|
|
int opcode;
|
|
struct stream *s;
|
|
|
|
s = libxrdp_force_read(self->iso_layer->trans);
|
|
if (s == 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Processing [ITU-T T.125] AttachUserRequest failed");
|
|
return 1;
|
|
}
|
|
|
|
if (xrdp_iso_recv(self->iso_layer, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Processing [ITU-T T.125] AttachUserRequest failed");
|
|
return 1;
|
|
}
|
|
|
|
if (!s_check_rem_and_log(s, 1, "Parsing [ITU-T T.125] DomainMCSPDU"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/* The DomainMCSPDU choice index is a 6-bit int with the next bit as the
|
|
bit field of the optional field in the struct
|
|
*/
|
|
in_uint8(s, opcode);
|
|
|
|
if ((opcode >> 2) != MCS_AURQ)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Parsed [ITU-T T.125] DomainMCSPDU choice index "
|
|
"expected %d, received %d", MCS_AURQ, (opcode >> 2));
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* [MS-RDPBCGR] 2.2.1.6 says that the mcsAUrq field is 1 bytes (which have
|
|
* already been read into the opcode), so the nonStandard field should
|
|
* never be present.
|
|
*/
|
|
if (opcode & 2)
|
|
{
|
|
if (!s_check_rem_and_log(s, 2, "Parsing [ITU-T T.125] AttachUserRequest nonStandard"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint16_be(s, self->userid); /* NonStandardParameter.key
|
|
NonStandardParameter.data */
|
|
}
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [ITU-T T.125] DomainMCSPDU "
|
|
"choice index %d (AttachUserRequest)", (opcode >> 2));
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [ITU-T T.125] AttachUserRequest "
|
|
"nonStandard (%s)",
|
|
(opcode & 2) ? "present" : "not present");
|
|
|
|
if (!s_check_end_and_log(s, "MCS protocol error [ITU-T T.125] AttachUserRequest"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Send a [ITU-T T.125] DomainMCSPDU with type AttachUserConfirm.
|
|
*
|
|
* Note: a parsing example can be found in [MS-RDPBCGR] 4.1.7
|
|
*
|
|
* returns error */
|
|
static int
|
|
xrdp_mcs_send_aucf(struct xrdp_mcs *self)
|
|
{
|
|
struct stream *s;
|
|
|
|
make_stream(s);
|
|
init_stream(s, 8192);
|
|
|
|
if (xrdp_iso_init(self->iso_layer, s) != 0)
|
|
{
|
|
free_stream(s);
|
|
LOG(LOG_LEVEL_ERROR, "xrdp_mcs_send_aucf: xrdp_iso_init failed");
|
|
return 1;
|
|
}
|
|
|
|
out_uint8(s, ((MCS_AUCF << 2) | 2)); /* AttachUserConfirm
|
|
optional field initiator is present */
|
|
out_uint8s(s, 1); /* result = 0 rt-successful */
|
|
out_uint16_be(s, self->userid); /* initiator */
|
|
s_mark_end(s);
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Sending [ITU-T T.125] DomainMCSPDU "
|
|
"of type AttachUserConfirm: result SUCCESS, initiator %d",
|
|
self->userid);
|
|
|
|
if (xrdp_iso_send(self->iso_layer, s) != 0)
|
|
{
|
|
free_stream(s);
|
|
LOG(LOG_LEVEL_ERROR, "Sending [ITU-T T.125] AttachUserConfirm failed");
|
|
return 1;
|
|
}
|
|
|
|
free_stream(s);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Processes a [ITU-T T.25] DomainMCSPDU with type ChannelJoinRequest
|
|
*
|
|
* Note: a parsing example can be found in [MS-RDPBCGR] 4.1.8.1.1
|
|
*
|
|
* returns error */
|
|
static int
|
|
xrdp_mcs_recv_cjrq(struct xrdp_mcs *self, int *channel_id)
|
|
{
|
|
int opcode;
|
|
struct stream *s;
|
|
int initiator;
|
|
|
|
s = libxrdp_force_read(self->iso_layer->trans);
|
|
if (s == 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Processing [ITU-T T.25] ChannelJoinRequest failed");
|
|
return 1;
|
|
}
|
|
|
|
if (xrdp_iso_recv(self->iso_layer, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Processing [ITU-T T.25] ChannelJoinRequest failed");
|
|
return 1;
|
|
}
|
|
|
|
if (!s_check_rem_and_log(s, 1, "Parsing [ITU-T T.125] DomainMCSPDU"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
in_uint8(s, opcode);
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [ITU-T T.125] DomainMCSPDU "
|
|
"choice index %d (ChannelJoinRequest)", (opcode >> 2));
|
|
|
|
if ((opcode >> 2) != MCS_CJRQ)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Parsed [ITU-T T.125] DomainMCSPDU choice index "
|
|
"expected %d, received %d", MCS_CJRQ, (opcode >> 2));
|
|
return 1;
|
|
}
|
|
|
|
if (!s_check_rem_and_log(s, 4, "Parsing [ITU-T T.125] ChannelJoinRequest"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
in_uint16_be(s, initiator);
|
|
in_uint16_be(s, *channel_id);
|
|
|
|
/*
|
|
* [MS-RDPBCGR] 2.2.1.8 says that the mcsAUrq field is 5 bytes (which have
|
|
* already been read into the opcode and other fields), so the nonStandard
|
|
* field should never be present.
|
|
*/
|
|
if (opcode & 2)
|
|
{
|
|
if (!s_check_rem_and_log(s, 2, "Parsing [ITU-T T.125] ChannelJoinRequest nonStandard"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint8s(s, 2); /* NonStandardParameter.key
|
|
NonStandardParameter.data */
|
|
}
|
|
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [ITU-T T.125] ChannelJoinRequest "
|
|
"initiator %d, channelId %d, "
|
|
"nonStandard (%s)",
|
|
initiator, *channel_id,
|
|
(opcode & 2) ? "present" : "not present");
|
|
|
|
if (!s_check_end_and_log(s, "MCS protocol error [ITU-T T.125] ChannelJoinRequest"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Write the identifier and length of a [ITU-T X.690] BER (Basic Encoding Rules)
|
|
* structure header.
|
|
* returns error */
|
|
static int
|
|
xrdp_mcs_ber_out_header(struct xrdp_mcs *self, struct stream *s,
|
|
int tag_val, int len)
|
|
{
|
|
if (tag_val > 0xff)
|
|
{
|
|
out_uint16_be(s, tag_val);
|
|
}
|
|
else
|
|
{
|
|
out_uint8(s, tag_val);
|
|
}
|
|
|
|
if (len >= 0x80)
|
|
{
|
|
out_uint8(s, 0x82);
|
|
out_uint16_be(s, len);
|
|
}
|
|
else
|
|
{
|
|
out_uint8(s, len);
|
|
}
|
|
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding header [ITU-T X.690] Identifier %d, Length %d",
|
|
tag_val, len);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* returns error */
|
|
static int
|
|
xrdp_mcs_ber_out_int8(struct xrdp_mcs *self, struct stream *s, int value)
|
|
{
|
|
xrdp_mcs_ber_out_header(self, s, BER_TAG_INTEGER, 1);
|
|
out_uint8(s, value);
|
|
return 0;
|
|
}
|
|
|
|
#if 0 /* not used */
|
|
/*****************************************************************************/
|
|
/* returns error */
|
|
static int
|
|
xrdp_mcs_ber_out_int16(struct xrdp_mcs *self, struct stream *s, int value)
|
|
{
|
|
xrdp_mcs_ber_out_header(self, s, BER_TAG_INTEGER, 2);
|
|
out_uint8(s, (value >> 8));
|
|
out_uint8(s, value);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
/* returns error */
|
|
static int
|
|
xrdp_mcs_ber_out_int24(struct xrdp_mcs *self, struct stream *s, int value)
|
|
{
|
|
xrdp_mcs_ber_out_header(self, s, BER_TAG_INTEGER, 3);
|
|
out_uint8(s, (value >> 16));
|
|
out_uint8(s, (value >> 8));
|
|
out_uint8(s, value);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* returns error */
|
|
static int
|
|
xrdp_mcs_out_domain_params(struct xrdp_mcs *self, struct stream *s,
|
|
int max_channels,
|
|
int max_users, int max_tokens,
|
|
int max_pdu_size)
|
|
{
|
|
xrdp_mcs_ber_out_header(self, s, MCS_TAG_DOMAIN_PARAMS, 26);
|
|
xrdp_mcs_ber_out_int8(self, s, max_channels);
|
|
xrdp_mcs_ber_out_int8(self, s, max_users);
|
|
xrdp_mcs_ber_out_int8(self, s, max_tokens);
|
|
xrdp_mcs_ber_out_int8(self, s, 1); /* numPriorities */
|
|
xrdp_mcs_ber_out_int8(self, s, 0); /* minThroughput */
|
|
xrdp_mcs_ber_out_int8(self, s, 1); /* maxHeight */
|
|
xrdp_mcs_ber_out_int24(self, s, max_pdu_size);
|
|
xrdp_mcs_ber_out_int8(self, s, 2); /* protocolVersion */
|
|
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [ITU-T T.125] DomainParameters "
|
|
"maxChannelIds %d, maxUserIds %d, maxTokenIds %d, numPriorities 1, "
|
|
"minThroughput 0 B/s, maxHeight 1, maxMCSPDUsize %d, "
|
|
"protocolVersion 2",
|
|
max_channels, max_users, max_tokens, max_pdu_size);
|
|
return 0;
|
|
}
|
|
/*****************************************************************************/
|
|
/* Write an [ITU-T T.124] ConnectData (ALIGNED variant of BASIC-PER) message
|
|
* with ConnectGCCPDU, ConferenceCreateResponse,
|
|
* and [MS-RDPBCGR] Server Data Blocks as user data.
|
|
*/
|
|
int
|
|
xrdp_mcs_out_gcc_data(struct xrdp_sec *self)
|
|
{
|
|
struct stream *s;
|
|
int num_channels_even;
|
|
int num_channels;
|
|
int index;
|
|
int channel;
|
|
int gcc_size;
|
|
char *gcc_size_ptr;
|
|
char *ud_ptr;
|
|
int header_length = 0;
|
|
int server_cert_len = 0;
|
|
int public_key_blob_len = 0;
|
|
int key_len = 0;
|
|
int bit_len = 0;
|
|
int data_len = 0;
|
|
int modulus_len = 0;
|
|
|
|
num_channels = self->mcs_layer->channel_list->count;
|
|
num_channels_even = num_channels + (num_channels & 1);
|
|
s = &(self->server_mcs_data);
|
|
init_stream(s, 8192);
|
|
|
|
/* [ITU-T T.124] ConnectData (ALIGNED variant of BASIC-PER) */
|
|
out_uint16_be(s, 5); /* = 0x00 0x05 */
|
|
/* t124Identifier choice index = 0 (object) */
|
|
/* object length = 5 */
|
|
out_uint16_be(s, 0x14); /* t124Identifier.object = ??? (0x00 0x14 0x7c 0x00 0x01) */
|
|
out_uint8(s, 0x7c);
|
|
out_uint16_be(s, 1); /* -- */
|
|
out_uint8(s, 0x2a); /* connectPDU length = 42 */
|
|
/* connectPDU octet string of type ConnectGCCPDU
|
|
(unknown where this octet string is defined to be
|
|
of type ConnectGCCPDU) */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [ITU-T T.124] ConnectData "
|
|
"t124Identifier.object 0x00 0x14 0x7c 0x00 0x01, connectPDU length %d",
|
|
0x2a);
|
|
|
|
/* [ITU-T T.124] ConnectGCCPDU (ALIGNED variant of BASIC-PER) */
|
|
out_uint8(s, 0x14); /* ConnectGCCPDU choice index 1 = ConferenceCreateResponse with userData present */
|
|
|
|
/* [ITU-T T.124] ConferenceCreateResponse (ALIGNED variant of BASIC-PER) */
|
|
out_uint8(s, 0x76); /* nodeID = 31219 - 1001 (PER offset for min value)
|
|
= 30218 (big-endian 0x760a) */
|
|
out_uint8(s, 0x0a);
|
|
out_uint8(s, 1); /* tag length */
|
|
out_uint8(s, 1); /* tag */
|
|
out_uint8(s, 0); /* result = 0 (success) */
|
|
out_uint16_le(s, 0xc001); /* userData set count = 1 (0x01),
|
|
userData.isPresent = 0x80 (yes) | userData.key choice index = 0x40 (1 = h221NonStandard) */
|
|
out_uint8(s, 0); /* userData.key.h221NonStandard length
|
|
= 4 - 4 (PER offset for min value) (H221NonStandardIdentifier is an octet string SIZE (4..255))
|
|
= 0
|
|
*/
|
|
|
|
/* [ITU-T H.221] H221NonStandardIdentifier uses country codes and
|
|
manufactuer codes from [ITU-T T.35]. Unknown why these values are used,
|
|
maybe this is just copied from the [MS-RDPBCGR] 4.1.4 example which uses
|
|
the value "McDn" */
|
|
out_uint8(s, 0x4d); /* M */
|
|
out_uint8(s, 0x63); /* c */
|
|
out_uint8(s, 0x44); /* D */
|
|
out_uint8(s, 0x6e); /* n */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [ITU-T T.124] ConferenceCreateResponse "
|
|
"nodeID %d, result SUCCESS", 0x760a + 1001);
|
|
|
|
/* ConferenceCreateResponse.userData.key.value (octet string) */
|
|
/* GCC Response Total Length - 2 bytes , set later */
|
|
gcc_size_ptr = s->p; /* RDPGCCUserDataResponseLength */
|
|
out_uint8s(s, 2);
|
|
ud_ptr = s->p; /* User Data */
|
|
|
|
/* [MS-RDPBCGR] TS_UD_HEADER */
|
|
out_uint16_le(s, SEC_TAG_SRV_INFO); /* type */
|
|
if (self->mcs_layer->iso_layer->rdpNegData)
|
|
{
|
|
out_uint16_le(s, 12); /* length */
|
|
}
|
|
else
|
|
{
|
|
out_uint16_le(s, 8); /* length */
|
|
}
|
|
/* [MS-RDPBCGR] TS_UD_SC_CORE */
|
|
out_uint8(s, 4); /* version (0x00080004 = rdp5, 0x00080001 = rdp4) */
|
|
out_uint8(s, 0);
|
|
out_uint8(s, 8);
|
|
out_uint8(s, 0); /* version (last byte) */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct header [MS-RDPBCGR] TS_UD_HEADER "
|
|
"type 0x%4.4x, length %d",
|
|
SEC_TAG_SRV_INFO,
|
|
self->mcs_layer->iso_layer->rdpNegData ? 12 : 8);
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [MS-RDPBCGR] TS_UD_SC_CORE "
|
|
"<Requiered fields> version 0x%8.8x", 0x00080004);
|
|
if (self->mcs_layer->iso_layer->rdpNegData)
|
|
{
|
|
/* RequestedProtocol */
|
|
out_uint32_le(s, self->mcs_layer->iso_layer->requestedProtocol); /* clientRequestedProtocols */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [MS-RDPBCGR] TS_UD_SC_CORE "
|
|
"<Optional fields> clientRequestedProtocols 0x%8.8x",
|
|
self->mcs_layer->iso_layer->requestedProtocol);
|
|
}
|
|
|
|
|
|
/* [MS-RDPBCGR] TS_UD_HEADER */
|
|
out_uint16_le(s, SEC_TAG_SRV_CHANNELS); /* type */
|
|
out_uint16_le(s, 8 + (num_channels_even * 2)); /* length */
|
|
/* [MS-RDPBCGR] TS_UD_SC_NET */
|
|
out_uint16_le(s, MCS_GLOBAL_CHANNEL); /* 1003, 0x03eb main channel (MCSChannelId) */
|
|
out_uint16_le(s, num_channels); /* number of other channels (channelCount) */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct header [MS-RDPBCGR] TS_UD_HEADER "
|
|
"type 0x%4.4x, length %d",
|
|
SEC_TAG_SRV_CHANNELS, 8 + (num_channels_even * 2));
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [MS-RDPBCGR] TS_UD_SC_NET "
|
|
"MCSChannelId %d, channelCount %d",
|
|
MCS_GLOBAL_CHANNEL, num_channels);
|
|
for (index = 0; index < num_channels_even; index++)
|
|
{
|
|
if (index < num_channels)
|
|
{
|
|
channel = MCS_GLOBAL_CHANNEL + (index + 1);
|
|
out_uint16_le(s, channel); /* channelIdArray[index] (channel allocated) */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [MS-RDPBCGR] TS_UD_SC_NET channelIdArray[%d] "
|
|
"channelId %d", index, channel);
|
|
}
|
|
else
|
|
{
|
|
out_uint16_le(s, 0); /* padding or channelIdArray[index] (channel not allocated) */
|
|
}
|
|
|
|
}
|
|
|
|
if (self->rsa_key_bytes == 64 || self->rsa_key_bytes == 256)
|
|
{
|
|
if (self->rsa_key_bytes == 64)
|
|
{
|
|
header_length = 0x00ec; /* length = 236 */
|
|
server_cert_len = 0xb8; /* serverCertLen (184 bytes) */
|
|
public_key_blob_len = 0x005c; /* wPublicKeyBlobLen (92 bytes) */
|
|
key_len = 0x0048; /* keylen (72 bytes = (bitlen / 8) modulus + 8 padding) */
|
|
bit_len = 512; /* bitlen = 512 */
|
|
data_len = 63; /* datalen (63 = (bitlen / 8) - 1) */
|
|
modulus_len = 64;
|
|
}
|
|
else /* if (self->rsa_key_bytes == 256) */
|
|
{
|
|
header_length = 0x01ac; /* length = 428 */
|
|
server_cert_len = 0x178; /* serverCertLen (376 bytes) */
|
|
public_key_blob_len = 0x011c; /* wPublicKeyBlobLen (284 bytes) */
|
|
key_len = 0x0108; /* keylen (264 bytes = (bitlen / 8) modulus + 8 padding) */
|
|
bit_len = 2048; /* bitlen = 2048 */
|
|
data_len = 255; /* datalen (255 = (bitlen / 8) - 1) */
|
|
modulus_len = 256;
|
|
}
|
|
LOG(LOG_LEVEL_DEBUG, "using %d bit RSA key", bit_len);
|
|
|
|
/* [MS-RDPBCGR] TS_UD_HEADER */
|
|
out_uint16_le(s, SEC_TAG_SRV_CRYPT); /* type */
|
|
out_uint16_le(s, header_length); /* length */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct header [MS-RDPBCGR] TS_UD_HEADER "
|
|
"type 0x%4.4x, length %d", SEC_TAG_SRV_CRYPT, header_length);
|
|
|
|
/* [MS-RDPBCGR] TS_UD_SC_SEC1 */
|
|
out_uint32_le(s, self->crypt_method); /* encryptionMethod */
|
|
out_uint32_le(s, self->crypt_level); /* encryptionLevel */
|
|
out_uint32_le(s, 32); /* serverRandomLen */
|
|
out_uint32_le(s, server_cert_len); /* serverCertLen */
|
|
out_uint8a(s, self->server_random, 32); /* serverRandom */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [MS-RDPBCGR] TS_UD_SC_SEC1 "
|
|
"encryptionMethod 0x%8.8x, encryptionLevel 0x%8.8x, "
|
|
"serverRandomLen 32, serverCertLen %d, serverRandom (omitted), ",
|
|
self->crypt_method, self->crypt_level, server_cert_len);
|
|
|
|
/* (field serverCertificate) [MS-RDPBCGR] SERVER_CERTIFICATE */
|
|
/* HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ */
|
|
/* TermService\Parameters\Certificate */
|
|
out_uint32_le(s, 1); /* dwVersion (1 = PROPRIETARYSERVERCERTIFICATE) */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [MS-RDPBCGR] SERVER_CERTIFICATE "
|
|
"dwVersion.certChainVersion 1 (CERT_CHAIN_VERSION_1), "
|
|
"dwVersion.t 0 (permanent)");
|
|
|
|
/* [MS-RDPBCGR] PROPRIETARYSERVERCERTIFICATE */
|
|
out_uint32_le(s, 1); /* dwSigAlgId (1 = RSA) */
|
|
out_uint32_le(s, 1); /* dwKeyAlgId (1 = RSA) */
|
|
out_uint16_le(s, SEC_TAG_PUBKEY); /* wPublicKeyBlobType (BB_RSA_KEY_BLOB) */
|
|
out_uint16_le(s, public_key_blob_len); /* wPublicKeyBlobLen */
|
|
|
|
/* (field PublicKeyBlob) [MS-RDPBCGR] RSA_PUBLIC_KEY */
|
|
out_uint32_le(s, SEC_RSA_MAGIC); /* magic (0x31415352 'RSA1') */
|
|
out_uint32_le(s, key_len); /* keylen */
|
|
out_uint32_le(s, bit_len); /* bitlen */
|
|
out_uint32_le(s, data_len); /* datalen */
|
|
out_uint8a(s, self->pub_exp, 4); /* pubExp */
|
|
out_uint8a(s, self->pub_mod, modulus_len); /* modulus */
|
|
out_uint8s(s, 8); /* modulus zero padding */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [MS-RDPBCGR] RSA_PUBLIC_KEY "
|
|
"magic 0x%8.8x, keylen %d, bitlen %d, datalen %d, "
|
|
"pubExp <omitted>, modulus <omitted>, ",
|
|
SEC_RSA_MAGIC, key_len, bit_len, data_len);
|
|
|
|
out_uint16_le(s, SEC_TAG_KEYSIG); /* wSignatureBlobType (0x0008 RSA) */
|
|
out_uint16_le(s, 72); /* wSignatureBlobLen */
|
|
out_uint8a(s, self->pub_sig, 64); /* SignatureBlob */
|
|
out_uint8s(s, 8); /* modulus zero padding */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [MS-RDPBCGR] PROPRIETARYSERVERCERTIFICATE "
|
|
"dwKeyAlgId 1, "
|
|
"wPublicKeyBlobType 0x%4.4x, "
|
|
"wPublicKeyBlobLen %d, PublicKeyBlob <see RSA_PUBLIC_KEY above>"
|
|
"wSignatureBlobType 0x%4.4x, "
|
|
"wSignatureBlobLen %d, "
|
|
"SignatureBlob <omitted>",
|
|
SEC_TAG_PUBKEY, public_key_blob_len,
|
|
SEC_TAG_KEYSIG, 72);
|
|
}
|
|
else if (self->rsa_key_bytes == 0) /* no security */
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "using no security");
|
|
/* [MS-RDPBCGR] TS_UD_HEADER */
|
|
out_uint16_le(s, SEC_TAG_SRV_CRYPT); /* type*/
|
|
out_uint16_le(s, 12); /* length */
|
|
/* [MS-RDPBCGR] TS_UD_SC_SEC1 */
|
|
out_uint32_le(s, self->crypt_method); /* encryptionMethod */
|
|
out_uint32_le(s, self->crypt_level); /* encryptionLevel */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct header [MS-RDPBCGR] TS_UD_HEADER "
|
|
"type 0x%4.4x, length %d", SEC_TAG_SRV_CRYPT, 12);
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding struct [MS-RDPBCGR] TS_UD_SC_SEC1 "
|
|
"encryptionMethod 0x%8.8x, encryptionMethod 0x%8.8x",
|
|
self->crypt_method, self->crypt_level);
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"Unsupported xrdp_sec.rsa_key_bytes value: %d, the client "
|
|
"will not be sent a [MS-RDPBCGR] TS_UD_SC_SEC1 message.",
|
|
self->rsa_key_bytes);
|
|
}
|
|
s_mark_end(s);
|
|
|
|
gcc_size = (int)(s->end - ud_ptr) | 0x8000;
|
|
gcc_size_ptr[0] = gcc_size >> 8;
|
|
gcc_size_ptr[1] = gcc_size;
|
|
|
|
return 0;
|
|
}
|
|
/*****************************************************************************/
|
|
/* Send an [ITU-T T.125] Connect-Response message.
|
|
*
|
|
* Note: the xrdp_mcs_out_gcc_data() function must be called (to populate the
|
|
* xrdp_mcs.server_mcs_data stream) before this method is called.
|
|
*
|
|
* returns error
|
|
*/
|
|
static int
|
|
xrdp_mcs_send_connect_response(struct xrdp_mcs *self)
|
|
{
|
|
int data_len;
|
|
struct stream *s;
|
|
|
|
make_stream(s);
|
|
init_stream(s, 8192);
|
|
data_len = (int) (self->server_mcs_data->end - self->server_mcs_data->data);
|
|
xrdp_iso_init(self->iso_layer, s);
|
|
//TODO: we should calculate the whole length include MCS_CONNECT_RESPONSE
|
|
xrdp_mcs_ber_out_header(self, s, MCS_CONNECT_RESPONSE,
|
|
data_len > 0x80 ? data_len + 38 : data_len + 36);
|
|
xrdp_mcs_ber_out_header(self, s, BER_TAG_RESULT, 1);
|
|
out_uint8(s, 0); /* result choice index 0 = rt-successful */
|
|
xrdp_mcs_ber_out_header(self, s, BER_TAG_INTEGER, 1);
|
|
out_uint8(s, 0); /* calledConnectId */
|
|
xrdp_mcs_out_domain_params(self, s, 22, 3, 0, 0xfff8);
|
|
xrdp_mcs_ber_out_header(self, s, BER_TAG_OCTET_STRING, data_len);
|
|
/* mcs data */
|
|
out_uint8a(s, self->server_mcs_data->data, data_len);
|
|
s_mark_end(s);
|
|
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Sending [ITU-T T.125] Connect-Response "
|
|
"result SUCCESS, calledConnectId 0, "
|
|
"domainParameters (see xrdp_mcs_out_domain_params() trace logs), "
|
|
"userData (see xrdp_mcs_out_gcc_data() trace logs and "
|
|
"hex dump below)");
|
|
LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "[ITU-T T.125] Connect-Response userData",
|
|
self->server_mcs_data->data, data_len);
|
|
if (xrdp_iso_send(self->iso_layer, s) != 0)
|
|
{
|
|
free_stream(s);
|
|
LOG(LOG_LEVEL_ERROR, "Sending [ITU-T T.125] Connect-Response failed");
|
|
return 1;
|
|
}
|
|
|
|
free_stream(s);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Handle all client channel join requests for a non-TLS connection
|
|
*
|
|
* @param self MCS structure
|
|
* @param s Input stream
|
|
* @param[in,out] appid Type of the MCS PDU whose header has just been read.
|
|
* @return 0 for success
|
|
*
|
|
* For non-TLS connections, the channel join MCS PDUs are followed by
|
|
* another MCS PDU. This not the case for TLS connections.
|
|
*
|
|
* Called when an MCS PDU header has been read, but the PDU has not
|
|
* been processed.
|
|
*
|
|
* If the PDU is a channel join request, it is processed, and the next
|
|
* PDU header is read. When we've exhausted all the channel join requests,
|
|
* the type of the next PDU is passed back to the caller for the caller
|
|
* to process.
|
|
*
|
|
* In order to cater for older clients which may not conform exactly to
|
|
* the specification, we simply take all the join requests which come in,
|
|
* and respond to them.
|
|
*
|
|
* See :-
|
|
* - https://github.com/neutrinolabs/xrdp/issues/2166
|
|
* - [MS-RDPBCGR] 3.2.5.3.8 and 3.2.5.3.8
|
|
*/
|
|
static int
|
|
handle_non_tls_client_channel_join_requests(struct xrdp_mcs *self,
|
|
struct stream *s, int *appid)
|
|
{
|
|
int rv = 0;
|
|
while (*appid == MCS_CJRQ)
|
|
{
|
|
int userid;
|
|
int chanid;
|
|
|
|
if (!s_check_rem_and_log(s, 4, "Parsing [ITU-T T.125] "
|
|
"ChannelJoinRequest"))
|
|
{
|
|
rv = 1;
|
|
break;
|
|
}
|
|
|
|
in_uint16_be(s, userid);
|
|
in_uint16_be(s, chanid);
|
|
LOG_DEVEL(LOG_LEVEL_TRACE,
|
|
"Received [ITU-T T.125] ChannelJoinRequest "
|
|
"initiator 0x%4.4x, channelId 0x%4.4x", userid, chanid);
|
|
|
|
if (xrdp_mcs_send_cjcf(self, userid, chanid) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"[ITU-T T.125] Channel join sequence: failed");
|
|
}
|
|
|
|
/* Get the next PDU header */
|
|
s = libxrdp_force_read(self->iso_layer->trans);
|
|
if (s == 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "xrdp_mcs_recv: libxrdp_force_read failed");
|
|
rv = 1;
|
|
break;
|
|
}
|
|
if (get_domain_mcs_pdu_header(self, s, appid) != 0)
|
|
{
|
|
rv = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Handle all client channel join requests for a TLS connection
|
|
*
|
|
* @param self MCS structure
|
|
* @return 0 for success
|
|
*
|
|
* When were are about to negotiate a TLS connection, it is important that
|
|
* we agree on the exact number of client join request / client join confirm
|
|
* PDUs, so that we get the TLS 'client hello' message exactly when
|
|
* expected.
|
|
*
|
|
* See [MS-RDPBCGR] 3.2.5.3.8 and 3.2.5.3.8
|
|
*/
|
|
static int
|
|
handle_tls_client_channel_join_requests(struct xrdp_mcs *self)
|
|
{
|
|
int index;
|
|
int rv = 0;
|
|
|
|
static const char *tag = "[MCS Connection Sequence (TLS)]";
|
|
/*
|
|
* Expect a channel join request PDU for each of the static virtual
|
|
* channels, plus the user channel (self->chanid) and the I/O channel
|
|
* (MCS_GLOBAL_CHANNEL) */
|
|
for (index = 0; index < self->channel_list->count + 2; index++)
|
|
{
|
|
int channel_id;
|
|
LOG(LOG_LEVEL_DEBUG, "%s receive channel join request", tag);
|
|
if (xrdp_mcs_recv_cjrq(self, &channel_id) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "%s receive channel join request failed", tag);
|
|
rv = 1;
|
|
break;
|
|
}
|
|
|
|
LOG(LOG_LEVEL_DEBUG, "%s send channel join confirm", tag);
|
|
if (xrdp_mcs_send_cjcf(self, self->userid, channel_id) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "%s send channel join confirm failed", tag);
|
|
rv = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Process and send the MCS messages for the RDP Connection Sequence
|
|
* [MS-RDPBCGR] 1.3.1.1
|
|
*
|
|
* returns error
|
|
*/
|
|
int
|
|
xrdp_mcs_incoming(struct xrdp_mcs *self)
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "[MCS Connection Sequence] receive connection request");
|
|
if (xrdp_mcs_recv_connect_initial(self) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "[MCS Connection Sequence] receive connection request failed");
|
|
return 1;
|
|
}
|
|
|
|
/* in xrdp_sec.c */
|
|
if (xrdp_sec_process_mcs_data(self->sec_layer) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "[MCS Connection Sequence] Connect Initial PDU with GCC Conference Create Request failed");
|
|
return 1;
|
|
}
|
|
|
|
LOG(LOG_LEVEL_DEBUG, "[MCS Connection Sequence] construct connection reponse");
|
|
if (xrdp_mcs_out_gcc_data(self->sec_layer) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "[MCS Connection Sequence] construct connection reponse failed");
|
|
return 1;
|
|
}
|
|
|
|
LOG(LOG_LEVEL_DEBUG, "[MCS Connection Sequence] send connection reponse");
|
|
if (xrdp_mcs_send_connect_response(self) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "[MCS Connection Sequence] send connection reponse failed");
|
|
return 1;
|
|
}
|
|
|
|
LOG(LOG_LEVEL_DEBUG, "[MCS Connection Sequence] receive erect domain request");
|
|
if (xrdp_mcs_recv_edrq(self) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "[MCS Connection Sequence] receive erect domain request failed");
|
|
return 1;
|
|
}
|
|
|
|
LOG(LOG_LEVEL_DEBUG, "[MCS Connection Sequence] receive attach user request");
|
|
if (xrdp_mcs_recv_aurq(self) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "[MCS Connection Sequence] receive attach user request failed");
|
|
return 1;
|
|
}
|
|
|
|
LOG(LOG_LEVEL_DEBUG, "[MCS Connection Sequence] send attach user confirm");
|
|
if (xrdp_mcs_send_aucf(self) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "[MCS Connection Sequence] send attach user confirm failed");
|
|
return 1;
|
|
}
|
|
|
|
if (self->iso_layer->selectedProtocol > PROTOCOL_RDP)
|
|
{
|
|
/* TLS connection. Client and server have to agree on MCS channel
|
|
* join messages, and these have to be processed before the TLS
|
|
* client hello */
|
|
if (handle_tls_client_channel_join_requests(self) != 0)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
LOG(LOG_LEVEL_DEBUG, "[MCS Connection Sequence (TLS)] completed");
|
|
}
|
|
else
|
|
{
|
|
/* Non-TLS connection - channel joins handled in MCS PDU
|
|
* processing loop */
|
|
self->expecting_channel_join_requests = 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* returns error */
|
|
int
|
|
xrdp_mcs_init(struct xrdp_mcs *self, struct stream *s)
|
|
{
|
|
xrdp_iso_init(self->iso_layer, s);
|
|
s_push_layer(s, mcs_hdr, 8);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* returns error */
|
|
/* Inform the callback that an mcs packet has been sent. This is needed so
|
|
the module can send any high priority mcs packets like audio. */
|
|
static int
|
|
xrdp_mcs_call_callback(struct xrdp_mcs *self)
|
|
{
|
|
int rv;
|
|
struct xrdp_session *session;
|
|
|
|
rv = 0;
|
|
/* if there is a callback, call it here */
|
|
session = self->sec_layer->rdp_layer->session;
|
|
|
|
if (session != 0)
|
|
{
|
|
if (session->callback != 0)
|
|
{
|
|
if (session->check_for_app_input)
|
|
{
|
|
/* in xrdp_wm.c */
|
|
rv = session->callback(session->id, 0x5556, 0, 0, 0, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_WARNING, "session->callback is NULL");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_WARNING, "session is NULL");
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Send a [ITU-T T.125] SendDataIndication message
|
|
* returns error */
|
|
int
|
|
xrdp_mcs_send(struct xrdp_mcs *self, struct stream *s, int chan)
|
|
{
|
|
int len;
|
|
char *lp;
|
|
//static int max_len = 0;
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
len = (s->end - s->p) - 8;
|
|
|
|
if (len > 8192 * 2)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING, "xrdp_mcs_send: stream size too big: %d bytes", len);
|
|
}
|
|
|
|
/* The DomainMCSPDU choice index is a 6-bit int with the 2 least
|
|
significant bits of the byte as padding */
|
|
out_uint8(s, MCS_SDIN << 2); /* DomainMCSPDU choice index */
|
|
out_uint16_be(s, self->userid); /* initiator */
|
|
out_uint16_be(s, chan); /* channelId */
|
|
out_uint8(s, 0x70); /* dataPriority (upper 2 bits),
|
|
segmentation (next 2 bits),
|
|
padding (4 bits) */
|
|
|
|
if (len >= 128)
|
|
{
|
|
len = len | 0x8000;
|
|
out_uint16_be(s, len); /* userData length */
|
|
}
|
|
else
|
|
{
|
|
out_uint8(s, len); /* userData length */
|
|
/* move everything up one byte */
|
|
lp = s->p;
|
|
|
|
while (lp < s->end)
|
|
{
|
|
lp[0] = lp[1];
|
|
lp++;
|
|
}
|
|
|
|
s->end--;
|
|
}
|
|
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding header [ITU-T T.125] SendDataIndication "
|
|
"initiator %d, channelId %d, dataPriority %d, segmentation 0x0, "
|
|
"userData length %d",
|
|
self->userid, chan, 0x70 >> 6, (0x70 >> 4) & 0x03);
|
|
if (xrdp_iso_send(self->iso_layer, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "xrdp_mcs_send: xrdp_iso_send failed");
|
|
return 1;
|
|
}
|
|
|
|
/* todo, do we need to call this for every mcs packet,
|
|
maybe every 5 or so */
|
|
if (chan == MCS_GLOBAL_CHANNEL)
|
|
{
|
|
xrdp_mcs_call_callback(self);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Internal help function to close the socket
|
|
* @param self
|
|
*/
|
|
void
|
|
close_rdp_socket(struct xrdp_mcs *self)
|
|
{
|
|
if (self->iso_layer != 0)
|
|
{
|
|
if (self->iso_layer->trans != 0)
|
|
{
|
|
trans_shutdown_tls_mode(self->iso_layer->trans);
|
|
g_tcp_close(self->iso_layer->trans->sck);
|
|
self->iso_layer->trans->sck = -1;
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "xrdp_mcs_disconnect - socket closed");
|
|
return;
|
|
}
|
|
}
|
|
LOG_DEVEL(LOG_LEVEL_WARNING, "Failed to close socket");
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* returns error */
|
|
int
|
|
xrdp_mcs_disconnect(struct xrdp_mcs *self)
|
|
{
|
|
struct stream *s;
|
|
|
|
make_stream(s);
|
|
init_stream(s, 8192);
|
|
|
|
if (xrdp_iso_init(self->iso_layer, s) != 0)
|
|
{
|
|
free_stream(s);
|
|
close_rdp_socket(self);
|
|
LOG(LOG_LEVEL_ERROR, "xrdp_mcs_disconnect: xrdp_iso_init failed");
|
|
return 1;
|
|
}
|
|
|
|
out_uint8(s, (MCS_DPUM << 2) | 1);
|
|
out_uint8(s, 0x80); /* reason (upper 3 bits) (4 = rn-channel-purged)*/
|
|
s_mark_end(s);
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Sending [ITU T.125] DisconnectProviderUltimatum "
|
|
"reason %d", 0x80 >> 5);
|
|
|
|
if (xrdp_iso_send(self->iso_layer, s) != 0)
|
|
{
|
|
free_stream(s);
|
|
close_rdp_socket(self);
|
|
LOG(LOG_LEVEL_ERROR, "Sending [ITU T.125] DisconnectProviderUltimatum failed");
|
|
return 1;
|
|
}
|
|
|
|
free_stream(s);
|
|
close_rdp_socket(self);
|
|
return 0;
|
|
}
|