xrdp/libxrdp/xrdp_iso.c

645 lines
21 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2004-2014
* Copyright (C) Idan Freiberg 2013-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.
*
* iso layer
*
* Note: [ITU-T X.224] and [ISO/IEC 8073] are essentially two specifications
* of the same protocol (see [ITU-T X.224] Appendix I Differences between
* ITU-T Rec. X.224 (1993) and ISO/IEC 8073:1992). The RDP protocol
* specification [MS-RDPBCGR] makes reference to the [ITU-T X.224] specificaiton.
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include "libxrdp.h"
#include "ms-rdpbcgr.h"
#include "string_calls.h"
#include "log.h"
/*****************************************************************************/
/**
* Converts a protocol mask ([MS-RDPBCGR] 2.2.1.1.1 to a string)
*
* @param protocol Protocol mask
* @param buff Output buffer
* @param bufflen total length of buff
* @return As for snprintf()
*
* The string "RDP" is always added to the output, even if other bits
* are set
*/
static int
protocol_mask_to_str(int protocol, char *buff, int bufflen)
{
char delim = '|';
static const struct bitmask_string bits[] =
{
{ PROTOCOL_SSL, "SSL" },
{ PROTOCOL_HYBRID, "HYBRID" },
{ PROTOCOL_RDSTLS, "RDSTLS" },
{ PROTOCOL_HYBRID_EX, "HYBRID_EX"},
BITMASK_STRING_END_OF_LIST
};
int rlen = g_bitmask_to_str(protocol, bits, delim, buff, bufflen);
/* Append "RDP" */
if (rlen == 0)
{
/* String is empty */
rlen = g_snprintf(buff, bufflen, "RDP");
}
else if (rlen < bufflen)
{
rlen += g_snprintf(buff + rlen, bufflen - rlen, "%cRDP", delim);
}
return rlen;
}
/*****************************************************************************/
struct xrdp_iso *
xrdp_iso_create(struct xrdp_mcs *owner, struct trans *trans)
{
struct xrdp_iso *self;
self = (struct xrdp_iso *) g_malloc(sizeof(struct xrdp_iso), 1);
self->mcs_layer = owner;
self->trans = trans;
return self;
}
/*****************************************************************************/
void
xrdp_iso_delete(struct xrdp_iso *self)
{
if (self == 0)
{
return;
}
g_free(self);
}
/*****************************************************************************/
/* returns error */
static int
xrdp_iso_negotiate_security(struct xrdp_iso *self)
{
char requested_str[64];
const char *selected_str = "";
const char *configured_str = "";
int rv = 0;
struct xrdp_client_info *client_info = &(self->mcs_layer->sec_layer->rdp_layer->client_info);
/* Can we do TLS/SSL? (basic check) */
int ssl_capable = g_file_readable(client_info->certificate) &&
g_file_readable(client_info->key_file);
/* Work out what's actually configured in xrdp.ini. The
* selection happens later, but we can do some error checking here */
switch (client_info->security_layer)
{
case PROTOCOL_RDP:
configured_str = "RDP";
break;
case PROTOCOL_SSL:
/* We *must* use TLS. Check we can offer it, and it's requested */
if (ssl_capable)
{
configured_str = "SSL";
if ((self->requestedProtocol & PROTOCOL_SSL) == 0)
{
LOG(LOG_LEVEL_ERROR, "Server requires TLS for security, "
"but the client did not request TLS.");
self->failureCode = SSL_REQUIRED_BY_SERVER;
rv = 1; /* error */
}
}
else
{
configured_str = "";
LOG(LOG_LEVEL_ERROR, "Cannot accept TLS connections because "
"certificate or private key file is not readable. "
"certificate file: [%s], private key file: [%s]",
client_info->certificate, client_info->key_file);
self->failureCode = SSL_CERT_NOT_ON_SERVER;
rv = 1; /* error */
}
break;
case PROTOCOL_HYBRID:
case PROTOCOL_HYBRID_EX:
default:
/* We don't yet support CredSSP */
if (ssl_capable)
{
configured_str = "SSL|RDP";
}
else
{
/*
* Tell the user we can't offer TLS, but this isn't fatal */
configured_str = "RDP";
LOG(LOG_LEVEL_WARNING, "Cannot accept TLS connections because "
"certificate or private key file is not readable. "
"certificate file: [%s], private key file: [%s]",
client_info->certificate, client_info->key_file);
}
break;
}
/* Currently the choice comes down to RDP or SSL */
if (rv != 0)
{
self->selectedProtocol = PROTOCOL_RDP;
selected_str = "";
}
else if (ssl_capable && (self->requestedProtocol &
client_info->security_layer &
PROTOCOL_SSL) != 0)
{
self->selectedProtocol = PROTOCOL_SSL;
selected_str = "SSL";
}
else
{
self->selectedProtocol = PROTOCOL_RDP;
selected_str = "RDP";
}
protocol_mask_to_str(self->requestedProtocol,
requested_str, sizeof(requested_str));
LOG(LOG_LEVEL_INFO, "Security protocol: configured [%s], requested [%s],"
" selected [%s]",
configured_str, requested_str, selected_str);
return rv;
}
/*****************************************************************************/
/* Process a [MS-RDPBCGR] RDP_NEG_REQ message.
* returns error
*/
static int
xrdp_iso_process_rdp_neg_req(struct xrdp_iso *self, struct stream *s)
{
int flags;
int len;
if (!s_check_rem_and_log(s, 7, "Parsing [MS-RDPBCGR] RDP_NEG_REQ"))
{
return 1;
}
/* The type field has already been read to determine that this function
should be called */
in_uint8(s, flags); /* flags */
if (flags != 0x0 && flags != 0x8 && flags != 0x1)
{
LOG(LOG_LEVEL_ERROR,
"Unsupported [MS-RDPBCGR] RDP_NEG_REQ flags: 0x%2.2x", flags);
return 1;
}
in_uint16_le(s, len); /* length */
if (len != 8)
{
LOG(LOG_LEVEL_ERROR,
"Protocol error: [MS-RDPBCGR] RDP_NEG_REQ length must be 8, "
"received %d", len);
return 1;
}
in_uint32_le(s, self->requestedProtocol); /* requestedProtocols */
/* TODO: why is requestedProtocols flag value bigger than 0xb invalid? */
if (self->requestedProtocol > 0xb)
{
LOG(LOG_LEVEL_ERROR,
"Unknown requested protocol flag [MS-RDPBCGR] RDP_NEG_REQ, "
"requestedProtocol 0x%8.8x", self->requestedProtocol);
return 1;
}
LOG_DEVEL(LOG_LEVEL_TRACE, "Received struct [MS-RDPBCGR] RDP_NEG_REQ "
"flags 0x%2.2x, length 8, requestedProtocol 0x%8.8x",
flags, self->requestedProtocol);
return 0;
}
/*****************************************************************************
* Reads an X.224 PDU (X.224 section 13) preceded by a T.123 TPKT
* header (T.123 section 8)
*
* On entry, the TPKT header length field will have been inspected and used to
* set up the input stream.
*
* On exit, the TPKT header and the fixed part of the PDU header will have been
* removed from the stream.
*
* @param self
* @param s [in]
* @param code [out]
* @param len [out]
* Returns error
*****************************************************************************/
static int
xrdp_iso_recv_msg(struct xrdp_iso *self, struct stream *s, int *code, int *len)
{
int ver;
*code = 0;
*len = 0;
if (s != self->trans->in_s)
{
LOG(LOG_LEVEL_WARNING,
"Bug: the input stream is not the same stream as the "
"transport input stream");
}
/* [ITU-T T.123] TPKT header is 4 bytes, then first 2 bytes of the X.224 CR-TPDU */
if (!s_check_rem_and_log(s, 6,
"Parsing [ITU-T T.123] TPKT header and [ITU-T X.224] TPDU header"))
{
return 1;
}
/* [ITU-T T.123] TPKT header */
in_uint8(s, ver); /* version */
in_uint8s(s, 3); /* Skip reserved field (1 byte), plus length (2 bytes) */
LOG_DEVEL(LOG_LEVEL_TRACE, "Received header [ITU-T T.123] TPKT "
"version %d, length (ignored)", ver);
/* [ITU-T X.224] TPDU header */
in_uint8(s, *len); /* LI (length indicator) */
in_uint8(s, *code); /* TPDU code */
LOG_DEVEL(LOG_LEVEL_TRACE, "Received header [ITU-T X.224] TPDU "
"length indicator %d, TDPU code 0x%2.2x", *len, *code);
if (ver != 3)
{
LOG(LOG_LEVEL_ERROR,
"Unsupported [ITU-T T.123] TPKT header version: %d", ver);
LOG_DEVEL_HEXDUMP(LOG_LEVEL_ERROR, "[ITU-T T.123] TPKT header", s->data, 4);
return 1;
}
if (*len == 255)
{
/* X.224 13.2.1 - reserved value */
LOG(LOG_LEVEL_ERROR,
"[ITU-T X.224] TPDU header: unsupported use of reserved length value");
LOG_DEVEL_HEXDUMP(LOG_LEVEL_ERROR, "[ITU-T X.224] TPDU header", s->data + 4, 4);
return 1;
}
if (*code == ISO_PDU_DT)
{
/* Data PDU : X.224 13.7 class 0 */
if (!s_check_rem_and_log(s, 1, "Parsing [ITU-T X.224] DT-TPDU (Data) header"))
{
return 1;
}
in_uint8s(s, 1); /* EOT (End of TSDU Mark) (upper 1 bit) and
TPDU-NR (Data TPDU Number) (lower 7 bits) */
}
else
{
/* Other supported X.224 class 0 PDUs all have 5 bytes remaining
in the fixed header :
CR Connection request (13.3)
CC Connection confirm (13.4)
DR Disconnect request (13.5) */
if (!s_check_rem_and_log(s, 5, "Parsing [ITU-T X.224] Other PDU header"))
{
return 1;
}
in_uint8s(s, 5); /* DST-REF (2 bytes)
SRC-REF (2 bytes)
[CR, CC] CLASS OPTION (1 byte) or [DR] REASON (1 byte) */
}
return 0;
}
/*****************************************************************************/
/* Process the header of a [ITU-T X.224] DT-TPDU (Data) message.
*
* returns error
*/
int
xrdp_iso_recv(struct xrdp_iso *self, struct stream *s)
{
int code;
int len;
if (xrdp_iso_recv_msg(self, s, &code, &len) != 0)
{
LOG(LOG_LEVEL_ERROR, "xrdp_iso_recv: xrdp_iso_recv_msg failed");
return 1;
}
if (code != ISO_PDU_DT || len != 2)
{
LOG(LOG_LEVEL_ERROR, "xrdp_iso_recv only supports processing "
"[ITU-T X.224] DT-TPDU (Data) headers. Received TPDU header: "
"length indicator %d, TDPU code 0x%2.2x", len, code);
return 1;
}
return 0;
}
/*****************************************************************************/
/*
* Send a [ITU-T X.224] CC-TPDU (Connection Confirm) message with
* [ITU-T T.123] TPKT header.
*
* returns error
*/
static int
xrdp_iso_send_cc(struct xrdp_iso *self)
{
struct stream *s;
char *holdp;
char *len_ptr;
char *len_indicator_ptr;
int len;
int len_indicator;
make_stream(s);
init_stream(s, 8192);
holdp = s->p;
/* [ITU-T T.123] TPKT header */
out_uint8(s, 3); /* version */
out_uint8(s, 0); /* reserved (padding) */
len_ptr = s->p;
out_uint16_be(s, 0); /* length, set later */
/* [ITU-T X.224] CC-TPDU */
len_indicator_ptr = s->p;
out_uint8(s, 0); /* length indicator, set later */
out_uint8(s, ISO_PDU_CC); /* Connection Confirm PDU */
out_uint16_be(s, 0); /* DST-REF */
out_uint16_be(s, 0x1234); /* SRC-REF */
out_uint8(s, 0); /* CLASS OPTION */
/* [MS-RDPBCGR] 2.2.1.2 rdpNegData */
if (self->rdpNegData)
{
if (self->failureCode)
{
/* [MS-RDPBCGR] RDP_NEG_FAILURE */
out_uint8(s, RDP_NEG_FAILURE); /* type*/
out_uint8(s, 0); /* flags (none) */
out_uint16_le(s, 8); /* length (must be 8) */
out_uint32_le(s, self->failureCode); /* failureCode */
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding structure [MS-RDPBCGR] RDP_NEG_FAILURE "
"flags 0, length 8, failureCode 0x%8.8x", self->failureCode);
}
else
{
/* [MS-RDPBCGR] RDP_NEG_RSP */
out_uint8(s, RDP_NEG_RSP); /* type*/
//TODO: hardcoded flags
out_uint8(s, EXTENDED_CLIENT_DATA_SUPPORTED); /* flags */
out_uint16_le(s, 8); /* length (must be 8) */
out_uint32_le(s, self->selectedProtocol); /* selectedProtocol */
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding structure [MS-RDPBCGR] RDP_NEG_RSP "
"flags 0x%02x, length 8, selectedProtocol 0x%8.8x",
EXTENDED_CLIENT_DATA_SUPPORTED,
self->selectedProtocol);
}
}
s_mark_end(s);
len = (int) (s->end - holdp);
len_indicator = (int) (s->end - len_indicator_ptr) - 1;
len_ptr[0] = len >> 8;
len_ptr[1] = len;
len_indicator_ptr[0] = len_indicator;
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding header [ITU-T T.123] TPKT "
"version 3, length %d", len);
LOG_DEVEL(LOG_LEVEL_TRACE, "Sending [ITU-T X.224] CC-TPDU (Connection Confirm) "
"length indicator %d, DST-REF 0, SRC-REF 0, CLASS OPTION 0",
len_indicator);
if (trans_write_copy_s(self->trans, s) != 0)
{
LOG(LOG_LEVEL_ERROR, "Sending [ITU-T X.224] CC-TPDU (Connection Confirm) failed");
free_stream(s);
return 1;
}
free_stream(s);
return 0;
}
/*****************************************************************************
* Process an X.224 connection request PDU
*
* See MS-RDPBCGR v20190923 sections 2.2.1.1 and 3.3.5.3.1.
*
* From the latter, in particular:-
* - The length embedded in the TPKT header MUST be examined for
* consistency with the received data. If there is a discrepancy, the
* connection SHOULD be dropped
* - If the optional routingToken field exists it MUST be ignored.
* - If the optional cookie field is present it MUST be ignored.
* - If both the routingToken and cookie fields are present, the server
* SHOULD continue with the connection.
*****************************************************************************/
/* returns error */
int
xrdp_iso_incoming(struct xrdp_iso *self)
{
int rv = 0;
int code;
int len;
int cc_type;
struct stream *s;
int expected_pdu_len;
LOG_DEVEL(LOG_LEVEL_DEBUG, "[ITU-T X.224] Connection Sequence: receive connection request");
s = libxrdp_force_read(self->trans);
if (s == NULL)
{
LOG(LOG_LEVEL_ERROR, "[ITU-T X.224] Connection Sequence: CR-TPDU (Connection Request) failed");
return 1;
}
if (xrdp_iso_recv_msg(self, s, &code, &len) != 0)
{
LOG(LOG_LEVEL_ERROR, "[ITU-T X.224] Connection Sequence: CR-TPDU (Connection Request) failed");
return 1;
}
if (code != ISO_PDU_CR)
{
LOG(LOG_LEVEL_ERROR, "xrdp_iso_incoming only supports processing "
"[ITU-T X.224] CR-TPDU (Connection Request) headers. "
"Received TPDU header: length indicator %d, TDPU code 0x%2.2x",
len, code);
return 1;
}
/*
* Make sure the length indicator field extracted from the X.224
* connection request TPDU corresponds to the length in the TPKT header.
*
* We do this by seeing how the indicator field minus the counted
* octets in the TPDU header (6) compares with the space left in
* the stream.
*/
expected_pdu_len = (s->end - s->p) + 6;
if (len != expected_pdu_len)
{
LOG(LOG_LEVEL_ERROR,
"Invalid length indicator in [ITU-T X.224] CR-TPDU (Connection Request). "
"expected %d, received %d",
expected_pdu_len, len);
return 1;
}
/* process connection request [MS-RDPBCGR] 2.2.1.1 */
while (s_check_rem(s, 1))
{
in_uint8(s, cc_type); /* type or 'C' */
switch (cc_type)
{
default:
LOG_DEVEL(LOG_LEVEL_WARNING,
"Ignoring unknown structure type in [ITU-T X.224] CR-TPDU (Connection Request). "
"type 0x%2.2x", cc_type);
break;
case RDP_NEG_REQ: /* rdpNegReq 1 */
self->rdpNegData = 1;
if (xrdp_iso_process_rdp_neg_req(self, s) != 0)
{
LOG(LOG_LEVEL_ERROR,
"[ITU-T X.224] Connection Sequence: failed");
return 1;
}
break;
case RDP_CORRELATION_INFO: /* rdpCorrelationInfo 6 */
// TODO
if (!s_check_rem_and_log(s, 1 + 2 + 16 + 16,
"Parsing [MS-RDPBCGR] RDP_NEG_CORRELATION_INFO"))
{
return 1;
}
in_uint8s(s, 1 + 2 + 16 + 16);
LOG_DEVEL(LOG_LEVEL_TRACE,
"Received struct [MS-RDPBCGR] RDP_NEG_CORRELATION_INFO "
"(all fields ignored)");
break;
case 'C': /* Cookie or routingToken */
/* The routingToken and cookie fields are both ASCII
* strings starting with the word 'Cookie: ' and
* ending with CR+LF. We ignore both, so we do
* not need to distinguish them */
while (s_check_rem(s, 1))
{
in_uint8(s, cc_type);
if (cc_type == 0x0D && s_check_rem(s, 1))
{
in_uint8(s, cc_type);
if (cc_type == 0x0A)
{
break;
}
}
}
LOG_DEVEL(LOG_LEVEL_TRACE,
"Received struct [MS-RDPBCGR] routingToken or cookie "
"(ignored)");
break;
}
}
/* negotiate client-server security layer */
rv = xrdp_iso_negotiate_security(self);
/* send connection confirm back to client */
LOG_DEVEL(LOG_LEVEL_DEBUG, "[ITU-T X.224] Connection Sequence: send connection confirmation");
if (xrdp_iso_send_cc(self) != 0)
{
LOG(LOG_LEVEL_ERROR, "[ITU-T X.224] Connection Sequence: send connection confirmation failed");
return 1;
}
LOG_DEVEL(LOG_LEVEL_DEBUG, "[ITU-T X.224] Connection Sequence: completed");
return rv;
}
/*****************************************************************************/
/* returns error */
int
xrdp_iso_init(struct xrdp_iso *self, struct stream *s)
{
init_stream(s, 8192 * 4); /* 32 KB */
s_push_layer(s, iso_hdr, 7);
return 0;
}
/*****************************************************************************/
/* Sends a message with the [ITU-T T.123] TPKT header (T.123 section 8) and
* [ITU-T X.224] DT-TPDU (Data) header (X.224 section 13)
* returns error
*/
int
xrdp_iso_send(struct xrdp_iso *self, struct stream *s)
{
int len;
s_pop_layer(s, iso_hdr);
len = (int) (s->end - s->p);
/* [ITU-T T.123] TPKT header */
out_uint8(s, 3); /* version */
out_uint8(s, 0); /* reserved (padding) */
out_uint16_be(s, len); /* length */
/* [ITU-T X.224] DT-TPDU (Data) header */
out_uint8(s, 2); /* LI (length indicator) */
out_uint8(s, ISO_PDU_DT); /* TPDU code */
out_uint8(s, 0x80); /* EOT (End of TSDU Mark) (upper 1 bit) and
TPDU-NR (Data TPDU Number) (lower 7 bits) */
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding header [ITU-T T.123] TPKT "
"version 3, length %d", len);
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding header [ITU-T X.224] DT-TPDU (Data) "
"length indicator 2, TPDU code 0x%2.2x, EOT 1, TPDU-NR 0x00",
ISO_PDU_DT);
if (trans_write_copy_s(self->trans, s) != 0)
{
LOG(LOG_LEVEL_ERROR, "xrdp_iso_send: trans_write_copy_s failed");
return 1;
}
return 0;
}