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