FreeRDP
Loading...
Searching...
No Matches
TestRdpPacketLengths.c
1
18#include <freerdp/config.h>
19
20#include <freerdp/freerdp.h>
21#include <freerdp/settings.h>
22#include <winpr/crypto.h>
23#include <winpr/stream.h>
24#include <winpr/ssl.h>
25
26#include "../connection.h"
27#include "../mcs.h"
28#include "../rdp.h"
29#include "../security.h"
30
31/* These are framing fixtures, not capability negotiation or cipher test vectors.
32 * The zero-capability Demand Active is sufficient for the real receive parser. */
33static const BYTE demandActive[] = {
34 0x16, 0x00, 0x11, 0x00, 0xEA, 0x03, /* Share Control: 22 bytes, Demand Active */
35 0x00, 0x00, 0x01, 0x00, /* shareId */
36 0x00, 0x00, 0x04, 0x00, /* descriptor length, capability length */
37 0x00, 0x00, 0x00, 0x00, /* capability count and padding */
38 0x00, 0x00, 0x00, 0x00 /* sessionId */
39};
40
41static const BYTE flowResponse[] = { 0x00, 0x80, PDU_TYPE_FLOW_RESPONSE, 0, 0, 0, 0, 0 };
42static const BYTE shortDeactivate[] = { 0x04, 0x00, 0x16, 0x00 };
43
44typedef enum
45{
46 TEST_TLS,
47 TEST_SECURITY_HEADER,
48 TEST_RC4,
49 TEST_FIPS
50} TestSecurity;
51
52static void free_client(freerdp* instance)
53{
54 if (instance)
55 {
56 freerdp_context_free(instance);
57 freerdp_free(instance);
58 }
59}
60
61WINPR_ATTR_MALLOC(free_client, 1)
62static freerdp* new_client(void)
63{
64 freerdp* instance = freerdp_new();
65 if (!instance)
66 return nullptr;
67 if (!freerdp_context_new(instance) ||
68 !freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseRdpSecurityLayer, FALSE))
69 {
70 freerdp_free(instance);
71 return nullptr;
72 }
73
74 return instance;
75}
76
77/* A complete TPKT may be embedded in a larger buffer. Its length is measured
78 * from the current position, not from the buffer start or its allocation size. */
79WINPR_ATTR_MALLOC(Stream_Free, 1)
80static wStream* packet(const BYTE* payload, size_t payloadLength, UINT16 declaredLength,
81 BOOL serverMode, BOOL longLength, size_t prefix, size_t suffix)
82{
83 const size_t headerLength = longLength ? 15 : 14;
84 wStream* s = Stream_New(nullptr, prefix + headerLength + payloadLength + suffix);
85 if (!s)
86 return nullptr;
87
88 Stream_Zero(s, prefix);
89 Stream_Write_UINT8(s, 3);
90 Stream_Write_UINT8(s, 0);
91 Stream_Write_UINT16_BE(s, (UINT16)(headerLength + payloadLength));
92 Stream_Write_UINT8(s, 2);
93 Stream_Write_UINT8(s, 0xF0);
94 Stream_Write_UINT8(s, 0x80);
95 Stream_Write_UINT8(s, serverMode ? 0x64 : 0x68); /* MCS SendDataRequest / Indication */
96 Stream_Write_UINT16_BE(s, 1);
97 Stream_Write_UINT16_BE(s, MCS_GLOBAL_CHANNEL_ID);
98 Stream_Write_UINT8(s, 0x70);
99 if (longLength)
100 Stream_Write_UINT16_BE(s, declaredLength | 0x8000);
101 else
102 Stream_Write_UINT8(s, (BYTE)declaredLength);
103 Stream_Write(s, payload, payloadLength);
104 Stream_Zero(s, suffix);
105 Stream_SealLength(s);
106 if (!Stream_SetPosition(s, prefix))
107 {
108 Stream_Free(s, TRUE);
109 return nullptr;
110 }
111 return s;
112}
113
114WINPR_ATTR_NODISCARD
115static BOOL test_mcs_headers(void)
116{
117 static const struct
118 {
119 UINT16 actual;
120 UINT16 declared;
121 BOOL longLength;
122 BOOL valid;
123 } cases[] = { { 0, 0, FALSE, TRUE }, { 1, 1, FALSE, TRUE }, { 26, 26, FALSE, TRUE },
124 { 127, 127, FALSE, TRUE }, { 127, 127, TRUE, TRUE }, { 128, 128, TRUE, TRUE },
125 { 386, 386, TRUE, TRUE }, { 26, 25, FALSE, FALSE }, { 26, 27, FALSE, FALSE },
126 { 386, 130, TRUE, FALSE }, { 386, 387, TRUE, FALSE }, { 0, 1, FALSE, FALSE },
127 { 1, 0, FALSE, FALSE } };
128 BYTE body[386] = WINPR_C_ARRAY_INIT;
129 BOOL ok = FALSE;
130 freerdp* instance = new_client();
131 if (!instance)
132 return FALSE;
133 rdpRdp* rdp = instance->context->rdp;
134 for (size_t server = 0; server < 2; server++)
135 {
136 if (!freerdp_settings_set_bool(rdp->settings, FreeRDP_ServerMode, server != 0))
137 goto fail;
138
139 for (size_t embedding = 0; embedding < 2; embedding++)
140 {
141 const size_t prefix = embedding ? 7 : 0;
142 const size_t suffix = embedding ? 29 : 0;
143 for (size_t x = 0; x < ARRAYSIZE(cases); x++)
144 {
145 UINT16 length = 0;
146 UINT16 channel = 0;
147 wStream* s = packet(body, cases[x].actual, cases[x].declared, (BOOL)server,
148 cases[x].longLength, prefix, suffix);
149 if (!s)
150 {
151 ok = FALSE;
152 continue;
153 }
154 const BOOL result = rdp_read_header(rdp, s, &length, &channel);
155 if ((result != cases[x].valid) ||
156 (result && ((length != cases[x].actual) || (channel != MCS_GLOBAL_CHANNEL_ID) ||
157 (Stream_GetRemainingLength(s) != cases[x].actual + suffix))))
158 {
159 (void)fprintf(stderr,
160 "MCS framing case %zu, server=%zu, embedding=%zu failed\n", x,
161 server, embedding);
162 ok = FALSE;
163 }
164 Stream_Free(s, TRUE);
165 }
166 }
167 }
168 ok = TRUE;
169fail:
170 free_client(instance);
171 return ok;
172}
173
174/* Exercise real RDP decryption, including FIPS block padding, before the Share
175 * Control check. Independent encrypt/decrypt states use matching fixture keys. */
176WINPR_ATTR_NODISCARD
177static BOOL setup_security(rdpRdp* rdp, TestSecurity security)
178{
179 static const BYTE iv[] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
180 if (!freerdp_settings_set_bool(rdp->settings, FreeRDP_UseRdpSecurityLayer,
181 security != TEST_TLS))
182 return FALSE;
183 if (security < TEST_RC4)
184 return TRUE;
185
186 if (security == TEST_RC4)
187 {
188 if (!freerdp_settings_set_uint32(rdp->settings, FreeRDP_EncryptionMethods,
189 ENCRYPTION_METHOD_128BIT))
190 return FALSE;
191 rdp->rc4_key_len = 16;
192 memset(rdp->sign_key, 0x3A, sizeof(rdp->sign_key));
193 for (size_t x = 0; x < sizeof(rdp->encrypt_key); x++)
194 rdp->encrypt_key[x] = rdp->decrypt_key[x] = (BYTE)(x + 1);
195 return rdp_reset_rc4_encrypt_keys(rdp) && rdp_reset_rc4_decrypt_keys(rdp);
196 }
197
198 if (!freerdp_settings_set_uint32(rdp->settings, FreeRDP_EncryptionMethods,
199 ENCRYPTION_METHOD_FIPS))
200 return FALSE;
201 memset(rdp->fips_sign_key, 0x3A, sizeof(rdp->fips_sign_key));
202 for (size_t x = 0; x < sizeof(rdp->fips_encrypt_key); x++)
203 rdp->fips_encrypt_key[x] = rdp->fips_decrypt_key[x] = (BYTE)(x + 1);
204 rdp->fips_encrypt =
205 winpr_Cipher_NewEx(WINPR_CIPHER_DES_EDE3_CBC, WINPR_ENCRYPT, rdp->fips_encrypt_key,
206 sizeof(rdp->fips_encrypt_key), iv, sizeof(iv));
207 rdp->fips_decrypt =
208 winpr_Cipher_NewEx(WINPR_CIPHER_DES_EDE3_CBC, WINPR_DECRYPT, rdp->fips_decrypt_key,
209 sizeof(rdp->fips_decrypt_key), iv, sizeof(iv));
210 return rdp->fips_encrypt && rdp->fips_decrypt;
211}
212
213WINPR_ATTR_MALLOC(Stream_Free, 1)
214static wStream* protect(rdpRdp* rdp, TestSecurity security, const BYTE* data, size_t length)
215{
216 BYTE signature[8] = WINPR_C_ARRAY_INIT;
217 const size_t pad = (security == TEST_FIPS) ? (8 - length % 8) % 8 : 0;
218 wStream* s = Stream_New(nullptr, length + 24);
219 if (!s || !setup_security(rdp, security))
220 goto fail;
221 if (security != TEST_TLS)
222 {
223 Stream_Write_UINT16(s, security >= TEST_RC4 ? SEC_ENCRYPT : 0);
224 Stream_Write_UINT16(s, 0);
225 }
226 if (security == TEST_FIPS)
227 {
228 Stream_Write_UINT16(s, 0x10);
229 Stream_Write_UINT8(s, 1);
230 Stream_Write_UINT8(s, (BYTE)pad);
231 if (!security_hmac_signature(data, length, signature, sizeof(signature), rdp))
232 goto fail;
233 }
234 else if (security == TEST_RC4)
235 {
236 if (!security_mac_signature(rdp, data, (UINT32)length, signature, sizeof(signature)))
237 goto fail;
238 }
239 if (security >= TEST_RC4)
240 Stream_Write(s, signature, sizeof(signature));
241
242 BYTE* encrypted = Stream_Pointer(s);
243 Stream_Write(s, data, length);
244 Stream_Zero(s, pad);
245 if ((security == TEST_RC4) && !security_encrypt(encrypted, length, rdp))
246 goto fail;
247 if ((security == TEST_FIPS) && !security_fips_encrypt(encrypted, length + pad, rdp))
248 goto fail;
249 Stream_SealLength(s);
250 return s;
251fail:
252 Stream_Free(s, TRUE);
253 return nullptr;
254}
255
256WINPR_ATTR_NODISCARD
257static BOOL test_demand_active(TestSecurity security, int shareDelta, int mcsDelta)
258{
259 BOOL ok = FALSE;
260 wStream* protected = nullptr;
261 wStream* s = nullptr;
262 BYTE data[sizeof(demandActive)] = WINPR_C_ARRAY_INIT;
263 freerdp* instance = new_client();
264 if (!instance)
265 return FALSE;
266 rdpRdp* rdp = instance->context->rdp;
267 memcpy(data, demandActive, sizeof(data));
268 data[0] = (BYTE)(sizeof(data) + shareDelta);
269 protected = protect(rdp, security, data, sizeof(data));
270 if (!protected)
271 goto out;
272 const size_t length = Stream_Length(protected);
273 s = packet(Stream_Buffer(protected), length, (UINT16)((int)length + mcsDelta), FALSE, FALSE, 0,
274 0);
275 if (!s)
276 goto out;
277 const state_run_t result = rdp_client_connect_demand_active(rdp, s);
278 if ((shareDelta == 0) && (mcsDelta == 0))
279 {
280 ok = (result == STATE_RUN_ACTIVE) && (Stream_GetRemainingLength(s) == 0) &&
281 (freerdp_settings_get_uint32(rdp->settings, FreeRDP_ShareId) == 0x10000) &&
282 (freerdp_settings_get_uint32(rdp->settings, FreeRDP_PduSource) == 1002);
283 if (security >= TEST_RC4)
284 ok = ok && (rdp->decrypt_use_count == 1);
285 /* FIPS has two pad bytes on the wire, which must no longer be present. */
286 if (security == TEST_FIPS)
287 ok = ok && (Stream_Length(s) == length + 14 - 2);
288 }
289 else
290 ok = state_run_failed(result);
291out:
292 if (!ok)
293 (void)fprintf(stderr, "Demand Active security=%d, share delta=%d, MCS delta=%d failed\n",
294 security, shareDelta, mcsDelta);
295 Stream_Free(s, TRUE);
296 Stream_Free(protected, TRUE);
297 free_client(instance);
298 return ok;
299}
300
301WINPR_ATTR_NODISCARD
302static BOOL test_share_compatibility(TestSecurity security, BOOL deactivate, BOOL demandState)
303{
304 BOOL ok = FALSE;
305 BYTE payload[sizeof(flowResponse) * 2 + sizeof(shortDeactivate)] = WINPR_C_ARRAY_INIT;
306 size_t length = sizeof(flowResponse);
307 wStream* protected = nullptr;
308 wStream* s = nullptr;
309 freerdp* instance = new_client();
310 if (!instance)
311 return FALSE;
312 rdpRdp* rdp = instance->context->rdp;
313 memcpy(payload, flowResponse, sizeof(flowResponse));
314 if (!demandState)
315 {
316 /* Multiple Share PDUs in one MCS payload remain valid. */
317 memcpy(payload + length, flowResponse, sizeof(flowResponse));
318 length += sizeof(flowResponse);
319 if (deactivate)
320 {
321 memcpy(payload + length, shortDeactivate, sizeof(shortDeactivate));
322 length += sizeof(shortDeactivate);
323 }
324 }
325 protected = protect(rdp, security, payload, length);
326 if (!protected)
327 goto out;
328 s = packet(Stream_Buffer(protected), Stream_Length(protected), (UINT16)Stream_Length(protected),
329 FALSE, FALSE, 0, 0);
330 if (!s)
331 goto out;
332 state_run_t result = STATE_RUN_FAILED;
333 if (demandState)
334 result = rdp_client_connect_demand_active(rdp, s);
335 else
336 {
337 rdp->state = CONNECTION_STATE_ACTIVE;
338 result = rdp_recv_callback(rdp->transport, s, rdp);
339 }
340 ok = state_run_success(result) && (Stream_GetRemainingLength(s) == 0);
341 if (!demandState)
342 {
343 ok = ok && (rdp->inPackets == (deactivate ? 3 : 2));
344 if (deactivate)
345 ok = ok && (rdp->state == CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE);
346 }
347out:
348 if (!ok)
349 (void)fprintf(stderr,
350 "Share compatibility security=%d, deactivate=%d, demand state=%d failed\n",
351 security, deactivate, demandState);
352 Stream_Free(s, TRUE);
353 Stream_Free(protected, TRUE);
354 free_client(instance);
355 return ok;
356}
357
358int TestRdpPacketLengths(WINPR_ATTR_UNUSED int argc, WINPR_ATTR_UNUSED char* argv[])
359{
360 if (!winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT))
361 return -1;
362 BOOL ok = test_mcs_headers();
363 for (TestSecurity security = TEST_TLS; security <= TEST_FIPS; security++)
364 {
365 for (int delta = -1; delta <= 1; delta++)
366 {
367 if (!test_demand_active(security, delta, 0))
368 ok = FALSE;
369 if ((delta != 0) && !test_demand_active(security, 0, delta))
370 ok = FALSE;
371 }
372 if (!test_share_compatibility(security, FALSE, FALSE) ||
373 !test_share_compatibility(security, TRUE, FALSE) ||
374 !test_share_compatibility(security, FALSE, TRUE))
375 ok = FALSE;
376 }
377 return ok ? 0 : -1;
378}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.