libfreerdp-core: fix parsing of MCS Disconnect Provider Ultimatum, workaround for 2008 R2 lack of error info pdu on user logoff

This commit is contained in:
Marc-André Moreau 2013-11-14 23:05:29 -05:00
parent c4492411e4
commit fa12414a4b
3 changed files with 100 additions and 17 deletions

View File

@ -818,6 +818,54 @@ BOOL mcs_send_channel_join_confirm(rdpMcs* mcs, UINT16 channel_id)
return (status < 0) ? FALSE : TRUE;
}
/**
* Receive MCS Disconnect Provider Ultimatum PDU.\n
* @param mcs mcs module
*/
BOOL mcs_recv_disconnect_provider_ultimatum(rdpMcs* mcs, wStream* s, int* reason)
{
BYTE b1, b2;
/*
* http://msdn.microsoft.com/en-us/library/cc240872.aspx:
*
* PER encoded (ALIGNED variant of BASIC-PER) PDU contents:
* 21 80
*
* 0x21:
* 0 - --\
* 0 - |
* 1 - | CHOICE: From DomainMCSPDU select disconnectProviderUltimatum (8)
* 0 - | of type DisconnectProviderUltimatum
* 0 - |
* 0 - --/
* 0 - --\
* 1 - |
* | DisconnectProviderUltimatum::reason = rn-user-requested (3)
* 0x80: |
* 1 - --/
* 0 - padding
* 0 - padding
* 0 - padding
* 0 - padding
* 0 - padding
* 0 - padding
* 0 - padding
*/
if (Stream_GetRemainingLength(s) < 1)
return FALSE;
Stream_Rewind_UINT8(s);
Stream_Read_UINT8(s, b1);
Stream_Read_UINT8(s, b2);
*reason = ((b1 & 0x01) << 1) | (b2 >> 7);
return TRUE;
}
/**
* Send MCS Disconnect Provider Ultimatum PDU.\n
* @param mcs mcs module

View File

@ -20,6 +20,8 @@
#ifndef __MCS_H
#define __MCS_H
typedef struct rdp_mcs rdpMcs;
#include "transport.h"
#include <freerdp/crypto/ber.h>
@ -52,6 +54,15 @@ enum MCS_Result
MCS_Result_enum_length = 16
};
enum MCS_Reason
{
MCS_Reason_domain_disconnected = 0,
MCS_Reason_provider_initiated = 1,
MCS_Reason_token_purged = 2,
MCS_Reason_user_requested = 3,
MCS_Reason_channel_purged = 4
};
enum DomainMCSPDU
{
DomainMCSPDU_PlumbDomainIndication = 0,
@ -115,7 +126,7 @@ typedef struct
struct rdp_mcs
{
UINT16 user_id;
struct rdp_transport* transport;
rdpTransport* transport;
DomainParameters domainParameters;
DomainParameters targetParameters;
DomainParameters minimumParameters;
@ -124,7 +135,6 @@ struct rdp_mcs
BOOL user_channel_joined;
BOOL global_channel_joined;
};
typedef struct rdp_mcs rdpMcs;
#define MCS_SEND_DATA_HEADER_MAX_LENGTH 8
@ -148,6 +158,7 @@ BOOL mcs_recv_channel_join_request(rdpMcs* mcs, wStream* s, UINT16* channel_id);
BOOL mcs_send_channel_join_request(rdpMcs* mcs, UINT16 channel_id);
BOOL mcs_recv_channel_join_confirm(rdpMcs* mcs, wStream* s, UINT16* channel_id);
BOOL mcs_send_channel_join_confirm(rdpMcs* mcs, UINT16 channel_id);
BOOL mcs_recv_disconnect_provider_ultimatum(rdpMcs* mcs, wStream* s, int* reason);
BOOL mcs_send_disconnect_provider_ultimatum(rdpMcs* mcs);
BOOL mcs_read_domain_mcspdu_header(wStream* s, enum DomainMCSPDU* domainMCSPDU, UINT16* length);
void mcs_write_domain_mcspdu_header(wStream* s, enum DomainMCSPDU domainMCSPDU, UINT16 length, BYTE options);

View File

@ -229,6 +229,25 @@ wStream* rdp_data_pdu_init(rdpRdp* rdp)
return s;
}
BOOL rdp_set_error_info(rdpRdp* rdp, UINT32 errorInfo)
{
rdp->errorInfo = errorInfo;
if (rdp->errorInfo != ERRINFO_SUCCESS)
{
ErrorInfoEventArgs e;
rdpContext* context = rdp->instance->context;
rdp_print_errinfo(rdp->errorInfo);
EventArgsInit(&e, "freerdp");
e.code = rdp->errorInfo;
PubSub_OnErrorInfo(context->pubSub, context, &e);
}
return TRUE;
}
/**
* Read an RDP packet header.\n
* @param rdp rdp module
@ -256,12 +275,25 @@ BOOL rdp_read_header(rdpRdp* rdp, wStream* s, UINT16* length, UINT16* channelId)
if (MCSPDU == DomainMCSPDU_DisconnectProviderUltimatum)
{
BYTE reason;
int reason = 0;
TerminateEventArgs e;
rdpContext* context = rdp->instance->context;
(void) per_read_enumerated(s, &reason, 0);
DEBUG_RDP("DisconnectProviderUltimatum from server, reason code 0x%02x\n", reason);
if (!mcs_recv_disconnect_provider_ultimatum(rdp->mcs, s, &reason))
return FALSE;
if (rdp->errorInfo == ERRINFO_SUCCESS)
{
/**
* Some servers like Windows Server 2008 R2 do not send the error info pdu
* when the user logs off like they should. Map DisconnectProviderUltimatum
* to a ERRINFO_LOGOFF_BY_USER when the errinfo code is ERRINFO_SUCCESS.
*/
rdp_set_error_info(rdp, ERRINFO_LOGOFF_BY_USER);
}
fprintf(stderr, "DisconnectProviderUltimatum: reason: %d\n", reason);
rdp->disconnect = TRUE;
@ -503,22 +535,14 @@ BOOL rdp_send_data_pdu(rdpRdp* rdp, wStream* s, BYTE type, UINT16 channel_id)
BOOL rdp_recv_set_error_info_data_pdu(rdpRdp* rdp, wStream* s)
{
UINT32 errorInfo;
if (Stream_GetRemainingLength(s) < 4)
return FALSE;
Stream_Read_UINT32(s, rdp->errorInfo); /* errorInfo (4 bytes) */
Stream_Read_UINT32(s, errorInfo); /* errorInfo (4 bytes) */
if (rdp->errorInfo != ERRINFO_SUCCESS)
{
ErrorInfoEventArgs e;
rdpContext* context = rdp->instance->context;
rdp_print_errinfo(rdp->errorInfo);
EventArgsInit(&e, "freerdp");
e.code = rdp->errorInfo;
PubSub_OnErrorInfo(context->pubSub, context, &e);
}
rdp_set_error_info(rdp, errorInfo);
return TRUE;
}