1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * Geometry tracking Virtual Channel Extension
4  *
5  * Copyright 2017 David Fort <contact@hardening-consulting.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include <winpr/crt.h>
29 #include <winpr/synch.h>
30 #include <winpr/print.h>
31 #include <winpr/stream.h>
32 #include <winpr/cmdline.h>
33 #include <winpr/collections.h>
34 
35 #include <freerdp/addin.h>
36 #include <freerdp/client/geometry.h>
37 #include <freerdp/channels/log.h>
38 
39 #define TAG CHANNELS_TAG("geometry.client")
40 
41 #include "geometry_main.h"
42 
43 struct _GEOMETRY_CHANNEL_CALLBACK
44 {
45 	IWTSVirtualChannelCallback iface;
46 
47 	IWTSPlugin* plugin;
48 	IWTSVirtualChannelManager* channel_mgr;
49 	IWTSVirtualChannel* channel;
50 };
51 typedef struct _GEOMETRY_CHANNEL_CALLBACK GEOMETRY_CHANNEL_CALLBACK;
52 
53 struct _GEOMETRY_LISTENER_CALLBACK
54 {
55 	IWTSListenerCallback iface;
56 
57 	IWTSPlugin* plugin;
58 	IWTSVirtualChannelManager* channel_mgr;
59 	GEOMETRY_CHANNEL_CALLBACK* channel_callback;
60 };
61 typedef struct _GEOMETRY_LISTENER_CALLBACK GEOMETRY_LISTENER_CALLBACK;
62 
63 struct _GEOMETRY_PLUGIN
64 {
65 	IWTSPlugin iface;
66 
67 	IWTSListener* listener;
68 	GEOMETRY_LISTENER_CALLBACK* listener_callback;
69 
70 	GeometryClientContext* context;
71 	BOOL initialized;
72 };
73 typedef struct _GEOMETRY_PLUGIN GEOMETRY_PLUGIN;
74 
mappedGeometryHash(UINT64 * g)75 static UINT32 mappedGeometryHash(UINT64* g)
76 {
77 	return (UINT32)((*g >> 32) + (*g & 0xffffffff));
78 }
79 
mappedGeometryKeyCompare(UINT64 * g1,UINT64 * g2)80 static BOOL mappedGeometryKeyCompare(UINT64* g1, UINT64* g2)
81 {
82 	return *g1 == *g2;
83 }
84 
freerdp_rgndata_reset(FREERDP_RGNDATA * data)85 static void freerdp_rgndata_reset(FREERDP_RGNDATA* data)
86 {
87 	data->nRectCount = 0;
88 }
89 
geometry_read_RGNDATA(wStream * s,UINT32 len,FREERDP_RGNDATA * rgndata)90 static UINT32 geometry_read_RGNDATA(wStream* s, UINT32 len, FREERDP_RGNDATA* rgndata)
91 {
92 	UINT32 dwSize, iType;
93 	INT32 right, bottom;
94 	INT32 x, y, w, h;
95 
96 	if (len < 32)
97 	{
98 		WLog_ERR(TAG, "invalid RGNDATA");
99 		return ERROR_INVALID_DATA;
100 	}
101 
102 	Stream_Read_UINT32(s, dwSize);
103 
104 	if (dwSize != 32)
105 	{
106 		WLog_ERR(TAG, "invalid RGNDATA dwSize");
107 		return ERROR_INVALID_DATA;
108 	}
109 
110 	Stream_Read_UINT32(s, iType);
111 
112 	if (iType != RDH_RECTANGLE)
113 	{
114 		WLog_ERR(TAG, "iType %" PRIu32 " for RGNDATA is not supported", iType);
115 		return ERROR_UNSUPPORTED_TYPE;
116 	}
117 
118 	Stream_Read_UINT32(s, rgndata->nRectCount);
119 	Stream_Seek_UINT32(s); /* nRgnSize IGNORED */
120 	Stream_Read_INT32(s, x);
121 	Stream_Read_INT32(s, y);
122 	Stream_Read_INT32(s, right);
123 	Stream_Read_INT32(s, bottom);
124 	if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
125 		return ERROR_INVALID_DATA;
126 	w = right - x;
127 	h = bottom - y;
128 	if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
129 		return ERROR_INVALID_DATA;
130 	rgndata->boundingRect.x = (INT16)x;
131 	rgndata->boundingRect.y = (INT16)y;
132 	rgndata->boundingRect.width = (INT16)w;
133 	rgndata->boundingRect.height = (INT16)h;
134 	len -= 32;
135 
136 	if (len / (4 * 4) < rgndata->nRectCount)
137 	{
138 		WLog_ERR(TAG, "not enough data for region rectangles");
139 	}
140 
141 	if (rgndata->nRectCount)
142 	{
143 		UINT32 i;
144 		RDP_RECT* tmp = realloc(rgndata->rects, rgndata->nRectCount * sizeof(RDP_RECT));
145 
146 		if (!tmp)
147 		{
148 			WLog_ERR(TAG, "unable to allocate memory for %" PRIu32 " RECTs", rgndata->nRectCount);
149 			return CHANNEL_RC_NO_MEMORY;
150 		}
151 		rgndata->rects = tmp;
152 
153 		for (i = 0; i < rgndata->nRectCount; i++)
154 		{
155 			Stream_Read_INT32(s, x);
156 			Stream_Read_INT32(s, y);
157 			Stream_Read_INT32(s, right);
158 			Stream_Read_INT32(s, bottom);
159 			if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
160 				return ERROR_INVALID_DATA;
161 			w = right - x;
162 			h = bottom - y;
163 			if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
164 				return ERROR_INVALID_DATA;
165 			rgndata->rects[i].x = (INT16)x;
166 			rgndata->rects[i].y = (INT16)y;
167 			rgndata->rects[i].width = (INT16)w;
168 			rgndata->rects[i].height = (INT16)h;
169 		}
170 	}
171 
172 	return CHANNEL_RC_OK;
173 }
174 
175 /**
176  * Function description
177  *
178  * @return 0 on success, otherwise a Win32 error code
179  */
geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK * callback,wStream * s)180 static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s)
181 {
182 	UINT32 length, cbGeometryBuffer;
183 	MAPPED_GEOMETRY* mappedGeometry;
184 	GEOMETRY_PLUGIN* geometry;
185 	GeometryClientContext* context;
186 	UINT ret = CHANNEL_RC_OK;
187 	UINT32 version, updateType, geometryType;
188 	UINT64 id;
189 
190 	geometry = (GEOMETRY_PLUGIN*)callback->plugin;
191 	context = (GeometryClientContext*)geometry->iface.pInterface;
192 
193 	if (Stream_GetRemainingLength(s) < 4)
194 	{
195 		WLog_ERR(TAG, "not enough remaining data");
196 		return ERROR_INVALID_DATA;
197 	}
198 
199 	Stream_Read_UINT32(s, length); /* Length (4 bytes) */
200 
201 	if (length < 73 || Stream_GetRemainingLength(s) < (length - 4))
202 	{
203 		WLog_ERR(TAG, "invalid packet length");
204 		return ERROR_INVALID_DATA;
205 	}
206 
207 	Stream_Read_UINT32(s, version);
208 	Stream_Read_UINT64(s, id);
209 	Stream_Read_UINT32(s, updateType);
210 	Stream_Seek_UINT32(s); /* flags */
211 
212 	mappedGeometry = HashTable_GetItemValue(context->geometries, &id);
213 
214 	if (updateType == GEOMETRY_CLEAR)
215 	{
216 		if (!mappedGeometry)
217 		{
218 			WLog_ERR(TAG, "geometry 0x%" PRIx64 " not found here, ignoring clear command", id);
219 			return CHANNEL_RC_OK;
220 		}
221 
222 		WLog_DBG(TAG, "clearing geometry 0x%" PRIx64 "", id);
223 
224 		if (mappedGeometry->MappedGeometryClear &&
225 		    !mappedGeometry->MappedGeometryClear(mappedGeometry))
226 			return ERROR_INTERNAL_ERROR;
227 
228 		if (!HashTable_Remove(context->geometries, &id))
229 			WLog_ERR(TAG, "geometry not removed from geometries");
230 	}
231 	else if (updateType == GEOMETRY_UPDATE)
232 	{
233 		BOOL newOne = FALSE;
234 
235 		if (!mappedGeometry)
236 		{
237 			newOne = TRUE;
238 			WLog_DBG(TAG, "creating geometry 0x%" PRIx64 "", id);
239 			mappedGeometry = calloc(1, sizeof(MAPPED_GEOMETRY));
240 			if (!mappedGeometry)
241 				return CHANNEL_RC_NO_MEMORY;
242 
243 			mappedGeometry->refCounter = 1;
244 			mappedGeometry->mappingId = id;
245 
246 			if (HashTable_Add(context->geometries, &(mappedGeometry->mappingId), mappedGeometry) <
247 			    0)
248 			{
249 				WLog_ERR(TAG, "unable to register geometry 0x%" PRIx64 " in the table", id);
250 				free(mappedGeometry);
251 				return CHANNEL_RC_NO_MEMORY;
252 			}
253 		}
254 		else
255 		{
256 			WLog_DBG(TAG, "updating geometry 0x%" PRIx64 "", id);
257 		}
258 
259 		Stream_Read_UINT64(s, mappedGeometry->topLevelId);
260 
261 		Stream_Read_INT32(s, mappedGeometry->left);
262 		Stream_Read_INT32(s, mappedGeometry->top);
263 		Stream_Read_INT32(s, mappedGeometry->right);
264 		Stream_Read_INT32(s, mappedGeometry->bottom);
265 
266 		Stream_Read_INT32(s, mappedGeometry->topLevelLeft);
267 		Stream_Read_INT32(s, mappedGeometry->topLevelTop);
268 		Stream_Read_INT32(s, mappedGeometry->topLevelRight);
269 		Stream_Read_INT32(s, mappedGeometry->topLevelBottom);
270 
271 		Stream_Read_UINT32(s, geometryType);
272 
273 		Stream_Read_UINT32(s, cbGeometryBuffer);
274 		if (Stream_GetRemainingLength(s) < cbGeometryBuffer)
275 		{
276 			WLog_ERR(TAG, "invalid packet length");
277 			return ERROR_INVALID_DATA;
278 		}
279 
280 		if (cbGeometryBuffer)
281 		{
282 			ret = geometry_read_RGNDATA(s, cbGeometryBuffer, &mappedGeometry->geometry);
283 			if (ret != CHANNEL_RC_OK)
284 				return ret;
285 		}
286 		else
287 		{
288 			freerdp_rgndata_reset(&mappedGeometry->geometry);
289 		}
290 
291 		if (newOne)
292 		{
293 			if (context->MappedGeometryAdded &&
294 			    !context->MappedGeometryAdded(context, mappedGeometry))
295 			{
296 				WLog_ERR(TAG, "geometry added callback failed");
297 				ret = ERROR_INTERNAL_ERROR;
298 			}
299 		}
300 		else
301 		{
302 			if (mappedGeometry->MappedGeometryUpdate &&
303 			    !mappedGeometry->MappedGeometryUpdate(mappedGeometry))
304 			{
305 				WLog_ERR(TAG, "geometry update callback failed");
306 				ret = ERROR_INTERNAL_ERROR;
307 			}
308 		}
309 	}
310 	else
311 	{
312 		WLog_ERR(TAG, "unknown updateType=%" PRIu32 "", updateType);
313 		ret = CHANNEL_RC_OK;
314 	}
315 
316 	return ret;
317 }
318 
319 /**
320  * Function description
321  *
322  * @return 0 on success, otherwise a Win32 error code
323  */
geometry_on_data_received(IWTSVirtualChannelCallback * pChannelCallback,wStream * data)324 static UINT geometry_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
325 {
326 	GEOMETRY_CHANNEL_CALLBACK* callback = (GEOMETRY_CHANNEL_CALLBACK*)pChannelCallback;
327 	return geometry_recv_pdu(callback, data);
328 }
329 
330 /**
331  * Function description
332  *
333  * @return 0 on success, otherwise a Win32 error code
334  */
geometry_on_close(IWTSVirtualChannelCallback * pChannelCallback)335 static UINT geometry_on_close(IWTSVirtualChannelCallback* pChannelCallback)
336 {
337 	free(pChannelCallback);
338 	return CHANNEL_RC_OK;
339 }
340 
341 /**
342  * Function description
343  *
344  * @return 0 on success, otherwise a Win32 error code
345  */
geometry_on_new_channel_connection(IWTSListenerCallback * pListenerCallback,IWTSVirtualChannel * pChannel,BYTE * Data,BOOL * pbAccept,IWTSVirtualChannelCallback ** ppCallback)346 static UINT geometry_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
347                                                IWTSVirtualChannel* pChannel, BYTE* Data,
348                                                BOOL* pbAccept,
349                                                IWTSVirtualChannelCallback** ppCallback)
350 {
351 	GEOMETRY_CHANNEL_CALLBACK* callback;
352 	GEOMETRY_LISTENER_CALLBACK* listener_callback = (GEOMETRY_LISTENER_CALLBACK*)pListenerCallback;
353 
354 	WINPR_UNUSED(Data);
355 	WINPR_UNUSED(pbAccept);
356 
357 	callback = (GEOMETRY_CHANNEL_CALLBACK*)calloc(1, sizeof(GEOMETRY_CHANNEL_CALLBACK));
358 
359 	if (!callback)
360 	{
361 		WLog_ERR(TAG, "calloc failed!");
362 		return CHANNEL_RC_NO_MEMORY;
363 	}
364 
365 	callback->iface.OnDataReceived = geometry_on_data_received;
366 	callback->iface.OnClose = geometry_on_close;
367 	callback->plugin = listener_callback->plugin;
368 	callback->channel_mgr = listener_callback->channel_mgr;
369 	callback->channel = pChannel;
370 	listener_callback->channel_callback = callback;
371 	*ppCallback = (IWTSVirtualChannelCallback*)callback;
372 	return CHANNEL_RC_OK;
373 }
374 
375 /**
376  * Function description
377  *
378  * @return 0 on success, otherwise a Win32 error code
379  */
geometry_plugin_initialize(IWTSPlugin * pPlugin,IWTSVirtualChannelManager * pChannelMgr)380 static UINT geometry_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
381 {
382 	UINT status;
383 	GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)pPlugin;
384 	if (geometry->initialized)
385 	{
386 		WLog_ERR(TAG, "[%s] channel initialized twice, aborting", GEOMETRY_DVC_CHANNEL_NAME);
387 		return ERROR_INVALID_DATA;
388 	}
389 	geometry->listener_callback =
390 	    (GEOMETRY_LISTENER_CALLBACK*)calloc(1, sizeof(GEOMETRY_LISTENER_CALLBACK));
391 
392 	if (!geometry->listener_callback)
393 	{
394 		WLog_ERR(TAG, "calloc failed!");
395 		return CHANNEL_RC_NO_MEMORY;
396 	}
397 
398 	geometry->listener_callback->iface.OnNewChannelConnection = geometry_on_new_channel_connection;
399 	geometry->listener_callback->plugin = pPlugin;
400 	geometry->listener_callback->channel_mgr = pChannelMgr;
401 	status =
402 	    pChannelMgr->CreateListener(pChannelMgr, GEOMETRY_DVC_CHANNEL_NAME, 0,
403 	                                &geometry->listener_callback->iface, &(geometry->listener));
404 	geometry->listener->pInterface = geometry->iface.pInterface;
405 
406 	geometry->initialized = status == CHANNEL_RC_OK;
407 	return status;
408 }
409 
410 /**
411  * Function description
412  *
413  * @return 0 on success, otherwise a Win32 error code
414  */
geometry_plugin_terminated(IWTSPlugin * pPlugin)415 static UINT geometry_plugin_terminated(IWTSPlugin* pPlugin)
416 {
417 	GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)pPlugin;
418 	GeometryClientContext* context = (GeometryClientContext*)geometry->iface.pInterface;
419 
420 	if (geometry && geometry->listener_callback)
421 	{
422 		IWTSVirtualChannelManager* mgr = geometry->listener_callback->channel_mgr;
423 		if (mgr)
424 			IFCALL(mgr->DestroyListener, mgr, geometry->listener);
425 	}
426 
427 	if (context)
428 		HashTable_Free(context->geometries);
429 
430 	free(geometry->listener_callback);
431 	free(geometry->iface.pInterface);
432 	free(pPlugin);
433 	return CHANNEL_RC_OK;
434 }
435 
436 /**
437  * Channel Client Interface
438  */
439 
440 #ifdef BUILTIN_CHANNELS
441 #define DVCPluginEntry geometry_DVCPluginEntry
442 #else
443 #define DVCPluginEntry FREERDP_API DVCPluginEntry
444 #endif
445 
446 /**
447  * Function description
448  *
449  * @return 0 on success, otherwise a Win32 error code
450  */
DVCPluginEntry(IDRDYNVC_ENTRY_POINTS * pEntryPoints)451 UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
452 {
453 	UINT error = CHANNEL_RC_OK;
454 	GEOMETRY_PLUGIN* geometry;
455 	GeometryClientContext* context;
456 	geometry = (GEOMETRY_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "geometry");
457 
458 	if (!geometry)
459 	{
460 		geometry = (GEOMETRY_PLUGIN*)calloc(1, sizeof(GEOMETRY_PLUGIN));
461 
462 		if (!geometry)
463 		{
464 			WLog_ERR(TAG, "calloc failed!");
465 			return CHANNEL_RC_NO_MEMORY;
466 		}
467 
468 		geometry->iface.Initialize = geometry_plugin_initialize;
469 		geometry->iface.Connected = NULL;
470 		geometry->iface.Disconnected = NULL;
471 		geometry->iface.Terminated = geometry_plugin_terminated;
472 		context = (GeometryClientContext*)calloc(1, sizeof(GeometryClientContext));
473 
474 		if (!context)
475 		{
476 			WLog_ERR(TAG, "calloc failed!");
477 			goto error_context;
478 		}
479 
480 		context->geometries = HashTable_New(FALSE);
481 		context->geometries->hash = (HASH_TABLE_HASH_FN)mappedGeometryHash;
482 		context->geometries->keyCompare = (HASH_TABLE_KEY_COMPARE_FN)mappedGeometryKeyCompare;
483 		context->geometries->valueFree = (HASH_TABLE_VALUE_FREE_FN)mappedGeometryUnref;
484 
485 		context->handle = (void*)geometry;
486 		geometry->iface.pInterface = (void*)context;
487 		geometry->context = context;
488 		error = pEntryPoints->RegisterPlugin(pEntryPoints, "geometry", (IWTSPlugin*)geometry);
489 	}
490 	else
491 	{
492 		WLog_ERR(TAG, "could not get geometry Plugin.");
493 		return CHANNEL_RC_BAD_CHANNEL;
494 	}
495 
496 	return error;
497 
498 error_context:
499 	free(geometry);
500 	return CHANNEL_RC_NO_MEMORY;
501 }
502