Skip to content

Commit

Permalink
Adds the RDP Channel IDs TDP message (#27514)
Browse files Browse the repository at this point in the history
  • Loading branch information
Isaiah Becker-Mayer authored Aug 14, 2023
1 parent 3b0c8b7 commit 8e8ead1
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 13 deletions.
22 changes: 20 additions & 2 deletions lib/srv/desktop/rdp/rdpclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -641,8 +641,8 @@ func (c *Client) handlePNG(cb *C.CGOPNG) C.CGOErrCode {
return C.ErrCodeSuccess
}

//export handle_remote_fx_frame
func handle_remote_fx_frame(handle C.uintptr_t, data *C.uint8_t, length C.uint32_t) C.CGOErrCode {
//export handle_fastpath_pdu
func handle_fastpath_pdu(handle C.uintptr_t, data *C.uint8_t, length C.uint32_t) C.CGOErrCode {
goData := asRustBackedSlice(data, int(length))
return cgo.Handle(handle).Value().(*Client).handleRDPFastPathPDU(goData)
}
Expand All @@ -660,6 +660,24 @@ func (c *Client) handleRDPFastPathPDU(data []byte) C.CGOErrCode {
return C.ErrCodeSuccess
}

//export handle_rdp_channel_ids
func handle_rdp_channel_ids(handle C.uintptr_t, io_channel_id C.uint16_t, user_channel_id C.uint16_t) C.CGOErrCode {
return cgo.Handle(handle).Value().(*Client).handleRDPChannelIDs(io_channel_id, user_channel_id)
}

func (c *Client) handleRDPChannelIDs(ioChannelID, userChannelID C.uint16_t) C.CGOErrCode {
c.cfg.Log.Debugf("Received RDP channel IDs: io_channel_id=%d, user_channel_id=%d", ioChannelID, userChannelID)

if err := c.cfg.Conn.WriteMessage(tdp.RDPChannelIDs{
IOChannelID: uint16(ioChannelID),
UserChannelID: uint16(userChannelID),
}); err != nil {
c.cfg.Log.Errorf("failed handling RDPChannelIDs: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
}

//export handle_remote_copy
func handle_remote_copy(handle C.uintptr_t, data *C.uint8_t, length C.uint32_t) C.CGOErrCode {
goData := C.GoBytes(unsafe.Pointer(data), C.int(length))
Expand Down
25 changes: 23 additions & 2 deletions lib/srv/desktop/rdp/rdpclient/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,22 @@ impl Client {

debug!("connection_result: {:?}", connection_result);

unsafe {
match handle_rdp_channel_ids(
go_ref,
connection_result.io_channel_id,
connection_result.user_channel_id,
) {
CGOErrCode::ErrCodeSuccess => {}
_ => {
return Err(ConnectError::IronRdpError(SessionError::new(
"handle_rdp_channel_ids error",
SessionErrorKind::General,
)));
}
};
};

let x224_processor = x224::Processor::new(
swap_hashmap_kv(connection_result.static_channels),
connection_result.user_channel_id,
Expand Down Expand Up @@ -288,7 +304,7 @@ impl Client {
ironrdp_pdu::Action::FastPath => {
let go_ref = self.go_ref;
match unsafe {
handle_remote_fx_frame(go_ref, frame.as_mut_ptr(), frame.len() as u32)
handle_fastpath_pdu(go_ref, frame.as_mut_ptr(), frame.len() as u32)
} {
CGOErrCode::ErrCodeSuccess => continue,
err => {
Expand Down Expand Up @@ -1550,7 +1566,12 @@ pub struct CGOSharedDirectoryListRequest {
extern "C" {
fn handle_png(client_ref: usize, b: *mut CGOPNG) -> CGOErrCode;
fn handle_remote_copy(client_ref: usize, data: *mut u8, len: u32) -> CGOErrCode;
fn handle_remote_fx_frame(client_ref: usize, data: *mut u8, len: u32) -> CGOErrCode;
fn handle_fastpath_pdu(client_ref: usize, data: *mut u8, len: u32) -> CGOErrCode;
fn handle_rdp_channel_ids(
client_ref: usize,
io_channel_id: u16,
user_channel_id: u16,
) -> CGOErrCode;
fn tdp_sd_acknowledge(client_ref: usize, ack: *mut CGOSharedDirectoryAcknowledge)
-> CGOErrCode;
fn tdp_sd_info_request(
Expand Down
33 changes: 33 additions & 0 deletions lib/srv/desktop/tdp/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const (
TypeNotification = MessageType(28)
TypeRDPFastPathPDU = MessageType(29)
TypeRDPResponsePDU = MessageType(30)
TypeRDPChannelIDs = MessageType(31)
)

// Message is a Go representation of a desktop protocol message.
Expand Down Expand Up @@ -118,6 +119,8 @@ func decodeMessage(firstByte byte, in byteReader) (Message, error) {
return decodeRDPFastPathPDU(in)
case TypeRDPResponsePDU:
return decodeRDPResponsePDU(in)
case TypeRDPChannelIDs:
return decodeRDPChannelIDs(in)
case TypeMouseMove:
return decodeMouseMove(in)
case TypeMouseButton:
Expand Down Expand Up @@ -352,6 +355,30 @@ func (r RDPResponsePDU) Encode() ([]byte, error) {
return buf.Bytes(), nil
}

// RDPChannelIDs are the IO and user channel IDs negotiated during the RDP connection.
//
// See "3. Channel Connection" at https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/023f1e69-cfe8-4ee6-9ee0-7e759fb4e4ee
//
// | message type (31) | io_channel_id uint16 | user_channel_id uint16 |
type RDPChannelIDs struct {
IOChannelID uint16
UserChannelID uint16
}

func (c RDPChannelIDs) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeRDPChannelIDs))
writeUint16(buf, c.IOChannelID)
writeUint16(buf, c.UserChannelID)
return buf.Bytes(), nil
}

func decodeRDPChannelIDs(in byteReader) (RDPChannelIDs, error) {
var ids RDPChannelIDs
err := binary.Read(in, binary.BigEndian, &ids)
return ids, trace.Wrap(err)
}

// MouseMove is the mouse movement message.
// | message type (3) | x uint32 | y uint32 |
type MouseMove struct {
Expand Down Expand Up @@ -1520,6 +1547,12 @@ func decodeString(r io.Reader, maxLen uint32) (string, error) {
return string(s), nil
}

// writeUint16 writes v to b in big endian order
func writeUint16(b *bytes.Buffer, v uint16) {
b.WriteByte(byte(v >> 8))
b.WriteByte(byte(v))
}

// writeUint32 writes v to b in big endian order
func writeUint32(b *bytes.Buffer, v uint32) {
b.WriteByte(byte(v >> 24))
Expand Down
12 changes: 12 additions & 0 deletions rfd/0037-desktop-access-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,15 @@ It is sent from TDP server to client. At the time of writing, the purpose of thi
Some messages passed to the TDP client via a FastPath Frame warrant a response, which can be sent from the TDP client to the server with this message.
At the time of writing this message is used to send responses to RemoteFX frames, which occasionaly demand such, but in theory it can be used to carry
any raw RDP response message intended to be written directly into the TDP server-side's RDP connection.

#### 31 - RDP Channel IDs

```
| message type (31) | io_channel_id uint16 | user_channel_id uint16 |
```

During the RDP connection sequence the client and server negotiate channel IDs for the I/O and user channels, which are used in the RemoteFX
response frames (see message type 30, above) . This message is sent by the TDP server to the TDP client so that such response frames can be
properly formulated.

See "3. Channel Connection" at https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/023f1e69-cfe8-4ee6-9ee0-7e759fb4e4ee
6 changes: 3 additions & 3 deletions web/packages/teleport/src/ironrdp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,11 @@ pub struct FastPathProcessor {
#[wasm_bindgen]
impl FastPathProcessor {
#[wasm_bindgen(constructor)]
pub fn new(width: u16, height: u16) -> Self {
pub fn new(width: u16, height: u16, io_channel_id: u16, user_channel_id: u16) -> Self {
Self {
fast_path_processor: IronRdpFastPathProcessorBuilder {
io_channel_id: 1003, // todo(isaiah)
user_channel_id: 1004, // todo(isaiah)
io_channel_id,
user_channel_id,
}
.build(),
image: DecodedImage::new(PixelFormat::RgbA32, width, height),
Expand Down
31 changes: 28 additions & 3 deletions web/packages/teleport/src/lib/tdp/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,18 @@ export default class Client extends EventEmitterWebAuthnSender {
protected codec: Codec;
protected socket: WebSocket | undefined;
private socketAddr: string;
private width: number;
private height: number;
private sdManager: SharedDirectoryManager;
private fastPathProcessor: FastPathProcessor;
private fastPathProcessor: FastPathProcessor | undefined;

private logger = Logger.create('TDPClient');

constructor(socketAddr: string, width: number, height: number) {
super();
this.socketAddr = socketAddr;
this.width = width;
this.height = height;
this.codec = new Codec();
this.sdManager = new SharedDirectoryManager();

Expand All @@ -102,7 +106,6 @@ export default class Client extends EventEmitterWebAuthnSender {
// init initializes the wasm module into memory
init().then(() => {
init_wasm_log(wasmLogLevel);
this.fastPathProcessor = new FastPathProcessor(width, height);
});
}

Expand Down Expand Up @@ -149,7 +152,10 @@ export default class Client extends EventEmitterWebAuthnSender {
case MessageType.PNG2_FRAME:
this.handlePng2Frame(buffer);
break;
case MessageType.REMOTE_FX_FRAME:
case MessageType.RDP_CHANNEL_IDS:
this.handleRDPChannelIDs(buffer);
break;
case MessageType.RDP_FASTPATH_PDU:
this.handleRDPFastPathPDU(buffer);
break;
case MessageType.CLIENT_SCREEN_SPEC:
Expand Down Expand Up @@ -270,9 +276,28 @@ export default class Client extends EventEmitterWebAuthnSender {
);
}

handleRDPChannelIDs(buffer: ArrayBuffer) {
const { ioChannelId, userChannelId } =
this.codec.decodeRDPChannelIDs(buffer);

this.fastPathProcessor = new FastPathProcessor(
this.width,
this.height,
ioChannelId,
userChannelId
);
}

handleRDPFastPathPDU(buffer: ArrayBuffer) {
let rdpFastPathPDU = this.codec.decodeRDPFastPathPDU(buffer);

// This should never happen but let's catch it with an error in case it does.
if (!this.fastPathProcessor)
this.handleError(
new Error('FastPathProcessor not initialized'),
TdpClientEvent.CLIENT_ERROR
);

this.fastPathProcessor.process(
rdpFastPathPDU,
this,
Expand Down
25 changes: 22 additions & 3 deletions web/packages/teleport/src/lib/tdp/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ export enum MessageType {
SHARED_DIRECTORY_LIST_RESPONSE = 26,
PNG2_FRAME = 27,
NOTIFICATION = 28,
REMOTE_FX_FRAME = 29,
RESPONSE_FRAME = 30,
RDP_FASTPATH_PDU = 29,
RDP_RESPONSE_PDU = 30,
RDP_CHANNEL_IDS = 31,
__LAST, // utility value
}

Expand Down Expand Up @@ -101,6 +102,12 @@ export type ClipboardData = {
data: string;
};

// | message type (31) | io_channel_id uint16 | user_channel_id uint16 |
export type RDPChannelIDs = {
ioChannelId: number;
userChannelId: number;
};

export enum Severity {
Info = 0,
Warning = 1,
Expand Down Expand Up @@ -808,7 +815,7 @@ export default class Codec {
const view = new DataView(buffer);
let offset = 0;

view.setUint8(offset, MessageType.RESPONSE_FRAME);
view.setUint8(offset, MessageType.RDP_RESPONSE_PDU);
offset += byteLength;
view.setUint32(offset, responseFrame.byteLength);
offset += uint32Length;
Expand Down Expand Up @@ -949,6 +956,17 @@ export default class Codec {
return new RDPFastPathPDU(new Uint8Array(data));
}

// | message type (31) | io_channel_id uint16 | user_channel_id uint16 |
decodeRDPChannelIDs(buffer: ArrayBuffer): RDPChannelIDs {
const dv = new DataView(buffer);
let offset = 0;
offset += byteLength; // eat message type
const ioChannelId = dv.getUint16(offset);
offset += uint16Length; // eat io_channel_id
const userChannelId = dv.getUint16(offset);
return { ioChannelId, userChannelId };
}

// | message type (12) | err_code error | directory_id uint32 |
decodeSharedDirectoryAcknowledge(
buffer: ArrayBuffer
Expand Down Expand Up @@ -1143,5 +1161,6 @@ export default class Codec {
}

const byteLength = 1;
const uint16Length = 2;
const uint32Length = 4;
const uint64Length = uint32Length * 2;

0 comments on commit 8e8ead1

Please sign in to comment.