FreeRDP
Loading...
Searching...
No Matches
server/rdpdr_main.c
1
24#include <freerdp/config.h>
25#include <freerdp/freerdp.h>
26#include <freerdp/utils/rdpdr_utils.h>
27
28#include <winpr/crt.h>
29#include <winpr/interlocked.h>
30#include <winpr/assert.h>
31#include <winpr/nt.h>
32#include <winpr/print.h>
33#include <winpr/stream.h>
34
35#include <freerdp/channels/log.h>
36#include <freerdp/channels/scard.h>
37#include <freerdp/utils/smartcard_operations.h>
38#include "rdpdr_main.h"
39#include <freerdp/utils/channel_pdu_tracker.h>
40
41#define RDPDR_ADD_PRINTER_EVENT 0x00000001
42#define RDPDR_UPDATE_PRINTER_EVENT 0x00000002
43#define RDPDR_DELETE_PRINTER_EVENT 0x00000003
44#define RDPDR_RENAME_PRINTER_EVENT 0x00000004
45
46#define RDPDR_HEADER_LENGTH 4
47#define RDPDR_CAPABILITY_HEADER_LENGTH 8
48
49/* [MS-RDPESC] 3.2.5.1: the output buffer length SHOULD be set to 2048 */
50#define SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH 2048
51
52struct s_rdpdr_server_private
53{
54 HANDLE Thread;
55 HANDLE StopEvent;
56 void* ChannelHandle;
57
58 UINT32 ClientId;
59 UINT16 VersionMajor;
60 UINT16 VersionMinor;
61 char* ClientComputerName;
62
63 BOOL UserLoggedOnPdu;
64
65 wListDictionary* IrpList;
66 LONG NextCompletionId; /* see rdpdr_server_next_completion_id() */
67
68 wHashTable* devicelist;
69 wLog* log;
70 UINT32 SpecialDeviceTypeCap;
71 UINT32 IoCode1;
72 UINT32 ExtendedPDU;
73 BOOL haveSmartcardDevice;
74 UINT32 smartcardDeviceId;
75};
76
77static UINT32 g_ClientId = 0;
78
79WINPR_ATTR_NODISCARD
80static const char* fileInformation2str(uint8_t val)
81{
82 switch (val)
83 {
84 case FILE_SUPERSEDED:
85 return "FILE_DOES_NOT_EXIST";
86 case FILE_OPENED:
87 return "FILE_EXISTS";
88 case FILE_CREATED:
89 return "FILE_OVERWRITTEN";
90 case FILE_OVERWRITTEN:
91 return "FILE_CREATED";
92 case FILE_EXISTS:
93 return "FILE_OPENED";
94 case FILE_DOES_NOT_EXIST:
95 return "FILE_SUPERSEDED";
96 default:
97 return "FILE_UNKNOWN";
98 }
99}
100
101WINPR_ATTR_NODISCARD
102static const char* DR_DRIVE_LOCK_REQ2str(uint32_t op)
103{
104 switch (op)
105 {
106 case RDP_LOWIO_OP_SHAREDLOCK:
107 return "RDP_LOWIO_OP_UNLOCK_MULTIPLE";
108 case RDP_LOWIO_OP_EXCLUSIVELOCK:
109 return "RDP_LOWIO_OP_UNLOCK";
110 case RDP_LOWIO_OP_UNLOCK:
111 return "RDP_LOWIO_OP_EXCLUSIVELOCK";
112 case RDP_LOWIO_OP_UNLOCK_MULTIPLE:
113 return "RDP_LOWIO_OP_SHAREDLOCK";
114 default:
115 return "RDP_LOWIO_OP_UNKNOWN";
116 }
117}
118
119static void rdpdr_device_free(RdpdrDevice* device)
120{
121 if (!device)
122 return;
123 free(device->DeviceData);
124 free(device);
125}
126
127static void rdpdr_device_free_h(void* obj)
128{
129 RdpdrDevice* other = obj;
130 rdpdr_device_free(other);
131}
132
133WINPR_ATTR_NODISCARD
134static UINT32 rdpdr_deviceid_hash(const void* id)
135{
136 WINPR_ASSERT(id);
137 return *((const UINT32*)id);
138}
139
140WINPR_ATTR_NODISCARD
141static BOOL rdpdr_device_equal(const void* v1, const void* v2)
142{
143 const UINT32* p1 = (const UINT32*)v1;
144 const UINT32* p2 = (const UINT32*)v2;
145 if (!p1 && !p2)
146 return TRUE;
147 if (!p1 || !p2)
148 return FALSE;
149 return *p1 == *p2;
150}
151
152WINPR_ATTR_MALLOC(rdpdr_device_free, 1)
153static RdpdrDevice* rdpdr_device_new(void)
154{
155 return calloc(1, sizeof(RdpdrDevice));
156}
157
158WINPR_ATTR_MALLOC(rdpdr_device_free_h, 1)
159static void* rdpdr_device_clone(const void* val)
160{
161 const RdpdrDevice* other = val;
162
163 if (!other)
164 return nullptr;
165
166 RdpdrDevice* tmp = rdpdr_device_new();
167 if (!tmp)
168 goto fail;
169
170 *tmp = *other;
171 if (other->DeviceData)
172 {
173 tmp->DeviceData = malloc(other->DeviceDataLength);
174 if (!tmp->DeviceData)
175 goto fail;
176 memcpy(tmp->DeviceData, other->DeviceData, other->DeviceDataLength);
177 }
178 return tmp;
179
180fail:
181 rdpdr_device_free(tmp);
182 return nullptr;
183}
184
185static void rdpdr_device_key_free(void* obj)
186{
187 free(obj);
188}
189
190WINPR_ATTR_MALLOC(rdpdr_device_key_free, 1)
191static void* rdpdr_device_key_clone(const void* pvval)
192{
193 const UINT32* val = pvval;
194 if (!val)
195 return nullptr;
196
197 UINT32* ptr = calloc(1, sizeof(UINT32));
198 if (!ptr)
199 return nullptr;
200 *ptr = *val;
201 return ptr;
202}
203
204WINPR_ATTR_NODISCARD
205static RdpdrDevice* rdpdr_get_device_by_id(RdpdrServerPrivate* priv, UINT32 DeviceId)
206{
207 WINPR_ASSERT(priv);
208 return HashTable_GetItemValue(priv->devicelist, &DeviceId);
209}
210
211WINPR_ATTR_NODISCARD
212static BOOL rdpdr_remove_device_by_id(RdpdrServerPrivate* priv, UINT32 DeviceId)
213{
214 WINPR_ASSERT(priv);
215
216 const RdpdrDevice* device = rdpdr_get_device_by_id(priv, DeviceId);
217 if (!device)
218 {
219 WLog_Print(priv->log, WLOG_WARN, "[del] Device Id: 0x%08" PRIX32 ": no such device",
220 DeviceId);
221 return FALSE;
222 }
223 WLog_Print(priv->log, WLOG_DEBUG,
224 "[del] Device Name: %s Id: 0x%08" PRIX32 " DataLength: %" PRIu32 "",
225 device->PreferredDosName, device->DeviceId, device->DeviceDataLength);
226 return HashTable_Remove(priv->devicelist, &DeviceId);
227}
228
229WINPR_ATTR_NODISCARD
230static BOOL rdpdr_add_device(RdpdrServerPrivate* priv, const RdpdrDevice* device)
231{
232 WINPR_ASSERT(priv);
233 WINPR_ASSERT(device);
234
235 WLog_Print(priv->log, WLOG_DEBUG,
236 "[add] Device Name: %s Id: 0x%08" PRIX32 " DataLength: %" PRIu32 "",
237 device->PreferredDosName, device->DeviceId, device->DeviceDataLength);
238
239 return HashTable_Insert(priv->devicelist, &device->DeviceId, device);
240}
241
242WINPR_ATTR_NODISCARD
243static const WCHAR* rdpdr_read_ustring(wLog* log, wStream* s, size_t bytelen)
244{
245 const size_t charlen = bytelen / sizeof(WCHAR);
246 const WCHAR* str = Stream_ConstPointer(s);
247 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, bytelen))
248 return nullptr;
249 if (_wcsnlen(str, charlen) == charlen)
250 {
251 WLog_Print(log, WLOG_WARN, "[rdpdr] unicode string not '\\0' terminated");
252 return nullptr;
253 }
254 Stream_Seek(s, bytelen);
255 return str;
256}
257
258static void rdpdr_server_irp_free(RDPDR_IRP* irp)
259{
260 if (!irp)
261 return;
262
263 free(irp->PathName);
264 free(irp->ExtraBuffer);
265 free(irp);
266}
267
268WINPR_ATTR_MALLOC(rdpdr_server_irp_free, 1)
269static RDPDR_IRP* rdpdr_server_irp_new(void)
270{
271 RDPDR_IRP* irp = (RDPDR_IRP*)calloc(1, sizeof(RDPDR_IRP));
272 return irp;
273}
274
275static void rdpdr_server_list_free(void* ptr)
276{
277 RDPDR_IRP* irp = ptr;
278 rdpdr_server_irp_free(irp);
279}
280
281WINPR_ATTR_NODISCARD
282static BOOL rdpdr_server_enqueue_irp(RdpdrServerContext* context, RDPDR_IRP* irp)
283{
284 WINPR_ASSERT(context);
285 WINPR_ASSERT(context->priv);
286
287 RdpdrServerPrivate* priv = context->priv;
288 const uintptr_t key = irp->CompletionId + 1ull;
289 return ListDictionary_Add(priv->IrpList, (void*)key, irp);
290}
291
292WINPR_ATTR_MALLOC(rdpdr_server_irp_free, 1)
293static RDPDR_IRP* rdpdr_server_dequeue_irp(RdpdrServerContext* context, UINT32 completionId)
294{
295 WINPR_ASSERT(context);
296 WINPR_ASSERT(context->priv);
297
298 RdpdrServerPrivate* priv = context->priv;
299 const uintptr_t key = completionId + 1ull;
300 RDPDR_IRP* irp = (RDPDR_IRP*)ListDictionary_Take(priv->IrpList, (void*)key);
301 return irp;
302}
303
304WINPR_ATTR_NODISCARD
305static UINT rdpdr_seal_send_free_request(RdpdrServerContext* context, wStream* s)
306{
307 WINPR_ASSERT(context);
308 WINPR_ASSERT(context->priv);
309 WINPR_ASSERT(s);
310
311 RdpdrServerPrivate* priv = context->priv;
312 Stream_SealLength(s);
313 const size_t length = Stream_Length(s);
314 WINPR_ASSERT(length <= UINT32_MAX);
315 Stream_ResetPosition(s);
316
317 if (length >= RDPDR_HEADER_LENGTH)
318 {
319 const RDPDR_HEADER header = {
320 .Component = Stream_Get_UINT16(s),
321 .PacketId = Stream_Get_UINT16(s),
322 };
323
324 WLog_Print(priv->log, WLOG_DEBUG,
325 "sending message {Component %s[%04" PRIx16 "], PacketId %s[%04" PRIx16 "]",
326 rdpdr_component_string(header.Component), header.Component,
327 rdpdr_packetid_string(header.PacketId), header.PacketId);
328 }
329 winpr_HexLogDump(priv->log, WLOG_DEBUG, Stream_Buffer(s), Stream_Length(s));
330 ULONG written = 0;
331 BOOL status = WTSVirtualChannelWrite(priv->ChannelHandle, Stream_BufferAs(s, char),
332 (ULONG)length, &written);
333 Stream_Release(s);
334 return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
335}
336
342WINPR_ATTR_NODISCARD
343static UINT rdpdr_server_send_announce_request(RdpdrServerContext* context)
344{
345 WINPR_ASSERT(context);
346 WINPR_ASSERT(context->priv);
347
348 UINT error = IFCALLRESULT(CHANNEL_RC_OK, context->SendServerAnnounce, context);
349 if (error != CHANNEL_RC_OK)
350 return error;
351
352 RdpdrServerPrivate* priv = context->priv;
353 wStream* s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 8);
354 if (!s)
355 {
356 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
357 return CHANNEL_RC_NO_MEMORY;
358 }
359
360 const RDPDR_HEADER header = {
361 .Component = RDPDR_CTYP_CORE,
362 .PacketId = PAKID_CORE_SERVER_ANNOUNCE,
363 };
364 Stream_Write_UINT16(s, header.Component); /* Component (2 bytes) */
365 Stream_Write_UINT16(s, header.PacketId); /* PacketId (2 bytes) */
366 Stream_Write_UINT16(s, priv->VersionMajor); /* VersionMajor (2 bytes) */
367 Stream_Write_UINT16(s, priv->VersionMinor); /* VersionMinor (2 bytes) */
368 Stream_Write_UINT32(s, priv->ClientId); /* ClientId (4 bytes) */
369 return rdpdr_seal_send_free_request(context, s);
370}
371
377WINPR_ATTR_NODISCARD
378static UINT rdpdr_server_receive_announce_response(RdpdrServerContext* context, wStream* s,
379 const RDPDR_HEADER* header)
380{
381 WINPR_ASSERT(context);
382 WINPR_ASSERT(context->priv);
383 WINPR_ASSERT(header);
384
385 RdpdrServerPrivate* priv = context->priv;
386
387 WINPR_UNUSED(header);
388
389 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8))
390 return ERROR_INVALID_DATA;
391
392 const UINT16 VersionMajor = Stream_Get_UINT16(s); /* VersionMajor (2 bytes) */
393 const UINT16 VersionMinor = Stream_Get_UINT16(s); /* VersionMinor (2 bytes) */
394 const UINT32 ClientId = Stream_Get_UINT32(s); /* ClientId (4 bytes) */
395 WLog_Print(priv->log, WLOG_DEBUG,
396 "Client Announce Response: VersionMajor: 0x%08" PRIX16 " VersionMinor: 0x%04" PRIX16
397 " ClientId: 0x%08" PRIX32 "",
398 VersionMajor, VersionMinor, ClientId);
399 priv->ClientId = ClientId;
400
401 return IFCALLRESULT(CHANNEL_RC_OK, context->ReceiveAnnounceResponse, context, VersionMajor,
402 VersionMinor, ClientId);
403}
404
410WINPR_ATTR_NODISCARD
411static UINT rdpdr_server_receive_client_name_request(RdpdrServerContext* context, wStream* s,
412 const RDPDR_HEADER* header)
413{
414 WINPR_ASSERT(context);
415 WINPR_ASSERT(context->priv);
416 WINPR_ASSERT(s);
417 WINPR_ASSERT(header);
418 WINPR_UNUSED(header);
419
420 RdpdrServerPrivate* priv = context->priv;
421
422 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 12))
423 return ERROR_INVALID_DATA;
424
425 UINT32 UnicodeFlag = Stream_Get_UINT32(s); /* UnicodeFlag (4 bytes) */
426 const UINT32 CodePage = Stream_Get_UINT32(s); /* CodePage (4 bytes) */
427 const UINT32 ComputerNameLen = Stream_Get_UINT32(s); /* ComputerNameLen (4 bytes) */
428 /* UnicodeFlag is either 0 or 1, the other 31 bits must be ignored.
429 */
430 UnicodeFlag = UnicodeFlag & 0x00000001;
431
432 if (CodePage != 0)
433 WLog_Print(priv->log, WLOG_WARN,
434 "[MS-RDPEFS] 2.2.2.4 Client Name Request (DR_CORE_CLIENT_NAME_REQ)::CodePage "
435 "must be 0, but is 0x%08" PRIx32,
436 CodePage);
437
443 if (UnicodeFlag)
444 {
445 if ((ComputerNameLen % 2) || ComputerNameLen > 512 || ComputerNameLen < 2)
446 {
447 WLog_Print(priv->log, WLOG_ERROR, "invalid unicode computer name length: %" PRIu32 "",
448 ComputerNameLen);
449 return ERROR_INVALID_DATA;
450 }
451 }
452 else
453 {
454 if (ComputerNameLen > 256 || ComputerNameLen < 1)
455 {
456 WLog_Print(priv->log, WLOG_ERROR, "invalid ascii computer name length: %" PRIu32 "",
457 ComputerNameLen);
458 return ERROR_INVALID_DATA;
459 }
460 }
461
462 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, ComputerNameLen))
463 return ERROR_INVALID_DATA;
464
465 /* ComputerName must be null terminated, check if it really is */
466 const char* computerName = Stream_ConstPointer(s);
467 if (computerName[ComputerNameLen - 1] || (UnicodeFlag && computerName[ComputerNameLen - 2]))
468 {
469 WLog_Print(priv->log, WLOG_ERROR, "computer name must be null terminated");
470 return ERROR_INVALID_DATA;
471 }
472
473 if (priv->ClientComputerName)
474 {
475 free(priv->ClientComputerName);
476 priv->ClientComputerName = nullptr;
477 }
478
479 if (UnicodeFlag)
480 {
481 priv->ClientComputerName =
482 Stream_Read_UTF16_String_As_UTF8(s, ComputerNameLen / sizeof(WCHAR), nullptr);
483 if (!priv->ClientComputerName)
484 {
485 WLog_Print(priv->log, WLOG_ERROR, "failed to convert client computer name");
486 return ERROR_INVALID_DATA;
487 }
488 }
489 else
490 {
491 const char* name = Stream_ConstPointer(s);
492 priv->ClientComputerName = _strdup(name);
493 Stream_Seek(s, ComputerNameLen);
494
495 if (!priv->ClientComputerName)
496 {
497 WLog_Print(priv->log, WLOG_ERROR, "failed to duplicate client computer name");
498 return CHANNEL_RC_NO_MEMORY;
499 }
500 }
501
502 WLog_Print(priv->log, WLOG_DEBUG, "ClientComputerName: %s", priv->ClientComputerName);
503 return IFCALLRESULT(CHANNEL_RC_OK, context->ReceiveClientNameRequest, context, ComputerNameLen,
504 priv->ClientComputerName);
505}
506
507WINPR_ATTR_NODISCARD
508static UINT rdpdr_server_write_capability_set_header_cb(RdpdrServerContext* context, wStream* s,
509 const RDPDR_CAPABILITY_HEADER* header)
510{
511 WINPR_ASSERT(context);
512 WINPR_ASSERT(context->priv);
513
514 RdpdrServerPrivate* priv = context->priv;
515 UINT error = rdpdr_write_capset_header(priv->log, s, header);
516 if (error != CHANNEL_RC_OK)
517 return error;
518
519 return IFCALLRESULT(CHANNEL_RC_OK, context->SendCaps, context, header, 0, nullptr);
520}
521
527WINPR_ATTR_NODISCARD
528static UINT rdpdr_server_read_general_capability_set(RdpdrServerContext* context, wStream* s,
529 const RDPDR_CAPABILITY_HEADER* header)
530{
531 WINPR_ASSERT(context);
532 WINPR_ASSERT(context->priv);
533 WINPR_ASSERT(header);
534
535 RdpdrServerPrivate* priv = context->priv;
536
537 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
538 return ERROR_INVALID_DATA;
539
540 const UINT32 OsType = Stream_Get_UINT32(s); /* osType (4 bytes),
541 ignored on receipt */
542 const UINT32 OsVersion = Stream_Get_UINT32(s); /* osVersion (4 bytes),
543 unused and must be set to zero */
544 const UINT32 VersionMajor = Stream_Get_UINT16(s); /* protocolMajorVersion (2 bytes) */
545 const UINT32 VersionMinor = Stream_Get_UINT16(s); /* protocolMinorVersion (2 bytes) */
546 const UINT32 IoCode1 = Stream_Get_UINT32(s); /* ioCode1 (4 bytes) */
547 const UINT32 IoCode2 = Stream_Get_UINT32(s); /* ioCode2 (4 bytes),
548 must be set to zero, reserved for future use */
549 const UINT32 ExtendedPdu = Stream_Get_UINT32(s); /* extendedPdu (4 bytes) */
550 const UINT32 ExtraFlags1 = Stream_Get_UINT32(s); /* extraFlags1 (4 bytes) */
551 const UINT32 ExtraFlags2 = Stream_Get_UINT32(s); /* extraFlags2 (4 bytes),
552 must be set to zero, reserved for future use */
553
554 {
555 char buffer[1024] = WINPR_C_ARRAY_INIT;
556 WLog_Print(priv->log, WLOG_TRACE,
557 "OsType=%" PRIu32 ", OsVersion=%" PRIu32 ", VersionMajor=%" PRIu32
558 ", VersionMinor=%" PRIu32 ", IoCode1=%s, IoCode2=%" PRIu32
559 ", ExtendedPdu=%" PRIu32 ", ExtraFlags1=%" PRIu32 ", ExtraFlags2=%" PRIu32,
560 OsType, OsVersion, VersionMajor, VersionMinor,
561 rdpdr_irp_mask2str(IoCode1, buffer, sizeof(buffer)), IoCode2, ExtendedPdu,
562 ExtraFlags1, ExtraFlags2);
563 }
564
565 if (VersionMajor != RDPDR_MAJOR_RDP_VERSION)
566 {
567 WLog_Print(priv->log, WLOG_ERROR, "unsupported RDPDR version %" PRIu16 ".%" PRIu16,
568 VersionMajor, VersionMinor);
569 return ERROR_INVALID_DATA;
570 }
571
572 switch (VersionMinor)
573 {
574 case RDPDR_MINOR_RDP_VERSION_13:
575 case RDPDR_MINOR_RDP_VERSION_6_X:
576 case RDPDR_MINOR_RDP_VERSION_5_2:
577 case RDPDR_MINOR_RDP_VERSION_5_1:
578 case RDPDR_MINOR_RDP_VERSION_5_0:
579 break;
580 default:
581 WLog_Print(priv->log, WLOG_WARN, "unsupported RDPDR minor version %" PRIu16 ".%" PRIu16,
582 VersionMajor, VersionMinor);
583 break;
584 }
585
586 UINT32 SpecialTypeDeviceCap = 0;
587 if (header->Version == GENERAL_CAPABILITY_VERSION_02)
588 {
589 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
590 return ERROR_INVALID_DATA;
591
592 SpecialTypeDeviceCap = Stream_Get_UINT32(s); /* SpecialTypeDeviceCap (4 bytes) */
593 }
594 priv->SpecialDeviceTypeCap = SpecialTypeDeviceCap;
595
596 const UINT32 mask =
597 RDPDR_IRP_MJ_CREATE | RDPDR_IRP_MJ_CLEANUP | RDPDR_IRP_MJ_CLOSE | RDPDR_IRP_MJ_READ |
598 RDPDR_IRP_MJ_WRITE | RDPDR_IRP_MJ_FLUSH_BUFFERS | RDPDR_IRP_MJ_SHUTDOWN |
599 RDPDR_IRP_MJ_DEVICE_CONTROL | RDPDR_IRP_MJ_QUERY_VOLUME_INFORMATION |
600 RDPDR_IRP_MJ_SET_VOLUME_INFORMATION | RDPDR_IRP_MJ_QUERY_INFORMATION |
601 RDPDR_IRP_MJ_SET_INFORMATION | RDPDR_IRP_MJ_DIRECTORY_CONTROL | RDPDR_IRP_MJ_LOCK_CONTROL;
602
603 if ((IoCode1 & mask) == 0)
604 {
605 char buffer[1024] = WINPR_C_ARRAY_INIT;
606 WLog_Print(priv->log, WLOG_ERROR, "Missing IRP mask values %s",
607 rdpdr_irp_mask2str(IoCode1 & mask, buffer, sizeof(buffer)));
608 return ERROR_INVALID_DATA;
609 }
610 priv->IoCode1 = IoCode1;
611
612 if (IoCode2 != 0)
613 {
614 WLog_Print(priv->log, WLOG_WARN,
615 "[MS-RDPEFS] 2.2.2.7.1 General Capability Set (GENERAL_CAPS_SET) ioCode2 is "
616 "reserved for future use, expected value 0 got %" PRIu32,
617 IoCode2);
618 }
619
620 if ((ExtendedPdu & RDPDR_CLIENT_DISPLAY_NAME_PDU) == 0)
621 {
622 WLog_Print(priv->log, WLOG_WARN,
623 "[MS-RDPEFS] 2.2.2.7.1 General Capability Set (GENERAL_CAPS_SET) extendedPDU "
624 "should always set RDPDR_CLIENT_DISPLAY_NAME_PDU[0x00000002]");
625 }
626
627 priv->ExtendedPDU = ExtendedPdu;
628 priv->UserLoggedOnPdu = (ExtendedPdu & RDPDR_USER_LOGGEDON_PDU) != 0;
629
630 if (ExtraFlags2 != 0)
631 {
632 WLog_Print(priv->log, WLOG_WARN,
633 "[MS-RDPEFS] 2.2.2.7.1 General Capability Set (GENERAL_CAPS_SET) ExtraFlags2 is "
634 "reserved for future use, expected value 0 got %" PRIu32,
635 ExtraFlags2);
636 }
637
638 return CHANNEL_RC_OK;
639}
640
646WINPR_ATTR_NODISCARD
647static UINT rdpdr_server_write_general_capability_set(RdpdrServerContext* context, wStream* s)
648{
649 WINPR_ASSERT(context);
650 WINPR_ASSERT(context->priv);
651
652 const RDPDR_CAPABILITY_HEADER header = { CAP_GENERAL_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH + 36,
653 GENERAL_CAPABILITY_VERSION_02 };
654
655 UINT32 ioCode1 = 0;
656 ioCode1 |= RDPDR_IRP_MJ_CREATE; /* always set */
657 ioCode1 |= RDPDR_IRP_MJ_CLEANUP; /* always set */
658 ioCode1 |= RDPDR_IRP_MJ_CLOSE; /* always set */
659 ioCode1 |= RDPDR_IRP_MJ_READ; /* always set */
660 ioCode1 |= RDPDR_IRP_MJ_WRITE; /* always set */
661 ioCode1 |= RDPDR_IRP_MJ_FLUSH_BUFFERS; /* always set */
662 ioCode1 |= RDPDR_IRP_MJ_SHUTDOWN; /* always set */
663 ioCode1 |= RDPDR_IRP_MJ_DEVICE_CONTROL; /* always set */
664 ioCode1 |= RDPDR_IRP_MJ_QUERY_VOLUME_INFORMATION; /* always set */
665 ioCode1 |= RDPDR_IRP_MJ_SET_VOLUME_INFORMATION; /* always set */
666 ioCode1 |= RDPDR_IRP_MJ_QUERY_INFORMATION; /* always set */
667 ioCode1 |= RDPDR_IRP_MJ_SET_INFORMATION; /* always set */
668 ioCode1 |= RDPDR_IRP_MJ_DIRECTORY_CONTROL; /* always set */
669 ioCode1 |= RDPDR_IRP_MJ_LOCK_CONTROL; /* always set */
670 ioCode1 |= RDPDR_IRP_MJ_QUERY_SECURITY; /* optional */
671 ioCode1 |= RDPDR_IRP_MJ_SET_SECURITY; /* optional */
672
673 UINT32 extendedPdu = 0;
674 extendedPdu |= RDPDR_CLIENT_DISPLAY_NAME_PDU; /* always set */
675 extendedPdu |= RDPDR_DEVICE_REMOVE_PDUS; /* optional */
676
677 RdpdrServerPrivate* priv = context->priv;
678 if (priv->UserLoggedOnPdu)
679 extendedPdu |= RDPDR_USER_LOGGEDON_PDU; /* optional */
680
681 UINT32 extraFlags1 = 0;
682 extraFlags1 |= ENABLE_ASYNCIO; /* optional */
683 UINT32 SpecialTypeDeviceCap = 0;
684
685 UINT error = rdpdr_write_capset_header(priv->log, s, &header);
686 if (error != CHANNEL_RC_OK)
687 return error;
688
689 const BYTE* data = Stream_ConstPointer(s);
690 const size_t start = Stream_GetPosition(s);
691 Stream_Write_UINT32(s, 0); /* osType (4 bytes), ignored on receipt */
692 Stream_Write_UINT32(s, 0); /* osVersion (4 bytes), unused and must be set to zero */
693 Stream_Write_UINT16(s, priv->VersionMajor); /* protocolMajorVersion (2 bytes) */
694 Stream_Write_UINT16(s, priv->VersionMinor); /* protocolMinorVersion (2 bytes) */
695 Stream_Write_UINT32(s, ioCode1); /* ioCode1 (4 bytes) */
696 Stream_Write_UINT32(s, 0); /* ioCode2 (4 bytes), must be set to zero, reserved for future use */
697 Stream_Write_UINT32(s, extendedPdu); /* extendedPdu (4 bytes) */
698 Stream_Write_UINT32(s, extraFlags1); /* extraFlags1 (4 bytes) */
699 Stream_Write_UINT32(
700 s, 0); /* extraFlags2 (4 bytes), must be set to zero, reserved for future use */
701 Stream_Write_UINT32(s, SpecialTypeDeviceCap); /* SpecialTypeDeviceCap (4 bytes) */
702 const size_t end = Stream_GetPosition(s);
703 return IFCALLRESULT(CHANNEL_RC_OK, context->SendCaps, context, &header, end - start, data);
704}
705
711WINPR_ATTR_NODISCARD
712static UINT rdpdr_server_read_printer_capability_set(RdpdrServerContext* context, wStream* s,
713 const RDPDR_CAPABILITY_HEADER* header)
714{
715 WINPR_ASSERT(context);
716 WINPR_ASSERT(context->priv);
717 WINPR_ASSERT(s);
718 WINPR_ASSERT(header);
719 WINPR_UNUSED(context);
720 WINPR_UNUSED(header);
721 WINPR_UNUSED(s);
722
723 return CHANNEL_RC_OK;
724}
725
731WINPR_ATTR_NODISCARD
732static UINT rdpdr_server_write_printer_capability_set(RdpdrServerContext* context, wStream* s)
733{
734 WINPR_UNUSED(context);
735 WINPR_ASSERT(context);
736 WINPR_ASSERT(context->priv);
737
738 const RDPDR_CAPABILITY_HEADER header = { CAP_PRINTER_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH,
739 PRINT_CAPABILITY_VERSION_01 };
740
741 return rdpdr_server_write_capability_set_header_cb(context, s, &header);
742}
743
749WINPR_ATTR_NODISCARD
750static UINT rdpdr_server_read_port_capability_set(RdpdrServerContext* context, wStream* s,
751 const RDPDR_CAPABILITY_HEADER* header)
752{
753 WINPR_UNUSED(context);
754 WINPR_UNUSED(s);
755 WINPR_UNUSED(header);
756 WINPR_ASSERT(context);
757 WINPR_ASSERT(context->priv);
758
759 return CHANNEL_RC_OK;
760}
761
767WINPR_ATTR_NODISCARD
768static UINT rdpdr_server_write_port_capability_set(RdpdrServerContext* context, wStream* s)
769{
770 WINPR_UNUSED(context);
771 WINPR_ASSERT(context);
772 WINPR_ASSERT(context->priv);
773
774 const RDPDR_CAPABILITY_HEADER header = { CAP_PORT_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH,
775 PORT_CAPABILITY_VERSION_01 };
776
777 return rdpdr_server_write_capability_set_header_cb(context, s, &header);
778}
779
785WINPR_ATTR_NODISCARD
786static UINT rdpdr_server_read_drive_capability_set(RdpdrServerContext* context, wStream* s,
787 const RDPDR_CAPABILITY_HEADER* header)
788{
789 WINPR_ASSERT(context);
790 WINPR_ASSERT(context->priv);
791 WINPR_UNUSED(context);
792 WINPR_UNUSED(header);
793 WINPR_UNUSED(s);
794
795 return CHANNEL_RC_OK;
796}
797
803WINPR_ATTR_NODISCARD
804static UINT rdpdr_server_write_drive_capability_set(RdpdrServerContext* context, wStream* s)
805{
806 WINPR_ASSERT(context);
807 WINPR_ASSERT(context->priv);
808 WINPR_UNUSED(context);
809
810 const RDPDR_CAPABILITY_HEADER header = { CAP_DRIVE_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH,
811 DRIVE_CAPABILITY_VERSION_02 };
812
813 return rdpdr_server_write_capability_set_header_cb(context, s, &header);
814}
815
821WINPR_ATTR_NODISCARD
822static UINT rdpdr_server_read_smartcard_capability_set(RdpdrServerContext* context, wStream* s,
823 const RDPDR_CAPABILITY_HEADER* header)
824{
825 WINPR_ASSERT(context);
826 WINPR_ASSERT(context->priv);
827 WINPR_UNUSED(context);
828 WINPR_UNUSED(header);
829 WINPR_UNUSED(s);
830
831 return CHANNEL_RC_OK;
832}
833
839WINPR_ATTR_NODISCARD
840static UINT rdpdr_server_write_smartcard_capability_set(RdpdrServerContext* context, wStream* s)
841{
842 WINPR_ASSERT(context);
843 WINPR_ASSERT(context->priv);
844 WINPR_UNUSED(context);
845
846 const RDPDR_CAPABILITY_HEADER header = { CAP_SMARTCARD_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH,
847 SMARTCARD_CAPABILITY_VERSION_01 };
848
849 return rdpdr_server_write_capability_set_header_cb(context, s, &header);
850}
851
857WINPR_ATTR_NODISCARD
858static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext* context)
859{
860 WINPR_ASSERT(context);
861 WINPR_ASSERT(context->priv);
862
863 UINT16 numCapabilities = 1;
864 if ((context->supported & RDPDR_DTYP_FILESYSTEM) != 0)
865 numCapabilities++;
866
867 if (((context->supported & RDPDR_DTYP_PARALLEL) != 0) ||
868 ((context->supported & RDPDR_DTYP_SERIAL) != 0))
869 numCapabilities++;
870
871 if ((context->supported & RDPDR_DTYP_PRINT) != 0)
872 numCapabilities++;
873
874 if ((context->supported & RDPDR_DTYP_SMARTCARD) != 0)
875 numCapabilities++;
876
877 RdpdrServerPrivate* priv = context->priv;
878 wStream* s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 512);
879 if (!s)
880 {
881 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
882 return CHANNEL_RC_NO_MEMORY;
883 }
884
885 const RDPDR_HEADER header = {
886 .Component = RDPDR_CTYP_CORE,
887 .PacketId = PAKID_CORE_SERVER_CAPABILITY,
888 };
889 Stream_Write_UINT16(s, header.Component); /* Component (2 bytes) */
890 Stream_Write_UINT16(s, header.PacketId); /* PacketId (2 bytes) */
891 Stream_Write_UINT16(s, numCapabilities); /* numCapabilities (2 bytes) */
892 Stream_Write_UINT16(s, 0); /* Padding (2 bytes) */
893
894 UINT error = 0;
895 if ((error = rdpdr_server_write_general_capability_set(context, s)))
896 {
897 WLog_Print(priv->log, WLOG_ERROR,
898 "rdpdr_server_write_general_capability_set failed with error %" PRIu32 "!",
899 error);
900 goto out;
901 }
902
903 if ((context->supported & RDPDR_DTYP_FILESYSTEM) != 0)
904 {
905 if ((error = rdpdr_server_write_drive_capability_set(context, s)))
906 {
907 WLog_Print(priv->log, WLOG_ERROR,
908 "rdpdr_server_write_drive_capability_set failed with error %" PRIu32 "!",
909 error);
910 goto out;
911 }
912 }
913
914 if (((context->supported & RDPDR_DTYP_PARALLEL) != 0) ||
915 ((context->supported & RDPDR_DTYP_SERIAL) != 0))
916 {
917 if ((error = rdpdr_server_write_port_capability_set(context, s)))
918 {
919 WLog_Print(priv->log, WLOG_ERROR,
920 "rdpdr_server_write_port_capability_set failed with error %" PRIu32 "!",
921 error);
922 goto out;
923 }
924 }
925
926 if ((context->supported & RDPDR_DTYP_PRINT) != 0)
927 {
928 if ((error = rdpdr_server_write_printer_capability_set(context, s)))
929 {
930 WLog_Print(priv->log, WLOG_ERROR,
931 "rdpdr_server_write_printer_capability_set failed with error %" PRIu32 "!",
932 error);
933 goto out;
934 }
935 }
936
937 if ((context->supported & RDPDR_DTYP_SMARTCARD) != 0)
938 {
939 if ((error = rdpdr_server_write_smartcard_capability_set(context, s)))
940 {
941 WLog_Print(priv->log, WLOG_ERROR,
942 "rdpdr_server_write_printer_capability_set failed with error %" PRIu32 "!",
943 error);
944 goto out;
945 }
946 }
947
948 return rdpdr_seal_send_free_request(context, s);
949out:
950 Stream_Release(s);
951 return error;
952}
953
954WINPR_ATTR_NODISCARD
955static UINT16 rdpdr_cap_type_to_dtyp(UINT16 capabilityType)
956{
957 switch (capabilityType)
958 {
959 case CAP_PRINTER_TYPE:
960 return RDPDR_DTYP_PRINT;
961 case CAP_PORT_TYPE:
962 return RDPDR_DTYP_SERIAL | RDPDR_DTYP_PARALLEL;
963 case CAP_DRIVE_TYPE:
964 return RDPDR_DTYP_FILESYSTEM;
965 case CAP_SMARTCARD_TYPE:
966 return RDPDR_DTYP_SMARTCARD;
967 case CAP_GENERAL_TYPE:
968 default:
969 return 0;
970 }
971}
972
978WINPR_ATTR_NODISCARD
979static UINT rdpdr_server_receive_core_capability_response(RdpdrServerContext* context, wStream* s,
980 const RDPDR_HEADER* header)
981{
982 WINPR_ASSERT(context);
983 WINPR_ASSERT(context->priv);
984
985 RdpdrServerPrivate* priv = context->priv;
986
987 WINPR_UNUSED(header);
988
989 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
990 return ERROR_INVALID_DATA;
991
992 const UINT16 numCapabilities = Stream_Get_UINT16(s); /* numCapabilities (2 bytes) */
993 Stream_Seek_UINT16(s); /* Padding (2 bytes) */
994
995 UINT16 caps = 0;
996 for (UINT16 i = 0; i < numCapabilities; i++)
997 {
998 RDPDR_CAPABILITY_HEADER capabilityHeader = WINPR_C_ARRAY_INIT;
999 const size_t start = Stream_GetPosition(s);
1000
1001 UINT status = 0;
1002 if ((status = rdpdr_read_capset_header(priv->log, s, &capabilityHeader)))
1003 {
1004 WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status);
1005 return status;
1006 }
1007
1008 status = IFCALLRESULT(CHANNEL_RC_OK, context->ReceiveCaps, context, &capabilityHeader,
1009 Stream_GetRemainingLength(s), Stream_ConstPointer(s));
1010 if (status != CHANNEL_RC_OK)
1011 return status;
1012
1013 switch (capabilityHeader.CapabilityType)
1014 {
1015 case CAP_GENERAL_TYPE:
1016 if ((status =
1017 rdpdr_server_read_general_capability_set(context, s, &capabilityHeader)))
1018 {
1019 WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status);
1020 return status;
1021 }
1022
1023 break;
1024
1025 case CAP_PRINTER_TYPE:
1026 if ((status =
1027 rdpdr_server_read_printer_capability_set(context, s, &capabilityHeader)))
1028 {
1029 WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status);
1030 return status;
1031 }
1032
1033 break;
1034
1035 case CAP_PORT_TYPE:
1036 if ((status = rdpdr_server_read_port_capability_set(context, s, &capabilityHeader)))
1037 {
1038 WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status);
1039 return status;
1040 }
1041
1042 break;
1043
1044 case CAP_DRIVE_TYPE:
1045 if ((status =
1046 rdpdr_server_read_drive_capability_set(context, s, &capabilityHeader)))
1047 {
1048 WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status);
1049 return status;
1050 }
1051
1052 break;
1053
1054 case CAP_SMARTCARD_TYPE:
1055 if ((status =
1056 rdpdr_server_read_smartcard_capability_set(context, s, &capabilityHeader)))
1057 {
1058 WLog_Print(priv->log, WLOG_ERROR, "failed with error %" PRIu32 "!", status);
1059 return status;
1060 }
1061
1062 break;
1063
1064 default:
1065 WLog_Print(priv->log, WLOG_WARN, "Unknown capabilityType %" PRIu16 "",
1066 capabilityHeader.CapabilityType);
1067 Stream_Seek(s, capabilityHeader.CapabilityLength);
1068 return ERROR_INVALID_DATA;
1069 }
1070
1071 const UINT16 client_dtyp = rdpdr_cap_type_to_dtyp(capabilityHeader.CapabilityType);
1072 if ((client_dtyp != 0) && ((client_dtyp & context->supported) == 0))
1073 {
1074 WLog_Print(priv->log, WLOG_WARN, "client sent capability %s we did not announce!",
1075 rdpdr_cap_type_string(capabilityHeader.CapabilityType));
1076 }
1077 caps |= client_dtyp;
1078
1079 const size_t end = Stream_GetPosition(s);
1080 const size_t diff = end - start;
1081 if (diff != capabilityHeader.CapabilityLength + RDPDR_CAPABILITY_HEADER_LENGTH)
1082 {
1083 WLog_Print(priv->log, WLOG_WARN,
1084 "{capability %s[0x%04" PRIx16 "]} processed %" PRIuz
1085 " bytes, but expected to be %" PRIu16,
1086 rdpdr_cap_type_string(capabilityHeader.CapabilityType),
1087 capabilityHeader.CapabilityType, diff, capabilityHeader.CapabilityLength);
1088 }
1089 }
1090
1091 /* Only deactivate what the client did not respond with */
1092 context->supported &= caps;
1093
1094 return CHANNEL_RC_OK;
1095}
1096
1102WINPR_ATTR_NODISCARD
1103static UINT rdpdr_server_send_client_id_confirm(RdpdrServerContext* context)
1104{
1105 WINPR_ASSERT(context);
1106 WINPR_ASSERT(context->priv);
1107
1108 RdpdrServerPrivate* priv = context->priv;
1109 wStream* s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 8);
1110 if (!s)
1111 {
1112 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
1113 return CHANNEL_RC_NO_MEMORY;
1114 }
1115
1116 const RDPDR_HEADER header = {
1117 .Component = RDPDR_CTYP_CORE,
1118 .PacketId = PAKID_CORE_CLIENTID_CONFIRM,
1119 };
1120 Stream_Write_UINT16(s, header.Component); /* Component (2 bytes) */
1121 Stream_Write_UINT16(s, header.PacketId); /* PacketId (2 bytes) */
1122 Stream_Write_UINT16(s, priv->VersionMajor); /* VersionMajor (2 bytes) */
1123 Stream_Write_UINT16(s, priv->VersionMinor); /* VersionMinor (2 bytes) */
1124 Stream_Write_UINT32(s, priv->ClientId); /* ClientId (4 bytes) */
1125 return rdpdr_seal_send_free_request(context, s);
1126}
1127
1133WINPR_ATTR_NODISCARD
1134static UINT rdpdr_server_send_device_reply(RdpdrServerContext* context, UINT32 deviceId,
1135 UINT32 resultCode)
1136{
1137 WINPR_ASSERT(context);
1138 WINPR_ASSERT(context->priv);
1139
1140 RdpdrServerPrivate* priv = context->priv;
1141 wStream* s = Stream_New(nullptr, RDPDR_HEADER_LENGTH + 8);
1142 if (!s)
1143 {
1144 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
1145 return CHANNEL_RC_NO_MEMORY;
1146 }
1147
1148 const RDPDR_HEADER header = {
1149 .Component = RDPDR_CTYP_CORE,
1150 .PacketId = PAKID_CORE_DEVICE_REPLY,
1151 };
1152 Stream_Write_UINT16(s, header.Component); /* Component (2 bytes) */
1153 Stream_Write_UINT16(s, header.PacketId); /* PacketId (2 bytes) */
1154 Stream_Write_UINT32(s, deviceId); /* DeviceId (4 bytes) */
1155 Stream_Write_UINT32(s, resultCode); /* ResultCode (4 bytes) */
1156 return rdpdr_seal_send_free_request(context, s);
1157}
1158
1164WINPR_ATTR_NODISCARD
1165static UINT rdpdr_server_receive_device_list_announce_request(RdpdrServerContext* context,
1166 wStream* s,
1167 const RDPDR_HEADER* header)
1168{
1169 WINPR_ASSERT(context);
1170 WINPR_ASSERT(context->priv);
1171
1172 RdpdrServerPrivate* priv = context->priv;
1173
1174 WINPR_UNUSED(header);
1175
1176 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
1177 return ERROR_INVALID_DATA;
1178
1179 const UINT32 DeviceCount = Stream_Get_UINT32(s); /* DeviceCount (4 bytes) */
1180 WLog_Print(priv->log, WLOG_DEBUG, "DeviceCount: %" PRIu32 "", DeviceCount);
1181
1182 for (UINT32 i = 0; i < DeviceCount; i++)
1183 {
1184 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 20))
1185 return ERROR_INVALID_DATA;
1186
1187 RdpdrDevice device = WINPR_C_ARRAY_INIT;
1188 device.DeviceType = Stream_Get_UINT32(s); /* DeviceType (4 bytes) */
1189 device.DeviceId = Stream_Get_UINT32(s); /* DeviceId (4 bytes) */
1190 Stream_Read(s, device.PreferredDosName, 8); /* PreferredDosName (8 bytes) */
1191 device.DeviceDataLength = Stream_Get_UINT32(s); /* DeviceDataLength (4 bytes) */
1192 device.DeviceData = Stream_PointerAs(s, BYTE);
1193
1194 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, device.DeviceDataLength))
1195 return ERROR_INVALID_DATA;
1196
1197 if (!rdpdr_add_device(context->priv, &device))
1198 return ERROR_INTERNAL_ERROR;
1199
1200 UINT error = IFCALLRESULT(CHANNEL_RC_OK, context->ReceiveDeviceAnnounce, context, &device);
1201 if (error != CHANNEL_RC_OK)
1202 return error;
1203
1204 error = ERROR_NOT_SUPPORTED;
1205 switch (device.DeviceType)
1206 {
1207 case RDPDR_DTYP_FILESYSTEM:
1208 if ((context->supported & RDPDR_DTYP_FILESYSTEM) != 0)
1209 error = IFCALLRESULT(CHANNEL_RC_OK, context->OnDriveCreate, context, &device);
1210 break;
1211
1212 case RDPDR_DTYP_PRINT:
1213 if ((context->supported & RDPDR_DTYP_PRINT) != 0)
1214 error = IFCALLRESULT(CHANNEL_RC_OK, context->OnPrinterCreate, context, &device);
1215 break;
1216
1217 case RDPDR_DTYP_SERIAL:
1218 if (device.DeviceDataLength != 0)
1219 {
1220 WLog_Print(priv->log, WLOG_WARN,
1221 "[rdpdr] RDPDR_DTYP_SERIAL::DeviceDataLength != 0 [%" PRIu32 "]",
1222 device.DeviceDataLength);
1223 error = ERROR_INVALID_DATA;
1224 }
1225 else if ((context->supported & RDPDR_DTYP_SERIAL) != 0)
1226 error =
1227 IFCALLRESULT(CHANNEL_RC_OK, context->OnSerialPortCreate, context, &device);
1228 break;
1229
1230 case RDPDR_DTYP_PARALLEL:
1231 if (device.DeviceDataLength != 0)
1232 {
1233 WLog_Print(priv->log, WLOG_WARN,
1234 "[rdpdr] RDPDR_DTYP_PARALLEL::DeviceDataLength != 0 [%" PRIu32 "]",
1235 device.DeviceDataLength);
1236 error = ERROR_INVALID_DATA;
1237 }
1238 else if ((context->supported & RDPDR_DTYP_PARALLEL) != 0)
1239 error = IFCALLRESULT(CHANNEL_RC_OK, context->OnParallelPortCreate, context,
1240 &device);
1241 break;
1242
1243 case RDPDR_DTYP_SMARTCARD:
1244 if ((context->supported & RDPDR_DTYP_SMARTCARD) != 0)
1245 {
1246 priv->smartcardDeviceId = device.DeviceId;
1247 priv->haveSmartcardDevice = TRUE;
1248 error =
1249 IFCALLRESULT(STATUS_SUCCESS, context->OnSmartcardCreate, context, &device);
1250 }
1251 break;
1252
1253 default:
1254 WLog_Print(priv->log, WLOG_WARN,
1255 "[MS-RDPEFS] 2.2.2.9 Client Device List Announce Request "
1256 "(DR_CORE_DEVICELIST_ANNOUNCE_REQ) unknown device type %04" PRIx16
1257 " at position %" PRIu32,
1258 device.DeviceType, i);
1259 error = ERROR_INVALID_DATA;
1260 break;
1261 }
1262
1263 Stream_Seek(s, device.DeviceDataLength);
1264
1265 error = rdpdr_server_send_device_reply(context, device.DeviceId, error);
1266 if (error != CHANNEL_RC_OK)
1267 return error;
1268 }
1269
1270 return CHANNEL_RC_OK;
1271}
1272
1278WINPR_ATTR_NODISCARD
1279static UINT rdpdr_server_receive_device_list_remove_request(RdpdrServerContext* context, wStream* s,
1280 const RDPDR_HEADER* header)
1281{
1282 WINPR_ASSERT(context);
1283 WINPR_ASSERT(context->priv);
1284
1285 RdpdrServerPrivate* priv = context->priv;
1286
1287 WINPR_UNUSED(header);
1288
1289 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
1290 return ERROR_INVALID_DATA;
1291
1292 const UINT32 DeviceCount = Stream_Get_UINT32(s); /* DeviceCount (4 bytes) */
1293 WLog_Print(priv->log, WLOG_DEBUG, "DeviceCount: %" PRIu32 "", DeviceCount);
1294
1295 for (UINT32 i = 0; i < DeviceCount; i++)
1296 {
1297 UINT error = 0;
1298 const RdpdrDevice* device = nullptr;
1299
1300 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
1301 return ERROR_INVALID_DATA;
1302
1303 const UINT32 DeviceId = Stream_Get_UINT32(s); /* DeviceId (4 bytes) */
1304 device = rdpdr_get_device_by_id(context->priv, DeviceId);
1305 WLog_Print(priv->log, WLOG_DEBUG, "Device %" PRIu32 " Id: 0x%08" PRIX32 "", i, DeviceId);
1306 UINT32 DeviceType = 0;
1307 if (device)
1308 DeviceType = device->DeviceType;
1309
1310 error =
1311 IFCALLRESULT(CHANNEL_RC_OK, context->ReceiveDeviceRemove, context, DeviceId, device);
1312 if (error != CHANNEL_RC_OK)
1313 return error;
1314
1315 switch (DeviceType)
1316 {
1317 case RDPDR_DTYP_FILESYSTEM:
1318 if ((context->supported & RDPDR_DTYP_FILESYSTEM) != 0)
1319 error = IFCALLRESULT(CHANNEL_RC_OK, context->OnDriveDelete, context, DeviceId);
1320 break;
1321
1322 case RDPDR_DTYP_PRINT:
1323 if ((context->supported & RDPDR_DTYP_PRINT) != 0)
1324 error =
1325 IFCALLRESULT(CHANNEL_RC_OK, context->OnPrinterDelete, context, DeviceId);
1326 break;
1327
1328 case RDPDR_DTYP_SERIAL:
1329 if ((context->supported & RDPDR_DTYP_SERIAL) != 0)
1330 error =
1331 IFCALLRESULT(CHANNEL_RC_OK, context->OnSerialPortDelete, context, DeviceId);
1332 break;
1333
1334 case RDPDR_DTYP_PARALLEL:
1335 if ((context->supported & RDPDR_DTYP_PARALLEL) != 0)
1336 error = IFCALLRESULT(CHANNEL_RC_OK, context->OnParallelPortDelete, context,
1337 DeviceId);
1338 break;
1339
1340 case RDPDR_DTYP_SMARTCARD:
1341 if (priv->haveSmartcardDevice)
1342 {
1343 priv->haveSmartcardDevice = FALSE;
1344 priv->smartcardDeviceId = 0;
1345 }
1346 if ((context->supported & RDPDR_DTYP_SMARTCARD) != 0)
1347 error =
1348 IFCALLRESULT(CHANNEL_RC_OK, context->OnSmartcardDelete, context, DeviceId);
1349 break;
1350
1351 default:
1352 break;
1353 }
1354
1355 if (error != CHANNEL_RC_OK)
1356 return error;
1357
1358 if (!rdpdr_remove_device_by_id(context->priv, DeviceId))
1359 return ERROR_INVALID_DATA;
1360 }
1361
1362 return CHANNEL_RC_OK;
1363}
1364
1365WINPR_ATTR_NODISCARD
1366static UINT rdpdr_server_receive_io_create_request(RdpdrServerContext* context, wStream* s,
1367 WINPR_ATTR_UNUSED UINT32 DeviceId,
1368 WINPR_ATTR_UNUSED UINT32 FileId,
1369 WINPR_ATTR_UNUSED UINT32 CompletionId)
1370{
1371 WINPR_ASSERT(context);
1372
1373 RdpdrServerPrivate* priv = context->priv;
1374 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
1375 return ERROR_INVALID_DATA;
1376
1377 WINPR_ATTR_UNUSED const UINT32 DesiredAccess = Stream_Get_UINT32(s);
1378 WINPR_ATTR_UNUSED const UINT32 AllocationSize = Stream_Get_UINT32(s);
1379 WINPR_ATTR_UNUSED const UINT32 FileAttributes = Stream_Get_UINT32(s);
1380 WINPR_ATTR_UNUSED const UINT32 SharedAccess = Stream_Get_UINT32(s);
1381 WINPR_ATTR_UNUSED const UINT32 CreateDisposition = Stream_Get_UINT32(s);
1382 WINPR_ATTR_UNUSED const UINT32 CreateOptions = Stream_Get_UINT32(s);
1383 const UINT32 PathLength = Stream_Get_UINT32(s);
1384
1385 const WCHAR* path = rdpdr_read_ustring(priv->log, s, PathLength);
1386 if (!path && (PathLength > 0))
1387 return ERROR_INVALID_DATA;
1388
1389 WLog_Print(priv->log, WLOG_WARN,
1390 "[MS-RDPEFS] 2.2.1.4.1 Device Create Request (DR_CREATE_REQ) not implemented");
1391 WLog_Print(priv->log, WLOG_WARN, "TODO");
1392
1393 return CHANNEL_RC_OK;
1394}
1395
1396WINPR_ATTR_NODISCARD
1397static UINT rdpdr_server_receive_io_close_request(RdpdrServerContext* context, wStream* s,
1398 WINPR_ATTR_UNUSED UINT32 DeviceId,
1399 WINPR_ATTR_UNUSED UINT32 FileId,
1400 WINPR_ATTR_UNUSED UINT32 CompletionId)
1401{
1402 WINPR_ASSERT(context);
1403 WINPR_ASSERT(context->priv);
1404
1405 RdpdrServerPrivate* priv = context->priv;
1406
1407 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
1408 return ERROR_INVALID_DATA;
1409
1410 Stream_Seek(s, 32); /* Padding */
1411
1412 WLog_Print(priv->log, WLOG_WARN,
1413 "[MS-RDPEFS] 2.2.1.4.2 Device Close Request (DR_CLOSE_REQ) not implemented");
1414 WLog_Print(priv->log, WLOG_WARN, "TODO");
1415
1416 return CHANNEL_RC_OK;
1417}
1418
1419WINPR_ATTR_NODISCARD
1420static UINT rdpdr_server_receive_io_read_request(RdpdrServerContext* context, wStream* s,
1421 WINPR_ATTR_UNUSED UINT32 DeviceId,
1422 WINPR_ATTR_UNUSED UINT32 FileId,
1423 WINPR_ATTR_UNUSED UINT32 CompletionId)
1424{
1425 WINPR_ASSERT(context);
1426 WINPR_ASSERT(context->priv);
1427
1428 RdpdrServerPrivate* priv = context->priv;
1429 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
1430 return ERROR_INVALID_DATA;
1431
1432 const UINT32 Length = Stream_Get_UINT32(s);
1433 const UINT64 Offset = Stream_Get_UINT64(s);
1434 Stream_Seek(s, 20); /* Padding */
1435
1436 WLog_Print(priv->log, WLOG_DEBUG, "Got Offset [0x%016" PRIx64 "], Length %" PRIu32, Offset,
1437 Length);
1438
1439 WLog_Print(priv->log, WLOG_WARN,
1440 "[MS-RDPEFS] 2.2.1.4.3 Device Read Request (DR_READ_REQ) not implemented");
1441 WLog_Print(priv->log, WLOG_WARN, "TODO");
1442
1443 return CHANNEL_RC_OK;
1444}
1445
1446WINPR_ATTR_NODISCARD
1447static UINT rdpdr_server_receive_io_write_request(RdpdrServerContext* context, wStream* s,
1448 WINPR_ATTR_UNUSED UINT32 DeviceId,
1449 WINPR_ATTR_UNUSED UINT32 FileId,
1450 WINPR_ATTR_UNUSED UINT32 CompletionId)
1451{
1452 WINPR_ASSERT(context);
1453 WINPR_ASSERT(context->priv);
1454
1455 RdpdrServerPrivate* priv = context->priv;
1456
1457 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
1458 return ERROR_INVALID_DATA;
1459
1460 const UINT32 Length = Stream_Get_UINT32(s);
1461 const UINT64 Offset = Stream_Get_UINT64(s);
1462 Stream_Seek(s, 20); /* Padding */
1463
1464 WLog_Print(priv->log, WLOG_DEBUG, "Got Offset [0x%016" PRIx64 "], Length %" PRIu32, Offset,
1465 Length);
1466
1467 const BYTE* data = Stream_ConstPointer(s);
1468 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, Length))
1469 return ERROR_INVALID_DATA;
1470 Stream_Seek(s, Length);
1471
1472 WLog_Print(priv->log, WLOG_WARN,
1473 "[MS-RDPEFS] 2.2.1.4.4 Device Write Request (DR_WRITE_REQ) not implemented");
1474 WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p", (const void*)data);
1475
1476 return CHANNEL_RC_OK;
1477}
1478
1479WINPR_ATTR_NODISCARD
1480static UINT rdpdr_server_receive_io_device_control_request(RdpdrServerContext* context, wStream* s,
1481 WINPR_ATTR_UNUSED UINT32 DeviceId,
1482 WINPR_ATTR_UNUSED UINT32 FileId,
1483 WINPR_ATTR_UNUSED UINT32 CompletionId)
1484{
1485 WINPR_ASSERT(context);
1486 WINPR_ASSERT(context->priv);
1487
1488 RdpdrServerPrivate* priv = context->priv;
1489
1490 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
1491 return ERROR_INVALID_DATA;
1492
1493 WINPR_ATTR_UNUSED const UINT32 OutputBufferLength = Stream_Get_UINT32(s);
1494 WINPR_ATTR_UNUSED const UINT32 InputBufferLength = Stream_Get_UINT32(s);
1495 WINPR_ATTR_UNUSED const UINT32 IoControlCode = Stream_Get_UINT32(s);
1496 Stream_Seek(s, 20); /* Padding */
1497
1498 const BYTE* InputBuffer = Stream_ConstPointer(s);
1499 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, InputBufferLength))
1500 return ERROR_INVALID_DATA;
1501 Stream_Seek(s, InputBufferLength);
1502
1503 WLog_Print(priv->log, WLOG_WARN,
1504 "[MS-RDPEFS] 2.2.1.4.5 Device Control Request (DR_CONTROL_REQ) not implemented");
1505 WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p [%" PRIu32 "], OutputBufferLength=%" PRIu32,
1506 (const void*)InputBuffer, InputBufferLength, OutputBufferLength);
1507
1508 return CHANNEL_RC_OK;
1509}
1510
1511WINPR_ATTR_NODISCARD
1512static UINT rdpdr_server_receive_io_query_volume_information_request(
1513 RdpdrServerContext* context, wStream* s, WINPR_ATTR_UNUSED UINT32 DeviceId,
1514 WINPR_ATTR_UNUSED UINT32 FileId, WINPR_ATTR_UNUSED UINT32 CompletionId)
1515{
1516 WINPR_ASSERT(context);
1517
1518 RdpdrServerPrivate* priv = context->priv;
1519 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
1520 return ERROR_INVALID_DATA;
1521
1522 const UINT32 FsInformationClass = Stream_Get_UINT32(s);
1523 const UINT32 Length = Stream_Get_UINT32(s);
1524 Stream_Seek(s, 24); /* Padding */
1525
1526 WLog_Print(priv->log, WLOG_DEBUG,
1527 "Got FSInformationClass %s [0x%08" PRIx32 "], Length %" PRIu32,
1528 FSInformationClass2Tag(FsInformationClass), FsInformationClass, Length);
1529
1530 const BYTE* QueryVolumeBuffer = Stream_ConstPointer(s);
1531 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, Length))
1532 return ERROR_INVALID_DATA;
1533 Stream_Seek(s, Length);
1534
1535 WLog_Print(priv->log, WLOG_WARN,
1536 "[MS-RDPEFS] 2.2.3.3.6 Server Drive Query Volume Information Request "
1537 "(DR_DRIVE_QUERY_VOLUME_INFORMATION_REQ) not implemented");
1538 WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p", (const void*)QueryVolumeBuffer);
1539
1540 return CHANNEL_RC_OK;
1541}
1542
1543WINPR_ATTR_NODISCARD
1544static UINT rdpdr_server_receive_io_set_volume_information_request(
1545 RdpdrServerContext* context, wStream* s, WINPR_ATTR_UNUSED UINT32 DeviceId,
1546 WINPR_ATTR_UNUSED UINT32 FileId, WINPR_ATTR_UNUSED UINT32 CompletionId)
1547{
1548 WINPR_ASSERT(context);
1549 WINPR_ASSERT(context->priv);
1550
1551 RdpdrServerPrivate* priv = context->priv;
1552
1553 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
1554 return ERROR_INVALID_DATA;
1555
1556 const UINT32 FsInformationClass = Stream_Get_UINT32(s);
1557 const UINT32 Length = Stream_Get_UINT32(s);
1558 Stream_Seek(s, 24); /* Padding */
1559
1560 WLog_Print(priv->log, WLOG_DEBUG,
1561 "Got FSInformationClass %s [0x%08" PRIx32 "], Length %" PRIu32,
1562 FSInformationClass2Tag(FsInformationClass), FsInformationClass, Length);
1563
1564 const BYTE* SetVolumeBuffer = Stream_ConstPointer(s);
1565 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, Length))
1566 return ERROR_INVALID_DATA;
1567 Stream_Seek(s, Length);
1568
1569 WLog_Print(priv->log, WLOG_WARN,
1570 "[MS-RDPEFS] 2.2.3.3.7 Server Drive Set Volume Information Request "
1571 "(DR_DRIVE_SET_VOLUME_INFORMATION_REQ) not implemented");
1572 WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p", (const void*)SetVolumeBuffer);
1573
1574 return CHANNEL_RC_OK;
1575}
1576
1577WINPR_ATTR_NODISCARD
1578static UINT rdpdr_server_receive_io_query_information_request(RdpdrServerContext* context,
1579 wStream* s,
1580 WINPR_ATTR_UNUSED UINT32 DeviceId,
1581 WINPR_ATTR_UNUSED UINT32 FileId,
1582 WINPR_ATTR_UNUSED UINT32 CompletionId)
1583{
1584 WINPR_ASSERT(context);
1585 WINPR_ASSERT(context->priv);
1586
1587 RdpdrServerPrivate* priv = context->priv;
1588
1589 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
1590 return ERROR_INVALID_DATA;
1591
1592 const UINT32 FsInformationClass = Stream_Get_UINT32(s);
1593 const UINT32 Length = Stream_Get_UINT32(s);
1594 Stream_Seek(s, 24); /* Padding */
1595
1596 WLog_Print(priv->log, WLOG_DEBUG,
1597 "Got FSInformationClass %s [0x%08" PRIx32 "], Length %" PRIu32,
1598 FSInformationClass2Tag(FsInformationClass), FsInformationClass, Length);
1599
1600 const BYTE* QueryBuffer = Stream_ConstPointer(s);
1601 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, Length))
1602 return ERROR_INVALID_DATA;
1603 Stream_Seek(s, Length);
1604
1605 WLog_Print(priv->log, WLOG_WARN,
1606 "[MS-RDPEFS] 2.2.3.3.8 Server Drive Query Information Request "
1607 "(DR_DRIVE_QUERY_INFORMATION_REQ) not implemented");
1608 WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p", (const void*)QueryBuffer);
1609
1610 return CHANNEL_RC_OK;
1611}
1612
1613WINPR_ATTR_NODISCARD
1614static UINT rdpdr_server_receive_io_set_information_request(RdpdrServerContext* context, wStream* s,
1615 WINPR_ATTR_UNUSED UINT32 DeviceId,
1616 WINPR_ATTR_UNUSED UINT32 FileId,
1617 WINPR_ATTR_UNUSED UINT32 CompletionId)
1618{
1619 WINPR_ASSERT(context);
1620 WINPR_ASSERT(context->priv);
1621
1622 RdpdrServerPrivate* priv = context->priv;
1623
1624 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
1625 return ERROR_INVALID_DATA;
1626
1627 const UINT32 FsInformationClass = Stream_Get_UINT32(s);
1628 const UINT32 Length = Stream_Get_UINT32(s);
1629 Stream_Seek(s, 24); /* Padding */
1630
1631 WLog_Print(priv->log, WLOG_DEBUG,
1632 "Got FSInformationClass %s [0x%08" PRIx32 "], Length %" PRIu32,
1633 FSInformationClass2Tag(FsInformationClass), FsInformationClass, Length);
1634
1635 const BYTE* SetBuffer = Stream_ConstPointer(s);
1636 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, Length))
1637 return ERROR_INVALID_DATA;
1638 Stream_Seek(s, Length);
1639
1640 WLog_Print(priv->log, WLOG_WARN,
1641 "[MS-RDPEFS] 2.2.3.3.9 Server Drive Set Information Request "
1642 "(DR_DRIVE_SET_INFORMATION_REQ) not implemented");
1643 WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p", (const void*)SetBuffer);
1644
1645 return CHANNEL_RC_OK;
1646}
1647
1648WINPR_ATTR_NODISCARD
1649static UINT rdpdr_server_receive_io_query_directory_request(RdpdrServerContext* context, wStream* s,
1650 WINPR_ATTR_UNUSED UINT32 DeviceId,
1651 WINPR_ATTR_UNUSED UINT32 FileId,
1652 WINPR_ATTR_UNUSED UINT32 CompletionId)
1653{
1654 WINPR_ASSERT(context);
1655 WINPR_ASSERT(context->priv);
1656
1657 RdpdrServerPrivate* priv = context->priv;
1658
1659 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
1660 return ERROR_INVALID_DATA;
1661
1662 const UINT32 FsInformationClass = Stream_Get_UINT32(s);
1663 const BYTE InitialQuery = Stream_Get_UINT8(s);
1664 const UINT32 PathLength = Stream_Get_UINT32(s);
1665 Stream_Seek(s, 23); /* Padding */
1666
1667 const WCHAR* wPath = rdpdr_read_ustring(priv->log, s, PathLength);
1668 if (!wPath && (PathLength > 0))
1669 return ERROR_INVALID_DATA;
1670
1671 char* Path = ConvertWCharNToUtf8Alloc(wPath, PathLength / sizeof(WCHAR), nullptr);
1672 WLog_Print(priv->log, WLOG_DEBUG,
1673 "Got FSInformationClass %s [0x%08" PRIx32 "], InitialQuery [%" PRIu8
1674 "] Path[%" PRIu32 "] %s",
1675 FSInformationClass2Tag(FsInformationClass), FsInformationClass, InitialQuery,
1676 PathLength, Path);
1677 free(Path);
1678
1679 WLog_Print(priv->log, WLOG_WARN,
1680 "[MS-RDPEFS] 2.2.3.3.10 Server Drive Query Directory Request "
1681 "(DR_DRIVE_QUERY_DIRECTORY_REQ) not implemented");
1682 WLog_Print(priv->log, WLOG_WARN, "TODO");
1683
1684 return CHANNEL_RC_OK;
1685}
1686
1687WINPR_ATTR_NODISCARD
1688static UINT rdpdr_server_receive_io_change_directory_request(RdpdrServerContext* context,
1689 wStream* s,
1690 WINPR_ATTR_UNUSED UINT32 DeviceId,
1691 WINPR_ATTR_UNUSED UINT32 FileId,
1692 WINPR_ATTR_UNUSED UINT32 CompletionId)
1693{
1694 WINPR_ASSERT(context);
1695 WINPR_ASSERT(context->priv);
1696
1697 RdpdrServerPrivate* priv = context->priv;
1698
1699 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
1700 return ERROR_INVALID_DATA;
1701
1702 WINPR_ATTR_UNUSED const BYTE WatchTree = Stream_Get_UINT8(s);
1703 WINPR_ATTR_UNUSED const UINT32 CompletionFilter = Stream_Get_UINT32(s);
1704 Stream_Seek(s, 27); /* Padding */
1705
1706 WLog_Print(priv->log, WLOG_WARN,
1707 "[MS-RDPEFS] 2.2.3.3.11 Server Drive NotifyChange Directory Request "
1708 "(DR_DRIVE_NOTIFY_CHANGE_DIRECTORY_REQ) not implemented");
1709 WLog_Print(priv->log, WLOG_WARN, "TODO");
1710
1711 return CHANNEL_RC_OK;
1712}
1713
1714WINPR_ATTR_NODISCARD
1715static UINT rdpdr_server_receive_io_directory_control_request(RdpdrServerContext* context,
1716 wStream* s, UINT32 DeviceId,
1717 UINT32 FileId, UINT32 CompletionId,
1718 UINT32 MinorFunction)
1719{
1720 WINPR_ASSERT(context);
1721 WINPR_ASSERT(context->priv);
1722
1723 RdpdrServerPrivate* priv = context->priv;
1724 switch (MinorFunction)
1725 {
1726 case IRP_MN_QUERY_DIRECTORY:
1727 return rdpdr_server_receive_io_query_directory_request(context, s, DeviceId, FileId,
1728 CompletionId);
1729 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
1730 return rdpdr_server_receive_io_change_directory_request(context, s, DeviceId, FileId,
1731 CompletionId);
1732 default:
1733 WLog_Print(priv->log, WLOG_WARN,
1734 "[MS-RDPEFS] 2.2.1.4 Device I/O Request (DR_DEVICE_IOREQUEST) "
1735 "MajorFunction=%s, MinorFunction=%08" PRIx32 " is not supported",
1736 rdpdr_irp_string(IRP_MJ_DIRECTORY_CONTROL), MinorFunction);
1737 return ERROR_INVALID_DATA;
1738 }
1739}
1740
1741WINPR_ATTR_NODISCARD
1742static UINT rdpdr_server_receive_io_lock_control_request(RdpdrServerContext* context, wStream* s,
1743 WINPR_ATTR_UNUSED UINT32 DeviceId,
1744 WINPR_ATTR_UNUSED UINT32 FileId,
1745 WINPR_ATTR_UNUSED UINT32 CompletionId)
1746{
1747 WINPR_ASSERT(context);
1748 WINPR_ASSERT(context->priv);
1749
1750 RdpdrServerPrivate* priv = context->priv;
1751
1752 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
1753 return ERROR_INVALID_DATA;
1754
1755 const uint32_t Operation = Stream_Get_UINT32(s);
1756 uint32_t Lock = Stream_Get_UINT32(s);
1757 const uint32_t NumLocks = Stream_Get_UINT32(s);
1758 Stream_Seek(s, 20); /* Padding */
1759
1760 WLog_Print(priv->log, WLOG_DEBUG,
1761 "IRP_MJ_LOCK_CONTROL, Operation=%s, Lock=0x%08" PRIx32 ", NumLocks=%" PRIu32,
1762 DR_DRIVE_LOCK_REQ2str(Operation), Lock, NumLocks);
1763
1764 Lock &= 0x01; /* Only bit 0 is of importance */
1765
1766 for (UINT32 x = 0; x < NumLocks; x++)
1767 {
1768 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 16))
1769 return ERROR_INVALID_DATA;
1770
1771 const UINT64 Length = Stream_Get_UINT64(s);
1772 const UINT64 Offset = Stream_Get_UINT64(s);
1773
1774 WLog_Print(priv->log, WLOG_DEBUG, "Locking at Offset=0x%08" PRIx64 " [Length %" PRIu64 "]",
1775 Offset, Length);
1776 }
1777
1778 WLog_Print(priv->log, WLOG_WARN,
1779 "[MS-RDPEFS] 2.2.3.3.12 Server Drive Lock Control Request (DR_DRIVE_LOCK_REQ) "
1780 "[Lock=0x%08" PRIx32 "]"
1781 "not implemented",
1782 Lock);
1783 WLog_Print(priv->log, WLOG_WARN, "TODO");
1784
1785 return CHANNEL_RC_OK;
1786}
1787
1788WINPR_ATTR_NODISCARD
1789static UINT rdpdr_server_receive_device_io_request(RdpdrServerContext* context, wStream* s,
1790 WINPR_ATTR_UNUSED const RDPDR_HEADER* header)
1791{
1792 WINPR_ASSERT(context);
1793 WINPR_ASSERT(context->priv);
1794 WINPR_ASSERT(header);
1795
1796 RdpdrServerPrivate* priv = context->priv;
1797
1798 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 20))
1799 return ERROR_INVALID_DATA;
1800
1801 const UINT32 DeviceId = Stream_Get_UINT32(s);
1802 const UINT32 FileId = Stream_Get_UINT32(s);
1803 const UINT32 CompletionId = Stream_Get_UINT32(s);
1804 const UINT32 MajorFunction = Stream_Get_UINT32(s);
1805 const UINT32 MinorFunction = Stream_Get_UINT32(s);
1806 if ((MinorFunction != 0) && (MajorFunction != IRP_MJ_DIRECTORY_CONTROL))
1807 WLog_Print(priv->log, WLOG_WARN,
1808 "[MS-RDPEFS] 2.2.1.4 Device I/O Request (DR_DEVICE_IOREQUEST) MajorFunction=%s, "
1809 "MinorFunction=0x%08" PRIx32 " != 0",
1810 rdpdr_irp_string(MajorFunction), MinorFunction);
1811
1812 switch (MajorFunction)
1813 {
1814 case IRP_MJ_CREATE:
1815 return rdpdr_server_receive_io_create_request(context, s, DeviceId, FileId,
1816 CompletionId);
1817 case IRP_MJ_CLOSE:
1818 return rdpdr_server_receive_io_close_request(context, s, DeviceId, FileId,
1819 CompletionId);
1820 case IRP_MJ_READ:
1821 return rdpdr_server_receive_io_read_request(context, s, DeviceId, FileId, CompletionId);
1822 case IRP_MJ_WRITE:
1823 return rdpdr_server_receive_io_write_request(context, s, DeviceId, FileId,
1824 CompletionId);
1825 case IRP_MJ_DEVICE_CONTROL:
1826 return rdpdr_server_receive_io_device_control_request(context, s, DeviceId, FileId,
1827 CompletionId);
1828 case IRP_MJ_QUERY_VOLUME_INFORMATION:
1829 return rdpdr_server_receive_io_query_volume_information_request(context, s, DeviceId,
1830 FileId, CompletionId);
1831 case IRP_MJ_QUERY_INFORMATION:
1832 return rdpdr_server_receive_io_query_information_request(context, s, DeviceId, FileId,
1833 CompletionId);
1834 case IRP_MJ_SET_INFORMATION:
1835 return rdpdr_server_receive_io_set_information_request(context, s, DeviceId, FileId,
1836 CompletionId);
1837 case IRP_MJ_DIRECTORY_CONTROL:
1838 return rdpdr_server_receive_io_directory_control_request(context, s, DeviceId, FileId,
1839 CompletionId, MinorFunction);
1840 case IRP_MJ_LOCK_CONTROL:
1841 return rdpdr_server_receive_io_lock_control_request(context, s, DeviceId, FileId,
1842 CompletionId);
1843 case IRP_MJ_SET_VOLUME_INFORMATION:
1844 return rdpdr_server_receive_io_set_volume_information_request(context, s, DeviceId,
1845 FileId, CompletionId);
1846
1847 default:
1848 WLog_Print(
1849 priv->log, WLOG_WARN,
1850 "[MS-RDPEFS] 2.2.1.4 Device I/O Request (DR_DEVICE_IOREQUEST) not implemented");
1851 WLog_Print(priv->log, WLOG_WARN,
1852 "got DeviceId=0x%08" PRIx32 ", FileId=0x%08" PRIx32
1853 ", CompletionId=0x%08" PRIx32 ", MajorFunction=0x%08" PRIx32
1854 ", MinorFunction=0x%08" PRIx32,
1855 DeviceId, FileId, CompletionId, MajorFunction, MinorFunction);
1856 return ERROR_INVALID_DATA;
1857 }
1858}
1859
1865WINPR_ATTR_NODISCARD
1866static UINT rdpdr_server_receive_device_io_completion(RdpdrServerContext* context, wStream* s,
1867 const RDPDR_HEADER* header)
1868{
1869 WINPR_ASSERT(context);
1870 WINPR_ASSERT(context->priv);
1871 WINPR_ASSERT(s);
1872 WINPR_ASSERT(header);
1873
1874 RdpdrServerPrivate* priv = context->priv;
1875
1876 WINPR_UNUSED(header);
1877
1878 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 12))
1879 return ERROR_INVALID_DATA;
1880
1881 const UINT32 deviceId = Stream_Get_UINT32(s);
1882 const UINT32 completionId = Stream_Get_UINT32(s);
1883 const UINT32 ioStatus = Stream_Get_UINT32(s);
1884 WLog_Print(priv->log, WLOG_DEBUG,
1885 "deviceId=%" PRIu32 ", completionId=0x%" PRIx32 ", ioStatus=0x%" PRIx32 "", deviceId,
1886 completionId, ioStatus);
1887 RDPDR_IRP* irp = rdpdr_server_dequeue_irp(context, completionId);
1888
1889 if (!irp)
1890 {
1891 WLog_Print(priv->log, WLOG_ERROR, "IRP not found for completionId=0x%" PRIx32 "",
1892 completionId);
1893 return CHANNEL_RC_OK;
1894 }
1895
1896 /* Invoke the callback. */
1897 UINT error = CHANNEL_RC_OK;
1898 if (irp->Callback)
1899 {
1900 error = (*irp->Callback)(context, s, irp, deviceId, completionId, ioStatus);
1901 }
1902
1903 return error;
1904}
1905
1911WINPR_ATTR_NODISCARD
1912static UINT rdpdr_server_send_user_logged_on(RdpdrServerContext* context)
1913{
1914 WINPR_ASSERT(context);
1915 WINPR_ASSERT(context->priv);
1916
1917 RdpdrServerPrivate* priv = context->priv;
1918 wStream* s = Stream_New(nullptr, RDPDR_HEADER_LENGTH);
1919 if (!s)
1920 {
1921 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
1922 return CHANNEL_RC_NO_MEMORY;
1923 }
1924
1925 const RDPDR_HEADER header = {
1926 .Component = RDPDR_CTYP_CORE,
1927 .PacketId = PAKID_CORE_USER_LOGGEDON,
1928 };
1929 Stream_Write_UINT16(s, header.Component); /* Component (2 bytes) */
1930 Stream_Write_UINT16(s, header.PacketId); /* PacketId (2 bytes) */
1931 return rdpdr_seal_send_free_request(context, s);
1932}
1933
1934WINPR_ATTR_NODISCARD
1935static UINT rdpdr_server_receive_prn_cache_add_printer(RdpdrServerContext* context, wStream* s)
1936{
1937 WINPR_ASSERT(context);
1938 WINPR_ASSERT(context->priv);
1939
1940 RdpdrServerPrivate* priv = context->priv;
1941
1942 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 24))
1943 return ERROR_INVALID_DATA;
1944
1945 char PortDosName[9] = WINPR_C_ARRAY_INIT;
1946 Stream_Read(s, PortDosName, 8);
1947 const UINT32 PnPNameLen = Stream_Get_UINT32(s);
1948 const UINT32 DriverNameLen = Stream_Get_UINT32(s);
1949 const UINT32 PrinterNameLen = Stream_Get_UINT32(s);
1950 const UINT32 CachedFieldsLen = Stream_Get_UINT32(s);
1951
1952 const WCHAR* PnPName = rdpdr_read_ustring(priv->log, s, PnPNameLen);
1953 if (!PnPName && (PnPNameLen > 0))
1954 return ERROR_INVALID_DATA;
1955 const WCHAR* DriverName = rdpdr_read_ustring(priv->log, s, DriverNameLen);
1956 if (!DriverName && (DriverNameLen > 0))
1957 return ERROR_INVALID_DATA;
1958 const WCHAR* PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen);
1959 if (!PrinterName && (PrinterNameLen > 0))
1960 return ERROR_INVALID_DATA;
1961
1962 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, CachedFieldsLen))
1963 return ERROR_INVALID_DATA;
1964 Stream_Seek(s, CachedFieldsLen);
1965
1966 WLog_Print(priv->log, WLOG_WARN,
1967 "[MS-RDPEPC] 2.2.2.3 Add Printer Cachedata (DR_PRN_ADD_CACHEDATA) not implemented");
1968 WLog_Print(priv->log, WLOG_WARN, "TODO");
1969 return CHANNEL_RC_OK;
1970}
1971
1972WINPR_ATTR_NODISCARD
1973static UINT rdpdr_server_receive_prn_cache_update_printer(RdpdrServerContext* context, wStream* s)
1974{
1975 WINPR_ASSERT(context);
1976
1977 RdpdrServerPrivate* priv = context->priv;
1978
1979 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8))
1980 return ERROR_INVALID_DATA;
1981
1982 const UINT32 PrinterNameLen = Stream_Get_UINT32(s);
1983 const UINT32 CachedFieldsLen = Stream_Get_UINT32(s);
1984
1985 const WCHAR* PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen);
1986 if (!PrinterName && (PrinterNameLen > 0))
1987 return ERROR_INVALID_DATA;
1988
1989 const BYTE* config = Stream_ConstPointer(s);
1990 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, CachedFieldsLen))
1991 return ERROR_INVALID_DATA;
1992 Stream_Seek(s, CachedFieldsLen);
1993
1994 WLog_Print(
1995 priv->log, WLOG_WARN,
1996 "[MS-RDPEPC] 2.2.2.4 Update Printer Cachedata (DR_PRN_UPDATE_CACHEDATA) not implemented");
1997 WLog_Print(priv->log, WLOG_WARN, "TODO: parse %p", (const void*)config);
1998 return CHANNEL_RC_OK;
1999}
2000
2001WINPR_ATTR_NODISCARD
2002static UINT rdpdr_server_receive_prn_cache_delete_printer(RdpdrServerContext* context, wStream* s)
2003{
2004 WINPR_ASSERT(context);
2005 WINPR_ASSERT(context->priv);
2006
2007 RdpdrServerPrivate* priv = context->priv;
2008
2009 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
2010 return ERROR_INVALID_DATA;
2011
2012 const UINT32 PrinterNameLen = Stream_Get_UINT32(s);
2013
2014 const WCHAR* PrinterName = rdpdr_read_ustring(priv->log, s, PrinterNameLen);
2015 if (!PrinterName && (PrinterNameLen > 0))
2016 return ERROR_INVALID_DATA;
2017
2018 WLog_Print(
2019 priv->log, WLOG_WARN,
2020 "[MS-RDPEPC] 2.2.2.5 Delete Printer Cachedata (DR_PRN_DELETE_CACHEDATA) not implemented");
2021 WLog_Print(priv->log, WLOG_WARN, "TODO");
2022 return CHANNEL_RC_OK;
2023}
2024
2025WINPR_ATTR_NODISCARD
2026static UINT rdpdr_server_receive_prn_cache_rename_cachedata(RdpdrServerContext* context, wStream* s)
2027{
2028 WINPR_ASSERT(context);
2029 WINPR_ASSERT(context->priv);
2030
2031 RdpdrServerPrivate* priv = context->priv;
2032
2033 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8))
2034 return ERROR_INVALID_DATA;
2035
2036 const UINT32 OldPrinterNameLen = Stream_Get_UINT32(s);
2037 const UINT32 NewPrinterNameLen = Stream_Get_UINT32(s);
2038
2039 const WCHAR* OldPrinterName = rdpdr_read_ustring(priv->log, s, OldPrinterNameLen);
2040 if (!OldPrinterName && (OldPrinterNameLen > 0))
2041 return ERROR_INVALID_DATA;
2042 const WCHAR* NewPrinterName = rdpdr_read_ustring(priv->log, s, NewPrinterNameLen);
2043 if (!NewPrinterName && (NewPrinterNameLen > 0))
2044 return ERROR_INVALID_DATA;
2045
2046 WLog_Print(
2047 priv->log, WLOG_WARN,
2048 "[MS-RDPEPC] 2.2.2.6 Rename Printer Cachedata (DR_PRN_RENAME_CACHEDATA) not implemented");
2049 WLog_Print(priv->log, WLOG_WARN, "TODO");
2050 return CHANNEL_RC_OK;
2051}
2052
2053WINPR_ATTR_NODISCARD
2054static UINT
2055rdpdr_server_receive_prn_cache_data_request(RdpdrServerContext* context, wStream* s,
2056 WINPR_ATTR_UNUSED const RDPDR_HEADER* header)
2057{
2058 WINPR_ASSERT(context);
2059 WINPR_ASSERT(context->priv);
2060 WINPR_ASSERT(header);
2061
2062 RdpdrServerPrivate* priv = context->priv;
2063 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
2064 return ERROR_INVALID_DATA;
2065
2066 const UINT32 EventId = Stream_Get_UINT32(s);
2067 switch (EventId)
2068 {
2069 case RDPDR_ADD_PRINTER_EVENT:
2070 return rdpdr_server_receive_prn_cache_add_printer(context, s);
2071 case RDPDR_UPDATE_PRINTER_EVENT:
2072 return rdpdr_server_receive_prn_cache_update_printer(context, s);
2073 case RDPDR_DELETE_PRINTER_EVENT:
2074 return rdpdr_server_receive_prn_cache_delete_printer(context, s);
2075 case RDPDR_RENAME_PRINTER_EVENT:
2076 return rdpdr_server_receive_prn_cache_rename_cachedata(context, s);
2077 default:
2078 WLog_Print(priv->log, WLOG_WARN,
2079 "[MS-RDPEPC] PAKID_PRN_CACHE_DATA unknown EventId=0x%08" PRIx32, EventId);
2080 return ERROR_INVALID_DATA;
2081 }
2082}
2083
2084WINPR_ATTR_NODISCARD
2085static UINT rdpdr_server_receive_prn_using_xps_request(RdpdrServerContext* context, wStream* s,
2086 WINPR_ATTR_UNUSED const RDPDR_HEADER* header)
2087{
2088 WINPR_ASSERT(context);
2089 WINPR_ASSERT(context->priv);
2090 WINPR_ASSERT(header);
2091
2092 RdpdrServerPrivate* priv = context->priv;
2093
2094 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 8))
2095 return ERROR_INVALID_DATA;
2096
2097 const UINT32 PrinterId = Stream_Get_UINT32(s);
2098 const UINT32 Flags = Stream_Get_UINT32(s);
2099
2100 WLog_Print(
2101 priv->log, WLOG_WARN,
2102 "[MS-RDPEPC] 2.2.2.2 Server Printer Set XPS Mode (DR_PRN_USING_XPS) not implemented");
2103 WLog_Print(priv->log, WLOG_WARN, "PrinterId=0x%08" PRIx32 ", Flags=0x%08" PRIx32, PrinterId,
2104 Flags);
2105 return CHANNEL_RC_OK;
2106}
2107
2113WINPR_ATTR_NODISCARD
2114static UINT rdpdr_server_receive_pdu(RdpdrServerContext* context, wStream* s,
2115 const RDPDR_HEADER* header)
2116{
2117 UINT error = ERROR_INVALID_DATA;
2118 WINPR_ASSERT(context);
2119 WINPR_ASSERT(context->priv);
2120 WINPR_ASSERT(s);
2121 WINPR_ASSERT(header);
2122
2123 RdpdrServerPrivate* priv = context->priv;
2124 WLog_Print(priv->log, WLOG_DEBUG,
2125 "receiving message {Component %s[%04" PRIx16 "], PacketId %s[%04" PRIx16 "]",
2126 rdpdr_component_string(header->Component), header->Component,
2127 rdpdr_packetid_string(header->PacketId), header->PacketId);
2128
2129 if (header->Component == RDPDR_CTYP_CORE)
2130 {
2131 switch (header->PacketId)
2132 {
2133 case PAKID_CORE_SERVER_ANNOUNCE:
2134 WLog_Print(priv->log, WLOG_ERROR,
2135 "[MS-RDPEFS] 2.2.2.2 Server Announce Request "
2136 "(DR_CORE_SERVER_ANNOUNCE_REQ) must not be sent by a client!");
2137 break;
2138
2139 case PAKID_CORE_CLIENTID_CONFIRM:
2140 error = rdpdr_server_receive_announce_response(context, s, header);
2141 break;
2142
2143 case PAKID_CORE_CLIENT_NAME:
2144 error = rdpdr_server_receive_client_name_request(context, s, header);
2145 if (error == CHANNEL_RC_OK)
2146 error = rdpdr_server_send_core_capability_request(context);
2147 if (error == CHANNEL_RC_OK)
2148 error = rdpdr_server_send_client_id_confirm(context);
2149 break;
2150
2151 case PAKID_CORE_USER_LOGGEDON:
2152 WLog_Print(priv->log, WLOG_ERROR,
2153 "[MS-RDPEFS] 2.2.2.5 Server User Logged On (DR_CORE_USER_LOGGEDON) "
2154 "must not be sent by a client!");
2155 break;
2156
2157 case PAKID_CORE_SERVER_CAPABILITY:
2158 WLog_Print(priv->log, WLOG_ERROR,
2159 "[MS-RDPEFS] 2.2.2.7 Server Core Capability Request "
2160 "(DR_CORE_CAPABILITY_REQ) must not be sent by a client!");
2161 break;
2162
2163 case PAKID_CORE_CLIENT_CAPABILITY:
2164 error = rdpdr_server_receive_core_capability_response(context, s, header);
2165 if (error == CHANNEL_RC_OK)
2166 {
2167 if (priv->UserLoggedOnPdu)
2168 error = rdpdr_server_send_user_logged_on(context);
2169 }
2170
2171 break;
2172
2173 case PAKID_CORE_DEVICELIST_ANNOUNCE:
2174 error = rdpdr_server_receive_device_list_announce_request(context, s, header);
2175 break;
2176
2177 case PAKID_CORE_DEVICELIST_REMOVE:
2178 error = rdpdr_server_receive_device_list_remove_request(context, s, header);
2179 break;
2180
2181 case PAKID_CORE_DEVICE_REPLY:
2182 WLog_Print(priv->log, WLOG_ERROR,
2183 "[MS-RDPEFS] 2.2.2.1 Server Device Announce Response "
2184 "(DR_CORE_DEVICE_ANNOUNCE_RSP) must not be sent by a client!");
2185 break;
2186
2187 case PAKID_CORE_DEVICE_IOREQUEST:
2188 error = rdpdr_server_receive_device_io_request(context, s, header);
2189 break;
2190
2191 case PAKID_CORE_DEVICE_IOCOMPLETION:
2192 error = rdpdr_server_receive_device_io_completion(context, s, header);
2193 break;
2194
2195 default:
2196 WLog_Print(priv->log, WLOG_WARN,
2197 "Unknown RDPDR_HEADER.Component: %s [0x%04" PRIx16 "], PacketId: %s",
2198 rdpdr_component_string(header->Component), header->Component,
2199 rdpdr_packetid_string(header->PacketId));
2200 break;
2201 }
2202 }
2203 else if (header->Component == RDPDR_CTYP_PRN)
2204 {
2205 switch (header->PacketId)
2206 {
2207 case PAKID_PRN_CACHE_DATA:
2208 error = rdpdr_server_receive_prn_cache_data_request(context, s, header);
2209 break;
2210
2211 case PAKID_PRN_USING_XPS:
2212 error = rdpdr_server_receive_prn_using_xps_request(context, s, header);
2213 break;
2214
2215 default:
2216 WLog_Print(priv->log, WLOG_WARN,
2217 "Unknown RDPDR_HEADER.Component: %s [0x%04" PRIx16 "], PacketId: %s",
2218 rdpdr_component_string(header->Component), header->Component,
2219 rdpdr_packetid_string(header->PacketId));
2220 break;
2221 }
2222 }
2223 else
2224 {
2225 WLog_Print(priv->log, WLOG_WARN,
2226 "Unknown RDPDR_HEADER.Component: %s [0x%04" PRIx16 "], PacketId: %s",
2227 rdpdr_component_string(header->Component), header->Component,
2228 rdpdr_packetid_string(header->PacketId));
2229 }
2230
2231 return IFCALLRESULT(error, context->ReceivePDU, context, header, error);
2232}
2233
2234WINPR_ATTR_NODISCARD
2235static DWORD WINAPI rdpdr_server_thread(LPVOID arg)
2236{
2237 RdpdrServerContext* context = (RdpdrServerContext*)arg;
2238
2239 WINPR_ASSERT(context);
2240 WINPR_ASSERT(context->priv);
2241
2242 UINT error = 0;
2243 wStream* s = nullptr;
2244 RdpdrServerPrivate* priv = context->priv;
2245 ChannelPduTracker* tracker = ChannelPduTracker_new(priv->ChannelHandle);
2246 if (!tracker)
2247 goto out;
2248
2249 void* buffer = nullptr;
2250 HANDLE ChannelEvent = nullptr;
2251 DWORD BytesReturned = 0;
2252 if (!WTSVirtualChannelQuery(priv->ChannelHandle, WTSVirtualEventHandle, &buffer,
2253 &BytesReturned))
2254 {
2255 WLog_Print(priv->log, WLOG_ERROR, "error retrieving WTSVirtualEventHandle");
2256 goto out;
2257 }
2258
2259 if (BytesReturned != sizeof(HANDLE))
2260 {
2261 WLog_Print(priv->log, WLOG_ERROR, "invalid size for WTSVirtualEventHandle");
2262 WTSFreeMemory(buffer);
2263 goto out;
2264 }
2265
2266 ChannelEvent = *(HANDLE*)buffer;
2267 WTSFreeMemory(buffer);
2268
2269 if ((error = rdpdr_server_send_announce_request(context)))
2270 {
2271 WLog_Print(priv->log, WLOG_ERROR,
2272 "rdpdr_server_send_announce_request failed with error %" PRIu32 "!", error);
2273 goto out;
2274 }
2275
2276 HANDLE events[2] = { priv->StopEvent, ChannelEvent };
2277
2278 while (1)
2279 {
2280 DWORD status = WaitForMultipleObjects(2, events, FALSE, INFINITE);
2281
2282 switch (status)
2283 {
2284 case WAIT_OBJECT_0:
2285 /* StopEvent */
2286 goto out;
2287 case WAIT_OBJECT_0 + 1:
2288 {
2289 BOOL ok = FALSE;
2290 s = ChannelPduTracker_poll(tracker, &ok);
2291 if (!s && ok)
2292 continue;
2293 if (!s)
2294 break;
2295
2296 if (Stream_GetRemainingLength(s) < RDPDR_HEADER_LENGTH)
2297 {
2298 error = ERROR_INTERNAL_ERROR;
2299 goto out;
2300 }
2301
2302 const RDPDR_HEADER header = {
2303 .Component = Stream_Get_UINT16(s), /* Component (2 bytes) */
2304 .PacketId = Stream_Get_UINT16(s), /* PacketId (2 bytes) */
2305 };
2306
2307 if ((error = rdpdr_server_receive_pdu(context, s, &header)))
2308 {
2309 WLog_Print(priv->log, WLOG_ERROR,
2310 "rdpdr_server_receive_pdu failed with error %" PRIu32 "!", error);
2311 goto out;
2312 }
2313 Stream_Release(s);
2314 s = nullptr;
2315 break;
2316 }
2317 case WAIT_FAILED:
2318 default:
2319 error = GetLastError();
2320 WLog_Print(priv->log, WLOG_ERROR,
2321 "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
2322 goto out;
2323 }
2324 }
2325
2326out:
2327 ChannelPduTracker_free(tracker);
2328
2329 if (s)
2330 Stream_Release(s);
2331
2332 if (error && context->rdpcontext)
2333 setChannelError(context->rdpcontext, error, "rdpdr_server_thread reported an error");
2334
2335 ExitThread(error);
2336 return error;
2337}
2338
2344WINPR_ATTR_NODISCARD
2345static UINT rdpdr_server_start(RdpdrServerContext* context)
2346{
2347 WINPR_ASSERT(context);
2348 WINPR_ASSERT(context->priv);
2349
2350 PULONG pSessionId = nullptr;
2351 DWORD BytesReturned = 0;
2352
2353 if (!WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
2354 (LPSTR*)&pSessionId, &BytesReturned))
2355 return CHANNEL_RC_BAD_CHANNEL;
2356
2357 DWORD SessionId = (DWORD)*pSessionId;
2358 WTSFreeMemory(pSessionId);
2359
2360 RdpdrServerPrivate* priv = context->priv;
2361 priv->ChannelHandle =
2362 WTSVirtualChannelOpenEx(SessionId, RDPDR_SVC_CHANNEL_NAME, CHANNEL_OPTION_SHOW_PROTOCOL);
2363
2364 if (!priv->ChannelHandle)
2365 {
2366 WLog_Print(priv->log, WLOG_ERROR, "WTSVirtualChannelOpen failed!");
2367 return CHANNEL_RC_BAD_CHANNEL;
2368 }
2369
2370 if (!(priv->StopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
2371 {
2372 WLog_Print(priv->log, WLOG_ERROR, "CreateEvent failed!");
2373 return ERROR_INTERNAL_ERROR;
2374 }
2375
2376 if (!(priv->Thread = CreateThread(nullptr, 0, rdpdr_server_thread, (void*)context, 0, nullptr)))
2377 {
2378 WLog_Print(priv->log, WLOG_ERROR, "CreateThread failed!");
2379 (void)CloseHandle(priv->StopEvent);
2380 priv->StopEvent = nullptr;
2381 return ERROR_INTERNAL_ERROR;
2382 }
2383
2384 return CHANNEL_RC_OK;
2385}
2386
2392WINPR_ATTR_NODISCARD
2393static UINT rdpdr_server_stop(RdpdrServerContext* context)
2394{
2395 WINPR_ASSERT(context);
2396 WINPR_ASSERT(context->priv);
2397
2398 RdpdrServerPrivate* priv = context->priv;
2399 if (priv->StopEvent)
2400 {
2401 (void)SetEvent(priv->StopEvent);
2402
2403 if (WaitForSingleObject(priv->Thread, INFINITE) == WAIT_FAILED)
2404 {
2405 UINT error = GetLastError();
2406 WLog_Print(priv->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!",
2407 error);
2408 return error;
2409 }
2410
2411 (void)CloseHandle(priv->Thread);
2412 priv->Thread = nullptr;
2413 (void)CloseHandle(priv->StopEvent);
2414 priv->StopEvent = nullptr;
2415 }
2416
2417 if (priv->ChannelHandle)
2418 {
2419 (void)WTSVirtualChannelClose(priv->ChannelHandle);
2420 priv->ChannelHandle = nullptr;
2421 }
2422 return CHANNEL_RC_OK;
2423}
2424
2425static void rdpdr_server_write_device_iorequest(wStream* s, UINT32 deviceId, UINT32 fileId,
2426 UINT32 completionId, UINT32 majorFunction,
2427 UINT32 minorFunction)
2428{
2429 Stream_Write_UINT16(s, RDPDR_CTYP_CORE); /* Component (2 bytes) */
2430 Stream_Write_UINT16(s, PAKID_CORE_DEVICE_IOREQUEST); /* PacketId (2 bytes) */
2431 Stream_Write_UINT32(s, deviceId); /* DeviceId (4 bytes) */
2432 Stream_Write_UINT32(s, fileId); /* FileId (4 bytes) */
2433 Stream_Write_UINT32(s, completionId); /* CompletionId (4 bytes) */
2434 Stream_Write_UINT32(s, majorFunction); /* MajorFunction (4 bytes) */
2435 Stream_Write_UINT32(s, minorFunction); /* MinorFunction (4 bytes) */
2436}
2437
2443WINPR_ATTR_NODISCARD
2444static UINT rdpdr_server_read_file_directory_information(wLog* log, wStream* s,
2446{
2447 WINPR_ASSERT(fdi);
2448 ZeroMemory(fdi, sizeof(FILE_DIRECTORY_INFORMATION));
2449
2450 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 64))
2451 return ERROR_INVALID_DATA;
2452
2453 fdi->NextEntryOffset = Stream_Get_UINT32(s); /* NextEntryOffset (4 bytes) */
2454 fdi->FileIndex = Stream_Get_UINT32(s); /* FileIndex (4 bytes) */
2455 fdi->CreationTime.QuadPart = Stream_Get_INT64(s); /* CreationTime (8 bytes) */
2456 fdi->LastAccessTime.QuadPart = Stream_Get_INT64(s); /* LastAccessTime (8 bytes) */
2457 fdi->LastWriteTime.QuadPart = Stream_Get_INT64(s); /* LastWriteTime (8 bytes) */
2458 fdi->ChangeTime.QuadPart = Stream_Get_INT64(s); /* ChangeTime (8 bytes) */
2459 fdi->EndOfFile.QuadPart = Stream_Get_INT64(s); /* EndOfFile (8 bytes) */
2460 fdi->AllocationSize.QuadPart = Stream_Get_INT64(s); /* AllocationSize (8 bytes) */
2461 fdi->FileAttributes = Stream_Get_UINT32(s); /* FileAttributes (4 bytes) */
2462 const UINT32 fileNameLength = Stream_Get_UINT32(s); /* FileNameLength (4 bytes) */
2463
2464 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, fileNameLength))
2465 return ERROR_INVALID_DATA;
2466
2467 if (fileNameLength / sizeof(WCHAR) > ARRAYSIZE(fdi->FileName))
2468 return ERROR_INVALID_DATA;
2469
2470#if defined(__MINGW32__) || defined(WITH_WCHAR_FILE_DIRECTORY_INFORMATION)
2471 if (Stream_Read_UTF16_String(s, fdi->FileName, fileNameLength / sizeof(WCHAR)))
2472 return ERROR_INVALID_DATA;
2473#else
2474 if (Stream_Read_UTF16_String_As_UTF8_Buffer(s, fileNameLength / sizeof(WCHAR), fdi->FileName,
2475 ARRAYSIZE(fdi->FileName)) < 0)
2476 return ERROR_INVALID_DATA;
2477#endif
2478 return CHANNEL_RC_OK;
2479}
2480
2481/* The drive and smartcard APIs are called from the application's threads,
2482 * while the completion callbacks that send follow-up requests run on the
2483 * channel thread, so the completion IDs must be handed out atomically. */
2484WINPR_ATTR_NODISCARD
2485static UINT32 rdpdr_server_next_completion_id(RdpdrServerPrivate* priv)
2486{
2487 WINPR_ASSERT(priv);
2488 return (UINT32)(InterlockedIncrement(&priv->NextCompletionId) - 1);
2489}
2490
2491/* Copies a path for an IRP, with the '\\' separators the client expects */
2492WINPR_ATTR_NODISCARD
2493static char* rdpdr_server_dup_path(const char* path)
2494{
2495 WINPR_ASSERT(path);
2496
2497 char* copy = _strdup(path);
2498 if (!copy)
2499 return nullptr;
2500
2501 for (char* c = copy; *c != '\0'; c++)
2502 {
2503 if (*c == '/')
2504 *c = '\\';
2505 }
2506 return copy;
2507}
2508
2509WINPR_ATTR_NODISCARD
2510static UINT prepare_path_irp(RdpdrServerContext* context, UINT32 deviceId,
2511 RDPDR_IRP_Callback callback, void* callbackData, const char* path,
2512 const char* extraPath, RDPDR_IRP** outIrp)
2513{
2514 WINPR_ASSERT(context);
2515 WINPR_ASSERT(context->priv);
2516 WINPR_ASSERT(callbackData);
2517 WINPR_ASSERT(outIrp);
2518
2519 RdpdrServerPrivate* priv = context->priv;
2520
2521 RDPDR_IRP* irp = rdpdr_server_irp_new();
2522 if (!irp)
2523 {
2524 WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_irp_new failed!");
2525 return CHANNEL_RC_NO_MEMORY;
2526 }
2527
2528 irp->CompletionId = rdpdr_server_next_completion_id(priv);
2529 irp->Callback = callback;
2530 irp->CallbackData = callbackData;
2531 irp->DeviceId = deviceId;
2532
2533 if (path)
2534 irp->PathName = rdpdr_server_dup_path(path);
2535 if (extraPath)
2536 irp->ExtraBuffer = rdpdr_server_dup_path(extraPath);
2537 if ((path && !irp->PathName) || (extraPath && !irp->ExtraBuffer))
2538 {
2539 WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_dup_path failed!");
2540 rdpdr_server_irp_free(irp);
2541 return CHANNEL_RC_NO_MEMORY;
2542 }
2543
2544 if (!rdpdr_server_enqueue_irp(context, irp))
2545 {
2546 WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!");
2547 rdpdr_server_irp_free(irp);
2548 return ERROR_INTERNAL_ERROR;
2549 }
2550
2551 *outIrp = irp;
2552 return CHANNEL_RC_OK;
2553}
2554
2555WINPR_ATTR_NODISCARD
2556static UINT prepare_irp(RdpdrServerContext* context, UINT32 deviceId, RDPDR_IRP_Callback callback,
2557 void* callbackData, RDPDR_IRP** outIrp)
2558{
2559 return prepare_path_irp(context, deviceId, callback, callbackData, nullptr, nullptr, outIrp);
2560}
2561
2562WINPR_ATTR_NODISCARD
2563static UINT prepare_smartcard_irp(RdpdrServerContext* context, UINT32 ioControlCode,
2564 RDPDR_IRP_Callback callback, void* callbackData,
2565 RDPDR_IRP** outIrp)
2566{
2567 WINPR_ASSERT(context);
2568 WINPR_ASSERT(context->priv);
2569 WINPR_ASSERT(callbackData);
2570
2571 const char* cmd = scard_get_ioctl_string(ioControlCode, FALSE);
2572
2573 RdpdrServerPrivate* priv = context->priv;
2574 if (!priv->haveSmartcardDevice)
2575 {
2576 WLog_Print(priv->log, WLOG_ERROR, "%s - no smartcard device registered", cmd);
2577 return ERROR_BAD_DEVICE;
2578 }
2579
2580 RDPDR_IRP* irp = nullptr;
2581 UINT error = prepare_irp(context, priv->smartcardDeviceId, callback, callbackData, &irp);
2582 if (error != CHANNEL_RC_OK)
2583 return error;
2584
2585 irp->IoControlCode = ioControlCode;
2586 *outIrp = irp;
2587 return CHANNEL_RC_OK;
2588}
2589
2590static void dequeueAndDiscard(RdpdrServerContext* context, RDPDR_IRP* irp)
2591{
2592 WINPR_ASSERT(context);
2593 WINPR_ASSERT(irp);
2594 RDPDR_IRP* val = rdpdr_server_dequeue_irp(context, irp->CompletionId);
2595 rdpdr_server_irp_free(val);
2596}
2597
2598/* Send a request for an enqueued IRP, drop the IRP if the request can not be sent */
2599WINPR_ATTR_NODISCARD
2600static UINT rdpdr_seal_send_irp_request(RdpdrServerContext* context, RDPDR_IRP* irp, wStream* s)
2601{
2602 const UINT rc = rdpdr_seal_send_free_request(context, s);
2603 if (rc != CHANNEL_RC_OK)
2604 dequeueAndDiscard(context, irp);
2605 return rc;
2606}
2607
2613WINPR_ATTR_NODISCARD
2614static UINT rdpdr_server_send_device_create_request(RdpdrServerContext* context, RDPDR_IRP* irp,
2615 UINT32 desiredAccess, UINT32 createOptions,
2616 UINT32 createDisposition)
2617{
2618 WINPR_ASSERT(context);
2619 WINPR_ASSERT(context->priv);
2620 WINPR_ASSERT(irp);
2621
2622 const UINT32 deviceId = irp->DeviceId;
2623 const UINT32 completionId = irp->CompletionId;
2624 const char* path = irp->PathName;
2625
2626 RdpdrServerPrivate* priv = context->priv;
2627
2628 WLog_Print(priv->log, WLOG_DEBUG,
2629 "RdpdrServerSendDeviceCreateRequest: deviceId=%" PRIu32
2630 ", path=%s, desiredAccess=0x%" PRIx32 " createOptions=0x%" PRIx32
2631 " createDisposition=0x%" PRIx32 "",
2632 deviceId, path, desiredAccess, createOptions, createDisposition);
2633 /* Compute the required Unicode size. */
2634 size_t pathStrLength = 0;
2635 size_t pathLength = 0;
2636 if (path)
2637 {
2638 pathStrLength = strlen(path);
2639 const SSIZE_T wlen = ConvertUtf8ToWChar(path, nullptr, 0);
2640 if (wlen < 0)
2641 {
2642 dequeueAndDiscard(context, irp);
2643 return ERROR_INTERNAL_ERROR;
2644 }
2645 pathLength = (WINPR_ASSERTING_INT_CAST(size_t, wlen) + 1) * sizeof(WCHAR);
2646 }
2647 wStream* s = Stream_New(nullptr, 256U + pathLength);
2648
2649 if (!s)
2650 {
2651 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
2652 dequeueAndDiscard(context, irp);
2653 return CHANNEL_RC_NO_MEMORY;
2654 }
2655
2656 rdpdr_server_write_device_iorequest(s, deviceId, 0, completionId, IRP_MJ_CREATE, 0);
2657 Stream_Write_UINT32(s, desiredAccess); /* DesiredAccess (4 bytes) */
2658 Stream_Write_UINT32(s, 0); /* AllocationSize (8 bytes) */
2659 Stream_Write_UINT32(s, 0);
2660 Stream_Write_UINT32(s, 0); /* FileAttributes (4 bytes) */
2661 Stream_Write_UINT32(s, 3); /* SharedAccess (4 bytes) */
2662 Stream_Write_UINT32(s, createDisposition); /* CreateDisposition (4 bytes) */
2663 Stream_Write_UINT32(s, createOptions); /* CreateOptions (4 bytes) */
2664 WINPR_ASSERT(pathLength <= UINT32_MAX);
2665 Stream_Write_UINT32(s, (UINT32)pathLength); /* PathLength (4 bytes) */
2666 /* Convert the path to Unicode. */
2667 if (Stream_Write_UTF16_String_From_UTF8(s, pathLength / sizeof(WCHAR), path, pathStrLength,
2668 TRUE) < 0)
2669 {
2670 Stream_Release(s);
2671 dequeueAndDiscard(context, irp);
2672 return ERROR_INTERNAL_ERROR;
2673 }
2674 return rdpdr_seal_send_irp_request(context, irp, s);
2675}
2676
2682WINPR_ATTR_NODISCARD
2683static UINT rdpdr_server_send_device_close_request(RdpdrServerContext* context, RDPDR_IRP* irp)
2684{
2685 WINPR_ASSERT(context);
2686 WINPR_ASSERT(context->priv);
2687 WINPR_ASSERT(irp);
2688
2689 RdpdrServerPrivate* priv = context->priv;
2690
2691 WLog_Print(priv->log, WLOG_DEBUG,
2692 "RdpdrServerSendDeviceCloseRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32 "",
2693 irp->DeviceId, irp->FileId);
2694 wStream* s = Stream_New(nullptr, 128);
2695
2696 if (!s)
2697 {
2698 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
2699 dequeueAndDiscard(context, irp);
2700 return CHANNEL_RC_NO_MEMORY;
2701 }
2702
2703 rdpdr_server_write_device_iorequest(s, irp->DeviceId, irp->FileId, irp->CompletionId,
2704 IRP_MJ_CLOSE, 0);
2705 Stream_Zero(s, 32); /* Padding (32 bytes) */
2706 return rdpdr_seal_send_irp_request(context, irp, s);
2707}
2708
2714WINPR_ATTR_NODISCARD
2715static UINT rdpdr_server_send_device_read_request(RdpdrServerContext* context, RDPDR_IRP* irp,
2716 UINT32 length, UINT64 offset)
2717{
2718 WINPR_ASSERT(context);
2719 WINPR_ASSERT(context->priv);
2720 WINPR_ASSERT(irp);
2721
2722 RdpdrServerPrivate* priv = context->priv;
2723
2724 WLog_Print(priv->log, WLOG_DEBUG,
2725 "RdpdrServerSendDeviceReadRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32
2726 ", length=%" PRIu32 ", offset=%" PRIu64 "",
2727 irp->DeviceId, irp->FileId, length, offset);
2728 wStream* s = Stream_New(nullptr, 128);
2729
2730 if (!s)
2731 {
2732 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
2733 dequeueAndDiscard(context, irp);
2734 return CHANNEL_RC_NO_MEMORY;
2735 }
2736
2737 rdpdr_server_write_device_iorequest(s, irp->DeviceId, irp->FileId, irp->CompletionId,
2738 IRP_MJ_READ, 0);
2739 Stream_Write_UINT32(s, length); /* Length (4 bytes) */
2740 Stream_Write_UINT64(s, offset); /* Offset (8 bytes) */
2741 Stream_Zero(s, 20); /* Padding (20 bytes) */
2742 return rdpdr_seal_send_irp_request(context, irp, s);
2743}
2744
2750WINPR_ATTR_NODISCARD
2751static UINT rdpdr_server_send_device_write_request(RdpdrServerContext* context, RDPDR_IRP* irp,
2752 const char* data, UINT32 length, UINT64 offset)
2753{
2754 WINPR_ASSERT(context);
2755 WINPR_ASSERT(context->priv);
2756 WINPR_ASSERT(irp);
2757
2758 RdpdrServerPrivate* priv = context->priv;
2759
2760 WLog_Print(priv->log, WLOG_DEBUG,
2761 "RdpdrServerSendDeviceWriteRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32
2762 ", length=%" PRIu32 ", offset=%" PRIu64 "",
2763 irp->DeviceId, irp->FileId, length, offset);
2764 wStream* s = Stream_New(nullptr, 64 + length);
2765
2766 if (!s)
2767 {
2768 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
2769 dequeueAndDiscard(context, irp);
2770 return CHANNEL_RC_NO_MEMORY;
2771 }
2772
2773 rdpdr_server_write_device_iorequest(s, irp->DeviceId, irp->FileId, irp->CompletionId,
2774 IRP_MJ_WRITE, 0);
2775 Stream_Write_UINT32(s, length); /* Length (4 bytes) */
2776 Stream_Write_UINT64(s, offset); /* Offset (8 bytes) */
2777 Stream_Zero(s, 20); /* Padding (20 bytes) */
2778 Stream_Write(s, data, length); /* WriteData (variable) */
2779 return rdpdr_seal_send_irp_request(context, irp, s);
2780}
2781
2787WINPR_ATTR_NODISCARD
2788static UINT rdpdr_server_send_device_query_directory_request(RdpdrServerContext* context,
2789 RDPDR_IRP* irp, const char* path)
2790{
2791 WINPR_ASSERT(context);
2792 WINPR_ASSERT(context->priv);
2793 WINPR_ASSERT(irp);
2794
2795 RdpdrServerPrivate* priv = context->priv;
2796
2797 WLog_Print(priv->log, WLOG_DEBUG,
2798 "RdpdrServerSendDeviceQueryDirectoryRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32
2799 ", path=%s",
2800 irp->DeviceId, irp->FileId, path);
2801 /* Compute the required Unicode size. */
2802 size_t pathStrLength = 0;
2803 size_t pathLength = 0;
2804 if (path)
2805 {
2806 pathStrLength = strlen(path);
2807 const SSIZE_T wlen = ConvertUtf8ToWChar(path, nullptr, 0);
2808 if (wlen < 0)
2809 {
2810 dequeueAndDiscard(context, irp);
2811 return ERROR_INTERNAL_ERROR;
2812 }
2813 pathLength = (WINPR_ASSERTING_INT_CAST(size_t, wlen) + 1) * sizeof(WCHAR);
2814 }
2815 wStream* s = Stream_New(nullptr, 64 + pathLength);
2816
2817 if (!s)
2818 {
2819 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
2820 dequeueAndDiscard(context, irp);
2821 return CHANNEL_RC_NO_MEMORY;
2822 }
2823
2824 rdpdr_server_write_device_iorequest(s, irp->DeviceId, irp->FileId, irp->CompletionId,
2825 IRP_MJ_DIRECTORY_CONTROL, IRP_MN_QUERY_DIRECTORY);
2826 Stream_Write_UINT32(s, FileDirectoryInformation); /* FsInformationClass (4 bytes) */
2827 Stream_Write_UINT8(s, path ? 1 : 0); /* InitialQuery (1 byte) */
2828 WINPR_ASSERT(pathLength <= UINT32_MAX);
2829 Stream_Write_UINT32(s, (UINT32)pathLength); /* PathLength (4 bytes) */
2830 Stream_Zero(s, 23); /* Padding (23 bytes) */
2831
2832 /* Convert the path to Unicode. */
2833 if (pathLength > 0)
2834 {
2835 if (Stream_Write_UTF16_String_From_UTF8(s, pathLength / sizeof(WCHAR), path, pathStrLength,
2836 TRUE) < 0)
2837 {
2838 Stream_Release(s);
2839 dequeueAndDiscard(context, irp);
2840 return ERROR_INTERNAL_ERROR;
2841 }
2842 }
2843
2844 return rdpdr_seal_send_irp_request(context, irp, s);
2845}
2846
2852WINPR_ATTR_NODISCARD
2853static UINT rdpdr_server_send_device_file_rename_request(RdpdrServerContext* context,
2854 RDPDR_IRP* irp, const char* path,
2855 BOOL replaceIfExists)
2856{
2857 WINPR_ASSERT(context);
2858 WINPR_ASSERT(context->priv);
2859 WINPR_ASSERT(irp);
2860
2861 RdpdrServerPrivate* priv = context->priv;
2862
2863 WLog_Print(priv->log, WLOG_DEBUG,
2864 "RdpdrServerSendDeviceFileNameRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32
2865 ", path=%s",
2866 irp->DeviceId, irp->FileId, path);
2867 /* Compute the required Unicode size. */
2868 size_t pathStrLength = 0;
2869 size_t pathLength = 0;
2870 if (path)
2871 {
2872 pathStrLength = strlen(path);
2873 const SSIZE_T wlen = ConvertUtf8ToWChar(path, nullptr, 0);
2874 if (wlen < 0)
2875 {
2876 dequeueAndDiscard(context, irp);
2877 return ERROR_INTERNAL_ERROR;
2878 }
2879 pathLength = (WINPR_ASSERTING_INT_CAST(size_t, wlen) + 1) * sizeof(WCHAR);
2880 }
2881
2882 /* Covers the stream size and Length, FileNameLength plus the 6 bytes before FileName */
2883 if (pathLength > UINT32_MAX - 64U)
2884 {
2885 dequeueAndDiscard(context, irp);
2886 return ERROR_INVALID_PARAMETER;
2887 }
2888
2889 wStream* s = Stream_New(nullptr, 64 + pathLength);
2890
2891 if (!s)
2892 {
2893 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
2894 dequeueAndDiscard(context, irp);
2895 return CHANNEL_RC_NO_MEMORY;
2896 }
2897
2898 rdpdr_server_write_device_iorequest(s, irp->DeviceId, irp->FileId, irp->CompletionId,
2899 IRP_MJ_SET_INFORMATION, 0);
2900 Stream_Write_UINT32(s, FileRenameInformation); /* FsInformationClass (4 bytes) */
2901 Stream_Write_UINT32(s, (UINT32)pathLength + 6U); /* Length (4 bytes) */
2902 Stream_Zero(s, 24); /* Padding (24 bytes) */
2903 /* RDP_FILE_RENAME_INFORMATION */
2904 Stream_Write_UINT8(s, replaceIfExists ? 1 : 0); /* ReplaceIfExists (1 byte) */
2905 Stream_Write_UINT8(s, 0); /* RootDirectory (1 byte) */
2906 Stream_Write_UINT32(s, (UINT32)pathLength); /* FileNameLength (4 bytes) */
2907
2908 /* Convert the path to Unicode. */
2909 if (pathLength > 0)
2910 {
2911 if (Stream_Write_UTF16_String_From_UTF8(s, pathLength / sizeof(WCHAR), path, pathStrLength,
2912 TRUE) < 0)
2913 {
2914 Stream_Release(s);
2915 dequeueAndDiscard(context, irp);
2916 return ERROR_INTERNAL_ERROR;
2917 }
2918 }
2919
2920 return rdpdr_seal_send_irp_request(context, irp, s);
2921}
2922
2923WINPR_ATTR_NODISCARD
2924static UINT rdpdr_server_send_device_set_information_request(RdpdrServerContext* context,
2925 RDPDR_IRP* irp,
2926 UINT32 fsInformationClass,
2927 const BYTE* buffer, UINT32 length)
2928{
2929 WINPR_ASSERT(context);
2930 WINPR_ASSERT(context->priv);
2931 WINPR_ASSERT(irp);
2932
2933 RdpdrServerPrivate* priv = context->priv;
2934 WLog_Print(priv->log, WLOG_DEBUG,
2935 "RdpdrServerSendDeviceSetInformationRequest: deviceId=%" PRIu32 ", fileId=%" PRIu32
2936 ", class=%" PRIu32 ", length=%" PRIu32 "",
2937 irp->DeviceId, irp->FileId, fsInformationClass, length);
2938
2939 if (length > UINT32_MAX - 64U)
2940 {
2941 dequeueAndDiscard(context, irp);
2942 return ERROR_INVALID_PARAMETER;
2943 }
2944
2945 wStream* s = Stream_New(nullptr, 64U + length);
2946 if (!s)
2947 {
2948 dequeueAndDiscard(context, irp);
2949 return CHANNEL_RC_NO_MEMORY;
2950 }
2951
2952 rdpdr_server_write_device_iorequest(s, irp->DeviceId, irp->FileId, irp->CompletionId,
2953 IRP_MJ_SET_INFORMATION, 0);
2954 Stream_Write_UINT32(s, fsInformationClass); /* FsInformationClass (4 bytes) */
2955 Stream_Write_UINT32(s, length); /* Length (4 bytes) */
2956 Stream_Zero(s, 24); /* Padding (24 bytes) */
2957 Stream_Write(s, buffer, length); /* SetBuffer (variable) */
2958 return rdpdr_seal_send_irp_request(context, irp, s);
2959}
2960
2961WINPR_ATTR_NODISCARD
2962static UINT rdpdr_server_send_device_query_volume_information_request(RdpdrServerContext* context,
2963 RDPDR_IRP* irp,
2964 UINT32 fsInformationClass)
2965{
2966 WINPR_ASSERT(context);
2967 WINPR_ASSERT(context->priv);
2968 WINPR_ASSERT(irp);
2969
2970 RdpdrServerPrivate* priv = context->priv;
2971 WLog_Print(priv->log, WLOG_DEBUG,
2972 "RdpdrServerSendDeviceQueryVolumeInformationRequest: deviceId=%" PRIu32
2973 ", fileId=%" PRIu32 ", class=%" PRIu32 "",
2974 irp->DeviceId, irp->FileId, fsInformationClass);
2975
2976 wStream* s = Stream_New(nullptr, 64);
2977 if (!s)
2978 {
2979 dequeueAndDiscard(context, irp);
2980 return CHANNEL_RC_NO_MEMORY;
2981 }
2982
2983 rdpdr_server_write_device_iorequest(s, irp->DeviceId, irp->FileId, irp->CompletionId,
2984 IRP_MJ_QUERY_VOLUME_INFORMATION, 0);
2985 Stream_Write_UINT32(s, fsInformationClass); /* FsInformationClass (4 bytes) */
2986 Stream_Write_UINT32(s, 0); /* Length (4 bytes) */
2987 Stream_Zero(s, 24); /* Padding (24 bytes) */
2988 return rdpdr_seal_send_irp_request(context, irp, s);
2989}
2990
2991WINPR_ATTR_NODISCARD
2992static UINT rdpdr_server_send_device_control_request(RdpdrServerContext* context, UINT32 deviceId,
2993 UINT32 completionId, UINT32 ioControlCode,
2994 UINT32 outputBufferLength,
2995 const void* inputBuffer,
2996 size_t inputBufferLength)
2997{
2998 WINPR_ASSERT(context);
2999 WINPR_ASSERT(context->priv);
3000 WINPR_ASSERT(inputBuffer || (inputBufferLength == 0));
3001
3002 if (inputBufferLength > UINT32_MAX)
3003 {
3004 WLog_Print(context->priv->log, WLOG_ERROR,
3005 "inputBufferLength %" PRIuz " exceeds UINT32_MAX", inputBufferLength);
3006 return ERROR_INVALID_PARAMETER;
3007 }
3008
3009 RdpdrServerPrivate* priv = context->priv;
3010
3011 WLog_Print(priv->log, WLOG_DEBUG,
3012 "RdpdrServerSendDeviceControlRequest: deviceId=%" PRIu32 ","
3013 " ioControlCode=0x%" PRIx32 ", inputBufferLength=%" PRIuz,
3014 deviceId, ioControlCode, inputBufferLength);
3015
3016 if (inputBufferLength > 0)
3017 winpr_HexLogDump(priv->log, WLOG_DEBUG, inputBuffer, inputBufferLength);
3018
3019 wStream* s =
3020 Stream_New(nullptr, RDPDR_HEADER_LENGTH + RDPDR_DEVICE_IO_REQUEST_LENGTH +
3021 RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH + inputBufferLength);
3022 if (!s)
3023 {
3024 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
3025 return CHANNEL_RC_NO_MEMORY;
3026 }
3027
3028 UINT32 inputLength = WINPR_ASSERTING_INT_CAST(UINT32, inputBufferLength);
3029 rdpdr_server_write_device_iorequest(s, deviceId, 0, completionId, IRP_MJ_DEVICE_CONTROL, 0);
3030 Stream_Write_UINT32(s, outputBufferLength); /* OutputBufferLength (4 bytes) */
3031 Stream_Write_UINT32(s, inputLength); /* InputBufferLength (4 bytes) */
3032 Stream_Write_UINT32(s, ioControlCode); /* IoControlCode (4 bytes) */
3033 Stream_Zero(s, 20); /* Padding (20 bytes) */
3034
3035 if (inputBufferLength > 0)
3036 Stream_Write(s, inputBuffer, inputBufferLength);
3037
3038 return rdpdr_seal_send_free_request(context, s);
3039}
3040
3041/*************************************************
3042 * Drive Create Directory
3043 ************************************************/
3044
3050WINPR_ATTR_NODISCARD
3051static UINT rdpdr_server_drive_create_directory_callback2(RdpdrServerContext* context, wStream* s,
3052 RDPDR_IRP* irp, UINT32 deviceId,
3053 UINT32 completionId, UINT32 ioStatus)
3054{
3055 WINPR_ASSERT(context);
3056 WINPR_ASSERT(context->priv);
3057 WINPR_ASSERT(s);
3058 WINPR_ASSERT(irp);
3059 WINPR_UNUSED(s);
3060
3061 RdpdrServerPrivate* priv = context->priv;
3062 WLog_Print(priv->log, WLOG_DEBUG,
3063 "RdpdrServerDriveCreateDirectoryCallback2: deviceId=%" PRIu32
3064 ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "",
3065 deviceId, completionId, ioStatus);
3066 /* Invoke the create directory completion routine. */
3067 context->OnDriveCreateDirectoryComplete(context, irp->CallbackData, ioStatus);
3068 /* Destroy the IRP. */
3069 rdpdr_server_irp_free(irp);
3070 return CHANNEL_RC_OK;
3071}
3072
3078WINPR_ATTR_NODISCARD
3079static UINT rdpdr_server_drive_create_directory_callback1(RdpdrServerContext* context, wStream* s,
3080 RDPDR_IRP* irp, UINT32 deviceId,
3081 UINT32 completionId, UINT32 ioStatus)
3082{
3083 WINPR_ASSERT(context);
3084 WINPR_ASSERT(context->priv);
3085 WINPR_ASSERT(s);
3086 WINPR_ASSERT(irp);
3087
3088 RdpdrServerPrivate* priv = context->priv;
3089 WLog_Print(priv->log, WLOG_DEBUG,
3090 "RdpdrServerDriveCreateDirectoryCallback1: deviceId=%" PRIu32
3091 ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "",
3092 deviceId, completionId, ioStatus);
3093
3094 if (ioStatus != STATUS_SUCCESS)
3095 {
3096 /* Invoke the create directory completion routine. */
3097 context->OnDriveCreateDirectoryComplete(context, irp->CallbackData, ioStatus);
3098 /* Destroy the IRP. */
3099 rdpdr_server_irp_free(irp);
3100 return CHANNEL_RC_OK;
3101 }
3102
3103 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5))
3104 return ERROR_INVALID_DATA;
3105
3106 const uint32_t fileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */
3107 const uint8_t information = Stream_Get_UINT8(s); /* Information (1 byte) */
3108 WLog_Print(priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId,
3109 fileInformation2str(information));
3110
3111 /* Setup the IRP. */
3112 irp->CompletionId = rdpdr_server_next_completion_id(priv);
3113 irp->Callback = rdpdr_server_drive_create_directory_callback2;
3114 irp->DeviceId = deviceId;
3115 irp->FileId = fileId;
3116
3117 if (!rdpdr_server_enqueue_irp(context, irp))
3118 {
3119 WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!");
3120 rdpdr_server_irp_free(irp);
3121 return ERROR_INTERNAL_ERROR;
3122 }
3123
3124 /* Send a request to close the file */
3125 return rdpdr_server_send_device_close_request(context, irp);
3126}
3127
3133WINPR_ATTR_NODISCARD
3134static UINT rdpdr_server_drive_create_directory(RdpdrServerContext* context, void* callbackData,
3135 UINT32 deviceId, const char* path)
3136{
3137 WINPR_ASSERT(context);
3138 WINPR_ASSERT(context->priv);
3139 WINPR_ASSERT(callbackData);
3140 WINPR_ASSERT(path);
3141
3142 RDPDR_IRP* irp = nullptr;
3143 UINT ret = prepare_path_irp(context, deviceId, rdpdr_server_drive_create_directory_callback1,
3144 callbackData, path, nullptr, &irp);
3145 if (ret != CHANNEL_RC_OK)
3146 return ret;
3147
3148 /* Send a request to open the file. */
3149 return rdpdr_server_send_device_create_request(
3150 context, irp, FILE_READ_DATA | SYNCHRONIZE,
3151 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE);
3152}
3153
3154/*************************************************
3155 * Drive Delete Directory
3156 ************************************************/
3157
3163WINPR_ATTR_NODISCARD
3164static UINT rdpdr_server_drive_delete_directory_callback2(RdpdrServerContext* context, wStream* s,
3165 RDPDR_IRP* irp, UINT32 deviceId,
3166 UINT32 completionId, UINT32 ioStatus)
3167{
3168 WINPR_UNUSED(s);
3169 WINPR_ASSERT(context);
3170 WINPR_ASSERT(context->priv);
3171 WINPR_ASSERT(irp);
3172
3173 RdpdrServerPrivate* priv = context->priv;
3174 WLog_Print(priv->log, WLOG_DEBUG,
3175 "RdpdrServerDriveDeleteDirectoryCallback2: deviceId=%" PRIu32
3176 ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "",
3177 deviceId, completionId, ioStatus);
3178 /* Invoke the delete directory completion routine. */
3179 context->OnDriveDeleteDirectoryComplete(context, irp->CallbackData, ioStatus);
3180 /* Destroy the IRP. */
3181 rdpdr_server_irp_free(irp);
3182 return CHANNEL_RC_OK;
3183}
3184
3190WINPR_ATTR_NODISCARD
3191static UINT rdpdr_server_drive_delete_directory_callback1(RdpdrServerContext* context, wStream* s,
3192 RDPDR_IRP* irp, UINT32 deviceId,
3193 UINT32 completionId, UINT32 ioStatus)
3194{
3195 WINPR_ASSERT(context);
3196 WINPR_ASSERT(context->priv);
3197 WINPR_ASSERT(irp);
3198
3199 RdpdrServerPrivate* priv = context->priv;
3200 WLog_Print(priv->log, WLOG_DEBUG,
3201 "RdpdrServerDriveDeleteDirectoryCallback1: deviceId=%" PRIu32
3202 ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "",
3203 deviceId, completionId, ioStatus);
3204
3205 if (ioStatus != STATUS_SUCCESS)
3206 {
3207 /* Invoke the delete directory completion routine. */
3208 context->OnDriveDeleteFileComplete(context, irp->CallbackData, ioStatus);
3209 /* Destroy the IRP. */
3210 rdpdr_server_irp_free(irp);
3211 return CHANNEL_RC_OK;
3212 }
3213
3214 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5))
3215 return ERROR_INVALID_DATA;
3216
3217 const uint32_t fileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */
3218 const uint8_t information = Stream_Get_UINT8(s); /* Information (1 byte) */
3219 WLog_Print(priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId,
3220 fileInformation2str(information));
3221
3222 /* Setup the IRP. */
3223 irp->CompletionId = rdpdr_server_next_completion_id(priv);
3224 irp->Callback = rdpdr_server_drive_delete_directory_callback2;
3225 irp->DeviceId = deviceId;
3226 irp->FileId = fileId;
3227
3228 if (!rdpdr_server_enqueue_irp(context, irp))
3229 {
3230 WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!");
3231 rdpdr_server_irp_free(irp);
3232 return ERROR_INTERNAL_ERROR;
3233 }
3234
3235 /* Send a request to close the file */
3236 return rdpdr_server_send_device_close_request(context, irp);
3237}
3238
3244WINPR_ATTR_NODISCARD
3245static UINT rdpdr_server_drive_delete_directory(RdpdrServerContext* context, void* callbackData,
3246 UINT32 deviceId, const char* path)
3247{
3248 WINPR_ASSERT(context);
3249 WINPR_ASSERT(context->priv);
3250
3251 RDPDR_IRP* irp = nullptr;
3252 UINT ret = prepare_path_irp(context, deviceId, rdpdr_server_drive_delete_directory_callback1,
3253 callbackData, path, nullptr, &irp);
3254 if (ret != CHANNEL_RC_OK)
3255 return ret;
3256
3257 /* Send a request to open the file. */
3258 return rdpdr_server_send_device_create_request(
3259 context, irp, DELETE | SYNCHRONIZE,
3260 FILE_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN);
3261}
3262
3263/*************************************************
3264 * Drive Query Directory
3265 ************************************************/
3266
3272WINPR_ATTR_NODISCARD
3273static UINT rdpdr_server_drive_query_directory_callback3(RdpdrServerContext* context, wStream* s,
3274 RDPDR_IRP* irp, UINT32 deviceId,
3275 UINT32 completionId, UINT32 ioStatus)
3276{
3277 WINPR_UNUSED(context);
3278 WINPR_UNUSED(s);
3279 WINPR_UNUSED(deviceId);
3280 WINPR_UNUSED(completionId);
3281 WINPR_UNUSED(ioStatus);
3282
3283 /* The directory is closed. Destroy the IRP. */
3284 rdpdr_server_irp_free(irp);
3285 return CHANNEL_RC_OK;
3286}
3287
3288WINPR_ATTR_NODISCARD
3289static UINT rdpdr_server_drive_query_directory_callback2(RdpdrServerContext* context, wStream* s,
3290 RDPDR_IRP* irp, UINT32 deviceId,
3291 UINT32 completionId, UINT32 ioStatus)
3292{
3293 WINPR_ASSERT(context);
3294 WINPR_ASSERT(context->priv);
3295 WINPR_ASSERT(irp);
3296
3297 RdpdrServerPrivate* priv = context->priv;
3298 WLog_Print(priv->log, WLOG_DEBUG,
3299 "RdpdrServerDriveQueryDirectoryCallback2: deviceId=%" PRIu32
3300 ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "",
3301 deviceId, completionId, ioStatus);
3302
3303 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
3304 return ERROR_INVALID_DATA;
3305
3306 const UINT32 length = Stream_Get_UINT32(s); /* Length (4 bytes) */
3307
3308 FILE_DIRECTORY_INFORMATION fdi = WINPR_C_ARRAY_INIT;
3309 if (length > 0)
3310 {
3311 UINT error = rdpdr_server_read_file_directory_information(priv->log, s, &fdi);
3312 if (error)
3313 {
3314 WLog_Print(priv->log, WLOG_ERROR,
3315 "rdpdr_server_read_file_directory_information failed with error %" PRIu32
3316 "!",
3317 error);
3318 return error;
3319 }
3320 }
3321 else
3322 {
3323 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 1))
3324 return ERROR_INVALID_DATA;
3325
3326 Stream_Seek(s, 1); /* Padding (1 byte) */
3327 }
3328
3329 if (ioStatus == STATUS_SUCCESS)
3330 {
3331 /* Invoke the query directory completion routine. */
3332 context->OnDriveQueryDirectoryComplete(context, irp->CallbackData, ioStatus,
3333 length > 0 ? &fdi : nullptr);
3334 /* Setup the IRP. */
3335 irp->CompletionId = rdpdr_server_next_completion_id(priv);
3336 irp->Callback = rdpdr_server_drive_query_directory_callback2;
3337
3338 if (!rdpdr_server_enqueue_irp(context, irp))
3339 {
3340 WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!");
3341 rdpdr_server_irp_free(irp);
3342 return ERROR_INTERNAL_ERROR;
3343 }
3344
3345 /* Send a request to query the directory. */
3346 return rdpdr_server_send_device_query_directory_request(context, irp, nullptr);
3347 }
3348 else
3349 {
3350 /* Invoke the query directory completion routine. */
3351 context->OnDriveQueryDirectoryComplete(context, irp->CallbackData, ioStatus, nullptr);
3352
3353 /* Close the directory, so that the client does not leak its handle */
3354 irp->CompletionId = rdpdr_server_next_completion_id(priv);
3355 irp->Callback = rdpdr_server_drive_query_directory_callback3;
3356
3357 if (!rdpdr_server_enqueue_irp(context, irp))
3358 {
3359 rdpdr_server_irp_free(irp);
3360 return ERROR_INTERNAL_ERROR;
3361 }
3362
3363 return rdpdr_server_send_device_close_request(context, irp);
3364 }
3365}
3366
3372WINPR_ATTR_NODISCARD
3373static UINT rdpdr_server_drive_query_directory_callback1(RdpdrServerContext* context, wStream* s,
3374 RDPDR_IRP* irp, UINT32 deviceId,
3375 UINT32 completionId, UINT32 ioStatus)
3376{
3377 WINPR_ASSERT(context);
3378 WINPR_ASSERT(context->priv);
3379 WINPR_ASSERT(irp);
3380
3381 RdpdrServerPrivate* priv = context->priv;
3382 WLog_Print(priv->log, WLOG_DEBUG,
3383 "RdpdrServerDriveQueryDirectoryCallback1: deviceId=%" PRIu32
3384 ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "",
3385 deviceId, completionId, ioStatus);
3386
3387 if (ioStatus != STATUS_SUCCESS)
3388 {
3389 /* Invoke the query directory completion routine. */
3390 context->OnDriveQueryDirectoryComplete(context, irp->CallbackData, ioStatus, nullptr);
3391 /* Destroy the IRP. */
3392 rdpdr_server_irp_free(irp);
3393 return CHANNEL_RC_OK;
3394 }
3395
3396 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
3397 return ERROR_INVALID_DATA;
3398
3399 const uint32_t fileId = Stream_Get_UINT32(s);
3400 /* Setup the IRP. */
3401 irp->CompletionId = rdpdr_server_next_completion_id(priv);
3402 irp->Callback = rdpdr_server_drive_query_directory_callback2;
3403 irp->DeviceId = deviceId;
3404 irp->FileId = fileId;
3405 /* "*.*" only matches names with a dot outside of Windows, e.g. in WinPR */
3406 char* pattern = nullptr;
3407 size_t patternLength = 0;
3408 if (winpr_asprintf(&pattern, &patternLength, "%s\\*", irp->PathName) < 0)
3409 {
3410 WLog_Print(priv->log, WLOG_ERROR, "winpr_asprintf failed!");
3411 rdpdr_server_irp_free(irp);
3412 return CHANNEL_RC_NO_MEMORY;
3413 }
3414 free(irp->PathName);
3415 irp->PathName = pattern;
3416
3417 if (!rdpdr_server_enqueue_irp(context, irp))
3418 {
3419 WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!");
3420 rdpdr_server_irp_free(irp);
3421 return ERROR_INTERNAL_ERROR;
3422 }
3423
3424 /* Send a request to query the directory. */
3425 return rdpdr_server_send_device_query_directory_request(context, irp, irp->PathName);
3426}
3427
3433WINPR_ATTR_NODISCARD
3434static UINT rdpdr_server_drive_query_directory(RdpdrServerContext* context, void* callbackData,
3435 UINT32 deviceId, const char* path)
3436{
3437 WINPR_ASSERT(context);
3438 WINPR_ASSERT(context->priv);
3439
3440 RDPDR_IRP* irp = nullptr;
3441 UINT ret = prepare_path_irp(context, deviceId, rdpdr_server_drive_query_directory_callback1,
3442 callbackData, path, nullptr, &irp);
3443 if (ret != CHANNEL_RC_OK)
3444 return ret;
3445
3446 /* Send a request to open the directory. */
3447 return rdpdr_server_send_device_create_request(
3448 context, irp, FILE_READ_DATA | SYNCHRONIZE,
3449 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN);
3450}
3451
3452/*************************************************
3453 * Drive Open File
3454 ************************************************/
3455
3461WINPR_ATTR_NODISCARD
3462static UINT rdpdr_server_drive_open_file_callback(RdpdrServerContext* context, wStream* s,
3463 RDPDR_IRP* irp, UINT32 deviceId,
3464 UINT32 completionId, UINT32 ioStatus)
3465{
3466 WINPR_ASSERT(context);
3467 WINPR_ASSERT(context->priv);
3468 WINPR_ASSERT(irp);
3469
3470 RdpdrServerPrivate* priv = context->priv;
3471 WLog_Print(priv->log, WLOG_DEBUG,
3472 "RdpdrServerDriveOpenFileCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
3473 ", ioStatus=0x%" PRIx32 "",
3474 deviceId, completionId, ioStatus);
3475
3476 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5))
3477 return ERROR_INVALID_DATA;
3478
3479 const uint32_t fileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */
3480 const uint8_t information = Stream_Get_UINT8(s); /* Information (1 byte) */
3481 WLog_Print(priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId,
3482 fileInformation2str(information));
3483
3484 /* Invoke the open file completion routine. */
3485 context->OnDriveOpenFileComplete(context, irp->CallbackData, ioStatus, deviceId, fileId);
3486 /* Destroy the IRP. */
3487 rdpdr_server_irp_free(irp);
3488 return CHANNEL_RC_OK;
3489}
3490
3496WINPR_ATTR_NODISCARD
3497static UINT rdpdr_server_drive_open_file(RdpdrServerContext* context, void* callbackData,
3498 UINT32 deviceId, const char* path, UINT32 desiredAccess,
3499 UINT32 createDisposition)
3500{
3501 WINPR_ASSERT(context);
3502 WINPR_ASSERT(context->priv);
3503
3504 RDPDR_IRP* irp = nullptr;
3505 UINT ret = prepare_path_irp(context, deviceId, rdpdr_server_drive_open_file_callback,
3506 callbackData, path, nullptr, &irp);
3507 if (ret != CHANNEL_RC_OK)
3508 return ret;
3509
3510 /* Send a request to open the file. */
3511 return rdpdr_server_send_device_create_request(context, irp, desiredAccess | SYNCHRONIZE,
3512 FILE_SYNCHRONOUS_IO_NONALERT, createDisposition);
3513}
3514
3515/*************************************************
3516 * Drive Read File
3517 ************************************************/
3518
3524WINPR_ATTR_NODISCARD
3525static UINT rdpdr_server_drive_read_file_callback(RdpdrServerContext* context, wStream* s,
3526 RDPDR_IRP* irp, UINT32 deviceId,
3527 UINT32 completionId, UINT32 ioStatus)
3528{
3529 WINPR_ASSERT(context);
3530 WINPR_ASSERT(context->priv);
3531 WINPR_ASSERT(irp);
3532
3533 RdpdrServerPrivate* priv = context->priv;
3534 WLog_Print(priv->log, WLOG_DEBUG,
3535 "RdpdrServerDriveReadFileCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
3536 ", ioStatus=0x%" PRIx32 "",
3537 deviceId, completionId, ioStatus);
3538
3539 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
3540 return ERROR_INVALID_DATA;
3541
3542 const UINT32 length = Stream_Get_UINT32(s); /* Length (4 bytes) */
3543 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, length))
3544 return ERROR_INVALID_DATA;
3545
3546 char* buffer = nullptr;
3547 if (length > 0)
3548 {
3549 buffer = Stream_PointerAs(s, char);
3550 Stream_Seek(s, length);
3551 }
3552
3553 /* Invoke the read file completion routine. */
3554 context->OnDriveReadFileComplete(context, irp->CallbackData, ioStatus, buffer, length);
3555 /* Destroy the IRP. */
3556 rdpdr_server_irp_free(irp);
3557 return CHANNEL_RC_OK;
3558}
3559
3565WINPR_ATTR_NODISCARD
3566static UINT rdpdr_server_drive_read_file_ex(RdpdrServerContext* context, void* callbackData,
3567 UINT32 deviceId, UINT32 fileId, UINT32 length,
3568 UINT64 offset)
3569{
3570 WINPR_ASSERT(context);
3571 WINPR_ASSERT(context->priv);
3572
3573 RDPDR_IRP* irp = nullptr;
3574 UINT ret =
3575 prepare_irp(context, deviceId, rdpdr_server_drive_read_file_callback, callbackData, &irp);
3576 if (ret != CHANNEL_RC_OK)
3577 return ret;
3578
3579 irp->FileId = fileId;
3580
3581 /* Send a request to open the directory. */
3582 return rdpdr_server_send_device_read_request(context, irp, length, offset);
3583}
3584
3585/*************************************************
3586 * Drive Write File
3587 ************************************************/
3588
3594WINPR_ATTR_NODISCARD
3595static UINT rdpdr_server_drive_write_file_callback(RdpdrServerContext* context, wStream* s,
3596 RDPDR_IRP* irp, UINT32 deviceId,
3597 UINT32 completionId, UINT32 ioStatus)
3598{
3599 WINPR_ASSERT(context);
3600 WINPR_ASSERT(context->priv);
3601 WINPR_ASSERT(irp);
3602
3603 RdpdrServerPrivate* priv = context->priv;
3604 WLog_Print(priv->log, WLOG_DEBUG,
3605 "RdpdrServerDriveWriteFileCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
3606 ", ioStatus=0x%" PRIx32 "",
3607 deviceId, completionId, ioStatus);
3608
3609 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
3610 return ERROR_INVALID_DATA;
3611
3612 /* [MS-RDPEFS] 2.2.1.5.4 DR_WRITE_RSP: the number of bytes written, no data */
3613 const UINT32 length = Stream_Get_UINT32(s); /* Length (4 bytes) */
3614 /* Padding (1 byte), optional like the padding of other responses */
3615 Stream_Seek(s, MIN(Stream_GetRemainingLength(s), 1));
3616
3617 /* Invoke the write file completion routine. */
3618 context->OnDriveWriteFileComplete(context, irp->CallbackData, ioStatus, length);
3619 /* Destroy the IRP. */
3620 rdpdr_server_irp_free(irp);
3621 return CHANNEL_RC_OK;
3622}
3623
3629WINPR_ATTR_NODISCARD
3630static UINT rdpdr_server_drive_write_file_ex(RdpdrServerContext* context, void* callbackData,
3631 UINT32 deviceId, UINT32 fileId, const char* buffer,
3632 UINT32 length, UINT64 offset)
3633{
3634 WINPR_ASSERT(context);
3635 WINPR_ASSERT(context->priv);
3636
3637 RDPDR_IRP* irp = nullptr;
3638 UINT ret =
3639 prepare_irp(context, deviceId, rdpdr_server_drive_write_file_callback, callbackData, &irp);
3640 if (ret != CHANNEL_RC_OK)
3641 return ret;
3642
3643 irp->FileId = fileId;
3644
3645 /* Send a request to open the directory. */
3646 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
3647 return rdpdr_server_send_device_write_request(context, irp, buffer, length, offset);
3648 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
3649}
3650
3651/*************************************************
3652 * Drive Close File
3653 ************************************************/
3654
3660WINPR_ATTR_NODISCARD
3661static UINT rdpdr_server_drive_close_file_callback(RdpdrServerContext* context, wStream* s,
3662 RDPDR_IRP* irp, UINT32 deviceId,
3663 UINT32 completionId, UINT32 ioStatus)
3664{
3665 WINPR_ASSERT(context);
3666 WINPR_ASSERT(context->priv);
3667 WINPR_ASSERT(irp);
3668
3669 RdpdrServerPrivate* priv = context->priv;
3670
3671 WLog_Print(priv->log, WLOG_DEBUG,
3672 "RdpdrServerDriveCloseFileCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
3673 ", ioStatus=0x%" PRIx32 "",
3674 deviceId, completionId, ioStatus);
3675
3676 /* [MS-RDPEFS] 2.2.1.5.2 DR_CLOSE_RSP: Padding (4 bytes) that MUST be ignored.
3677 * FreeRDP clients send 5 bytes, the extra byte is ignored as well. */
3678 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
3679 return ERROR_INVALID_DATA;
3680
3681 Stream_Seek(s, 4); /* Padding (4 bytes) */
3682
3683 /* Invoke the close file completion routine. */
3684 context->OnDriveCloseFileComplete(context, irp->CallbackData, ioStatus);
3685 /* Destroy the IRP. */
3686 rdpdr_server_irp_free(irp);
3687 return CHANNEL_RC_OK;
3688}
3689
3695WINPR_ATTR_NODISCARD
3696static UINT rdpdr_server_drive_close_file(RdpdrServerContext* context, void* callbackData,
3697 UINT32 deviceId, UINT32 fileId)
3698{
3699 WINPR_ASSERT(context);
3700 WINPR_ASSERT(context->priv);
3701
3702 RDPDR_IRP* irp = nullptr;
3703 UINT ret =
3704 prepare_irp(context, deviceId, rdpdr_server_drive_close_file_callback, callbackData, &irp);
3705 if (ret != CHANNEL_RC_OK)
3706 return ret;
3707
3708 irp->FileId = fileId;
3709
3710 /* Send a request to open the directory. */
3711 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
3712 return rdpdr_server_send_device_close_request(context, irp);
3713 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
3714}
3715
3716/*************************************************
3717 * Drive Delete File
3718 ************************************************/
3719
3725WINPR_ATTR_NODISCARD
3726static UINT rdpdr_server_drive_delete_file_callback2(RdpdrServerContext* context, wStream* s,
3727 RDPDR_IRP* irp, UINT32 deviceId,
3728 UINT32 completionId, UINT32 ioStatus)
3729{
3730 WINPR_UNUSED(s);
3731 WINPR_ASSERT(context);
3732 WINPR_ASSERT(context->priv);
3733 WINPR_ASSERT(irp);
3734
3735 RdpdrServerPrivate* priv = context->priv;
3736 WLog_Print(priv->log, WLOG_DEBUG,
3737 "RdpdrServerDriveDeleteFileCallback2: deviceId=%" PRIu32 ", completionId=%" PRIu32
3738 ", ioStatus=0x%" PRIx32 "",
3739 deviceId, completionId, ioStatus);
3740 /* Invoke the delete file completion routine. */
3741 context->OnDriveDeleteFileComplete(context, irp->CallbackData, ioStatus);
3742 /* Destroy the IRP. */
3743 rdpdr_server_irp_free(irp);
3744 return CHANNEL_RC_OK;
3745}
3746
3752WINPR_ATTR_NODISCARD
3753static UINT rdpdr_server_drive_delete_file_callback1(RdpdrServerContext* context, wStream* s,
3754 RDPDR_IRP* irp, UINT32 deviceId,
3755 UINT32 completionId, UINT32 ioStatus)
3756{
3757 WINPR_ASSERT(context);
3758 WINPR_ASSERT(context->priv);
3759 WINPR_ASSERT(irp);
3760
3761 RdpdrServerPrivate* priv = context->priv;
3762 WLog_Print(priv->log, WLOG_DEBUG,
3763 "RdpdrServerDriveDeleteFileCallback1: deviceId=%" PRIu32 ", completionId=%" PRIu32
3764 ", ioStatus=0x%" PRIx32 "",
3765 deviceId, completionId, ioStatus);
3766
3767 if (ioStatus != STATUS_SUCCESS)
3768 {
3769 /* Invoke the close file completion routine. */
3770 context->OnDriveDeleteFileComplete(context, irp->CallbackData, ioStatus);
3771 /* Destroy the IRP. */
3772 rdpdr_server_irp_free(irp);
3773 return CHANNEL_RC_OK;
3774 }
3775
3776 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5))
3777 return ERROR_INVALID_DATA;
3778
3779 const uint32_t fileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */
3780 const uint8_t information = Stream_Get_UINT8(s); /* Information (1 byte) */
3781
3782 WLog_Print(priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId,
3783 fileInformation2str(information));
3784 /* Setup the IRP. */
3785 irp->CompletionId = rdpdr_server_next_completion_id(priv);
3786 irp->Callback = rdpdr_server_drive_delete_file_callback2;
3787 irp->DeviceId = deviceId;
3788 irp->FileId = fileId;
3789
3790 if (!rdpdr_server_enqueue_irp(context, irp))
3791 {
3792 WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!");
3793 rdpdr_server_irp_free(irp);
3794 return ERROR_INTERNAL_ERROR;
3795 }
3796
3797 /* Send a request to close the file */
3798 return rdpdr_server_send_device_close_request(context, irp);
3799}
3800
3806WINPR_ATTR_NODISCARD
3807static UINT rdpdr_server_drive_delete_file(RdpdrServerContext* context, void* callbackData,
3808 UINT32 deviceId, const char* path)
3809{
3810 WINPR_ASSERT(context);
3811 WINPR_ASSERT(context->priv);
3812
3813 RDPDR_IRP* irp = nullptr;
3814 UINT ret = prepare_path_irp(context, deviceId, rdpdr_server_drive_delete_file_callback1,
3815 callbackData, path, nullptr, &irp);
3816 if (ret != CHANNEL_RC_OK)
3817 return ret;
3818
3819 /* Send a request to open the file. FILE_DELETE_ON_CLOSE needs DELETE access. */
3820 return rdpdr_server_send_device_create_request(
3821 context, irp, DELETE | SYNCHRONIZE, FILE_DELETE_ON_CLOSE | FILE_SYNCHRONOUS_IO_NONALERT,
3822 FILE_OPEN);
3823}
3824
3825/*************************************************
3826 * Drive Rename File
3827 ************************************************/
3828
3834WINPR_ATTR_NODISCARD
3835static UINT rdpdr_server_drive_rename_file_callback3(RdpdrServerContext* context, wStream* s,
3836 RDPDR_IRP* irp, UINT32 deviceId,
3837 UINT32 completionId, UINT32 ioStatus)
3838{
3839 WINPR_UNUSED(context);
3840 WINPR_UNUSED(s);
3841 WINPR_ASSERT(context);
3842 WINPR_ASSERT(context->priv);
3843 WINPR_ASSERT(irp);
3844
3845 RdpdrServerPrivate* priv = context->priv;
3846 WLog_Print(priv->log, WLOG_DEBUG,
3847 "RdpdrServerDriveRenameFileCallback3: deviceId=%" PRIu32 ", completionId=%" PRIu32
3848 ", ioStatus=0x%" PRIx32 "",
3849 deviceId, completionId, ioStatus);
3850 /* Destroy the IRP. */
3851 rdpdr_server_irp_free(irp);
3852 return CHANNEL_RC_OK;
3853}
3854
3860WINPR_ATTR_NODISCARD
3861static UINT rdpdr_server_drive_rename_file_callback2(RdpdrServerContext* context, wStream* s,
3862 RDPDR_IRP* irp, UINT32 deviceId,
3863 UINT32 completionId, UINT32 ioStatus)
3864{
3865 WINPR_ASSERT(context);
3866 WINPR_ASSERT(context->priv);
3867 WINPR_ASSERT(irp);
3868
3869 RdpdrServerPrivate* priv = context->priv;
3870 WLog_Print(priv->log, WLOG_DEBUG,
3871 "RdpdrServerDriveRenameFileCallback2: deviceId=%" PRIu32 ", completionId=%" PRIu32
3872 ", ioStatus=0x%" PRIx32 "",
3873 deviceId, completionId, ioStatus);
3874
3875 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
3876 return ERROR_INVALID_DATA;
3877
3878 WINPR_ATTR_UNUSED const UINT32 length = Stream_Get_UINT32(s); /* Length (4 bytes) */
3879 /* [MS-RDPEFS] 2.2.3.4.9 DR_DRIVE_SET_INFORMATION_RSP: the padding is optional */
3880 if (Stream_GetRemainingLength(s) > 0)
3881 Stream_Seek(s, 1); /* Padding (1 byte) */
3882
3883 /* Invoke the rename file completion routine. */
3884 context->OnDriveRenameFileComplete(context, irp->CallbackData, ioStatus);
3885 /* Setup the IRP. */
3886 irp->CompletionId = rdpdr_server_next_completion_id(priv);
3887 irp->Callback = rdpdr_server_drive_rename_file_callback3;
3888 irp->DeviceId = deviceId;
3889
3890 if (!rdpdr_server_enqueue_irp(context, irp))
3891 {
3892 WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!");
3893 rdpdr_server_irp_free(irp);
3894 return ERROR_INTERNAL_ERROR;
3895 }
3896
3897 /* Send a request to close the file */
3898 return rdpdr_server_send_device_close_request(context, irp);
3899}
3900
3906WINPR_ATTR_NODISCARD
3907static UINT rdpdr_server_drive_rename_file_callback1(RdpdrServerContext* context, wStream* s,
3908 RDPDR_IRP* irp, UINT32 deviceId,
3909 UINT32 completionId, UINT32 ioStatus)
3910{
3911 WINPR_ASSERT(context);
3912 WINPR_ASSERT(context->priv);
3913 WINPR_ASSERT(irp);
3914
3915 RdpdrServerPrivate* priv = context->priv;
3916 WLog_Print(priv->log, WLOG_DEBUG,
3917 "RdpdrServerDriveRenameFileCallback1: deviceId=%" PRIu32 ", completionId=%" PRIu32
3918 ", ioStatus=0x%" PRIx32 "",
3919 deviceId, completionId, ioStatus);
3920
3921 if (ioStatus != STATUS_SUCCESS)
3922 {
3923 /* Invoke the rename file completion routine. */
3924 context->OnDriveRenameFileComplete(context, irp->CallbackData, ioStatus);
3925 /* Destroy the IRP. */
3926 rdpdr_server_irp_free(irp);
3927 return CHANNEL_RC_OK;
3928 }
3929
3930 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 5))
3931 return ERROR_INVALID_DATA;
3932
3933 const uint32_t fileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */
3934 const uint8_t information = Stream_Get_UINT8(s); /* Information (1 byte) */
3935 WLog_Print(priv->log, WLOG_DEBUG, "fileId [0x%08" PRIx32 "], information %s", fileId,
3936 fileInformation2str(information));
3937
3938 /* Setup the IRP. */
3939 irp->CompletionId = rdpdr_server_next_completion_id(priv);
3940 irp->Callback = rdpdr_server_drive_rename_file_callback2;
3941 irp->DeviceId = deviceId;
3942 irp->FileId = fileId;
3943
3944 if (!rdpdr_server_enqueue_irp(context, irp))
3945 {
3946 WLog_Print(priv->log, WLOG_ERROR, "rdpdr_server_enqueue_irp failed!");
3947 rdpdr_server_irp_free(irp);
3948 return ERROR_INTERNAL_ERROR;
3949 }
3950
3951 /* Send a request to rename the file */
3952 return rdpdr_server_send_device_file_rename_request(context, irp, irp->ExtraBuffer,
3953 irp->ReplaceIfExists);
3954}
3955
3961WINPR_ATTR_NODISCARD
3962static UINT rdpdr_server_drive_rename_file_ex(RdpdrServerContext* context, void* callbackData,
3963 UINT32 deviceId, const char* oldPath,
3964 const char* newPath, BOOL replaceIfExists)
3965{
3966 WINPR_ASSERT(context);
3967 WINPR_ASSERT(context->priv);
3968
3969 RDPDR_IRP* irp = nullptr;
3970 UINT ret = prepare_path_irp(context, deviceId, rdpdr_server_drive_rename_file_callback1,
3971 callbackData, oldPath, newPath, &irp);
3972 if (ret != CHANNEL_RC_OK)
3973 return ret;
3974
3975 irp->ReplaceIfExists = replaceIfExists;
3976
3977 /* Send a request to open the file. Renaming needs DELETE access. */
3978 return rdpdr_server_send_device_create_request(context, irp,
3979 DELETE | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
3980 FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN);
3981}
3982
3983WINPR_ATTR_NODISCARD
3984static UINT rdpdr_server_drive_rename_file(RdpdrServerContext* context, void* callbackData,
3985 UINT32 deviceId, const char* oldPath,
3986 const char* newPath)
3987{
3988 return rdpdr_server_drive_rename_file_ex(context, callbackData, deviceId, oldPath, newPath,
3989 FALSE);
3990}
3991
3992WINPR_ATTR_NODISCARD
3993static UINT rdpdr_server_drive_read_file(RdpdrServerContext* context, void* callbackData,
3994 UINT32 deviceId, UINT32 fileId, UINT32 length,
3995 UINT32 offset)
3996{
3997 return rdpdr_server_drive_read_file_ex(context, callbackData, deviceId, fileId, length, offset);
3998}
3999
4000WINPR_ATTR_NODISCARD
4001static UINT rdpdr_server_drive_write_file(RdpdrServerContext* context, void* callbackData,
4002 UINT32 deviceId, UINT32 fileId, const char* buffer,
4003 UINT32 length, UINT32 offset)
4004{
4005 return rdpdr_server_drive_write_file_ex(context, callbackData, deviceId, fileId, buffer, length,
4006 offset);
4007}
4008
4009/*************************************************
4010 * Drive Set File Information (open, set, close)
4011 ************************************************/
4012
4013WINPR_ATTR_NODISCARD
4014static UINT rdpdr_server_drive_set_information_callback3(RdpdrServerContext* context, wStream* s,
4015 RDPDR_IRP* irp, UINT32 deviceId,
4016 UINT32 completionId, UINT32 ioStatus)
4017{
4018 WINPR_UNUSED(context);
4019 WINPR_UNUSED(s);
4020 WINPR_UNUSED(deviceId);
4021 WINPR_UNUSED(completionId);
4022 WINPR_UNUSED(ioStatus);
4023
4024 /* The file is closed. Destroy the IRP. */
4025 rdpdr_server_irp_free(irp);
4026 return CHANNEL_RC_OK;
4027}
4028
4029WINPR_ATTR_NODISCARD
4030static UINT rdpdr_server_drive_set_information_callback2(RdpdrServerContext* context, wStream* s,
4031 RDPDR_IRP* irp, UINT32 deviceId,
4032 UINT32 completionId, UINT32 ioStatus)
4033{
4034 WINPR_ASSERT(context);
4035 WINPR_ASSERT(context->priv);
4036 WINPR_ASSERT(irp);
4037 WINPR_UNUSED(completionId);
4038
4039 RdpdrServerPrivate* priv = context->priv;
4040
4041 /* [MS-RDPEFS] 2.2.3.4.9 DR_DRIVE_SET_INFORMATION_RSP: Length, optional padding */
4042 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
4043 return ERROR_INVALID_DATA;
4044 Stream_Seek(s, 4); /* Length (4 bytes) */
4045
4046 IFCALL(context->OnDriveSetFileInformationComplete, context, irp->CallbackData, ioStatus);
4047
4048 /* Close the file */
4049 irp->CompletionId = rdpdr_server_next_completion_id(priv);
4050 irp->Callback = rdpdr_server_drive_set_information_callback3;
4051 irp->DeviceId = deviceId;
4052
4053 if (!rdpdr_server_enqueue_irp(context, irp))
4054 {
4055 rdpdr_server_irp_free(irp);
4056 return ERROR_INTERNAL_ERROR;
4057 }
4058
4059 return rdpdr_server_send_device_close_request(context, irp);
4060}
4061
4062WINPR_ATTR_NODISCARD
4063static UINT rdpdr_server_drive_set_information_callback1(RdpdrServerContext* context, wStream* s,
4064 RDPDR_IRP* irp, UINT32 deviceId,
4065 UINT32 completionId, UINT32 ioStatus)
4066{
4067 WINPR_ASSERT(context);
4068 WINPR_ASSERT(context->priv);
4069 WINPR_ASSERT(irp);
4070 WINPR_UNUSED(completionId);
4071
4072 RdpdrServerPrivate* priv = context->priv;
4073
4074 if (ioStatus != STATUS_SUCCESS)
4075 {
4076 IFCALL(context->OnDriveSetFileInformationComplete, context, irp->CallbackData, ioStatus);
4077 rdpdr_server_irp_free(irp);
4078 return CHANNEL_RC_OK;
4079 }
4080
4081 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
4082 return ERROR_INVALID_DATA;
4083
4084 irp->FileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */
4085 irp->CompletionId = rdpdr_server_next_completion_id(priv);
4086 irp->Callback = rdpdr_server_drive_set_information_callback2;
4087 irp->DeviceId = deviceId;
4088
4089 if (!rdpdr_server_enqueue_irp(context, irp))
4090 {
4091 rdpdr_server_irp_free(irp);
4092 return ERROR_INTERNAL_ERROR;
4093 }
4094
4095 return rdpdr_server_send_device_set_information_request(context, irp, irp->InfoClass,
4096 irp->InfoBuffer, irp->InfoLength);
4097}
4098
4099/* Windows clients check the access of the handle, and do not map generic rights */
4100WINPR_ATTR_NODISCARD
4101static UINT32 rdpdr_server_set_information_access(UINT32 fsInformationClass)
4102{
4103 switch (fsInformationClass)
4104 {
4105 case FileBasicInformation:
4106 return FILE_WRITE_ATTRIBUTES;
4107 case FileEndOfFileInformation:
4108 case FileAllocationInformation:
4109 return FILE_WRITE_DATA;
4110 case FileDispositionInformation:
4111 case FileRenameInformation:
4112 return DELETE;
4113 default:
4114 return FILE_GENERIC_WRITE;
4115 }
4116}
4117
4118WINPR_ATTR_NODISCARD
4119static UINT rdpdr_server_drive_set_information(RdpdrServerContext* context, void* callbackData,
4120 UINT32 deviceId, const char* path,
4121 UINT32 fsInformationClass, const BYTE* buffer,
4122 UINT32 length)
4123{
4124 WINPR_ASSERT(context);
4125 WINPR_ASSERT(path);
4126 WINPR_ASSERT(buffer);
4127
4128 RDPDR_IRP* irp = nullptr;
4129 if (length > sizeof(irp->InfoBuffer))
4130 return ERROR_INVALID_PARAMETER;
4131
4132 UINT ret = prepare_path_irp(context, deviceId, rdpdr_server_drive_set_information_callback1,
4133 callbackData, path, nullptr, &irp);
4134 if (ret != CHANNEL_RC_OK)
4135 return ret;
4136
4137 irp->InfoClass = fsInformationClass;
4138 irp->InfoLength = length;
4139 memcpy(irp->InfoBuffer, buffer, length);
4140
4141 /* Send a request to open the file or directory. */
4142 return rdpdr_server_send_device_create_request(
4143 context, irp, rdpdr_server_set_information_access(fsInformationClass) | SYNCHRONIZE,
4144 FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN);
4145}
4146
4147WINPR_ATTR_NODISCARD
4148static UINT rdpdr_server_drive_set_file_size(RdpdrServerContext* context, void* callbackData,
4149 UINT32 deviceId, const char* path, UINT64 size)
4150{
4151 BYTE buffer[8] = WINPR_C_ARRAY_INIT;
4152 wStream sbuffer = WINPR_C_ARRAY_INIT;
4153 wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
4154
4155 Stream_Write_UINT64(s, size); /* EndOfFile (8 bytes) */
4156 return rdpdr_server_drive_set_information(context, callbackData, deviceId, path,
4157 FileEndOfFileInformation, buffer, sizeof(buffer));
4158}
4159
4160WINPR_ATTR_NODISCARD
4161static UINT rdpdr_server_drive_set_file_times(RdpdrServerContext* context, void* callbackData,
4162 UINT32 deviceId, const char* path,
4163 INT64 lastAccessTime, INT64 lastWriteTime)
4164{
4165 BYTE buffer[36] = WINPR_C_ARRAY_INIT;
4166 wStream sbuffer = WINPR_C_ARRAY_INIT;
4167 wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
4168
4169 /* [MS-FSCC] 2.4.7 FileBasicInformation: 0 leaves a time unchanged */
4170 Stream_Write_INT64(s, 0); /* CreationTime (8 bytes) */
4171 Stream_Write_INT64(s, lastAccessTime); /* LastAccessTime (8 bytes) */
4172 Stream_Write_INT64(s, lastWriteTime); /* LastWriteTime (8 bytes) */
4173 Stream_Write_INT64(s, 0); /* ChangeTime (8 bytes) */
4174 Stream_Write_UINT32(s, 0); /* FileAttributes (4 bytes) */
4175 return rdpdr_server_drive_set_information(context, callbackData, deviceId, path,
4176 FileBasicInformation, buffer, sizeof(buffer));
4177}
4178
4179/*************************************************
4180 * Drive Query Volume Information (open, query, close)
4181 ************************************************/
4182
4183WINPR_ATTR_NODISCARD
4184static UINT rdpdr_server_drive_query_volume_information_callback2(RdpdrServerContext* context,
4185 wStream* s, RDPDR_IRP* irp,
4186 UINT32 deviceId,
4187 UINT32 completionId,
4188 UINT32 ioStatus)
4189{
4190 WINPR_ASSERT(context);
4191 WINPR_ASSERT(context->priv);
4192 WINPR_ASSERT(irp);
4193 WINPR_UNUSED(completionId);
4194
4195 RdpdrServerPrivate* priv = context->priv;
4196 UINT64 totalBytes = 0;
4197 UINT64 availableBytes = 0;
4198
4199 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
4200 return ERROR_INVALID_DATA;
4201
4202 const UINT32 length = Stream_Get_UINT32(s); /* Length (4 bytes) */
4203 if ((ioStatus == STATUS_SUCCESS) && (length >= 32))
4204 {
4205 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 32))
4206 return ERROR_INVALID_DATA;
4207
4208 /* [MS-FSCC] 2.5.4 FileFsFullSizeInformation */
4209 const UINT64 totalUnits = Stream_Get_UINT64(s); /* TotalAllocationUnits */
4210 const UINT64 callerUnits = Stream_Get_UINT64(s); /* CallerAvailableAllocationUnits */
4211 Stream_Seek_UINT64(s); /* ActualAvailableAllocationUnits */
4212 const UINT32 sectorsPerUnit = Stream_Get_UINT32(s); /* SectorsPerAllocationUnit */
4213 const UINT32 bytesPerSector = Stream_Get_UINT32(s); /* BytesPerSector */
4214 const UINT64 unitSize = 1ull * sectorsPerUnit * bytesPerSector;
4215
4216 totalBytes = totalUnits * unitSize;
4217 availableBytes = callerUnits * unitSize;
4218 }
4219 else if (ioStatus == STATUS_SUCCESS)
4220 ioStatus = ERROR_INVALID_PARAMETER;
4221
4222 IFCALL(context->OnDriveQueryVolumeInformationComplete, context, irp->CallbackData, ioStatus,
4223 totalBytes, availableBytes);
4224
4225 /* Close the directory */
4226 irp->CompletionId = rdpdr_server_next_completion_id(priv);
4227 irp->Callback = rdpdr_server_drive_set_information_callback3;
4228 irp->DeviceId = deviceId;
4229
4230 if (!rdpdr_server_enqueue_irp(context, irp))
4231 {
4232 rdpdr_server_irp_free(irp);
4233 return ERROR_INTERNAL_ERROR;
4234 }
4235
4236 return rdpdr_server_send_device_close_request(context, irp);
4237}
4238
4239WINPR_ATTR_NODISCARD
4240static UINT rdpdr_server_drive_query_volume_information_callback1(RdpdrServerContext* context,
4241 wStream* s, RDPDR_IRP* irp,
4242 UINT32 deviceId,
4243 UINT32 completionId,
4244 UINT32 ioStatus)
4245{
4246 WINPR_ASSERT(context);
4247 WINPR_ASSERT(context->priv);
4248 WINPR_ASSERT(irp);
4249 WINPR_UNUSED(completionId);
4250
4251 RdpdrServerPrivate* priv = context->priv;
4252
4253 if (ioStatus != STATUS_SUCCESS)
4254 {
4255 IFCALL(context->OnDriveQueryVolumeInformationComplete, context, irp->CallbackData, ioStatus,
4256 0, 0);
4257 rdpdr_server_irp_free(irp);
4258 return CHANNEL_RC_OK;
4259 }
4260
4261 if (!Stream_CheckAndLogRequiredLengthWLog(priv->log, s, 4))
4262 return ERROR_INVALID_DATA;
4263
4264 irp->FileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */
4265 irp->CompletionId = rdpdr_server_next_completion_id(priv);
4266 irp->Callback = rdpdr_server_drive_query_volume_information_callback2;
4267 irp->DeviceId = deviceId;
4268
4269 if (!rdpdr_server_enqueue_irp(context, irp))
4270 {
4271 rdpdr_server_irp_free(irp);
4272 return ERROR_INTERNAL_ERROR;
4273 }
4274
4275 return rdpdr_server_send_device_query_volume_information_request(context, irp,
4276 FileFsFullSizeInformation);
4277}
4278
4279WINPR_ATTR_NODISCARD
4280static UINT rdpdr_server_drive_query_volume_information(RdpdrServerContext* context,
4281 void* callbackData, UINT32 deviceId)
4282{
4283 RDPDR_IRP* irp = nullptr;
4284 /* Open the root directory of the drive */
4285 UINT ret =
4286 prepare_path_irp(context, deviceId, rdpdr_server_drive_query_volume_information_callback1,
4287 callbackData, "\\", nullptr, &irp);
4288 if (ret != CHANNEL_RC_OK)
4289 return ret;
4290
4291 return rdpdr_server_send_device_create_request(
4292 context, irp, FILE_READ_DATA | SYNCHRONIZE,
4293 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN);
4294}
4295
4296static void rdpdr_server_private_free(RdpdrServerPrivate* ctx)
4297{
4298 if (!ctx)
4299 return;
4300 ListDictionary_Free(ctx->IrpList);
4301 HashTable_Free(ctx->devicelist);
4302 free(ctx->ClientComputerName);
4303 free(ctx);
4304}
4305
4306#define TAG CHANNELS_TAG("rdpdr.server")
4307WINPR_ATTR_MALLOC(rdpdr_server_private_free, 1)
4308static RdpdrServerPrivate* rdpdr_server_private_new(void)
4309{
4310 RdpdrServerPrivate* priv = (RdpdrServerPrivate*)calloc(1, sizeof(RdpdrServerPrivate));
4311
4312 if (!priv)
4313 goto fail;
4314
4315 priv->log = WLog_Get(TAG);
4316 priv->VersionMajor = RDPDR_VERSION_MAJOR;
4317 priv->VersionMinor = RDPDR_VERSION_MINOR_RDP6X;
4318 priv->ClientId = g_ClientId++;
4319 priv->UserLoggedOnPdu = TRUE;
4320 priv->NextCompletionId = 1;
4321 priv->haveSmartcardDevice = FALSE;
4322 priv->smartcardDeviceId = 0;
4323 priv->IrpList = ListDictionary_New(TRUE);
4324
4325 if (!priv->IrpList)
4326 goto fail;
4327
4328 ListDictionary_ValueObject(priv->IrpList)->fnObjectFree = rdpdr_server_list_free;
4329
4330 priv->devicelist = HashTable_New(FALSE);
4331 if (!priv->devicelist)
4332 goto fail;
4333
4334 if (!HashTable_SetHashFunction(priv->devicelist, rdpdr_deviceid_hash))
4335 goto fail;
4336
4337 {
4338 wObject* obj = HashTable_ValueObject(priv->devicelist);
4339 WINPR_ASSERT(obj);
4340 obj->fnObjectFree = rdpdr_device_free_h;
4341 obj->fnObjectNew = rdpdr_device_clone;
4342 }
4343
4344 {
4345 wObject* obj = HashTable_KeyObject(priv->devicelist);
4346 obj->fnObjectEquals = rdpdr_device_equal;
4347 obj->fnObjectFree = rdpdr_device_key_free;
4348 obj->fnObjectNew = rdpdr_device_key_clone;
4349 }
4350
4351 return priv;
4352fail:
4353 rdpdr_server_private_free(priv);
4354 return nullptr;
4355}
4356
4357WINPR_ATTR_NODISCARD
4358static UINT rdpdr_server_smartcard_establish_context_callback(RdpdrServerContext* context,
4359 wStream* s, RDPDR_IRP* irp,
4360 UINT32 deviceId, UINT32 completionId,
4361 UINT32 ioStatus)
4362{
4363 WINPR_ASSERT(context);
4364 WINPR_ASSERT(context->priv);
4365 WINPR_ASSERT(s);
4366 WINPR_ASSERT(irp);
4367
4368 UINT result = CHANNEL_RC_OK;
4369 RdpdrServerPrivate* priv = context->priv;
4370 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
4371
4372 WLog_Print(priv->log, WLOG_DEBUG,
4373 "RdpdrServerSmartcardEstablishContextCallback: deviceId=%" PRIu32
4374 ", completionId=%" PRIu32 ", ioStatus=0x%" PRIx32 "",
4375 deviceId, completionId, ioStatus);
4376
4377 if (ioStatus != STATUS_SUCCESS)
4378 {
4379 if (context->OnSmartcardEstablishContextComplete)
4380 context->OnSmartcardEstablishContextComplete(context, irp->CallbackData, ioStatus,
4381 SCARD_F_INTERNAL_ERROR, nullptr);
4382 goto out;
4383 }
4384
4385 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
4386 if (rc != SCARD_S_SUCCESS)
4387 {
4388 result = ERROR_INVALID_DATA;
4389 goto out;
4390 }
4391
4392 if (context->OnSmartcardEstablishContextComplete)
4393 context->OnSmartcardEstablishContextComplete(context, irp->CallbackData, ioStatus,
4394 op.returnCode, &op.ret.establishContext);
4395
4396out:
4397 smartcard_operation_free(&op, FALSE);
4398 rdpdr_server_irp_free(irp);
4399 return result;
4400}
4401
4402WINPR_ATTR_NODISCARD
4403static UINT rdpdr_server_smartcard_establish_context(RdpdrServerContext* context,
4404 void* callbackData, UINT32 dwScope,
4405 UINT32* completionId)
4406{
4407 WINPR_ASSERT(context);
4408 WINPR_ASSERT(context->priv);
4409 WINPR_ASSERT(completionId);
4410
4411 RdpdrServerPrivate* priv = context->priv;
4412 RDPDR_IRP* irp = nullptr;
4413
4414 switch (dwScope)
4415 {
4416 case SCARD_SCOPE_USER:
4417 case SCARD_SCOPE_TERMINAL:
4418 case SCARD_SCOPE_SYSTEM:
4419 break;
4420 default:
4421 WLog_Print(priv->log, WLOG_ERROR, "invalid dwScope=0x%08" PRIx32, dwScope);
4422 return ERROR_INVALID_PARAMETER;
4423 }
4424
4425 UINT result = prepare_smartcard_irp(context, SCARD_IOCTL_ESTABLISHCONTEXT,
4426 rdpdr_server_smartcard_establish_context_callback,
4427 callbackData, &irp);
4428 if (result != CHANNEL_RC_OK)
4429 return result;
4430
4431 WINPR_ASSERT(irp);
4432 *completionId = irp->CompletionId;
4433
4434 const SMARTCARD_OPERATION op = {
4435 .ioControlCode = SCARD_IOCTL_ESTABLISHCONTEXT,
4436 .call.establishContext.dwScope = dwScope,
4437 };
4438
4439 wStream* s = Stream_New(nullptr, 64);
4440 if (!s)
4441 {
4442 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
4443 result = CHANNEL_RC_NO_MEMORY;
4444 goto out;
4445 }
4446
4447 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
4448 if (rc != SCARD_S_SUCCESS)
4449 {
4450 result = ERROR_INTERNAL_ERROR;
4451 goto out;
4452 }
4453
4454 result = rdpdr_server_send_device_control_request(
4455 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
4456 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
4457
4458out:
4459 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
4460 if (result != CHANNEL_RC_OK)
4461 rdpdr_server_discard_request(context, irp->CompletionId);
4462 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
4463 if (s)
4464 Stream_Release(s);
4465 return result;
4466}
4467
4468/* ReleaseContext */
4469WINPR_ATTR_NODISCARD
4470static UINT rdpdr_server_smartcard_release_context_callback(RdpdrServerContext* context, wStream* s,
4471 RDPDR_IRP* irp, UINT32 deviceId,
4472 UINT32 completionId, UINT32 ioStatus)
4473{
4474 WINPR_ASSERT(context);
4475 WINPR_ASSERT(context->priv);
4476 WINPR_ASSERT(s);
4477 WINPR_ASSERT(irp);
4478
4479 UINT result = CHANNEL_RC_OK;
4480 RdpdrServerPrivate* priv = context->priv;
4481 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
4482
4483 WLog_Print(priv->log, WLOG_DEBUG,
4484 "SmartcardReleaseContextCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
4485 ", ioStatus=0x%" PRIx32,
4486 deviceId, completionId, ioStatus);
4487
4488 if (ioStatus != STATUS_SUCCESS)
4489 {
4490 if (context->OnSmartcardReleaseContextComplete)
4491 context->OnSmartcardReleaseContextComplete(context, irp->CallbackData, ioStatus,
4492 SCARD_F_INTERNAL_ERROR);
4493 goto out;
4494 }
4495
4496 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
4497 if (rc != SCARD_S_SUCCESS)
4498 {
4499 result = ERROR_INVALID_DATA;
4500 goto out;
4501 }
4502
4503 if (context->OnSmartcardReleaseContextComplete)
4504 context->OnSmartcardReleaseContextComplete(context, irp->CallbackData, ioStatus,
4505 op.returnCode);
4506
4507out:
4508 smartcard_operation_free(&op, FALSE);
4509 rdpdr_server_irp_free(irp);
4510 return result;
4511}
4512
4513WINPR_ATTR_NODISCARD
4514static UINT rdpdr_server_smartcard_release_context(RdpdrServerContext* context, void* callbackData,
4515 const REDIR_SCARDCONTEXT* hContext,
4516 UINT32* completionId)
4517{
4518 WINPR_ASSERT(context);
4519 WINPR_ASSERT(context->priv);
4520 WINPR_ASSERT(hContext);
4521 WINPR_ASSERT(completionId);
4522
4523 RdpdrServerPrivate* priv = context->priv;
4524 RDPDR_IRP* irp = nullptr;
4525
4526 UINT result =
4527 prepare_smartcard_irp(context, SCARD_IOCTL_RELEASECONTEXT,
4528 rdpdr_server_smartcard_release_context_callback, callbackData, &irp);
4529 if (result != CHANNEL_RC_OK)
4530 return result;
4531 WINPR_ASSERT(irp);
4532 *completionId = irp->CompletionId;
4533
4534 const SMARTCARD_OPERATION op = {
4535 .ioControlCode = SCARD_IOCTL_RELEASECONTEXT,
4536 .call.context.handles.hContext = *hContext,
4537 };
4538
4539 wStream* s = Stream_New(nullptr, 64);
4540 if (!s)
4541 {
4542 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
4543 result = CHANNEL_RC_NO_MEMORY;
4544 goto out;
4545 }
4546
4547 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
4548 if (rc != SCARD_S_SUCCESS)
4549 {
4550 result = ERROR_INTERNAL_ERROR;
4551 goto out;
4552 }
4553
4554 result = rdpdr_server_send_device_control_request(
4555 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
4556 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
4557
4558out:
4559 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
4560 if (result != CHANNEL_RC_OK)
4561 rdpdr_server_discard_request(context, irp->CompletionId);
4562 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
4563 if (s)
4564 Stream_Release(s);
4565 return result;
4566}
4567
4568/* IsValidContext */
4569WINPR_ATTR_NODISCARD
4570static UINT rdpdr_server_smartcard_is_valid_context_callback(RdpdrServerContext* context,
4571 wStream* s, RDPDR_IRP* irp,
4572 UINT32 deviceId, UINT32 completionId,
4573 UINT32 ioStatus)
4574{
4575 WINPR_ASSERT(context);
4576 WINPR_ASSERT(context->priv);
4577 WINPR_ASSERT(s);
4578 WINPR_ASSERT(irp);
4579
4580 UINT result = CHANNEL_RC_OK;
4581 RdpdrServerPrivate* priv = context->priv;
4582 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
4583
4584 WLog_Print(priv->log, WLOG_DEBUG,
4585 "SmartcardIsValidContextCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
4586 ", ioStatus=0x%" PRIx32,
4587 deviceId, completionId, ioStatus);
4588
4589 if (ioStatus != STATUS_SUCCESS)
4590 {
4591 if (context->OnSmartcardIsValidContextComplete)
4592 context->OnSmartcardIsValidContextComplete(context, irp->CallbackData, ioStatus,
4593 SCARD_F_INTERNAL_ERROR);
4594 goto out;
4595 }
4596
4597 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
4598 if (rc != SCARD_S_SUCCESS)
4599 {
4600 result = ERROR_INVALID_DATA;
4601 goto out;
4602 }
4603
4604 if (context->OnSmartcardIsValidContextComplete)
4605 context->OnSmartcardIsValidContextComplete(context, irp->CallbackData, ioStatus,
4606 op.returnCode);
4607
4608out:
4609 smartcard_operation_free(&op, FALSE);
4610 rdpdr_server_irp_free(irp);
4611 return result;
4612}
4613
4614WINPR_ATTR_NODISCARD
4615static UINT rdpdr_server_smartcard_is_valid_context(RdpdrServerContext* context, void* callbackData,
4616 const REDIR_SCARDCONTEXT* hContext,
4617 UINT32* completionId)
4618{
4619 WINPR_ASSERT(context);
4620 WINPR_ASSERT(context->priv);
4621 WINPR_ASSERT(hContext);
4622 WINPR_ASSERT(completionId);
4623
4624 RdpdrServerPrivate* priv = context->priv;
4625 RDPDR_IRP* irp = nullptr;
4626
4627 UINT result =
4628 prepare_smartcard_irp(context, SCARD_IOCTL_ISVALIDCONTEXT,
4629 rdpdr_server_smartcard_is_valid_context_callback, callbackData, &irp);
4630 if (result != CHANNEL_RC_OK)
4631 return result;
4632
4633 WINPR_ASSERT(irp);
4634 *completionId = irp->CompletionId;
4635
4636 const SMARTCARD_OPERATION op = {
4637 .ioControlCode = SCARD_IOCTL_ISVALIDCONTEXT,
4638 .call.context.handles.hContext = *hContext,
4639 };
4640
4641 wStream* s = Stream_New(nullptr, 64);
4642 if (!s)
4643 {
4644 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
4645 result = CHANNEL_RC_NO_MEMORY;
4646 goto out;
4647 }
4648
4649 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
4650 if (rc != SCARD_S_SUCCESS)
4651 {
4652 result = ERROR_INTERNAL_ERROR;
4653 goto out;
4654 }
4655
4656 result = rdpdr_server_send_device_control_request(
4657 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
4658 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
4659
4660out:
4661 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
4662 if (result != CHANNEL_RC_OK)
4663 rdpdr_server_discard_request(context, irp->CompletionId);
4664 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
4665 if (s)
4666 Stream_Release(s);
4667 return result;
4668}
4669
4670/* ListReaderGroups */
4671WINPR_ATTR_NODISCARD
4672static UINT rdpdr_server_smartcard_list_reader_groups_callback(RdpdrServerContext* context,
4673 wStream* s, RDPDR_IRP* irp,
4674 UINT32 deviceId, UINT32 completionId,
4675 UINT32 ioStatus)
4676{
4677 WINPR_ASSERT(context);
4678 WINPR_ASSERT(context->priv);
4679 WINPR_ASSERT(s);
4680 WINPR_ASSERT(irp);
4681
4682 UINT result = CHANNEL_RC_OK;
4683 RdpdrServerPrivate* priv = context->priv;
4684 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
4685
4686 WLog_Print(priv->log, WLOG_DEBUG,
4687 "SmartcardListReaderGroupsCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
4688 ", ioStatus=0x%" PRIx32,
4689 deviceId, completionId, ioStatus);
4690
4691 if (ioStatus != STATUS_SUCCESS)
4692 {
4693 if (context->OnSmartcardListReaderGroupsComplete)
4694 context->OnSmartcardListReaderGroupsComplete(context, irp->CallbackData, ioStatus,
4695 SCARD_F_INTERNAL_ERROR, nullptr);
4696 goto out;
4697 }
4698
4699 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
4700 if (rc != SCARD_S_SUCCESS)
4701 {
4702 result = ERROR_INVALID_DATA;
4703 goto out;
4704 }
4705
4706 if (context->OnSmartcardListReaderGroupsComplete)
4707 context->OnSmartcardListReaderGroupsComplete(context, irp->CallbackData, ioStatus,
4708 op.returnCode, &op.ret.listReaders);
4709
4710out:
4711 smartcard_operation_free(&op, FALSE);
4712 rdpdr_server_irp_free(irp);
4713 return result;
4714}
4715
4716WINPR_ATTR_NODISCARD
4717static UINT rdpdr_server_smartcard_list_reader_groups(RdpdrServerContext* context,
4718 void* callbackData, UINT32 ioControlCode,
4719 const ListReaderGroups_Call* call,
4720 UINT32* completionId)
4721{
4722 WINPR_ASSERT(context);
4723 WINPR_ASSERT(context->priv);
4724 WINPR_ASSERT(call);
4725 WINPR_ASSERT(completionId);
4726
4727 RdpdrServerPrivate* priv = context->priv;
4728 RDPDR_IRP* irp = nullptr;
4729
4730 UINT result = prepare_smartcard_irp(context, ioControlCode,
4731 rdpdr_server_smartcard_list_reader_groups_callback,
4732 callbackData, &irp);
4733 if (result != CHANNEL_RC_OK)
4734 return result;
4735
4736 WINPR_ASSERT(irp);
4737 *completionId = irp->CompletionId;
4738
4739 const SMARTCARD_OPERATION op = { .ioControlCode = ioControlCode,
4740 .call.listReaderGroups = {
4741 .handles.hContext = call->handles.hContext,
4742 .fmszGroupsIsNULL = call->fmszGroupsIsNULL,
4743 .cchGroups = call->cchGroups,
4744 } };
4745
4746 wStream* s = Stream_New(nullptr, 32);
4747 if (!s)
4748 {
4749 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
4750 result = CHANNEL_RC_NO_MEMORY;
4751 goto out;
4752 }
4753
4754 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
4755 if (rc != SCARD_S_SUCCESS)
4756 {
4757 result = ERROR_INTERNAL_ERROR;
4758 goto out;
4759 }
4760
4761 result = rdpdr_server_send_device_control_request(
4762 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
4763 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
4764
4765out:
4766 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
4767 if (result != CHANNEL_RC_OK)
4768 rdpdr_server_discard_request(context, irp->CompletionId);
4769 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
4770 if (s)
4771 Stream_Release(s);
4772 return result;
4773}
4774
4775WINPR_ATTR_NODISCARD
4776static UINT rdpdr_server_smartcard_list_reader_groupsA(RdpdrServerContext* context,
4777 void* callbackData,
4778 const ListReaderGroups_Call* call,
4779 UINT32* completionId)
4780{
4781 return rdpdr_server_smartcard_list_reader_groups(
4782 context, callbackData, SCARD_IOCTL_LISTREADERGROUPSA, call, completionId);
4783}
4784
4785WINPR_ATTR_NODISCARD
4786static UINT rdpdr_server_smartcard_list_reader_groupsW(RdpdrServerContext* context,
4787 void* callbackData,
4788 const ListReaderGroups_Call* call,
4789 UINT32* completionId)
4790{
4791 return rdpdr_server_smartcard_list_reader_groups(
4792 context, callbackData, SCARD_IOCTL_LISTREADERGROUPSW, call, completionId);
4793}
4794
4795/* ListReaders */
4796WINPR_ATTR_NODISCARD
4797static UINT rdpdr_server_smartcard_list_readers_callback(RdpdrServerContext* context, wStream* s,
4798 RDPDR_IRP* irp, UINT32 deviceId,
4799 UINT32 completionId, UINT32 ioStatus)
4800{
4801 WINPR_ASSERT(context);
4802 WINPR_ASSERT(context->priv);
4803 WINPR_ASSERT(s);
4804 WINPR_ASSERT(irp);
4805
4806 UINT result = CHANNEL_RC_OK;
4807 RdpdrServerPrivate* priv = context->priv;
4808 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
4809
4810 WLog_Print(priv->log, WLOG_DEBUG,
4811 "SmartcardListReadersCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
4812 ", ioStatus=0x%" PRIx32,
4813 deviceId, completionId, ioStatus);
4814
4815 if (ioStatus != STATUS_SUCCESS)
4816 {
4817 if (context->OnSmartcardListReadersComplete)
4818 context->OnSmartcardListReadersComplete(context, irp->CallbackData, ioStatus,
4819 SCARD_F_INTERNAL_ERROR, nullptr);
4820 goto out;
4821 }
4822
4823 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
4824 if (rc != SCARD_S_SUCCESS)
4825 {
4826 result = ERROR_INVALID_DATA;
4827 goto out;
4828 }
4829
4830 if (context->OnSmartcardListReadersComplete)
4831 context->OnSmartcardListReadersComplete(context, irp->CallbackData, ioStatus, op.returnCode,
4832 &op.ret.listReaders);
4833
4834out:
4835 smartcard_operation_free(&op, FALSE);
4836 rdpdr_server_irp_free(irp);
4837 return result;
4838}
4839
4840WINPR_ATTR_NODISCARD
4841static UINT rdpdr_server_smartcard_list_readers(RdpdrServerContext* context, void* callbackData,
4842 UINT32 ioControlCode, const ListReaders_Call* call,
4843 UINT32* completionId)
4844{
4845 WINPR_ASSERT(context);
4846 WINPR_ASSERT(context->priv);
4847 WINPR_ASSERT(call);
4848 WINPR_ASSERT(completionId);
4849
4850 RdpdrServerPrivate* priv = context->priv;
4851 RDPDR_IRP* irp = nullptr;
4852
4853 UINT result = prepare_smartcard_irp(
4854 context, ioControlCode, rdpdr_server_smartcard_list_readers_callback, callbackData, &irp);
4855 if (result != CHANNEL_RC_OK)
4856 return result;
4857
4858 WINPR_ASSERT(irp);
4859 *completionId = irp->CompletionId;
4860
4861 const SMARTCARD_OPERATION op = { .ioControlCode = ioControlCode,
4862 .call.listReaders = {
4863 .handles.hContext = call->handles.hContext,
4864 .cBytes = call->cBytes,
4865 .mszGroups = call->mszGroups,
4866 .fmszReadersIsNULL = call->fmszReadersIsNULL,
4867 .cchReaders = call->cchReaders,
4868 } };
4869
4870 wStream* s = Stream_New(nullptr, 256);
4871 if (!s)
4872 {
4873 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
4874 result = CHANNEL_RC_NO_MEMORY;
4875 goto out;
4876 }
4877
4878 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
4879 if (rc != SCARD_S_SUCCESS)
4880 {
4881 result = ERROR_INTERNAL_ERROR;
4882 goto out;
4883 }
4884
4885 result = rdpdr_server_send_device_control_request(
4886 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
4887 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
4888
4889out:
4890 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
4891 if (result != CHANNEL_RC_OK)
4892 rdpdr_server_discard_request(context, irp->CompletionId);
4893 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
4894 if (s)
4895 Stream_Release(s);
4896 return result;
4897}
4898
4899WINPR_ATTR_NODISCARD
4900static UINT rdpdr_server_smartcard_list_readersA(RdpdrServerContext* context, void* callbackData,
4901 const ListReaders_Call* call, UINT32* completionId)
4902{
4903 return rdpdr_server_smartcard_list_readers(context, callbackData, SCARD_IOCTL_LISTREADERSA,
4904 call, completionId);
4905}
4906
4907WINPR_ATTR_NODISCARD
4908static UINT rdpdr_server_smartcard_list_readersW(RdpdrServerContext* context, void* callbackData,
4909 const ListReaders_Call* call, UINT32* completionId)
4910{
4911 return rdpdr_server_smartcard_list_readers(context, callbackData, SCARD_IOCTL_LISTREADERSW,
4912 call, completionId);
4913}
4914
4915/* GetStatusChange */
4916WINPR_ATTR_NODISCARD
4917static UINT rdpdr_server_smartcard_get_status_change_callback(RdpdrServerContext* context,
4918 wStream* s, RDPDR_IRP* irp,
4919 UINT32 deviceId, UINT32 completionId,
4920 UINT32 ioStatus)
4921{
4922 WINPR_ASSERT(context);
4923 WINPR_ASSERT(context->priv);
4924 WINPR_ASSERT(s);
4925 WINPR_ASSERT(irp);
4926
4927 UINT result = CHANNEL_RC_OK;
4928 RdpdrServerPrivate* priv = context->priv;
4929 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
4930
4931 WLog_Print(priv->log, WLOG_DEBUG,
4932 "SmartcardGetStatusChangeCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
4933 ", ioStatus=0x%" PRIx32,
4934 deviceId, completionId, ioStatus);
4935
4936 if (ioStatus != STATUS_SUCCESS)
4937 {
4938 if (context->OnSmartcardGetStatusChangeComplete)
4939 context->OnSmartcardGetStatusChangeComplete(context, irp->CallbackData, ioStatus,
4940 SCARD_F_INTERNAL_ERROR, nullptr);
4941 goto out;
4942 }
4943
4944 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
4945 if (rc != SCARD_S_SUCCESS)
4946 {
4947 result = ERROR_INVALID_DATA;
4948 goto out;
4949 }
4950
4951 if (context->OnSmartcardGetStatusChangeComplete)
4952 context->OnSmartcardGetStatusChangeComplete(context, irp->CallbackData, ioStatus,
4953 op.returnCode, &op.ret.getStatusChange);
4954
4955out:
4956 smartcard_operation_free(&op, FALSE);
4957 rdpdr_server_irp_free(irp);
4958 return result;
4959}
4960
4961WINPR_ATTR_NODISCARD
4962static UINT rdpdr_server_smartcard_get_status_change(RdpdrServerContext* context,
4963 void* callbackData, UINT32 ioControlCode,
4964 const void* call, DWORD cReaders,
4965 UINT32* completionId)
4966{
4967 WINPR_ASSERT(context);
4968 WINPR_ASSERT(context->priv);
4969 WINPR_ASSERT(call);
4970 WINPR_ASSERT(completionId);
4971
4972 RdpdrServerPrivate* priv = context->priv;
4973 RDPDR_IRP* irp = nullptr;
4974
4975 UINT result = prepare_smartcard_irp(context, ioControlCode,
4976 rdpdr_server_smartcard_get_status_change_callback,
4977 callbackData, &irp);
4978 if (result != CHANNEL_RC_OK)
4979 return result;
4980
4981 WINPR_ASSERT(irp);
4982 *completionId = irp->CompletionId;
4983
4984 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
4985 op.ioControlCode = ioControlCode;
4986
4987 switch (ioControlCode)
4988 {
4989 case SCARD_IOCTL_GETSTATUSCHANGEA:
4990 op.call.getStatusChangeA = *(const GetStatusChangeA_Call*)call;
4991 break;
4992 case SCARD_IOCTL_GETSTATUSCHANGEW:
4993 op.call.getStatusChangeW = *(const GetStatusChangeW_Call*)call;
4994 break;
4995 default:
4996 WINPR_ASSERT(FALSE);
4997 return ERROR_INVALID_PARAMETER;
4998 }
4999
5000 wStream* s = Stream_New(nullptr, 256 + cReaders * 256);
5001 if (!s)
5002 {
5003 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
5004 result = CHANNEL_RC_NO_MEMORY;
5005 goto out;
5006 }
5007
5008 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
5009 if (rc != SCARD_S_SUCCESS)
5010 {
5011 result = ERROR_INTERNAL_ERROR;
5012 goto out;
5013 }
5014
5015 result = rdpdr_server_send_device_control_request(
5016 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
5017 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
5018
5019out:
5020 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5021 if (result != CHANNEL_RC_OK)
5022 rdpdr_server_discard_request(context, irp->CompletionId);
5023 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5024 if (s)
5025 Stream_Release(s);
5026 return result;
5027}
5028
5029WINPR_ATTR_NODISCARD
5030static UINT rdpdr_server_smartcard_get_status_changeA(RdpdrServerContext* context,
5031 void* callbackData,
5032 const GetStatusChangeA_Call* call,
5033 UINT32* completionId)
5034{
5035 return rdpdr_server_smartcard_get_status_change(
5036 context, callbackData, SCARD_IOCTL_GETSTATUSCHANGEA, call, call->cReaders, completionId);
5037}
5038
5039WINPR_ATTR_NODISCARD
5040static UINT rdpdr_server_smartcard_get_status_changeW(RdpdrServerContext* context,
5041 void* callbackData,
5042 const GetStatusChangeW_Call* call,
5043 UINT32* completionId)
5044{
5045 return rdpdr_server_smartcard_get_status_change(
5046 context, callbackData, SCARD_IOCTL_GETSTATUSCHANGEW, call, call->cReaders, completionId);
5047}
5048
5049/* Cancel */
5050WINPR_ATTR_NODISCARD
5051static UINT rdpdr_server_smartcard_cancel_callback(RdpdrServerContext* context, wStream* s,
5052 RDPDR_IRP* irp, UINT32 deviceId,
5053 UINT32 completionId, UINT32 ioStatus)
5054{
5055 WINPR_ASSERT(context);
5056 WINPR_ASSERT(context->priv);
5057 WINPR_ASSERT(s);
5058 WINPR_ASSERT(irp);
5059
5060 UINT result = CHANNEL_RC_OK;
5061 RdpdrServerPrivate* priv = context->priv;
5062 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
5063
5064 WLog_Print(priv->log, WLOG_DEBUG,
5065 "SmartcardCancelCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
5066 ", ioStatus=0x%" PRIx32,
5067 deviceId, completionId, ioStatus);
5068
5069 if (ioStatus != STATUS_SUCCESS)
5070 {
5071 if (context->OnSmartcardCancelComplete)
5072 context->OnSmartcardCancelComplete(context, irp->CallbackData, ioStatus,
5073 SCARD_F_INTERNAL_ERROR);
5074 goto out;
5075 }
5076
5077 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
5078 if (rc != SCARD_S_SUCCESS)
5079 {
5080 result = ERROR_INVALID_DATA;
5081 goto out;
5082 }
5083
5084 if (context->OnSmartcardCancelComplete)
5085 context->OnSmartcardCancelComplete(context, irp->CallbackData, ioStatus, op.returnCode);
5086
5087out:
5088 smartcard_operation_free(&op, FALSE);
5089 rdpdr_server_irp_free(irp);
5090 return result;
5091}
5092
5093WINPR_ATTR_NODISCARD
5094static UINT rdpdr_server_smartcard_cancel(RdpdrServerContext* context, void* callbackData,
5095 const REDIR_SCARDCONTEXT* hContext, UINT32* completionId)
5096{
5097 WINPR_ASSERT(context);
5098 WINPR_ASSERT(context->priv);
5099 WINPR_ASSERT(hContext);
5100 WINPR_ASSERT(completionId);
5101
5102 RdpdrServerPrivate* priv = context->priv;
5103 RDPDR_IRP* irp = nullptr;
5104
5105 UINT result = prepare_smartcard_irp(context, SCARD_IOCTL_CANCEL,
5106 rdpdr_server_smartcard_cancel_callback, callbackData, &irp);
5107 if (result != CHANNEL_RC_OK)
5108 return result;
5109
5110 WINPR_ASSERT(irp);
5111 *completionId = irp->CompletionId;
5112
5113 const SMARTCARD_OPERATION op = {
5114 .ioControlCode = SCARD_IOCTL_CANCEL,
5115 .call.context.handles.hContext = *hContext,
5116 };
5117
5118 wStream* s = Stream_New(nullptr, 64);
5119 if (!s)
5120 {
5121 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
5122 result = CHANNEL_RC_NO_MEMORY;
5123 goto out;
5124 }
5125
5126 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
5127 if (rc != SCARD_S_SUCCESS)
5128 {
5129 result = ERROR_INTERNAL_ERROR;
5130 goto out;
5131 }
5132
5133 result = rdpdr_server_send_device_control_request(
5134 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
5135 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
5136
5137out:
5138 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5139 if (result != CHANNEL_RC_OK)
5140 rdpdr_server_discard_request(context, irp->CompletionId);
5141 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5142 if (s)
5143 Stream_Release(s);
5144 return result;
5145}
5146
5147/* Connect */
5148WINPR_ATTR_NODISCARD
5149static UINT rdpdr_server_smartcard_connect_callback(RdpdrServerContext* context, wStream* s,
5150 RDPDR_IRP* irp, UINT32 deviceId,
5151 UINT32 completionId, UINT32 ioStatus)
5152{
5153 WINPR_ASSERT(context);
5154 WINPR_ASSERT(context->priv);
5155 WINPR_ASSERT(s);
5156 WINPR_ASSERT(irp);
5157
5158 UINT result = CHANNEL_RC_OK;
5159 RdpdrServerPrivate* priv = context->priv;
5160 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
5161
5162 WLog_Print(priv->log, WLOG_DEBUG,
5163 "SmartcardConnectCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
5164 ", ioStatus=0x%" PRIx32,
5165 deviceId, completionId, ioStatus);
5166
5167 if (ioStatus != STATUS_SUCCESS)
5168 {
5169 if (context->OnSmartcardConnectComplete)
5170 context->OnSmartcardConnectComplete(context, irp->CallbackData, ioStatus,
5171 SCARD_F_INTERNAL_ERROR, nullptr);
5172 goto out;
5173 }
5174
5175 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
5176 if (rc != SCARD_S_SUCCESS)
5177 {
5178 result = ERROR_INVALID_DATA;
5179 goto out;
5180 }
5181
5182 if (context->OnSmartcardConnectComplete)
5183 context->OnSmartcardConnectComplete(context, irp->CallbackData, ioStatus, op.returnCode,
5184 &op.ret.connect);
5185
5186out:
5187 smartcard_operation_free(&op, FALSE);
5188 rdpdr_server_irp_free(irp);
5189 return result;
5190}
5191
5192WINPR_ATTR_NODISCARD
5193static UINT rdpdr_server_smartcard_connect(RdpdrServerContext* context, void* callbackData,
5194 UINT32 ioControlCode, const void* call,
5195 UINT32* completionId)
5196{
5197 WINPR_ASSERT(context);
5198 WINPR_ASSERT(context->priv);
5199 WINPR_ASSERT(call);
5200 WINPR_ASSERT(completionId);
5201
5202 RdpdrServerPrivate* priv = context->priv;
5203 RDPDR_IRP* irp = nullptr;
5204
5205 UINT result = prepare_smartcard_irp(
5206 context, ioControlCode, rdpdr_server_smartcard_connect_callback, callbackData, &irp);
5207 if (result != CHANNEL_RC_OK)
5208 return result;
5209
5210 WINPR_ASSERT(irp);
5211 *completionId = irp->CompletionId;
5212
5213 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
5214 op.ioControlCode = ioControlCode;
5215
5216 switch (ioControlCode)
5217 {
5218 case SCARD_IOCTL_CONNECTA:
5219 op.call.connectA = *(const ConnectA_Call*)call;
5220 break;
5221 case SCARD_IOCTL_CONNECTW:
5222 op.call.connectW = *(const ConnectW_Call*)call;
5223 break;
5224 default:
5225 WINPR_ASSERT(FALSE);
5226 return ERROR_INVALID_PARAMETER;
5227 }
5228
5229 wStream* s = Stream_New(nullptr, 512);
5230 if (!s)
5231 {
5232 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
5233 result = CHANNEL_RC_NO_MEMORY;
5234 goto out;
5235 }
5236
5237 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
5238 if (rc != SCARD_S_SUCCESS)
5239 {
5240 result = ERROR_INTERNAL_ERROR;
5241 goto out;
5242 }
5243
5244 result = rdpdr_server_send_device_control_request(
5245 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
5246 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
5247
5248out:
5249 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5250 if (result != CHANNEL_RC_OK)
5251 rdpdr_server_discard_request(context, irp->CompletionId);
5252 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5253 if (s)
5254 Stream_Release(s);
5255 return result;
5256}
5257
5258WINPR_ATTR_NODISCARD
5259static UINT rdpdr_server_smartcard_connectA(RdpdrServerContext* context, void* callbackData,
5260 const ConnectA_Call* call, UINT32* completionId)
5261{
5262 return rdpdr_server_smartcard_connect(context, callbackData, SCARD_IOCTL_CONNECTA, call,
5263 completionId);
5264}
5265
5266WINPR_ATTR_NODISCARD
5267static UINT rdpdr_server_smartcard_connectW(RdpdrServerContext* context, void* callbackData,
5268 const ConnectW_Call* call, UINT32* completionId)
5269{
5270 return rdpdr_server_smartcard_connect(context, callbackData, SCARD_IOCTL_CONNECTW, call,
5271 completionId);
5272}
5273
5274/* Reconnect */
5275WINPR_ATTR_NODISCARD
5276static UINT rdpdr_server_smartcard_reconnect_callback(RdpdrServerContext* context, wStream* s,
5277 RDPDR_IRP* irp, UINT32 deviceId,
5278 UINT32 completionId, UINT32 ioStatus)
5279{
5280 WINPR_ASSERT(context);
5281 WINPR_ASSERT(context->priv);
5282 WINPR_ASSERT(s);
5283 WINPR_ASSERT(irp);
5284
5285 UINT result = CHANNEL_RC_OK;
5286 RdpdrServerPrivate* priv = context->priv;
5287 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
5288
5289 WLog_Print(priv->log, WLOG_DEBUG,
5290 "SmartcardReconnectCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
5291 ", ioStatus=0x%" PRIx32,
5292 deviceId, completionId, ioStatus);
5293
5294 if (ioStatus != STATUS_SUCCESS)
5295 {
5296 if (context->OnSmartcardReconnectComplete)
5297 context->OnSmartcardReconnectComplete(context, irp->CallbackData, ioStatus,
5298 SCARD_F_INTERNAL_ERROR, nullptr);
5299 goto out;
5300 }
5301
5302 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
5303 if (rc != SCARD_S_SUCCESS)
5304 {
5305 result = ERROR_INVALID_DATA;
5306 goto out;
5307 }
5308
5309 if (context->OnSmartcardReconnectComplete)
5310 context->OnSmartcardReconnectComplete(context, irp->CallbackData, ioStatus, op.returnCode,
5311 &op.ret.reconnect);
5312
5313out:
5314 smartcard_operation_free(&op, FALSE);
5315 rdpdr_server_irp_free(irp);
5316 return result;
5317}
5318
5319WINPR_ATTR_NODISCARD
5320static UINT rdpdr_server_smartcard_reconnect(RdpdrServerContext* context, void* callbackData,
5321 const Reconnect_Call* call, UINT32* completionId)
5322{
5323 WINPR_ASSERT(context);
5324 WINPR_ASSERT(context->priv);
5325 WINPR_ASSERT(call);
5326 WINPR_ASSERT(completionId);
5327
5328 RdpdrServerPrivate* priv = context->priv;
5329 RDPDR_IRP* irp = nullptr;
5330
5331 UINT result =
5332 prepare_smartcard_irp(context, SCARD_IOCTL_RECONNECT,
5333 rdpdr_server_smartcard_reconnect_callback, callbackData, &irp);
5334 if (result != CHANNEL_RC_OK)
5335 return result;
5336
5337 WINPR_ASSERT(irp);
5338 *completionId = irp->CompletionId;
5339
5340 const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_RECONNECT,
5341 .call.reconnect = {
5342 .handles.hContext = call->handles.hContext,
5343 .handles.hCard = call->handles.hCard,
5344 .dwShareMode = call->dwShareMode,
5345 .dwPreferredProtocols = call->dwPreferredProtocols,
5346 .dwInitialization = call->dwInitialization,
5347 } };
5348
5349 wStream* s = Stream_New(nullptr, 128);
5350 if (!s)
5351 {
5352 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
5353 result = CHANNEL_RC_NO_MEMORY;
5354 goto out;
5355 }
5356
5357 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
5358 if (rc != SCARD_S_SUCCESS)
5359 {
5360 result = ERROR_INTERNAL_ERROR;
5361 goto out;
5362 }
5363
5364 result = rdpdr_server_send_device_control_request(
5365 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
5366 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
5367
5368out:
5369 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5370 if (result != CHANNEL_RC_OK)
5371 rdpdr_server_discard_request(context, irp->CompletionId);
5372 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5373 if (s)
5374 Stream_Release(s);
5375 return result;
5376}
5377
5378/* Disconnect */
5379WINPR_ATTR_NODISCARD
5380static UINT rdpdr_server_smartcard_disconnect_callback(RdpdrServerContext* context, wStream* s,
5381 RDPDR_IRP* irp, UINT32 deviceId,
5382 UINT32 completionId, UINT32 ioStatus)
5383{
5384 WINPR_ASSERT(context);
5385 WINPR_ASSERT(context->priv);
5386 WINPR_ASSERT(s);
5387 WINPR_ASSERT(irp);
5388
5389 UINT result = CHANNEL_RC_OK;
5390 RdpdrServerPrivate* priv = context->priv;
5391 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
5392
5393 WLog_Print(priv->log, WLOG_DEBUG,
5394 "SmartcardDisconnectCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
5395 ", ioStatus=0x%" PRIx32,
5396 deviceId, completionId, ioStatus);
5397
5398 if (ioStatus != STATUS_SUCCESS)
5399 {
5400 if (context->OnSmartcardDisconnectComplete)
5401 context->OnSmartcardDisconnectComplete(context, irp->CallbackData, ioStatus,
5402 SCARD_F_INTERNAL_ERROR);
5403 goto out;
5404 }
5405
5406 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
5407 if (rc != SCARD_S_SUCCESS)
5408 {
5409 result = ERROR_INVALID_DATA;
5410 goto out;
5411 }
5412
5413 if (context->OnSmartcardDisconnectComplete)
5414 context->OnSmartcardDisconnectComplete(context, irp->CallbackData, ioStatus, op.returnCode);
5415
5416out:
5417 smartcard_operation_free(&op, FALSE);
5418 rdpdr_server_irp_free(irp);
5419 return result;
5420}
5421
5422WINPR_ATTR_NODISCARD
5423static UINT rdpdr_server_smartcard_disconnect(RdpdrServerContext* context, void* callbackData,
5424 const HCardAndDisposition_Call* call,
5425 UINT32* completionId)
5426{
5427 WINPR_ASSERT(context);
5428 WINPR_ASSERT(context->priv);
5429 WINPR_ASSERT(call);
5430 WINPR_ASSERT(completionId);
5431
5432 RdpdrServerPrivate* priv = context->priv;
5433 RDPDR_IRP* irp = nullptr;
5434
5435 UINT result =
5436 prepare_smartcard_irp(context, SCARD_IOCTL_DISCONNECT,
5437 rdpdr_server_smartcard_disconnect_callback, callbackData, &irp);
5438 if (result != CHANNEL_RC_OK)
5439 return result;
5440
5441 WINPR_ASSERT(irp);
5442 *completionId = irp->CompletionId;
5443
5444 const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_DISCONNECT,
5445 .call.hCardAndDisposition = {
5446 .handles.hContext = call->handles.hContext,
5447 .handles.hCard = call->handles.hCard,
5448 .dwDisposition = call->dwDisposition,
5449 } };
5450
5451 wStream* s = Stream_New(nullptr, 128);
5452 if (!s)
5453 {
5454 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
5455 result = CHANNEL_RC_NO_MEMORY;
5456 goto out;
5457 }
5458
5459 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
5460 if (rc != SCARD_S_SUCCESS)
5461 {
5462 result = ERROR_INTERNAL_ERROR;
5463 goto out;
5464 }
5465
5466 result = rdpdr_server_send_device_control_request(
5467 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
5468 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
5469
5470out:
5471 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5472 if (result != CHANNEL_RC_OK)
5473 rdpdr_server_discard_request(context, irp->CompletionId);
5474 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5475 if (s)
5476 Stream_Release(s);
5477 return result;
5478}
5479
5480/* BeginTransaction */
5481WINPR_ATTR_NODISCARD
5482static UINT rdpdr_server_smartcard_begin_transaction_callback(RdpdrServerContext* context,
5483 wStream* s, RDPDR_IRP* irp,
5484 UINT32 deviceId, UINT32 completionId,
5485 UINT32 ioStatus)
5486{
5487 WINPR_ASSERT(context);
5488 WINPR_ASSERT(context->priv);
5489 WINPR_ASSERT(s);
5490 WINPR_ASSERT(irp);
5491
5492 UINT result = CHANNEL_RC_OK;
5493 RdpdrServerPrivate* priv = context->priv;
5494 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
5495
5496 WLog_Print(priv->log, WLOG_DEBUG,
5497 "SmartcardBeginTransactionCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
5498 ", ioStatus=0x%" PRIx32,
5499 deviceId, completionId, ioStatus);
5500
5501 if (ioStatus != STATUS_SUCCESS)
5502 {
5503 if (context->OnSmartcardBeginTransactionComplete)
5504 context->OnSmartcardBeginTransactionComplete(context, irp->CallbackData, ioStatus,
5505 SCARD_F_INTERNAL_ERROR);
5506 goto out;
5507 }
5508
5509 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
5510 if (rc != SCARD_S_SUCCESS)
5511 {
5512 result = ERROR_INVALID_DATA;
5513 goto out;
5514 }
5515
5516 if (context->OnSmartcardBeginTransactionComplete)
5517 context->OnSmartcardBeginTransactionComplete(context, irp->CallbackData, ioStatus,
5518 op.returnCode);
5519
5520out:
5521 smartcard_operation_free(&op, FALSE);
5522 rdpdr_server_irp_free(irp);
5523 return result;
5524}
5525
5526WINPR_ATTR_NODISCARD
5527static UINT rdpdr_server_smartcard_begin_transaction(RdpdrServerContext* context,
5528 void* callbackData,
5529 const HCardAndDisposition_Call* call,
5530 UINT32* completionId)
5531{
5532 WINPR_ASSERT(context);
5533 WINPR_ASSERT(context->priv);
5534 WINPR_ASSERT(call);
5535 WINPR_ASSERT(completionId);
5536
5537 RdpdrServerPrivate* priv = context->priv;
5538 RDPDR_IRP* irp = nullptr;
5539
5540 UINT result = prepare_smartcard_irp(context, SCARD_IOCTL_BEGINTRANSACTION,
5541 rdpdr_server_smartcard_begin_transaction_callback,
5542 callbackData, &irp);
5543 if (result != CHANNEL_RC_OK)
5544 return result;
5545
5546 WINPR_ASSERT(irp);
5547 *completionId = irp->CompletionId;
5548
5549 const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_BEGINTRANSACTION,
5550 .call.hCardAndDisposition = {
5551 .handles.hContext = call->handles.hContext,
5552 .handles.hCard = call->handles.hCard,
5553 .dwDisposition = SCARD_LEAVE_CARD,
5554 } };
5555
5556 wStream* s = Stream_New(nullptr, 128);
5557 if (!s)
5558 {
5559 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
5560 result = CHANNEL_RC_NO_MEMORY;
5561 goto out;
5562 }
5563
5564 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
5565 if (rc != SCARD_S_SUCCESS)
5566 {
5567 result = ERROR_INTERNAL_ERROR;
5568 goto out;
5569 }
5570
5571 result = rdpdr_server_send_device_control_request(
5572 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
5573 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
5574
5575out:
5576 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5577 if (result != CHANNEL_RC_OK)
5578 rdpdr_server_discard_request(context, irp->CompletionId);
5579 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5580 if (s)
5581 Stream_Release(s);
5582 return result;
5583}
5584
5585/* EndTransaction */
5586WINPR_ATTR_NODISCARD
5587static UINT rdpdr_server_smartcard_end_transaction_callback(RdpdrServerContext* context, wStream* s,
5588 RDPDR_IRP* irp, UINT32 deviceId,
5589 UINT32 completionId, UINT32 ioStatus)
5590{
5591 WINPR_ASSERT(context);
5592 WINPR_ASSERT(context->priv);
5593 WINPR_ASSERT(s);
5594 WINPR_ASSERT(irp);
5595
5596 UINT result = CHANNEL_RC_OK;
5597 RdpdrServerPrivate* priv = context->priv;
5598 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
5599
5600 WLog_Print(priv->log, WLOG_DEBUG,
5601 "SmartcardEndTransactionCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
5602 ", ioStatus=0x%" PRIx32,
5603 deviceId, completionId, ioStatus);
5604
5605 if (ioStatus != STATUS_SUCCESS)
5606 {
5607 if (context->OnSmartcardEndTransactionComplete)
5608 context->OnSmartcardEndTransactionComplete(context, irp->CallbackData, ioStatus,
5609 SCARD_F_INTERNAL_ERROR);
5610 goto out;
5611 }
5612
5613 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
5614 if (rc != SCARD_S_SUCCESS)
5615 {
5616 result = ERROR_INVALID_DATA;
5617 goto out;
5618 }
5619
5620 if (context->OnSmartcardEndTransactionComplete)
5621 context->OnSmartcardEndTransactionComplete(context, irp->CallbackData, ioStatus,
5622 op.returnCode);
5623
5624out:
5625 smartcard_operation_free(&op, FALSE);
5626 rdpdr_server_irp_free(irp);
5627 return result;
5628}
5629
5630WINPR_ATTR_NODISCARD
5631static UINT rdpdr_server_smartcard_end_transaction(RdpdrServerContext* context, void* callbackData,
5632 const HCardAndDisposition_Call* call,
5633 UINT32* completionId)
5634{
5635 WINPR_ASSERT(context);
5636 WINPR_ASSERT(context->priv);
5637 WINPR_ASSERT(call);
5638 WINPR_ASSERT(completionId);
5639
5640 RdpdrServerPrivate* priv = context->priv;
5641 RDPDR_IRP* irp = nullptr;
5642
5643 UINT result =
5644 prepare_smartcard_irp(context, SCARD_IOCTL_ENDTRANSACTION,
5645 rdpdr_server_smartcard_end_transaction_callback, callbackData, &irp);
5646 if (result != CHANNEL_RC_OK)
5647 return result;
5648
5649 WINPR_ASSERT(irp);
5650 *completionId = irp->CompletionId;
5651
5652 const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_ENDTRANSACTION,
5653 .call.hCardAndDisposition = {
5654 .handles.hContext = call->handles.hContext,
5655 .handles.hCard = call->handles.hCard,
5656 .dwDisposition = call->dwDisposition,
5657 } };
5658
5659 wStream* s = Stream_New(nullptr, 128);
5660 if (!s)
5661 {
5662 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
5663 result = CHANNEL_RC_NO_MEMORY;
5664 goto out;
5665 }
5666
5667 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
5668 if (rc != SCARD_S_SUCCESS)
5669 {
5670 result = ERROR_INTERNAL_ERROR;
5671 goto out;
5672 }
5673
5674 result = rdpdr_server_send_device_control_request(
5675 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
5676 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
5677
5678out:
5679 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5680 if (result != CHANNEL_RC_OK)
5681 rdpdr_server_discard_request(context, irp->CompletionId);
5682 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5683 if (s)
5684 Stream_Release(s);
5685 return result;
5686}
5687
5688/* Status */
5689WINPR_ATTR_NODISCARD
5690static UINT rdpdr_server_smartcard_status_callback(RdpdrServerContext* context, wStream* s,
5691 RDPDR_IRP* irp, UINT32 deviceId,
5692 UINT32 completionId, UINT32 ioStatus)
5693{
5694 WINPR_ASSERT(context);
5695 WINPR_ASSERT(context->priv);
5696 WINPR_ASSERT(s);
5697 WINPR_ASSERT(irp);
5698
5699 UINT result = CHANNEL_RC_OK;
5700 RdpdrServerPrivate* priv = context->priv;
5701 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
5702
5703 WLog_Print(priv->log, WLOG_DEBUG,
5704 "SmartcardStatusCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
5705 ", ioStatus=0x%" PRIx32,
5706 deviceId, completionId, ioStatus);
5707
5708 if (ioStatus != STATUS_SUCCESS)
5709 {
5710 if (context->OnSmartcardStatusComplete)
5711 context->OnSmartcardStatusComplete(context, irp->CallbackData, ioStatus,
5712 SCARD_F_INTERNAL_ERROR, nullptr);
5713 goto out;
5714 }
5715
5716 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
5717 if (rc != SCARD_S_SUCCESS)
5718 {
5719 result = ERROR_INVALID_DATA;
5720 goto out;
5721 }
5722
5723 if (context->OnSmartcardStatusComplete)
5724 context->OnSmartcardStatusComplete(context, irp->CallbackData, ioStatus, op.returnCode,
5725 &op.ret.status);
5726
5727out:
5728 smartcard_operation_free(&op, FALSE);
5729 rdpdr_server_irp_free(irp);
5730 return result;
5731}
5732
5733WINPR_ATTR_NODISCARD
5734static UINT rdpdr_server_smartcard_status(RdpdrServerContext* context, void* callbackData,
5735 UINT32 ioControlCode, const Status_Call* call,
5736 UINT32* completionId)
5737{
5738 WINPR_ASSERT(context);
5739 WINPR_ASSERT(context->priv);
5740 WINPR_ASSERT(call);
5741 WINPR_ASSERT(completionId);
5742
5743 RdpdrServerPrivate* priv = context->priv;
5744 RDPDR_IRP* irp = nullptr;
5745
5746 UINT result = prepare_smartcard_irp(context, ioControlCode,
5747 rdpdr_server_smartcard_status_callback, callbackData, &irp);
5748 if (result != CHANNEL_RC_OK)
5749 return result;
5750
5751 WINPR_ASSERT(irp);
5752 *completionId = irp->CompletionId;
5753
5754 const SMARTCARD_OPERATION op = { .ioControlCode = ioControlCode,
5755 .call.status = {
5756 .handles.hContext = call->handles.hContext,
5757 .handles.hCard = call->handles.hCard,
5758 .fmszReaderNamesIsNULL = call->fmszReaderNamesIsNULL,
5759 .cchReaderLen = call->cchReaderLen,
5760 .cbAtrLen = call->cbAtrLen,
5761 } };
5762
5763 wStream* s = Stream_New(nullptr, 128);
5764 if (!s)
5765 {
5766 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
5767 result = CHANNEL_RC_NO_MEMORY;
5768 goto out;
5769 }
5770
5771 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
5772 if (rc != SCARD_S_SUCCESS)
5773 {
5774 result = ERROR_INTERNAL_ERROR;
5775 goto out;
5776 }
5777
5778 result = rdpdr_server_send_device_control_request(
5779 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
5780 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
5781
5782out:
5783 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5784 if (result != CHANNEL_RC_OK)
5785 rdpdr_server_discard_request(context, irp->CompletionId);
5786 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5787 if (s)
5788 Stream_Release(s);
5789 return result;
5790}
5791
5792WINPR_ATTR_NODISCARD
5793static UINT rdpdr_server_smartcard_statusA(RdpdrServerContext* context, void* callbackData,
5794 const Status_Call* call, UINT32* completionId)
5795{
5796 return rdpdr_server_smartcard_status(context, callbackData, SCARD_IOCTL_STATUSA, call,
5797 completionId);
5798}
5799
5800WINPR_ATTR_NODISCARD
5801static UINT rdpdr_server_smartcard_statusW(RdpdrServerContext* context, void* callbackData,
5802 const Status_Call* call, UINT32* completionId)
5803{
5804 return rdpdr_server_smartcard_status(context, callbackData, SCARD_IOCTL_STATUSW, call,
5805 completionId);
5806}
5807
5808/* Transmit */
5809WINPR_ATTR_NODISCARD
5810static UINT rdpdr_server_smartcard_transmit_callback(RdpdrServerContext* context, wStream* s,
5811 RDPDR_IRP* irp, UINT32 deviceId,
5812 UINT32 completionId, UINT32 ioStatus)
5813{
5814 WINPR_ASSERT(context);
5815 WINPR_ASSERT(context->priv);
5816 WINPR_ASSERT(s);
5817 WINPR_ASSERT(irp);
5818
5819 UINT result = CHANNEL_RC_OK;
5820 RdpdrServerPrivate* priv = context->priv;
5821 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
5822
5823 WLog_Print(priv->log, WLOG_DEBUG,
5824 "SmartcardTransmitCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
5825 ", ioStatus=0x%" PRIx32,
5826 deviceId, completionId, ioStatus);
5827
5828 if (ioStatus != STATUS_SUCCESS)
5829 {
5830 if (context->OnSmartcardTransmitComplete)
5831 context->OnSmartcardTransmitComplete(context, irp->CallbackData, ioStatus,
5832 SCARD_F_INTERNAL_ERROR, nullptr);
5833 goto out;
5834 }
5835
5836 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
5837 if (rc != SCARD_S_SUCCESS)
5838 {
5839 result = ERROR_INVALID_DATA;
5840 goto out;
5841 }
5842
5843 if (context->OnSmartcardTransmitComplete)
5844 context->OnSmartcardTransmitComplete(context, irp->CallbackData, ioStatus, op.returnCode,
5845 &op.ret.transmit);
5846
5847out:
5848 smartcard_operation_free(&op, FALSE);
5849 rdpdr_server_irp_free(irp);
5850 return result;
5851}
5852
5853WINPR_ATTR_NODISCARD
5854static UINT rdpdr_server_smartcard_transmit(RdpdrServerContext* context, void* callbackData,
5855 const Transmit_Call* call, UINT32* completionId)
5856{
5857 WINPR_ASSERT(context);
5858 WINPR_ASSERT(context->priv);
5859 WINPR_ASSERT(call);
5860 WINPR_ASSERT(completionId);
5861
5862 RdpdrServerPrivate* priv = context->priv;
5863 RDPDR_IRP* irp = nullptr;
5864
5865 UINT result =
5866 prepare_smartcard_irp(context, SCARD_IOCTL_TRANSMIT,
5867 rdpdr_server_smartcard_transmit_callback, callbackData, &irp);
5868 if (result != CHANNEL_RC_OK)
5869 return result;
5870
5871 WINPR_ASSERT(irp);
5872 *completionId = irp->CompletionId;
5873
5874 const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_TRANSMIT,
5875 .call.transmit = *call };
5876
5877 wStream* s = Stream_New(nullptr, 512 + call->cbSendLength);
5878 if (!s)
5879 {
5880 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
5881 result = CHANNEL_RC_NO_MEMORY;
5882 goto out;
5883 }
5884
5885 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
5886 if (rc != SCARD_S_SUCCESS)
5887 {
5888 result = ERROR_INTERNAL_ERROR;
5889 goto out;
5890 }
5891
5892 result = rdpdr_server_send_device_control_request(
5893 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
5894 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
5895
5896out:
5897 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5898 if (result != CHANNEL_RC_OK)
5899 rdpdr_server_discard_request(context, irp->CompletionId);
5900 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
5901 if (s)
5902 Stream_Release(s);
5903 return result;
5904}
5905
5906/* Control */
5907WINPR_ATTR_NODISCARD
5908static UINT rdpdr_server_smartcard_control_callback(RdpdrServerContext* context, wStream* s,
5909 RDPDR_IRP* irp, UINT32 deviceId,
5910 UINT32 completionId, UINT32 ioStatus)
5911{
5912 WINPR_ASSERT(context);
5913 WINPR_ASSERT(context->priv);
5914 WINPR_ASSERT(s);
5915 WINPR_ASSERT(irp);
5916
5917 UINT result = CHANNEL_RC_OK;
5918 RdpdrServerPrivate* priv = context->priv;
5919 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
5920
5921 WLog_Print(priv->log, WLOG_DEBUG,
5922 "SmartcardControlCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
5923 ", ioStatus=0x%" PRIx32,
5924 deviceId, completionId, ioStatus);
5925
5926 if (ioStatus != STATUS_SUCCESS)
5927 {
5928 if (context->OnSmartcardControlComplete)
5929 context->OnSmartcardControlComplete(context, irp->CallbackData, ioStatus,
5930 SCARD_F_INTERNAL_ERROR, nullptr);
5931 goto out;
5932 }
5933
5934 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
5935 if (rc != SCARD_S_SUCCESS)
5936 {
5937 result = ERROR_INVALID_DATA;
5938 goto out;
5939 }
5940
5941 if (context->OnSmartcardControlComplete)
5942 context->OnSmartcardControlComplete(context, irp->CallbackData, ioStatus, op.returnCode,
5943 &op.ret.control);
5944
5945out:
5946 smartcard_operation_free(&op, FALSE);
5947 rdpdr_server_irp_free(irp);
5948 return result;
5949}
5950
5951WINPR_ATTR_NODISCARD
5952static UINT rdpdr_server_smartcard_control(RdpdrServerContext* context, void* callbackData,
5953 const Control_Call* call, UINT32* completionId)
5954{
5955 WINPR_ASSERT(context);
5956 WINPR_ASSERT(context->priv);
5957 WINPR_ASSERT(call);
5958 WINPR_ASSERT(completionId);
5959
5960 RdpdrServerPrivate* priv = context->priv;
5961 RDPDR_IRP* irp = nullptr;
5962
5963 UINT result = prepare_smartcard_irp(
5964 context, SCARD_IOCTL_CONTROL, rdpdr_server_smartcard_control_callback, callbackData, &irp);
5965 if (result != CHANNEL_RC_OK)
5966 return result;
5967
5968 WINPR_ASSERT(irp);
5969 *completionId = irp->CompletionId;
5970
5971 const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_CONTROL,
5972 .call.control = {
5973 .handles.hContext = call->handles.hContext,
5974 .handles.hCard = call->handles.hCard,
5975 .dwControlCode = call->dwControlCode,
5976 .cbInBufferSize = call->cbInBufferSize,
5977 .pvInBuffer = call->pvInBuffer,
5978 .fpvOutBufferIsNULL = call->fpvOutBufferIsNULL,
5979 .cbOutBufferSize = call->cbOutBufferSize,
5980 } };
5981
5982 wStream* s = Stream_New(nullptr, 512 + call->cbOutBufferSize);
5983 if (!s)
5984 {
5985 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
5986 result = CHANNEL_RC_NO_MEMORY;
5987 goto out;
5988 }
5989
5990 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
5991 if (rc != SCARD_S_SUCCESS)
5992 {
5993 result = ERROR_INTERNAL_ERROR;
5994 goto out;
5995 }
5996
5997 result = rdpdr_server_send_device_control_request(
5998 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
5999 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
6000
6001out:
6002 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
6003 if (result != CHANNEL_RC_OK)
6004 rdpdr_server_discard_request(context, irp->CompletionId);
6005 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
6006 if (s)
6007 Stream_Release(s);
6008 return result;
6009}
6010
6011/* getAttrib */
6012WINPR_ATTR_NODISCARD
6013static UINT rdpdr_server_smartcard_get_attrib_callback(RdpdrServerContext* context, wStream* s,
6014 RDPDR_IRP* irp, UINT32 deviceId,
6015 UINT32 completionId, UINT32 ioStatus)
6016{
6017 WINPR_ASSERT(context);
6018 WINPR_ASSERT(context->priv);
6019 WINPR_ASSERT(s);
6020 WINPR_ASSERT(irp);
6021
6022 UINT result = CHANNEL_RC_OK;
6023 RdpdrServerPrivate* priv = context->priv;
6024 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
6025
6026 WLog_Print(priv->log, WLOG_DEBUG,
6027 "SmartcardGetAttribCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
6028 ", ioStatus=0x%" PRIx32,
6029 deviceId, completionId, ioStatus);
6030
6031 if (ioStatus != STATUS_SUCCESS)
6032 {
6033 if (context->OnSmartcardGetAttribComplete)
6034 context->OnSmartcardGetAttribComplete(context, irp->CallbackData, ioStatus,
6035 SCARD_F_INTERNAL_ERROR, nullptr);
6036 goto out;
6037 }
6038
6039 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
6040 if (rc != SCARD_S_SUCCESS)
6041 {
6042 result = ERROR_INVALID_DATA;
6043 goto out;
6044 }
6045
6046 if (context->OnSmartcardGetAttribComplete)
6047 context->OnSmartcardGetAttribComplete(context, irp->CallbackData, ioStatus, op.returnCode,
6048 &op.ret.getAttrib);
6049
6050out:
6051 smartcard_operation_free(&op, FALSE);
6052 rdpdr_server_irp_free(irp);
6053 return result;
6054}
6055
6056WINPR_ATTR_NODISCARD
6057static UINT rdpdr_server_smartcard_get_attrib(RdpdrServerContext* context, void* callbackData,
6058 const GetAttrib_Call* call, UINT32* completionId)
6059{
6060 WINPR_ASSERT(context);
6061 WINPR_ASSERT(context->priv);
6062 WINPR_ASSERT(call);
6063 WINPR_ASSERT(completionId);
6064
6065 RdpdrServerPrivate* priv = context->priv;
6066
6067 RDPDR_IRP* irp = nullptr;
6068 const UINT ret =
6069 prepare_smartcard_irp(context, SCARD_IOCTL_GETATTRIB,
6070 rdpdr_server_smartcard_get_attrib_callback, callbackData, &irp);
6071 if (ret != CHANNEL_RC_OK)
6072 return ret;
6073
6074 WINPR_ASSERT(irp);
6075 *completionId = irp->CompletionId;
6076
6077 const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_GETATTRIB,
6078 .call.getAttrib = *call };
6079
6080 UINT error = CHANNEL_RC_NO_MEMORY;
6081 wStream* s = Stream_New(nullptr, 64);
6082 if (!s)
6083 {
6084 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
6085 goto out;
6086 }
6087
6088 LONG status = smartcard_irp_device_control_encode_request(s, &op);
6089 if (status != SCARD_S_SUCCESS)
6090 {
6091 error = ERROR_INTERNAL_ERROR;
6092 goto out;
6093 }
6094
6095 error = rdpdr_server_send_device_control_request(
6096 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
6097 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
6098
6099out:
6100 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
6101 if (error != CHANNEL_RC_OK && irp)
6102 rdpdr_server_discard_request(context, irp->CompletionId);
6103 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
6104 if (s)
6105 Stream_Release(s);
6106 return error;
6107}
6108
6109/* SetAttrib */
6110WINPR_ATTR_NODISCARD
6111static UINT rdpdr_server_smartcard_set_attrib_callback(RdpdrServerContext* context, wStream* s,
6112 RDPDR_IRP* irp, UINT32 deviceId,
6113 UINT32 completionId, UINT32 ioStatus)
6114{
6115 WINPR_ASSERT(context);
6116 WINPR_ASSERT(context->priv);
6117 WINPR_ASSERT(s);
6118 WINPR_ASSERT(irp);
6119
6120 UINT result = CHANNEL_RC_OK;
6121 RdpdrServerPrivate* priv = context->priv;
6122 SMARTCARD_OPERATION op = WINPR_C_ARRAY_INIT;
6123
6124 WLog_Print(priv->log, WLOG_DEBUG,
6125 "SmartcardSetAttribCallback: deviceId=%" PRIu32 ", completionId=%" PRIu32
6126 ", ioStatus=0x%" PRIx32,
6127 deviceId, completionId, ioStatus);
6128
6129 if (ioStatus != STATUS_SUCCESS)
6130 {
6131 if (context->OnSmartcardSetAttribComplete)
6132 context->OnSmartcardSetAttribComplete(context, irp->CallbackData, ioStatus,
6133 SCARD_F_INTERNAL_ERROR);
6134 goto out;
6135 }
6136
6137 const LONG rc = smartcard_irp_device_control_decode_response(s, irp->IoControlCode, &op);
6138 if (rc != SCARD_S_SUCCESS)
6139 {
6140 result = ERROR_INVALID_DATA;
6141 goto out;
6142 }
6143
6144 if (context->OnSmartcardSetAttribComplete)
6145 context->OnSmartcardSetAttribComplete(context, irp->CallbackData, ioStatus, op.returnCode);
6146
6147out:
6148 smartcard_operation_free(&op, FALSE);
6149 rdpdr_server_irp_free(irp);
6150 return result;
6151}
6152
6153WINPR_ATTR_NODISCARD
6154static UINT rdpdr_server_smartcard_set_attrib(RdpdrServerContext* context, void* callbackData,
6155 const SetAttrib_Call* call, UINT32* completionId)
6156{
6157 WINPR_ASSERT(context);
6158 WINPR_ASSERT(context->priv);
6159 WINPR_ASSERT(call);
6160 WINPR_ASSERT(completionId);
6161
6162 RdpdrServerPrivate* priv = context->priv;
6163 RDPDR_IRP* irp = nullptr;
6164
6165 UINT result =
6166 prepare_smartcard_irp(context, SCARD_IOCTL_SETATTRIB,
6167 rdpdr_server_smartcard_set_attrib_callback, callbackData, &irp);
6168 if (result != CHANNEL_RC_OK)
6169 return result;
6170
6171 WINPR_ASSERT(irp);
6172 *completionId = irp->CompletionId;
6173
6174 const SMARTCARD_OPERATION op = { .ioControlCode = SCARD_IOCTL_SETATTRIB,
6175 .call.setAttrib = {
6176 .handles.hContext = call->handles.hContext,
6177 .handles.hCard = call->handles.hCard,
6178 .dwAttrId = call->dwAttrId,
6179 .cbAttrLen = call->cbAttrLen,
6180 .pbAttr = call->pbAttr,
6181 } };
6182
6183 wStream* s = Stream_New(nullptr, 64 + call->cbAttrLen);
6184 if (!s)
6185 {
6186 WLog_Print(priv->log, WLOG_ERROR, "Stream_New failed!");
6187 result = CHANNEL_RC_NO_MEMORY;
6188 goto out;
6189 }
6190
6191 const LONG rc = smartcard_irp_device_control_encode_request(s, &op);
6192 if (rc != SCARD_S_SUCCESS)
6193 {
6194 result = ERROR_INTERNAL_ERROR;
6195 goto out;
6196 }
6197
6198 result = rdpdr_server_send_device_control_request(
6199 context, irp->DeviceId, irp->CompletionId, irp->IoControlCode,
6200 SCARD_IOCTL_MAX_OUTPUT_BUFFER_LENGTH, Stream_Buffer(s), Stream_Length(s));
6201
6202out:
6203 // NOLINTBEGIN(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
6204 if (result != CHANNEL_RC_OK)
6205 rdpdr_server_discard_request(context, irp->CompletionId);
6206 // NOLINTEND(clang-analyzer-unix.Malloc): IRP is removed from context and freed there
6207 if (s)
6208 Stream_Release(s);
6209 return result;
6210}
6211
6212RdpdrServerContext* rdpdr_server_context_new(HANDLE vcm)
6213{
6214 RdpdrServerContext* context = (RdpdrServerContext*)calloc(1, sizeof(RdpdrServerContext));
6215
6216 if (!context)
6217 goto fail;
6218
6219 context->vcm = vcm;
6220 context->Start = rdpdr_server_start;
6221 context->Stop = rdpdr_server_stop;
6222 context->DriveCreateDirectory = rdpdr_server_drive_create_directory;
6223 context->DriveDeleteDirectory = rdpdr_server_drive_delete_directory;
6224 context->DriveQueryDirectory = rdpdr_server_drive_query_directory;
6225 context->DriveOpenFile = rdpdr_server_drive_open_file;
6226 context->DriveReadFile = rdpdr_server_drive_read_file;
6227 context->DriveWriteFile = rdpdr_server_drive_write_file;
6228 context->DriveCloseFile = rdpdr_server_drive_close_file;
6229 context->DriveDeleteFile = rdpdr_server_drive_delete_file;
6230 context->DriveRenameFile = rdpdr_server_drive_rename_file;
6231 context->DriveReadFileEx = rdpdr_server_drive_read_file_ex;
6232 context->DriveWriteFileEx = rdpdr_server_drive_write_file_ex;
6233 context->DriveRenameFileEx = rdpdr_server_drive_rename_file_ex;
6234 context->DriveSetFileSize = rdpdr_server_drive_set_file_size;
6235 context->DriveSetFileTimes = rdpdr_server_drive_set_file_times;
6236 context->DriveQueryVolumeInformation = rdpdr_server_drive_query_volume_information;
6237 context->SmartcardEstablishContext = rdpdr_server_smartcard_establish_context;
6238 context->SmartcardReleaseContext = rdpdr_server_smartcard_release_context;
6239 context->SmartcardIsValidContext = rdpdr_server_smartcard_is_valid_context;
6240 context->SmartcardListReaderGroupsA = rdpdr_server_smartcard_list_reader_groupsA;
6241 context->SmartcardListReaderGroupsW = rdpdr_server_smartcard_list_reader_groupsW;
6242 context->SmartcardListReadersA = rdpdr_server_smartcard_list_readersA;
6243 context->SmartcardListReadersW = rdpdr_server_smartcard_list_readersW;
6244 context->SmartcardGetStatusChangeA = rdpdr_server_smartcard_get_status_changeA;
6245 context->SmartcardGetStatusChangeW = rdpdr_server_smartcard_get_status_changeW;
6246 context->SmartcardCancel = rdpdr_server_smartcard_cancel;
6247 context->SmartcardConnectA = rdpdr_server_smartcard_connectA;
6248 context->SmartcardConnectW = rdpdr_server_smartcard_connectW;
6249 context->SmartcardReconnect = rdpdr_server_smartcard_reconnect;
6250 context->SmartcardDisconnect = rdpdr_server_smartcard_disconnect;
6251 context->SmartcardBeginTransaction = rdpdr_server_smartcard_begin_transaction;
6252 context->SmartcardEndTransaction = rdpdr_server_smartcard_end_transaction;
6253 context->SmartcardStatusA = rdpdr_server_smartcard_statusA;
6254 context->SmartcardStatusW = rdpdr_server_smartcard_statusW;
6255 context->SmartcardTransmit = rdpdr_server_smartcard_transmit;
6256 context->SmartcardControl = rdpdr_server_smartcard_control;
6257 context->SmartcardGetAttrib = rdpdr_server_smartcard_get_attrib;
6258 context->SmartcardSetAttrib = rdpdr_server_smartcard_set_attrib;
6259 context->priv = rdpdr_server_private_new();
6260 if (!context->priv)
6261 goto fail;
6262
6263 /* By default announce everything, the server application can deactivate that later on */
6264 context->supported = UINT16_MAX;
6265
6266 return context;
6267fail:
6268 WINPR_PRAGMA_DIAG_PUSH
6269 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
6270 rdpdr_server_context_free(context);
6271 WINPR_PRAGMA_DIAG_POP
6272 return nullptr;
6273}
6274
6275void rdpdr_server_context_free(RdpdrServerContext* context)
6276{
6277 if (!context)
6278 return;
6279
6280 rdpdr_server_private_free(context->priv);
6281 free(context);
6282}
6283
6284void rdpdr_server_discard_request(RdpdrServerContext* context, UINT32 completionId)
6285{
6286 WINPR_ASSERT(context);
6287
6288 RdpdrServerPrivate* priv = context->priv;
6289 const uintptr_t key = completionId + 1ull;
6290 ListDictionary_Remove(priv->IrpList, (void*)key);
6291}
UINT16 PacketId
UINT16 Component
union SMARTCARD_OPERATION::@41 ret
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59
WINPR_ATTR_NODISCARD OBJECT_EQUALS_FN fnObjectEquals
Definition collections.h:61
WINPR_ATTR_NODISCARD OBJECT_NEW_FN fnObjectNew
Definition collections.h:54