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