FreeRDP
Loading...
Searching...
No Matches
TestFuzzCoreGateway.c
1
12#include <stddef.h>
13#include <stdint.h>
14
15#include <openssl/bio.h>
16
17#include <winpr/crt.h>
18#include <winpr/stream.h>
19#include <winpr/wlog.h>
20
21#include <freerdp/client.h>
22
23#include "../rdp.h"
24#include "../gateway/http.h"
25#include "../gateway/rpc.h"
26#include "../gateway/rts.h"
27#include "../gateway/rts_signature.h"
28#include "../gateway/tsg.h"
29#include "../gateway/websocket.h"
30#include "../../crypto/tls.h"
31
32static void fuzz_rts(wStream* s)
33{
34 const RtsPduSignature* known[] = { &RTS_PDU_CONN_A3_SIGNATURE, &RTS_PDU_CONN_C2_SIGNATURE,
35 &RTS_PDU_OUT_R1_A2_SIGNATURE, &RTS_PDU_PING_SIGNATURE,
36 &RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE };
37 const size_t start = Stream_GetPosition(s);
38
39 rpcconn_hdr_t header = WINPR_C_ARRAY_INIT;
40 if (rts_read_pdu_header(s, &header) && (header.common.ptype == PTYPE_RTS))
41 {
42 RtsPduSignature signature = WINPR_C_ARRAY_INIT;
43 if (rts_extract_pdu_signature(&signature, s, &header))
44 (void)rts_identify_pdu_signature(&signature, nullptr);
45 }
46 rts_free_pdu_header(&header, FALSE);
47
48 for (size_t x = 0; x < ARRAYSIZE(known); x++)
49 {
50 if (!Stream_SetPosition(s, start))
51 return;
52 (void)rts_match_pdu_signature(known[x], s, nullptr);
53 }
54}
55
56static void fuzz_tsg(rdpContext* context, wStream* s)
57{
58 if (Stream_GetRemainingLength(s) < 2)
59 return;
60
61 const BYTE state = Stream_Get_UINT8(s);
62 const BYTE flags = Stream_Get_UINT8(s);
63 const TSG_STATE selectedState = (TSG_STATE)(state % (TSG_STATE_FINAL + 1));
64 if (selectedState == TSG_STATE_TUNNEL_CLOSE_PENDING)
65 return;
66
67 rdpTsg* tsg = tsg_new(context->rdp->transport);
68 if (!tsg)
69 return;
70
71 if (tsg_set_state(tsg, selectedState))
72 {
73 RPC_PDU pdu = WINPR_C_ARRAY_INIT;
74 pdu.s = s;
75 pdu.Flags = (flags & 0x01) ? RPC_PDU_FLAG_STUB : 0;
76 pdu.Type = PTYPE_RESPONSE;
77 (void)tsg_recv_pdu(tsg, &pdu);
78 }
79 tsg_free(tsg);
80}
81
82static void fuzz_http(rdpContext* context, wStream* s)
83{
84 if ((Stream_GetRemainingLength(s) < 1) || (Stream_GetRemainingLength(s) > INT32_MAX))
85 return;
86
87 const BOOL readContentLength = (Stream_Get_UINT8(s) & 0x01) != 0;
88 HttpContext* http = http_context_new();
89 BIO* bio = BIO_new_mem_buf(Stream_ConstPointer(s), (int)Stream_GetRemainingLength(s));
90 if (!http || !bio)
91 goto fail;
92
93 {
94 /* http_response_recv only uses the BIO and context of the TLS connection */
95 rdpTls tls = WINPR_C_ARRAY_INIT;
96 tls.bio = bio;
97 tls.context = context;
98
99 HttpResponse* response = http_response_recv(&tls, readContentLength);
100 if (response)
101 {
102 (void)http_response_get_status_code(response);
103 (void)http_response_get_body_length(response);
104 (void)http_response_get_body(response);
105 (void)http_response_get_auth_token(response, "NTLM");
106 (void)http_response_get_auth_token(response, "Negotiate");
107 (void)http_response_get_setcookie(response, "RdgAuthToken");
108 (void)http_response_get_transfer_encoding(response);
109 (void)http_response_extract_cookies(response, http);
110 (void)http_context_enable_websocket_upgrade(http, TRUE);
111 (void)http_response_is_websocket(http, response);
112 http_response_free(response);
113 }
114 }
115
116fail:
117 BIO_free(bio);
118 http_context_free(http);
119}
120
121static void fuzz_websocket(wStream* s)
122{
123 if (Stream_GetRemainingLength(s) > INT32_MAX)
124 return;
125
126 websocket_context* ws = websocket_context_new();
127 /* read-only: replies (pong/close) fail instead of being read back */
128 BIO* bio = BIO_new_mem_buf(Stream_ConstPointer(s), (int)Stream_GetRemainingLength(s));
129 if (!ws || !bio)
130 goto fail;
131
132 for (size_t x = 0; x < 64; x++)
133 {
134 BYTE buffer[1024] = WINPR_C_ARRAY_INIT;
135 if (websocket_context_read(ws, bio, buffer, sizeof(buffer)) <= 0)
136 break;
137 }
138
139fail:
140 BIO_free(bio);
141 websocket_context_free(ws);
142}
143
144int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
145{
146 if ((size < 1) || (size > (1u << 20)))
147 return 0;
148
149 wLog* root = WLog_GetRoot();
150 (void)WLog_SetLogLevel(root, WLOG_TRACE);
151 (void)WLog_SetLogAppenderType(root, WLOG_APPENDER_CALLBACK);
152
153 wStream buffer = WINPR_C_ARRAY_INIT;
154 wStream* s = Stream_StaticConstInit(&buffer, data, size);
155 const BYTE target = Stream_Get_UINT8(s);
156
157 switch (target % 4)
158 {
159 case 0:
160 fuzz_rts(s);
161 break;
162 case 3:
163 fuzz_websocket(s);
164 break;
165 default:
166 {
167 RDP_CLIENT_ENTRY_POINTS entry = WINPR_C_ARRAY_INIT;
168 entry.Version = RDP_CLIENT_INTERFACE_VERSION;
169 entry.Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
170 entry.ContextSize = sizeof(rdpContext);
171
172 rdpContext* context = freerdp_client_context_new(&entry);
173 if (!context)
174 break;
175
176 /* bound the time spent waiting for more (non existing) HTTP data */
177 if (freerdp_settings_set_uint32(context->settings, FreeRDP_GatewayResponseTimeout, 1))
178 {
179 if ((target % 4) == 1)
180 fuzz_tsg(context, s);
181 else
182 fuzz_http(context, s);
183 }
184 freerdp_client_context_free(context);
185 }
186 break;
187 }
188
189 return 0;
190}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.