FreeRDP
Loading...
Searching...
No Matches
TestFuzzServerSeedGen.c
1
34#include <freerdp/peer.h>
35#include <freerdp/freerdp.h>
36#include <freerdp/settings.h>
37#include <freerdp/constants.h>
38#include <freerdp/crypto/certificate.h>
39#include <freerdp/crypto/privatekey.h>
40#include <freerdp/channels/wtsvc.h>
41#include <freerdp/channels/channels.h>
42#include <freerdp/client.h>
43#include <freerdp/client/cmdline.h>
44#include <freerdp/input.h>
45#include <winpr/tools/makecert.h>
46
47#include "../multitransport.h"
48
49#include "TestFuzzServerCerts.h"
50
51#include <winpr/ssl.h>
52#include <winpr/synch.h>
53#include <winpr/thread.h>
54#include <winpr/crt.h>
55#include <winpr/path.h>
56
57#include <arpa/inet.h>
58#include <errno.h>
59#include <netinet/in.h>
60#include <pthread.h>
61#include <signal.h>
62#include <stdio.h>
63#include <stdlib.h>
64#include <string.h>
65#include <sys/socket.h>
66#include <unistd.h>
67
68struct test_peer_context
69{
70 rdpContext _p;
71
72 HANDLE vcm;
73};
74typedef struct test_peer_context testPeerContext;
75
76typedef struct
77{
78 BYTE* data;
79 size_t len;
80 size_t cap;
81} capture_t;
82
83typedef struct
84{
85 freerdp_peer* client;
86} peer_arg_t;
87
88typedef struct
89{
90 int client_sock;
91 int peer_sock;
92 capture_t* capture;
93} relay_arg_t;
94
95static void capture_add(capture_t* c, const void* data, size_t size)
96{
97 WINPR_ASSERT(c);
98 WINPR_ASSERT(data || (size == 0));
99
100 if (c->len + size > c->cap)
101 {
102 size_t ncap = c->cap ? c->cap * 2 : 65536;
103 while (ncap < c->len + size)
104 ncap *= 2;
105 BYTE* nd = realloc(c->data, ncap);
106 if (!nd)
107 {
108 (void)fprintf(stderr, "capture realloc failed\n");
109 return;
110 }
111 c->data = nd;
112 c->cap = ncap;
113 }
114 memcpy(c->data + c->len, data, size);
115 c->len += size;
116}
117
118static void test_peer_context_free(freerdp_peer* client, rdpContext* ctx)
119{
120 testPeerContext* context = (testPeerContext*)ctx;
121 WINPR_UNUSED(client);
122 if (context && context->vcm)
123 {
124 WTSCloseServer(context->vcm);
125 context->vcm = nullptr;
126 }
127}
128
129static BOOL test_peer_context_new(freerdp_peer* client, rdpContext* ctx)
130{
131 testPeerContext* context = (testPeerContext*)ctx;
132 WINPR_ASSERT(client);
133 WINPR_ASSERT(context);
134 context->vcm = WTSOpenServerA((LPSTR)client->context);
135 if (!context->vcm || context->vcm == INVALID_HANDLE_VALUE)
136 goto fail;
137 return TRUE;
138fail:
139 test_peer_context_free(client, ctx);
140 return FALSE;
141}
142
143static BOOL test_peer_post_connect(freerdp_peer* peer)
144{
145 WINPR_ASSERT(peer);
146 return TRUE;
147}
148
149static BOOL test_peer_activate(freerdp_peer* peer)
150{
151 WINPR_ASSERT(peer);
152 return TRUE;
153}
154
155// NOLINTNEXTLINE(readability-non-const-parameter)
156static int test_peer_virtual_channel_read(freerdp_peer* peer, HANDLE hChannel, BYTE* buffer,
157 UINT32 length)
158{
159 WINPR_UNUSED(peer);
160 WINPR_UNUSED(hChannel);
161 WINPR_UNUSED(buffer);
162 WINPR_UNUSED(length);
163 WINPR_ASSERT(buffer || (length == 0));
164 return 0;
165}
166
167/*
168 * Configure the server peer exactly like TestFuzzServer.c does so the
169 * captured client stream replays against the fuzzer.
170 */
171static BOOL configure_server(freerdp_peer* client)
172{
173 WINPR_ASSERT(client);
174 BOOL rc = FALSE;
175
176 char* path = getTestCredentialsFilePath();
177 char* fcert = getTestCredentialsFileNameFor("crt");
178 char* fkey = getTestCredentialsFileNameFor("key");
179 if (!path || !fcert || !fkey)
180 goto fail;
181
182 client->ContextSize = sizeof(testPeerContext);
183 client->ContextNew = test_peer_context_new;
184 client->ContextFree = test_peer_context_free;
185 if (!freerdp_peer_context_new(client))
186 goto fail;
187
188 WINPR_ASSERT(client->context);
189 rdpSettings* settings = client->context->settings;
190 WINPR_ASSERT(settings);
191
192 rdpCertificate* cert = freerdp_certificate_new_from_file(fcert);
193 if (!cert)
194 goto fail;
195 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1))
196 goto fail;
197
198 rdpPrivateKey* key = freerdp_key_new_from_file(fkey);
199 if (!key)
200 goto fail;
201 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))
202 goto fail;
203
204 /*
205 * Plain RDP security. Together with the local peer flag below this
206 * results in the plaintext local peer path (no RDP encryption), which
207 * allows the byte stream fuzzer to reach all the way into the post
208 * activation parsing code.
209 */
210 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, TRUE))
211 goto fail;
212 if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, TRUE))
213 goto fail;
214 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
215 goto fail;
216 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
217 goto fail;
218 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
219 goto fail;
220 if (!freerdp_settings_set_uint32(settings, FreeRDP_EncryptionLevel, ENCRYPTION_LEVEL_NONE))
221 goto fail;
222
223 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE))
224 goto fail;
225 if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, TRUE))
226 goto fail;
227 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportMultitransport, TRUE))
228 goto fail;
229 if (!freerdp_settings_set_uint32(settings, FreeRDP_MultitransportFlags,
230 INITIATE_REQUEST_PROTOCOL_UDPFECR))
231 goto fail;
232 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportSkipChannelJoin, FALSE))
233 goto fail;
234
235 client->PostConnect = test_peer_post_connect;
236 client->Activate = test_peer_activate;
237 client->VirtualChannelRead = test_peer_virtual_channel_read;
238
239 /* local connection => the server uses the plaintext local peer path */
240 client->local = TRUE;
241 if (!client->Initialize(client))
242 goto fail;
243
244 rc = TRUE;
245fail:
246
247 free(path);
248 free(fcert);
249 free(fkey);
250 return rc;
251}
252
253static DWORD WINAPI peer_thread(LPVOID arg)
254{
255 freerdp_peer* client = ((peer_arg_t*)arg)->client;
256 DWORD error = CHANNEL_RC_OK;
257
258 if (!configure_server(client))
259 {
260 freerdp_peer_context_free(client);
261 freerdp_peer_free(client);
262 return 1;
263 }
264
265 testPeerContext* context = (testPeerContext*)client->context;
266 WINPR_ASSERT(context);
267
268 while (error == CHANNEL_RC_OK)
269 {
270 HANDLE handles[32] = WINPR_C_ARRAY_INIT;
271 DWORD count = 0;
272 DWORD tmp = client->GetEventHandles(client, &handles[count], ARRAYSIZE(handles) - count);
273 if (tmp == 0)
274 break;
275 count += tmp;
276 handles[count++] = WTSVirtualChannelManagerGetEventHandle(context->vcm);
277 DWORD status = WaitForMultipleObjects(count, handles, FALSE, 5000);
278 if (status == WAIT_FAILED || status == WAIT_TIMEOUT)
279 break;
280 if (client->CheckFileDescriptor(client) != TRUE)
281 break;
282 if (WTSVirtualChannelManagerCheckFileDescriptor(context->vcm) != TRUE)
283 break;
284 }
285
286 client->Disconnect(client);
287 freerdp_peer_context_free(client);
288 freerdp_peer_free(client);
289 return 0;
290}
291
292static void* relay_c2s(void* arg)
293{
294 relay_arg_t* a = (relay_arg_t*)arg;
295 BYTE buf[65536] = WINPR_C_ARRAY_INIT;
296 for (;;)
297 {
298 ssize_t rc = read(a->client_sock, buf, sizeof(buf));
299 if (rc <= 0)
300 break;
301 capture_add(a->capture, buf, (size_t)rc);
302 ssize_t w = write(a->peer_sock, buf, (size_t)rc);
303 if (w != rc)
304 break;
305 }
306 shutdown(a->peer_sock, SHUT_WR);
307 return nullptr;
308}
309
310static void* relay_s2c(void* arg)
311{
312 relay_arg_t* a = (relay_arg_t*)arg;
313 BYTE buf[65536] = WINPR_C_ARRAY_INIT;
314 for (;;)
315 {
316 ssize_t rc = read(a->peer_sock, buf, sizeof(buf));
317 if (rc <= 0)
318 break;
319 ssize_t w = write(a->client_sock, buf, (size_t)rc);
320 if (w != rc)
321 break;
322 }
323 shutdown(a->client_sock, SHUT_WR);
324 return nullptr;
325}
326
327typedef struct
328{
329 int port;
330 int send_input;
331} client_arg_t;
332
333static DWORD WINAPI client_thread(LPVOID arg)
334{
335 client_arg_t* a = (client_arg_t*)arg;
336 char arg1[64] = WINPR_C_ARRAY_INIT;
337 (void)snprintf(arg1, sizeof(arg1), "/v:127.0.0.1:%d", a->port);
338 char* argv[] = { "seedgen", arg1, "/sec:rdp", "/cert:ignore", "/rfx", nullptr };
339 int argc = 5;
340
341 RDP_CLIENT_ENTRY_POINTS entry = WINPR_C_ARRAY_INIT;
342 entry.Size = sizeof(RDP_CLIENT_ENTRY_POINTS);
343 entry.Version = RDP_CLIENT_INTERFACE_VERSION;
344 entry.ContextSize = sizeof(rdpContext);
345
346 rdpContext* context = freerdp_client_context_new(&entry);
347 if (!context)
348 return 1;
349
350 if (!freerdp_settings_set_bool(context->settings, FreeRDP_DeactivateClientDecoding, TRUE))
351 goto fail;
352 if (!freerdp_settings_set_string(context->settings, FreeRDP_Username, "test"))
353 goto fail;
354 if (!freerdp_settings_set_string(context->settings, FreeRDP_Password, "test"))
355 goto fail;
356 /* Enable network autodetect so the client announces the MCS message
357 * channel; the server needs it to send the licensing and demand active
358 * PDUs on the correct channel. */
359 if (!freerdp_settings_set_bool(context->settings, FreeRDP_NetworkAutoDetect, TRUE))
360 goto fail;
361 if (!freerdp_settings_set_bool(context->settings, FreeRDP_SupportMultitransport, TRUE))
362 goto fail;
363
364 if (freerdp_client_settings_parse_command_line(context->settings, argc, argv, FALSE) < 0)
365 goto fail;
366
367 if (!freerdp_connect(context->instance))
368 {
369 (void)fprintf(stderr, "client connect failed\n");
370 return 1;
371 }
372
373 if (a->send_input)
374 {
375 rdpInput* input = context->input;
376 WINPR_ASSERT(input);
377 for (int i = 0; i < 3; i++)
378 {
379 freerdp_input_send_synchronize_event(input, 0);
380 freerdp_input_send_keyboard_event(input, KBD_FLAGS_DOWN, 0x1e); /* 'a' */
381 freerdp_input_send_keyboard_event(input, KBD_FLAGS_RELEASE, 0x1e);
382 freerdp_input_send_mouse_event(input, PTR_FLAGS_MOVE, 100 + i * 10, 100);
383 freerdp_input_send_mouse_event(input, PTR_FLAGS_BUTTON1, 100 + i * 10, 100);
384 Sleep(50);
385 }
386 Sleep(500);
387 }
388
389 freerdp_disconnect(context->instance);
390 freerdp_client_context_free(context);
391 return 0;
392fail:
393 return 1;
394}
395
396static int write_seed(const char* dir, const char* name, const capture_t* c)
397{
398 char path[512] = WINPR_C_ARRAY_INIT;
399 (void)snprintf(path, sizeof(path), "%s/%s", dir, name);
400 FILE* f = fopen(path, "wb");
401 if (!f)
402 return -1;
403 size_t w = fwrite(c->data, 1, c->len, f);
404 (void)fclose(f);
405 return (w == c->len) ? 0 : -1;
406}
407
408int main(int argc, char** argv)
409{
410 (void)signal(SIGPIPE, SIG_IGN);
411
412 const char* outdir = argc > 1 ? argv[1] : ".";
413 int do_input = 1;
414 if (argc > 2)
415 {
416 errno = 0;
417 const unsigned long val = strtoul(argv[2], nullptr, 0);
418 if ((errno != 0) || (val > INT_MAX))
419 return -1;
420 do_input = (int)val;
421 }
422
423 if (!WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi()))
424 return -1;
425 if (!winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT))
426 return -1;
427
428 int lfd = socket(AF_INET, SOCK_STREAM, 0);
429 if (lfd < 0)
430 return 1;
431 int one = 1;
432 setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
433 struct sockaddr_in sa = WINPR_C_ARRAY_INIT;
434 sa.sin_family = AF_INET;
435 sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
436 sa.sin_port = 0;
437 if (bind(lfd, (struct sockaddr*)&sa, sizeof(sa)) != 0)
438 return 1;
439 if (listen(lfd, 4) != 0)
440 return 1;
441 socklen_t slen = sizeof(sa);
442 getsockname(lfd, (struct sockaddr*)&sa, &slen);
443 int port = ntohs(sa.sin_port);
444 printf("listening on 127.0.0.1:%d\n", port);
445
446 client_arg_t carg = { port, do_input };
447 HANDLE hclient = CreateThread(nullptr, 0, client_thread, &carg, 0, nullptr);
448
449 int cfd = accept(lfd, nullptr, nullptr);
450 if (cfd < 0)
451 return 1;
452 printf("client connected\n");
453
454 int sv[2] = WINPR_C_ARRAY_INIT;
455 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0)
456 return 1;
457
458 freerdp_peer* client = freerdp_peer_new(sv[0]);
459 if (!client)
460 return 1;
461
462 capture_t capture = WINPR_C_ARRAY_INIT;
463 relay_arg_t ra = { cfd, sv[1], &capture };
464
465 pthread_t t1 = WINPR_C_ARRAY_INIT;
466 pthread_create(&t1, nullptr, relay_c2s, &ra);
467 pthread_t t2 = WINPR_C_ARRAY_INIT;
468 pthread_create(&t2, nullptr, relay_s2c, &ra);
469
470 peer_arg_t pa = { client };
471 HANDLE hpeer = CreateThread(nullptr, 0, peer_thread, &pa, 0, nullptr);
472
473 WaitForSingleObject(hclient, 30000);
474 /* give the peer a moment to drain */
475 Sleep(200);
476 close(cfd);
477 close(sv[1]);
478
479 /* wait for the relay threads to finish before touching the capture */
480 pthread_join(t1, nullptr);
481 pthread_join(t2, nullptr);
482
483 WaitForSingleObject(hpeer, 10000);
484
485 CloseHandle(hclient);
486 CloseHandle(hpeer);
487 close(lfd);
488
489 printf("captured %zu bytes\n", capture.len);
490
491 if (capture.len == 0)
492 return 1;
493
494 /* write full capture as the primary seed */
495 write_seed(outdir, "handshake", &capture);
496
497 /* write truncated seeds at phase boundaries to give the fuzzer
498 * incremental starting points */
499 static const size_t cuts[] = { 64, 128, 256, 512, 1024 };
500 for (size_t i = 0; i < sizeof(cuts) / sizeof(cuts[0]); i++)
501 {
502 if (capture.len > cuts[i])
503 {
504 char name[64] = WINPR_C_ARRAY_INIT;
505 capture_t t = WINPR_C_ARRAY_INIT;
506 t.data = capture.data;
507 t.len = cuts[i];
508 (void)snprintf(name, sizeof(name), "handshake_cut%zu", cuts[i]);
509 write_seed(outdir, name, &t);
510 }
511 }
512
513 free(capture.data);
514 return 0;
515}
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 BOOL freerdp_settings_set_pointer_len(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id, const void *data, size_t len)
Set a pointer to value data.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.