diff --git a/channels/rdpdr/client/rdpdr_capabilities.c b/channels/rdpdr/client/rdpdr_capabilities.c index 1d572e02d..0c5331be5 100644 --- a/channels/rdpdr/client/rdpdr_capabilities.c +++ b/channels/rdpdr/client/rdpdr_capabilities.c @@ -43,28 +43,58 @@ static void rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s) WINPR_UNUSED(rdpdr); const RDPDR_CAPABILITY_HEADER header = { CAP_GENERAL_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH + 36, GENERAL_CAPABILITY_VERSION_02 }; + + const UINT32 ioCode1 = rdpdr->clientIOCode1 & rdpdr->serverIOCode1; + const UINT32 ioCode2 = rdpdr->clientIOCode2 & rdpdr->serverIOCode2; + rdpdr_write_capset_header(rdpdr->log, s, &header); - Stream_Write_UINT32(s, 0); /* osType, ignored on receipt */ - Stream_Write_UINT32(s, 0); /* osVersion, unused and must be set to zero */ + Stream_Write_UINT32(s, rdpdr->clientOsType); /* osType, ignored on receipt */ + Stream_Write_UINT32(s, rdpdr->clientOsVersion); /* osVersion, unused and must be set to zero */ Stream_Write_UINT16(s, rdpdr->clientVersionMajor); /* protocolMajorVersion, must be set to 1 */ Stream_Write_UINT16(s, rdpdr->clientVersionMinor); /* protocolMinorVersion */ - Stream_Write_UINT32(s, 0x0000FFFF); /* ioCode1 */ - Stream_Write_UINT32(s, 0); /* ioCode2, must be set to zero, reserved for future use */ - Stream_Write_UINT32(s, RDPDR_DEVICE_REMOVE_PDUS | RDPDR_CLIENT_DISPLAY_NAME_PDU | - RDPDR_USER_LOGGEDON_PDU); /* extendedPDU */ - Stream_Write_UINT32(s, ENABLE_ASYNCIO); /* extraFlags1 */ - Stream_Write_UINT32(s, 0); /* extraFlags2, must be set to zero, reserved for future use */ + Stream_Write_UINT32(s, ioCode1); /* ioCode1 */ + Stream_Write_UINT32(s, ioCode2); /* ioCode2, must be set to zero, reserved for future use */ + Stream_Write_UINT32(s, rdpdr->clientExtendedPDU); /* extendedPDU */ + Stream_Write_UINT32(s, rdpdr->clientExtraFlags1); /* extraFlags1 */ Stream_Write_UINT32( - s, 0); /* SpecialTypeDeviceCap, number of special devices to be redirected before logon */ + s, + rdpdr->clientExtraFlags2); /* extraFlags2, must be set to zero, reserved for future use */ + Stream_Write_UINT32( + s, rdpdr->clientSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to + be redirected before logon */ } /* Process device direction general capability set */ static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s, const RDPDR_CAPABILITY_HEADER* header) { - WINPR_ASSERT(header); - Stream_Seek(s, header->CapabilityLength); + + if (header->CapabilityLength != 36) + { + WLog_Print(rdpdr->log, WLOG_ERROR, + "CAP_GENERAL_TYPE::CapabilityLength expected 36, got %" PRIu32, + header->CapabilityLength); + return ERROR_INVALID_DATA; + } + if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 36)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, rdpdr->serverOsType); /* osType, ignored on receipt */ + Stream_Read_UINT32(s, rdpdr->serverOsVersion); /* osVersion, unused and must be set to zero */ + Stream_Read_UINT16(s, rdpdr->serverVersionMajor); /* protocolMajorVersion, must be set to 1 */ + Stream_Read_UINT16(s, rdpdr->serverVersionMinor); /* protocolMinorVersion */ + Stream_Read_UINT32(s, rdpdr->serverIOCode1); /* ioCode1 */ + Stream_Read_UINT32( + s, rdpdr->serverIOCode2); /* ioCode2, must be set to zero, reserved for future use */ + Stream_Read_UINT32(s, rdpdr->serverExtendedPDU); /* extendedPDU */ + Stream_Read_UINT32(s, rdpdr->serverExtraFlags1); /* extraFlags1 */ + Stream_Read_UINT32( + s, + rdpdr->serverExtraFlags2); /* extraFlags2, must be set to zero, reserved for future use */ + Stream_Read_UINT32( + s, rdpdr->serverSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to + be redirected before logon */ return CHANNEL_RC_OK; } diff --git a/channels/rdpdr/client/rdpdr_main.c b/channels/rdpdr/client/rdpdr_main.c index 45798008e..bda07d0c2 100644 --- a/channels/rdpdr/client/rdpdr_main.c +++ b/channels/rdpdr/client/rdpdr_main.c @@ -102,6 +102,7 @@ static const char* rdpdr_state_str(enum RDPDR_CHANNEL_STATE state) return "RDPDR_CHANNEL_STATE_UNKNOWN"; } } + static const char* rdpdr_device_type_string(UINT32 type) { switch (type) @@ -121,6 +122,44 @@ static const char* rdpdr_device_type_string(UINT32 type) } } +static const char* support_str(BOOL val) +{ + if (val) + return "supported"; + return "not found"; +} + +static const char* rdpdr_caps_pdu_str(UINT32 flag) +{ + switch (flag) + { + case RDPDR_DEVICE_REMOVE_PDUS: + return "RDPDR_USER_LOGGEDON_PDU"; + case RDPDR_CLIENT_DISPLAY_NAME_PDU: + return "RDPDR_CLIENT_DISPLAY_NAME_PDU"; + case RDPDR_USER_LOGGEDON_PDU: + return "RDPDR_USER_LOGGEDON_PDU"; + default: + return "RDPDR_UNKNONW"; + } +} + +static BOOL rdpdr_check_extended_pdu_flag(rdpdrPlugin* rdpdr, UINT32 flag) +{ + WINPR_ASSERT(rdpdr); + + const BOOL client = (rdpdr->clientExtendedPDU & flag) != 0; + const BOOL server = (rdpdr->serverExtendedPDU & flag) != 0; + + if (!client || !server) + { + WLog_Print(rdpdr->log, WLOG_WARN, "Checking ExtendedPDU::%s, client %s, server %s", + rdpdr_caps_pdu_str(flag), support_str(client), support_str(server)); + return FALSE; + } + return TRUE; +} + BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next) { WINPR_ASSERT(rdpdr); @@ -201,6 +240,9 @@ static UINT rdpdr_send_device_list_remove_request(rdpdrPlugin* rdpdr, UINT32 cou if (count == 0) return CHANNEL_RC_OK; + if (!rdpdr_check_extended_pdu_flag(rdpdr, RDPDR_DEVICE_REMOVE_PDUS)) + return CHANNEL_RC_OK; + s = StreamPool_Take(rdpdr->pool, count * sizeof(UINT32) + 8); if (!s) @@ -1629,6 +1671,9 @@ static BOOL rdpdr_check_channel_state(rdpdrPlugin* rdpdr, UINT16 packetid) return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM, 1, RDPDR_CHANNEL_STATE_CLIENT_CAPS); case PAKID_CORE_USER_LOGGEDON: + if (!rdpdr_check_extended_pdu_flag(rdpdr, RDPDR_USER_LOGGEDON_PDU)) + return FALSE; + return rdpdr_state_check( rdpdr, packetid, RDPDR_CHANNEL_STATE_USER_LOGGEDON, 4, RDPDR_CHANNEL_STATE_NAME_REQUEST, RDPDR_CHANNEL_STATE_CLIENT_CAPS, @@ -2209,6 +2254,18 @@ FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS p } rdpdr->log = WLog_Get(TAG); + rdpdr->clientExtendedPDU = + RDPDR_DEVICE_REMOVE_PDUS | RDPDR_CLIENT_DISPLAY_NAME_PDU | RDPDR_USER_LOGGEDON_PDU; + rdpdr->clientIOCode1 = + RDPDR_IRP_MJ_CREATE | RDPDR_IRP_MJ_CLEANUP | RDPDR_IRP_MJ_CLOSE | RDPDR_IRP_MJ_READ | + RDPDR_IRP_MJ_WRITE | RDPDR_IRP_MJ_FLUSH_BUFFERS | RDPDR_IRP_MJ_SHUTDOWN | + RDPDR_IRP_MJ_DEVICE_CONTROL | RDPDR_IRP_MJ_QUERY_VOLUME_INFORMATION | + RDPDR_IRP_MJ_SET_VOLUME_INFORMATION | RDPDR_IRP_MJ_QUERY_INFORMATION | + RDPDR_IRP_MJ_SET_INFORMATION | RDPDR_IRP_MJ_DIRECTORY_CONTROL | RDPDR_IRP_MJ_LOCK_CONTROL | + RDPDR_IRP_MJ_QUERY_SECURITY | RDPDR_IRP_MJ_SET_SECURITY; + + rdpdr->clientExtraFlags1 = ENABLE_ASYNCIO; + rdpdr->pool = StreamPool_New(TRUE, 1024); if (!rdpdr->pool) { diff --git a/channels/rdpdr/client/rdpdr_main.h b/channels/rdpdr/client/rdpdr_main.h index 134dfa796..50627569a 100644 --- a/channels/rdpdr/client/rdpdr_main.h +++ b/channels/rdpdr/client/rdpdr_main.h @@ -70,10 +70,28 @@ typedef struct DEVMAN* devman; BOOL ignoreInvalidDevices; + UINT32 serverOsType; + UINT32 serverOsVersion; UINT16 serverVersionMajor; UINT16 serverVersionMinor; + UINT32 serverExtendedPDU; + UINT32 serverIOCode1; + UINT32 serverIOCode2; + UINT32 serverExtraFlags1; + UINT32 serverExtraFlags2; + UINT32 serverSpecialTypeDeviceCap; + + UINT32 clientOsType; + UINT32 clientOsVersion; UINT16 clientVersionMajor; UINT16 clientVersionMinor; + UINT32 clientExtendedPDU; + UINT32 clientIOCode1; + UINT32 clientIOCode2; + UINT32 clientExtraFlags1; + UINT32 clientExtraFlags2; + UINT32 clientSpecialTypeDeviceCap; + UINT32 clientID; char computerName[256];