1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * RDP Server Redirection
4  *
5  * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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 <winpr/crt.h>
25 #include <freerdp/log.h>
26 
27 #include "connection.h"
28 
29 #include "redirection.h"
30 
31 #define TAG FREERDP_TAG("core.redirection")
32 
33 struct rdp_redirection
34 {
35 	UINT32 flags;
36 	UINT32 sessionID;
37 	BYTE* TsvUrl;
38 	DWORD TsvUrlLength;
39 	char* Username;
40 	char* Domain;
41 	BYTE* Password;
42 	DWORD PasswordLength;
43 	char* TargetFQDN;
44 	BYTE* LoadBalanceInfo;
45 	DWORD LoadBalanceInfoLength;
46 	char* TargetNetBiosName;
47 	char* TargetNetAddress;
48 	UINT32 TargetNetAddressesCount;
49 	char** TargetNetAddresses;
50 };
51 
rdp_print_redirection_flags(UINT32 flags)52 static void rdp_print_redirection_flags(UINT32 flags)
53 {
54 	WLog_DBG(TAG, "redirectionFlags = {");
55 
56 	if (flags & LB_TARGET_NET_ADDRESS)
57 		WLog_DBG(TAG, "\tLB_TARGET_NET_ADDRESS");
58 
59 	if (flags & LB_LOAD_BALANCE_INFO)
60 		WLog_DBG(TAG, "\tLB_LOAD_BALANCE_INFO");
61 
62 	if (flags & LB_USERNAME)
63 		WLog_DBG(TAG, "\tLB_USERNAME");
64 
65 	if (flags & LB_DOMAIN)
66 		WLog_DBG(TAG, "\tLB_DOMAIN");
67 
68 	if (flags & LB_PASSWORD)
69 		WLog_DBG(TAG, "\tLB_PASSWORD");
70 
71 	if (flags & LB_DONTSTOREUSERNAME)
72 		WLog_DBG(TAG, "\tLB_DONTSTOREUSERNAME");
73 
74 	if (flags & LB_SMARTCARD_LOGON)
75 		WLog_DBG(TAG, "\tLB_SMARTCARD_LOGON");
76 
77 	if (flags & LB_NOREDIRECT)
78 		WLog_DBG(TAG, "\tLB_NOREDIRECT");
79 
80 	if (flags & LB_TARGET_FQDN)
81 		WLog_DBG(TAG, "\tLB_TARGET_FQDN");
82 
83 	if (flags & LB_TARGET_NETBIOS_NAME)
84 		WLog_DBG(TAG, "\tLB_TARGET_NETBIOS_NAME");
85 
86 	if (flags & LB_TARGET_NET_ADDRESSES)
87 		WLog_DBG(TAG, "\tLB_TARGET_NET_ADDRESSES");
88 
89 	if (flags & LB_CLIENT_TSV_URL)
90 		WLog_DBG(TAG, "\tLB_CLIENT_TSV_URL");
91 
92 	if (flags & LB_SERVER_TSV_CAPABLE)
93 		WLog_DBG(TAG, "\tLB_SERVER_TSV_CAPABLE");
94 
95 	WLog_DBG(TAG, "}");
96 }
97 
rdp_redirection_read_unicode_string(wStream * s,char ** str,size_t maxLength)98 static BOOL rdp_redirection_read_unicode_string(wStream* s, char** str, size_t maxLength)
99 {
100 	UINT32 length;
101 	WCHAR* wstr = NULL;
102 
103 	if (Stream_GetRemainingLength(s) < 4)
104 	{
105 		WLog_ERR(TAG, "rdp_redirection_read_string failure: cannot read length");
106 		return FALSE;
107 	}
108 
109 	Stream_Read_UINT32(s, length);
110 
111 	if ((length % 2) || length < 2 || length > maxLength)
112 	{
113 		WLog_ERR(TAG,
114 		         "rdp_redirection_read_string failure: invalid unicode string length: %" PRIu32 "",
115 		         length);
116 		return FALSE;
117 	}
118 
119 	if (Stream_GetRemainingLength(s) < length)
120 	{
121 		WLog_ERR(TAG,
122 		         "rdp_redirection_read_string failure: insufficient stream length (%" PRIu32
123 		         " bytes required)",
124 		         length);
125 		return FALSE;
126 	}
127 
128 	wstr = (WCHAR*)Stream_Pointer(s);
129 
130 	if (wstr[length / 2 - 1])
131 	{
132 		WLog_ERR(TAG, "rdp_redirection_read_string failure: unterminated unicode string");
133 		return FALSE;
134 	}
135 
136 	if (ConvertFromUnicode(CP_UTF8, 0, wstr, -1, str, 0, NULL, NULL) < 1)
137 	{
138 		WLog_ERR(TAG, "rdp_redirection_read_string failure: string conversion failed");
139 		return FALSE;
140 	}
141 
142 	Stream_Seek(s, length);
143 	return TRUE;
144 }
145 
rdp_redirection_apply_settings(rdpRdp * rdp)146 int rdp_redirection_apply_settings(rdpRdp* rdp)
147 {
148 	rdpSettings* settings = rdp->settings;
149 	rdpRedirection* redirection = rdp->redirection;
150 	settings->RedirectionFlags = redirection->flags;
151 	settings->RedirectedSessionId = redirection->sessionID;
152 
153 	if (settings->RedirectionFlags & LB_LOAD_BALANCE_INFO)
154 	{
155 		/* LoadBalanceInfo may not contain a null terminator */
156 		free(settings->LoadBalanceInfo);
157 		settings->LoadBalanceInfoLength = redirection->LoadBalanceInfoLength;
158 		settings->LoadBalanceInfo = (BYTE*)malloc(settings->LoadBalanceInfoLength);
159 
160 		if (!settings->LoadBalanceInfo)
161 			return -1;
162 
163 		CopyMemory(settings->LoadBalanceInfo, redirection->LoadBalanceInfo,
164 		           settings->LoadBalanceInfoLength);
165 	}
166 	else
167 	{
168 		/**
169 		 * Free previous LoadBalanceInfo, if any, otherwise it may end up
170 		 * being reused for the redirected session, which is not what we want.
171 		 */
172 		free(settings->LoadBalanceInfo);
173 		settings->LoadBalanceInfo = NULL;
174 		settings->LoadBalanceInfoLength = 0;
175 	}
176 
177 	if (settings->RedirectionFlags & LB_TARGET_FQDN)
178 	{
179 		free(settings->RedirectionTargetFQDN);
180 		settings->RedirectionTargetFQDN = _strdup(redirection->TargetFQDN);
181 
182 		if (!settings->RedirectionTargetFQDN)
183 			return -1;
184 	}
185 
186 	if (settings->RedirectionFlags & LB_TARGET_NET_ADDRESS)
187 	{
188 		free(settings->TargetNetAddress);
189 		settings->TargetNetAddress = _strdup(redirection->TargetNetAddress);
190 
191 		if (!settings->TargetNetAddress)
192 			return -1;
193 	}
194 
195 	if (settings->RedirectionFlags & LB_TARGET_NETBIOS_NAME)
196 	{
197 		free(settings->RedirectionTargetNetBiosName);
198 		settings->RedirectionTargetNetBiosName = _strdup(redirection->TargetNetBiosName);
199 
200 		if (!settings->RedirectionTargetNetBiosName)
201 			return -1;
202 	}
203 
204 	if (settings->RedirectionFlags & LB_USERNAME)
205 	{
206 		free(settings->RedirectionUsername);
207 		settings->RedirectionUsername = _strdup(redirection->Username);
208 
209 		if (!settings->RedirectionUsername)
210 			return -1;
211 	}
212 
213 	if (settings->RedirectionFlags & LB_DOMAIN)
214 	{
215 		free(settings->RedirectionDomain);
216 		settings->RedirectionDomain = _strdup(redirection->Domain);
217 
218 		if (!settings->RedirectionDomain)
219 			return -1;
220 	}
221 
222 	if (settings->RedirectionFlags & LB_PASSWORD)
223 	{
224 		/* Password may be a cookie without a null terminator */
225 		free(settings->RedirectionPassword);
226 		settings->RedirectionPasswordLength = redirection->PasswordLength;
227 		/* For security reasons we'll allocate an additional zero WCHAR at the
228 		 * end of the buffer that is not included in RedirectionPasswordLength
229 		 */
230 		settings->RedirectionPassword =
231 		    (BYTE*)calloc(1, settings->RedirectionPasswordLength + sizeof(WCHAR));
232 
233 		if (!settings->RedirectionPassword)
234 			return -1;
235 
236 		CopyMemory(settings->RedirectionPassword, redirection->Password,
237 		           settings->RedirectionPasswordLength);
238 	}
239 
240 	if (settings->RedirectionFlags & LB_CLIENT_TSV_URL)
241 	{
242 		/* TsvUrl may not contain a null terminator */
243 		free(settings->RedirectionTsvUrl);
244 		settings->RedirectionTsvUrlLength = redirection->TsvUrlLength;
245 		settings->RedirectionTsvUrl = (BYTE*)malloc(settings->RedirectionTsvUrlLength);
246 
247 		if (!settings->RedirectionTsvUrl)
248 			return -1;
249 
250 		CopyMemory(settings->RedirectionTsvUrl, redirection->TsvUrl,
251 		           settings->RedirectionTsvUrlLength);
252 	}
253 
254 	if (settings->RedirectionFlags & LB_TARGET_NET_ADDRESSES)
255 	{
256 		UINT32 i;
257 		freerdp_target_net_addresses_free(settings);
258 		settings->TargetNetAddressCount = redirection->TargetNetAddressesCount;
259 		settings->TargetNetAddresses =
260 		    (char**)calloc(settings->TargetNetAddressCount, sizeof(char*));
261 
262 		if (!settings->TargetNetAddresses)
263 		{
264 			settings->TargetNetAddressCount = 0;
265 			return -1;
266 		}
267 
268 		for (i = 0; i < settings->TargetNetAddressCount; i++)
269 		{
270 			settings->TargetNetAddresses[i] = _strdup(redirection->TargetNetAddresses[i]);
271 
272 			if (!settings->TargetNetAddresses[i])
273 			{
274 				UINT32 j;
275 
276 				for (j = 0; j < i; j++)
277 					free(settings->TargetNetAddresses[j]);
278 
279 				return -1;
280 			}
281 		}
282 	}
283 
284 	return 0;
285 }
286 
rdp_recv_server_redirection_pdu(rdpRdp * rdp,wStream * s)287 static BOOL rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
288 {
289 	UINT16 flags;
290 	UINT16 length;
291 	rdpRedirection* redirection = rdp->redirection;
292 
293 	if (Stream_GetRemainingLength(s) < 12)
294 		return -1;
295 
296 	Stream_Read_UINT16(s, flags);                  /* flags (2 bytes) */
297 	Stream_Read_UINT16(s, length);                 /* length (2 bytes) */
298 	Stream_Read_UINT32(s, redirection->sessionID); /* sessionID (4 bytes) */
299 	Stream_Read_UINT32(s, redirection->flags);     /* redirFlags (4 bytes) */
300 	WLog_DBG(TAG,
301 	         "flags: 0x%04" PRIX16 ", redirFlags: 0x%08" PRIX32 " length: %" PRIu16
302 	         ", sessionID: 0x%08" PRIX32 "",
303 	         flags, redirection->flags, length, redirection->sessionID);
304 	rdp_print_redirection_flags(redirection->flags);
305 
306 	/* Although MS-RDPBCGR does not mention any length constraints limits for the
307 	 * variable length null-terminated unicode strings in the RDP_SERVER_REDIRECTION_PACKET
308 	 * structure we will use the following limits in bytes including the null terminator:
309 	 *
310 	 * TargetNetAddress:     80 bytes
311 	 * UserName:            512 bytes
312 	 * Domain:               52 bytes
313 	 * Password(Cookie):    512 bytes
314 	 * TargetFQDN:          512 bytes
315 	 * TargetNetBiosName:    32 bytes
316 	 */
317 
318 	if (redirection->flags & LB_TARGET_NET_ADDRESS)
319 	{
320 		if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddress), 80))
321 			return -1;
322 	}
323 
324 	if (redirection->flags & LB_LOAD_BALANCE_INFO)
325 	{
326 		/* See [MSFT-SDLBTS] (a.k.a. TS_Session_Directory.doc)
327 		 * load balance info example data:
328 		 * 0000  43 6f 6f 6b 69 65 3a 20 6d 73 74 73 3d 32 31 33  Cookie: msts=213
329 		 * 0010  34 30 32 36 34 33 32 2e 31 35 36 32 39 2e 30 30  4026432.15629.00
330 		 * 0020  30 30 0d 0a                                      00..
331 		 */
332 		if (Stream_GetRemainingLength(s) < 4)
333 			return -1;
334 
335 		Stream_Read_UINT32(s, redirection->LoadBalanceInfoLength);
336 
337 		if (Stream_GetRemainingLength(s) < redirection->LoadBalanceInfoLength)
338 			return -1;
339 
340 		redirection->LoadBalanceInfo = (BYTE*)malloc(redirection->LoadBalanceInfoLength);
341 
342 		if (!redirection->LoadBalanceInfo)
343 			return -1;
344 
345 		Stream_Read(s, redirection->LoadBalanceInfo, redirection->LoadBalanceInfoLength);
346 		WLog_DBG(TAG, "loadBalanceInfo:");
347 		winpr_HexDump(TAG, WLOG_DEBUG, redirection->LoadBalanceInfo,
348 		              redirection->LoadBalanceInfoLength);
349 	}
350 
351 	if (redirection->flags & LB_USERNAME)
352 	{
353 		if (!rdp_redirection_read_unicode_string(s, &(redirection->Username), 512))
354 			return -1;
355 
356 		WLog_DBG(TAG, "Username: %s", redirection->Username);
357 	}
358 
359 	if (redirection->flags & LB_DOMAIN)
360 	{
361 		if (!rdp_redirection_read_unicode_string(s, &(redirection->Domain), 52))
362 			return FALSE;
363 
364 		WLog_DBG(TAG, "Domain: %s", redirection->Domain);
365 	}
366 
367 	if (redirection->flags & LB_PASSWORD)
368 	{
369 		/* Note: Password is a variable-length array of bytes containing the
370 		 * password used by the user in Unicode format, including a null-terminator
371 		 * or (!) or a cookie value that MUST be passed to the target server on
372 		 * successful connection.
373 		 * Since the format of the password cookie (probably some salted hash) is
374 		 * currently unknown we'll treat it as opaque data. All cookies seen so far
375 		 * are 120 bytes including \0\0 termination.
376 		 * Here is an observed example of a redirection password cookie:
377 		 *
378 		 * 0000  02 00 00 80 44 53 48 4c 60 ab 69 2f 07 d6 9e 2d  ....DSHL`.i/...-
379 		 * 0010  f0 3a 97 3b a9 c5 ec 7e 66 bd b3 84 6c b1 ef b9  .:.;...~f...l...
380 		 * 0020  b6 82 4e cc 3a df 64 b7 7b 25 04 54 c2 58 98 f8  ..N.:.d.{%.T.X..
381 		 * 0030  97 87 d4 93 c7 c1 e1 5b c2 85 f8 22 49 1f 81 88  .......[..."I...
382 		 * 0040  43 44 83 f6 9a 72 40 24 dc 4d 43 cb d9 92 3c 8f  CD...r@$.MC...<.
383 		 * 0050  3a 37 5c 77 13 a0 72 3c 72 08 64 2a 29 fb dc eb  :7\w..r<r.d*)...
384 		 * 0060  0d 2b 06 b4 c6 08 b4 73 34 16 93 62 6d 24 e9 93  .+.....s4..bm$..
385 		 * 0070  97 27 7b dd 9a 72 00 00                          .'{..r..
386 		 *
387 		 * Notwithstanding the above, we'll allocated an additional zero WCHAR at the
388 		 * end of the buffer which won't get counted in PasswordLength.
389 		 */
390 		if (Stream_GetRemainingLength(s) < 4)
391 			return -1;
392 
393 		Stream_Read_UINT32(s, redirection->PasswordLength);
394 
395 		/* [MS-RDPBCGR] specifies 512 bytes as the upper limit for the password length
396 		 * including the null terminatior(s). This should also be enough for the unknown
397 		 * password cookie format (see previous comment).
398 		 */
399 
400 		if (Stream_GetRemainingLength(s) < redirection->PasswordLength)
401 			return -1;
402 
403 		if (redirection->PasswordLength > LB_PASSWORD_MAX_LENGTH)
404 			return -1;
405 
406 		redirection->Password = (BYTE*)calloc(1, redirection->PasswordLength + sizeof(WCHAR));
407 
408 		if (!redirection->Password)
409 			return -1;
410 
411 		Stream_Read(s, redirection->Password, redirection->PasswordLength);
412 		WLog_DBG(TAG, "PasswordCookie:");
413 #if defined(WITH_DEBUG_REDIR)
414 		winpr_HexDump(TAG, WLOG_DEBUG, redirection->Password, redirection->PasswordLength);
415 #endif
416 	}
417 
418 	if (redirection->flags & LB_TARGET_FQDN)
419 	{
420 		if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetFQDN), 512))
421 			return -1;
422 
423 		WLog_DBG(TAG, "TargetFQDN: %s", redirection->TargetFQDN);
424 	}
425 
426 	if (redirection->flags & LB_TARGET_NETBIOS_NAME)
427 	{
428 		if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetBiosName), 32))
429 			return -1;
430 
431 		WLog_DBG(TAG, "TargetNetBiosName: %s", redirection->TargetNetBiosName);
432 	}
433 
434 	if (redirection->flags & LB_CLIENT_TSV_URL)
435 	{
436 		if (Stream_GetRemainingLength(s) < 4)
437 			return -1;
438 
439 		Stream_Read_UINT32(s, redirection->TsvUrlLength);
440 
441 		if (Stream_GetRemainingLength(s) < redirection->TsvUrlLength)
442 			return -1;
443 
444 		redirection->TsvUrl = (BYTE*)malloc(redirection->TsvUrlLength);
445 
446 		if (!redirection->TsvUrl)
447 			return -1;
448 
449 		Stream_Read(s, redirection->TsvUrl, redirection->TsvUrlLength);
450 		WLog_DBG(TAG, "TsvUrl:");
451 		winpr_HexDump(TAG, WLOG_DEBUG, redirection->TsvUrl, redirection->TsvUrlLength);
452 	}
453 
454 	if (redirection->flags & LB_TARGET_NET_ADDRESSES)
455 	{
456 		int i;
457 		UINT32 count;
458 		UINT32 targetNetAddressesLength;
459 
460 		if (Stream_GetRemainingLength(s) < 8)
461 			return -1;
462 
463 		Stream_Read_UINT32(s, targetNetAddressesLength);
464 		Stream_Read_UINT32(s, redirection->TargetNetAddressesCount);
465 		count = redirection->TargetNetAddressesCount;
466 		redirection->TargetNetAddresses = (char**)calloc(count, sizeof(char*));
467 
468 		if (!redirection->TargetNetAddresses)
469 			return FALSE;
470 
471 		WLog_DBG(TAG, "TargetNetAddressesCount: %" PRIu32 "", redirection->TargetNetAddressesCount);
472 
473 		for (i = 0; i < (int)count; i++)
474 		{
475 			if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddresses[i]), 80))
476 				return FALSE;
477 
478 			WLog_DBG(TAG, "TargetNetAddresses[%d]: %s", i, redirection->TargetNetAddresses[i]);
479 		}
480 	}
481 
482 	if (Stream_GetRemainingLength(s) >= 8)
483 	{
484 		/* some versions of windows don't included this padding before closing the connection */
485 		Stream_Seek(s, 8); /* pad (8 bytes) */
486 	}
487 
488 	if (redirection->flags & LB_NOREDIRECT)
489 		return 0;
490 
491 	return 1;
492 }
493 
rdp_recv_enhanced_security_redirection_packet(rdpRdp * rdp,wStream * s)494 int rdp_recv_enhanced_security_redirection_packet(rdpRdp* rdp, wStream* s)
495 {
496 	int status = 0;
497 
498 	if (!Stream_SafeSeek(s, 2)) /* pad2Octets (2 bytes) */
499 		return -1;
500 
501 	status = rdp_recv_server_redirection_pdu(rdp, s);
502 
503 	if (status < 0)
504 		return status;
505 
506 	if (Stream_GetRemainingLength(s) >= 1)
507 	{
508 		/* this field is optional, and its absence is not an error */
509 		Stream_Seek(s, 1); /* pad2Octets (1 byte) */
510 	}
511 
512 	return status;
513 }
514 
redirection_new()515 rdpRedirection* redirection_new()
516 {
517 	rdpRedirection* redirection;
518 	redirection = (rdpRedirection*)calloc(1, sizeof(rdpRedirection));
519 
520 	if (redirection)
521 	{
522 	}
523 
524 	return redirection;
525 }
526 
redirection_free(rdpRedirection * redirection)527 void redirection_free(rdpRedirection* redirection)
528 {
529 	if (redirection)
530 	{
531 		free(redirection->TsvUrl);
532 		free(redirection->Username);
533 		free(redirection->Domain);
534 		free(redirection->TargetFQDN);
535 		free(redirection->TargetNetBiosName);
536 		free(redirection->TargetNetAddress);
537 		free(redirection->LoadBalanceInfo);
538 		free(redirection->Password);
539 
540 		if (redirection->TargetNetAddresses)
541 		{
542 			int i;
543 
544 			for (i = 0; i < (int)redirection->TargetNetAddressesCount; i++)
545 			{
546 				free(redirection->TargetNetAddresses[i]);
547 			}
548 
549 			free(redirection->TargetNetAddresses);
550 		}
551 
552 		free(redirection);
553 	}
554 }
555