FreeRDP
Loading...
Searching...
No Matches
rdpewa_cbor.c
1
19#include <freerdp/config.h>
20
21#include <string.h>
22#include <stdlib.h>
23
24#include <cbor.h>
25
26#include <winpr/assert.h>
27#include <winpr/endian.h>
28
29#include <freerdp/channels/log.h>
30
31#include "rdpewa_cbor.h"
32
33#define TAG CHANNELS_TAG("rdpewa.client")
34
35BOOL rdpewa_cbor_decode_request(const BYTE* data, size_t length, RDPEWA_REQUEST* out)
36{
37 struct cbor_load_result result = WINPR_C_ARRAY_INIT;
38 BOOL ret = FALSE;
39
40 WINPR_ASSERT(data);
41 WINPR_ASSERT(out);
42
43 memset(out, 0, sizeof(*out));
44
45 cbor_item_t* root = cbor_load(data, length, &result);
46 if (!root || result.error.code != CBOR_ERR_NONE)
47 {
48 WLog_ERR(TAG, "Failed to decode CBOR request, error %u at position %" PRIuz,
49 result.error.code, result.error.position);
50 goto out;
51 }
52
53 if (!cbor_isa_map(root))
54 {
55 WLog_ERR(TAG, "CBOR request is not a map");
56 goto out;
57 }
58
59 const size_t mapSize = cbor_map_size(root);
60 struct cbor_pair* pairs = cbor_map_handle(root);
61
62 WLog_DBG(TAG, "Request CBOR map has %" PRIuz " entries", mapSize);
63
64 for (size_t i = 0; i < mapSize; i++)
65 {
66 cbor_item_t* key = pairs[i].key;
67 cbor_item_t* value = pairs[i].value;
68
69 if (!cbor_isa_string(key))
70 continue;
71
72 const char* keyStr = (const char*)cbor_string_handle(key);
73 const size_t keyLen = cbor_string_length(key);
74
75 WLog_DBG(TAG, " key[%" PRIuz "]: \"%.*s\" type=%u", i,
76 WINPR_ASSERTING_INT_CAST(int, keyLen), keyStr, cbor_typeof(value));
77
78 switch (keyLen)
79 {
80 case 4:
81 if (memcmp(keyStr, "rpId", 4) == 0)
82 {
83 if (cbor_isa_string(value))
84 {
85 size_t slen = cbor_string_length(value);
86 if (slen >= sizeof(out->rpId))
87 slen = sizeof(out->rpId) - 1;
88 memcpy(out->rpId, cbor_string_handle(value), slen);
89 out->rpId[slen] = '\0';
90 }
91 }
92 break;
93 case 5:
94 if (memcmp(keyStr, "flags", 5) == 0)
95 {
96 if (!cbor_isa_uint(value))
97 {
98 WLog_ERR(TAG, "\"flags\"is not an unsigned int");
99 goto out;
100 }
101 out->flags = WINPR_ASSERTING_INT_CAST(UINT32, cbor_get_int(value));
102 }
103 break;
104 case 7:
105 if (memcmp(keyStr, "command", 7) == 0)
106 {
107 if (!cbor_isa_uint(value))
108 {
109 WLog_ERR(TAG, "\"command\"is not an unsigned int");
110 goto out;
111 }
112 out->command = WINPR_ASSERTING_INT_CAST(UINT32, cbor_get_int(value));
113 }
114 else if (memcmp(keyStr, "request", 7) == 0)
115 {
116 if (!cbor_isa_bytestring(value))
117 {
118 WLog_ERR(TAG, "\"request\"is not a byte string");
119 goto out;
120 }
121 out->requestLen = cbor_bytestring_length(value);
122 out->request = malloc(out->requestLen);
123 if (!out->request)
124 {
125 out->requestLen = 0;
126 goto out;
127 }
128 memcpy(out->request, cbor_bytestring_handle(value), out->requestLen);
129 }
130 else if (memcmp(keyStr, "timeout", 7) == 0)
131 {
132 if (!cbor_isa_uint(value))
133 {
134 WLog_ERR(TAG, "\"timeout\"is not an unsigned int");
135 goto out;
136 }
137 out->timeout = WINPR_ASSERTING_INT_CAST(UINT32, cbor_get_int(value));
138 }
139 break;
140 case 13:
141 if (memcmp(keyStr, "transactionId", 13) == 0)
142 {
143 if (!cbor_isa_bytestring(value) || cbor_bytestring_length(value) != 16)
144 {
145 WLog_ERR(TAG, "\"transactionId\"is not a 16-byte byte string");
146 goto out;
147 }
148 memcpy(out->transactionId, cbor_bytestring_handle(value), 16);
149 out->hasTransactionId = TRUE;
150 }
151 break;
152 default:
153 break;
154 }
155 }
156
157 if (out->command == 0)
158 {
159 WLog_ERR(TAG, "Missing or invalid \"command\" in request");
160 goto out;
161 }
162
163 ret = TRUE;
164out:
165 if (!ret)
166 {
167 free(out->request);
168 out->request = nullptr;
169 out->requestLen = 0;
170 }
171 if (root)
172 cbor_decref(&root);
173 return ret;
174}
175
176static BOOL wrap_cbor_map_add(cbor_item_t* root, cbor_item_t* diKey, cbor_item_t* diMap)
177{
178 const bool rc = cbor_map_add(root, (struct cbor_pair){ .key = diKey, .value = diMap });
179 cbor_decref(&diKey);
180 cbor_decref(&diMap);
181 return rc;
182}
183
184wStream* rdpewa_cbor_encode_webauthn_response(HRESULT hresult, BYTE ctapStatus,
185 const BYTE* ctapData, size_t ctapLen,
186 const RDPEWA_DEVICE_INFO* devInfo)
187{
188 wStream* s = nullptr;
189 cbor_item_t* root = nullptr;
190 size_t cborLen = 0;
191
192 WINPR_ASSERT(devInfo);
193
194 /* Build the inner "Response" byte string: status byte + CTAP CBOR data */
195 size_t responseLen = 1 + ctapLen;
196 BYTE* responseData = malloc(responseLen);
197 if (!responseData)
198 return nullptr;
199
200 responseData[0] = ctapStatus;
201 if (ctapData && ctapLen > 0)
202 memcpy(responseData + 1, ctapData, ctapLen);
203
204 root = cbor_new_definite_map(3);
205 if (!root)
206 goto out;
207
208 /* "deviceInfo" key */
209 cbor_item_t* diKey = cbor_build_string("deviceInfo");
210 cbor_item_t* diMap = cbor_new_definite_map(11);
211 if (!diKey || !diMap)
212 {
213 if (diKey)
214 cbor_decref(&diKey);
215 if (diMap)
216 cbor_decref(&diMap);
217 goto out;
218 }
219 {
220 /* The casing is all over the place, but that's how it's defined in the spec */
221 cbor_item_t* pairs[][2] = {
222 { cbor_build_string("maxMsgSize"),
223 cbor_build_uint16(WINPR_ASSERTING_INT_CAST(uint16_t, devInfo->maxMsgSize)) },
224 { cbor_build_string("maxSerializedLargeBlobArray"),
225 cbor_build_uint16(
226 WINPR_ASSERTING_INT_CAST(uint16_t, devInfo->maxSerializedLargeBlobArray)) },
227 { cbor_build_string("providerType"), cbor_build_string(devInfo->providerType) },
228 { cbor_build_string("providerName"), cbor_build_string(devInfo->providerName) },
229 { cbor_build_string("devicePath"), cbor_build_string(devInfo->devicePath) },
230 { cbor_build_string("Manufacturer"), cbor_build_string(devInfo->manufacturer) },
231 { cbor_build_string("Product"), cbor_build_string(devInfo->product) },
232 { cbor_build_string("aaGuid"),
233 cbor_build_bytestring(devInfo->aaGuid, sizeof(devInfo->aaGuid)) },
234 { cbor_build_string("uvStatus"), cbor_build_uint8(devInfo->uvStatus) },
235 { cbor_build_string("uvRetries"), cbor_build_uint8(devInfo->uvRetries) },
236 { cbor_build_string("transports"),
237 cbor_build_uint8(WINPR_ASSERTING_INT_CAST(uint8_t, devInfo->transports)) }
238 };
239 for (size_t i = 0; i < 11; i++)
240 {
241 if (!wrap_cbor_map_add(diMap, pairs[i][0], pairs[i][1]))
242 goto out;
243 }
244 }
245 if (!wrap_cbor_map_add(root, diKey, diMap))
246 goto out;
247
248 /* "status" key (lowercase per spec example hex) */
249 cbor_item_t* statusKey = cbor_build_string("status");
250 cbor_item_t* statusVal = cbor_build_uint8(0);
251 if (!wrap_cbor_map_add(root, statusKey, statusVal))
252 goto out;
253
254 /* "response" key (lowercase per spec example hex) */
255 cbor_item_t* respKey = cbor_build_string("response");
256 cbor_item_t* respVal = cbor_build_bytestring(responseData, responseLen);
257 if (!wrap_cbor_map_add(root, respKey, respVal))
258 goto out;
259
260 cborLen = cbor_serialized_size(root);
261 if (cborLen == 0)
262 goto out;
263
264 /* Response: 4-byte LE HRESULT + CBOR map */
265 s = Stream_New(nullptr, 4 + cborLen);
266 if (!s)
267 goto out;
268
269 Stream_Write_UINT32(s, (UINT32)hresult);
270 if (cbor_serialize(root, Stream_Pointer(s), cborLen) == 0)
271 {
272 Stream_Free(s, TRUE);
273 s = nullptr;
274 goto out;
275 }
276 Stream_Seek(s, cborLen);
277
278out:
279 free(responseData);
280 if (root)
281 cbor_decref(&root);
282 return s;
283}
284
285wStream* rdpewa_cbor_encode_simple_response(HRESULT hresult, UINT32 value)
286{
287 wStream* s = Stream_New(nullptr, 8);
288 if (!s)
289 return nullptr;
290
291 Stream_Write_UINT32(s, (UINT32)hresult);
292 Stream_Write_UINT32(s, value);
293 return s;
294}
295
296wStream* rdpewa_cbor_encode_hresult_response(HRESULT hresult)
297{
298 wStream* s = Stream_New(nullptr, 4);
299 if (!s)
300 return nullptr;
301
302 Stream_Write_UINT32(s, (UINT32)hresult);
303 return s;
304}
Device info for the response map.
Definition rdpewa_cbor.h:42
Decoded MS-RDPEWA request message.
Definition rdpewa_cbor.h:29