[channels,rdpdr] enforce client state checks
Keep track of client channel state and abort on invalid messages for a certain state
This commit is contained in:
parent
d0bf018e95
commit
00b393c094
@ -149,6 +149,9 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
|
||||
if (!rdpdr || !s)
|
||||
return CHANNEL_RC_NULL_DATA;
|
||||
|
||||
WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_SERVER_CAPS);
|
||||
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_CLIENT_CAPS);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 4))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
|
@ -76,6 +76,32 @@ typedef struct
|
||||
BOOL automount;
|
||||
} DEVICE_DRIVE_EXT;
|
||||
|
||||
static const char* rdpdr_state_str(enum RDPDR_CHANNEL_STATE state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case RDPDR_CHANNEL_STATE_INITIAL:
|
||||
return "RDPDR_CHANNEL_STATE_INITIAL";
|
||||
case RDPDR_CHANNEL_STATE_ANNOUNCE:
|
||||
return "RDPDR_CHANNEL_STATE_ANNOUNCE";
|
||||
case RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY:
|
||||
return "RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY";
|
||||
case RDPDR_CHANNEL_STATE_NAME_REQUEST:
|
||||
return "RDPDR_CHANNEL_STATE_NAME_REQUEST";
|
||||
case RDPDR_CHANNEL_STATE_SERVER_CAPS:
|
||||
return "RDPDR_CHANNEL_STATE_SERVER_CAPS";
|
||||
case RDPDR_CHANNEL_STATE_CLIENT_CAPS:
|
||||
return "RDPDR_CHANNEL_STATE_CLIENT_CAPS";
|
||||
case RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM:
|
||||
return "RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM";
|
||||
case RDPDR_CHANNEL_STATE_READY:
|
||||
return "RDPDR_CHANNEL_STATE_READY";
|
||||
case RDPDR_CHANNEL_STATE_USER_LOGGEDON:
|
||||
return "RDPDR_CHANNEL_STATE_USER_LOGGEDON";
|
||||
default:
|
||||
return "RDPDR_CHANNEL_STATE_UNKNOWN";
|
||||
}
|
||||
}
|
||||
static const char* rdpdr_device_type_string(UINT32 type)
|
||||
{
|
||||
switch (type)
|
||||
@ -95,6 +121,17 @@ static const char* rdpdr_device_type_string(UINT32 type)
|
||||
}
|
||||
}
|
||||
|
||||
BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next)
|
||||
{
|
||||
WINPR_ASSERT(rdpdr);
|
||||
|
||||
if (next != rdpdr->state)
|
||||
WLog_Print(rdpdr->log, WLOG_DEBUG, "[RDPDR] transition from %s to %s",
|
||||
rdpdr_state_str(rdpdr->state), rdpdr_state_str(next));
|
||||
rdpdr->state = next;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL device_foreach(rdpdrPlugin* rdpdr, BOOL abortOnFail,
|
||||
BOOL (*fkt)(ULONG_PTR key, void* element, void* data), void* data)
|
||||
{
|
||||
@ -124,7 +161,7 @@ static BOOL device_foreach(rdpdrPlugin* rdpdr, BOOL abortOnFail,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL userLoggedOn);
|
||||
static UINT rdpdr_try_send_device_list_announce_request(rdpdrPlugin* rdpdr);
|
||||
|
||||
static BOOL rdpdr_load_drive(rdpdrPlugin* rdpdr, const char* name, const char* path, BOOL automount)
|
||||
{
|
||||
@ -268,7 +305,7 @@ static LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM
|
||||
if (check_path(drive_path))
|
||||
{
|
||||
rdpdr_load_drive(rdpdr, drive_name, drive_path, TRUE);
|
||||
rdpdr_send_device_list_announce_request(rdpdr, TRUE);
|
||||
rdpdr_try_send_device_list_announce_request(rdpdr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -585,7 +622,7 @@ static void drive_hotplug_fsevent_callback(ConstFSEventStreamRef streamRef,
|
||||
error);
|
||||
}
|
||||
else
|
||||
rdpdr_send_device_list_announce_request(rdpdr, TRUE);
|
||||
rdpdr_try_send_device_list_announce_request(rdpdr);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -973,7 +1010,7 @@ static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg)
|
||||
switch (error)
|
||||
{
|
||||
case ERROR_DISK_CHANGE:
|
||||
rdpdr_send_device_list_announce_request(rdpdr, TRUE);
|
||||
rdpdr_try_send_device_list_announce_request(rdpdr);
|
||||
break;
|
||||
case CHANNEL_RC_OK:
|
||||
case ERROR_OPEN_FAILED:
|
||||
@ -1152,6 +1189,8 @@ static UINT rdpdr_send_client_announce_reply(rdpdrPlugin* rdpdr)
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(rdpdr);
|
||||
WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_ANNOUNCE);
|
||||
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY);
|
||||
|
||||
s = StreamPool_Take(rdpdr->pool, 12);
|
||||
|
||||
@ -1181,6 +1220,8 @@ static UINT rdpdr_send_client_name_request(rdpdrPlugin* rdpdr)
|
||||
size_t computerNameLenW;
|
||||
|
||||
WINPR_ASSERT(rdpdr);
|
||||
WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY);
|
||||
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_NAME_REQUEST);
|
||||
|
||||
if (!rdpdr->computerName[0])
|
||||
{
|
||||
@ -1332,6 +1373,17 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
|
||||
WINPR_ASSERT(rdpdr);
|
||||
WINPR_ASSERT(rdpdr->devman);
|
||||
|
||||
if (userLoggedOn)
|
||||
{
|
||||
WINPR_ASSERT(rdpdr->state >= RDPDR_CHANNEL_STATE_READY);
|
||||
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_USER_LOGGEDON);
|
||||
}
|
||||
else
|
||||
{
|
||||
WINPR_ASSERT(rdpdr->state >= RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM);
|
||||
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_READY);
|
||||
}
|
||||
|
||||
s = StreamPool_Take(rdpdr->pool, 256);
|
||||
|
||||
if (!s)
|
||||
@ -1364,6 +1416,19 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
|
||||
return rdpdr_send(rdpdr, s);
|
||||
}
|
||||
|
||||
UINT rdpdr_try_send_device_list_announce_request(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
WINPR_ASSERT(rdpdr);
|
||||
if (rdpdr->state != RDPDR_CHANNEL_STATE_USER_LOGGEDON)
|
||||
{
|
||||
WLog_Print(rdpdr->log, WLOG_DEBUG,
|
||||
"hotplug event received, but channel [RDPDR] is not ready (state %s), ignoring.",
|
||||
rdpdr_state_str(rdpdr->state));
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
return rdpdr_send_device_list_announce_request(rdpdr, TRUE);
|
||||
}
|
||||
|
||||
static UINT dummy_irp_response(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
wStream* output;
|
||||
@ -1498,6 +1563,57 @@ static UINT rdpdr_process_init(rdpdrPlugin* rdpdr)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL rdpdr_state_check(rdpdrPlugin* rdpdr, UINT16 packetid,
|
||||
enum RDPDR_CHANNEL_STATE expected, enum RDPDR_CHANNEL_STATE next)
|
||||
{
|
||||
WINPR_ASSERT(rdpdr);
|
||||
|
||||
const char* strstate = rdpdr_state_str(rdpdr->state);
|
||||
if (rdpdr->state != expected)
|
||||
{
|
||||
WLog_Print(rdpdr->log, WLOG_ERROR,
|
||||
"channel [RDPDR] received %s, expected state %s but have state %s, aborting.",
|
||||
rdpdr_packetid_string(packetid), rdpdr_state_str(expected), strstate);
|
||||
|
||||
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_INITIAL);
|
||||
return FALSE;
|
||||
}
|
||||
return rdpdr_state_advance(rdpdr, next);
|
||||
}
|
||||
|
||||
static BOOL rdpdr_check_channel_state(rdpdrPlugin* rdpdr, UINT16 packetid)
|
||||
{
|
||||
WINPR_ASSERT(rdpdr);
|
||||
|
||||
switch (packetid)
|
||||
{
|
||||
case PAKID_CORE_SERVER_ANNOUNCE:
|
||||
/* windows servers sometimes send this message.
|
||||
* it seems related to session login (e.g. first initialization for RDP/TLS style login,
|
||||
* then reinitialize the channel after login successful
|
||||
*/
|
||||
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_INITIAL);
|
||||
return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_INITIAL,
|
||||
RDPDR_CHANNEL_STATE_ANNOUNCE);
|
||||
case PAKID_CORE_SERVER_CAPABILITY:
|
||||
return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_NAME_REQUEST,
|
||||
RDPDR_CHANNEL_STATE_SERVER_CAPS);
|
||||
case PAKID_CORE_CLIENTID_CONFIRM:
|
||||
return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_CLIENT_CAPS,
|
||||
RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM);
|
||||
case PAKID_CORE_USER_LOGGEDON:
|
||||
return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_READY,
|
||||
RDPDR_CHANNEL_STATE_USER_LOGGEDON);
|
||||
default:
|
||||
{
|
||||
enum RDPDR_CHANNEL_STATE state = RDPDR_CHANNEL_STATE_READY;
|
||||
if (rdpdr->state == RDPDR_CHANNEL_STATE_USER_LOGGEDON)
|
||||
state = RDPDR_CHANNEL_STATE_USER_LOGGEDON;
|
||||
return rdpdr_state_check(rdpdr, packetid, state, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -1522,6 +1638,9 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
|
||||
|
||||
if (component == RDPDR_CTYP_CORE)
|
||||
{
|
||||
if (!rdpdr_check_channel_state(rdpdr, packetId))
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
switch (packetId)
|
||||
{
|
||||
case PAKID_CORE_SERVER_ANNOUNCE:
|
||||
|
@ -42,11 +42,25 @@
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
enum RDPDR_CHANNEL_STATE
|
||||
{
|
||||
RDPDR_CHANNEL_STATE_INITIAL = 0,
|
||||
RDPDR_CHANNEL_STATE_ANNOUNCE,
|
||||
RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY,
|
||||
RDPDR_CHANNEL_STATE_NAME_REQUEST,
|
||||
RDPDR_CHANNEL_STATE_SERVER_CAPS,
|
||||
RDPDR_CHANNEL_STATE_CLIENT_CAPS,
|
||||
RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM,
|
||||
RDPDR_CHANNEL_STATE_READY,
|
||||
RDPDR_CHANNEL_STATE_USER_LOGGEDON
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CHANNEL_DEF channelDef;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
|
||||
|
||||
enum RDPDR_CHANNEL_STATE state;
|
||||
HANDLE thread;
|
||||
wStream* data_in;
|
||||
void* InitHandle;
|
||||
@ -81,6 +95,7 @@ typedef struct
|
||||
wLog* log;
|
||||
} rdpdrPlugin;
|
||||
|
||||
BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next);
|
||||
UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_MAIN_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user