FreeRDP
Loading...
Searching...
No Matches
pf_channel_smartcard.c
1
21#include <winpr/assert.h>
22#include <winpr/string.h>
23
24#include <winpr/smartcard.h>
25#include <winpr/pool.h>
26
27#include <freerdp/server/proxy/proxy_log.h>
28#include <freerdp/emulate/scard/smartcard_emulate.h>
29#include <freerdp/channels/scard.h>
30#include <freerdp/channels/rdpdr.h>
31#include <freerdp/utils/rdpdr_utils.h>
32
33#include <freerdp/utils/smartcard_operations.h>
34#include <freerdp/utils/smartcard_call.h>
35
36#include "pf_channel_smartcard.h"
37#include "pf_channel_rdpdr.h"
38
39#define TAG PROXY_TAG("channel.scard")
40
41#define SCARD_SVC_CHANNEL_NAME "SCARD"
42
43typedef struct
44{
46 scard_call_context* callctx;
47 PTP_POOL ThreadPool;
48 TP_CALLBACK_ENVIRON ThreadPoolEnv;
49 wArrayList* workObjects;
50} pf_channel_client_context;
51
52typedef struct
53{
55 wStream* out;
56 pClientContext* pc;
57 wLog* log;
58 pf_scard_send_fkt_t send_fkt;
59} pf_channel_client_queue_element;
60
61static pf_channel_client_context* scard_get_client_context(pClientContext* pc)
62{
63 pf_channel_client_context* scard = NULL;
64
65 WINPR_ASSERT(pc);
66 WINPR_ASSERT(pc->interceptContextMap);
67
68 scard = HashTable_GetItemValue(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME);
69 if (!scard)
70 WLog_WARN(TAG, "[%s] missing in pc->interceptContextMap", SCARD_SVC_CHANNEL_NAME);
71 return scard;
72}
73
74static BOOL pf_channel_client_write_iostatus(wStream* out, const SMARTCARD_OPERATION* op,
75 NTSTATUS ioStatus)
76{
77 UINT16 component = 0;
78 UINT16 packetid = 0;
79 UINT32 dID = 0;
80 UINT32 cID = 0;
81 size_t pos = 0;
82
83 WINPR_ASSERT(op);
84 WINPR_ASSERT(out);
85
86 pos = Stream_GetPosition(out);
87 Stream_SetPosition(out, 0);
88 if (!Stream_CheckAndLogRequiredLength(TAG, out, 16))
89 return FALSE;
90
91 Stream_Read_UINT16(out, component);
92 Stream_Read_UINT16(out, packetid);
93
94 Stream_Read_UINT32(out, dID);
95 Stream_Read_UINT32(out, cID);
96
97 WINPR_ASSERT(component == RDPDR_CTYP_CORE);
98 WINPR_ASSERT(packetid == PAKID_CORE_DEVICE_IOCOMPLETION);
99 WINPR_ASSERT(dID == op->deviceID);
100 WINPR_ASSERT(cID == op->completionID);
101
102 Stream_Write_INT32(out, ioStatus);
103 Stream_SetPosition(out, pos);
104 return TRUE;
105}
106
107struct thread_arg
108{
109 pf_channel_client_context* scard;
110 pf_channel_client_queue_element* e;
111};
112
113static void queue_free(void* obj);
114static void* queue_copy(const void* obj);
115
116static VOID irp_thread(WINPR_ATTR_UNUSED PTP_CALLBACK_INSTANCE Instance, PVOID Context,
117 PTP_WORK Work)
118{
119 struct thread_arg* arg = Context;
120 pf_channel_client_context* scard = arg->scard;
121 {
122 NTSTATUS ioStatus = 0;
123 LONG rc = smartcard_irp_device_control_call(arg->scard->callctx, arg->e->out, &ioStatus,
124 &arg->e->op);
125 if (rc == CHANNEL_RC_OK)
126 {
127 if (pf_channel_client_write_iostatus(arg->e->out, &arg->e->op, ioStatus))
128 arg->e->send_fkt(arg->e->log, arg->e->pc, arg->e->out);
129 }
130 }
131 queue_free(arg->e);
132 free(arg);
133 ArrayList_Remove(scard->workObjects, Work);
134}
135
136static BOOL start_irp_thread(pf_channel_client_context* scard,
137 const pf_channel_client_queue_element* e)
138{
139 PTP_WORK work = NULL;
140 struct thread_arg* arg = calloc(1, sizeof(struct thread_arg));
141 if (!arg)
142 return FALSE;
143 arg->scard = scard;
144 arg->e = queue_copy(e);
145 if (!arg->e)
146 goto fail;
147
148 work = CreateThreadpoolWork(irp_thread, arg, &scard->ThreadPoolEnv);
149 if (!work)
150 goto fail;
151 ArrayList_Append(scard->workObjects, work);
152 SubmitThreadpoolWork(work);
153
154 return TRUE;
155
156fail:
157 if (arg)
158 queue_free(arg->e);
159 free(arg);
160 return FALSE;
161}
162
163BOOL pf_channel_smartcard_client_handle(wLog* log, pClientContext* pc, wStream* s, wStream* out,
164 pf_scard_send_fkt_t send_fkt)
165{
166 BOOL rc = FALSE;
167 LONG status = 0;
168 UINT32 FileId = 0;
169 UINT32 CompletionId = 0;
170 NTSTATUS ioStatus = 0;
171 pf_channel_client_queue_element e = { 0 };
172 pf_channel_client_context* scard = scard_get_client_context(pc);
173
174 WINPR_ASSERT(log);
175 WINPR_ASSERT(send_fkt);
176 WINPR_ASSERT(s);
177
178 if (!scard)
179 return FALSE;
180
181 e.log = log;
182 e.pc = pc;
183 e.out = out;
184 e.send_fkt = send_fkt;
185
186 /* Skip IRP header */
187 if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
188 return FALSE;
189 else
190 {
191 const uint32_t DeviceId = Stream_Get_UINT32(s); /* DeviceId (4 bytes) */
192 FileId = Stream_Get_UINT32(s); /* FileId (4 bytes) */
193 CompletionId = Stream_Get_UINT32(s); /* CompletionId (4 bytes) */
194 const uint32_t MajorFunction = Stream_Get_UINT32(s); /* MajorFunction (4 bytes) */
195 const uint32_t MinorFunction = Stream_Get_UINT32(s); /* MinorFunction (4 bytes) */
196
197 if (MajorFunction != IRP_MJ_DEVICE_CONTROL)
198 {
199 WLog_WARN(TAG, "[%s] Invalid IRP received, expected %s, got %s [0x%08" PRIx32 "]",
200 SCARD_SVC_CHANNEL_NAME, rdpdr_irp_string(IRP_MJ_DEVICE_CONTROL),
201 rdpdr_irp_string(MajorFunction), MinorFunction);
202 return FALSE;
203 }
204 e.op.completionID = CompletionId;
205 e.op.deviceID = DeviceId;
206
207 if (!rdpdr_write_iocompletion_header(out, DeviceId, CompletionId, 0))
208 return FALSE;
209 }
210
211 status = smartcard_irp_device_control_decode(s, CompletionId, FileId, &e.op);
212 if (status != 0)
213 goto fail;
214
215 switch (e.op.ioControlCode)
216 {
217 case SCARD_IOCTL_LISTREADERGROUPSA:
218 case SCARD_IOCTL_LISTREADERGROUPSW:
219 case SCARD_IOCTL_LISTREADERSA:
220 case SCARD_IOCTL_LISTREADERSW:
221 case SCARD_IOCTL_LOCATECARDSA:
222 case SCARD_IOCTL_LOCATECARDSW:
223 case SCARD_IOCTL_LOCATECARDSBYATRA:
224 case SCARD_IOCTL_LOCATECARDSBYATRW:
225 case SCARD_IOCTL_GETSTATUSCHANGEA:
226 case SCARD_IOCTL_GETSTATUSCHANGEW:
227 case SCARD_IOCTL_CONNECTA:
228 case SCARD_IOCTL_CONNECTW:
229 case SCARD_IOCTL_RECONNECT:
230 case SCARD_IOCTL_DISCONNECT:
231 case SCARD_IOCTL_BEGINTRANSACTION:
232 case SCARD_IOCTL_ENDTRANSACTION:
233 case SCARD_IOCTL_STATE:
234 case SCARD_IOCTL_STATUSA:
235 case SCARD_IOCTL_STATUSW:
236 case SCARD_IOCTL_TRANSMIT:
237 case SCARD_IOCTL_CONTROL:
238 case SCARD_IOCTL_GETATTRIB:
239 case SCARD_IOCTL_SETATTRIB:
240 if (!start_irp_thread(scard, &e))
241 goto fail;
242 return TRUE;
243
244 default:
245 status = smartcard_irp_device_control_call(scard->callctx, out, &ioStatus, &e.op);
246 if (status != 0)
247 goto fail;
248 if (!pf_channel_client_write_iostatus(out, &e.op, ioStatus))
249 goto fail;
250 break;
251 }
252
253 rc = send_fkt(log, pc, out) == CHANNEL_RC_OK;
254
255fail:
256 smartcard_operation_free(&e.op, FALSE);
257 return rc;
258}
259
260BOOL pf_channel_smartcard_server_handle(WINPR_ATTR_UNUSED pServerContext* ps,
261 WINPR_ATTR_UNUSED wStream* s)
262{
263 WLog_ERR(TAG, "TODO: unimplemented");
264 return TRUE;
265}
266
267static void channel_stop_and_wait(pf_channel_client_context* scard, BOOL reset)
268{
269 WINPR_ASSERT(scard);
270 smartcard_call_context_signal_stop(scard->callctx, FALSE);
271
272 while (ArrayList_Count(scard->workObjects) > 0)
273 {
274 PTP_WORK work = ArrayList_GetItem(scard->workObjects, 0);
275 if (!work)
276 continue;
277 WaitForThreadpoolWorkCallbacks(work, TRUE);
278 }
279
280 smartcard_call_context_signal_stop(scard->callctx, reset);
281}
282
283static void pf_channel_scard_client_context_free(InterceptContextMapEntry* base)
284{
285 pf_channel_client_context* entry = (pf_channel_client_context*)base;
286 if (!entry)
287 return;
288
289 /* Set the stop event.
290 * All threads waiting in blocking operations will abort at the next
291 * available polling slot */
292 channel_stop_and_wait(entry, FALSE);
293 ArrayList_Free(entry->workObjects);
294 CloseThreadpool(entry->ThreadPool);
295 DestroyThreadpoolEnvironment(&entry->ThreadPoolEnv);
296
297 smartcard_call_context_free(entry->callctx);
298 free(entry);
299}
300
301static void queue_free(void* obj)
302{
303 pf_channel_client_queue_element* element = obj;
304 if (!element)
305 return;
306 smartcard_operation_free(&element->op, FALSE);
307 Stream_Free(element->out, TRUE);
308 free(element);
309}
310
311static void* queue_copy(const void* obj)
312{
313 const pf_channel_client_queue_element* other = obj;
314 pf_channel_client_queue_element* copy = NULL;
315 if (!other)
316 return NULL;
317 copy = calloc(1, sizeof(pf_channel_client_queue_element));
318 if (!copy)
319 return NULL;
320
321 *copy = *other;
322 copy->out = Stream_New(NULL, Stream_Capacity(other->out));
323 if (!copy->out)
324 goto fail;
325 Stream_Write(copy->out, Stream_Buffer(other->out), Stream_GetPosition(other->out));
326 return copy;
327fail:
328 queue_free(copy);
329 return NULL;
330}
331
332static void work_object_free(void* arg)
333{
334 PTP_WORK work = arg;
335 CloseThreadpoolWork(work);
336}
337
338BOOL pf_channel_smartcard_client_new(pClientContext* pc)
339{
340 pf_channel_client_context* scard = NULL;
341 wObject* obj = NULL;
342
343 WINPR_ASSERT(pc);
344 WINPR_ASSERT(pc->interceptContextMap);
345
346 scard = calloc(1, sizeof(pf_channel_client_context));
347 if (!scard)
348 return FALSE;
349 scard->base.free = pf_channel_scard_client_context_free;
350 scard->callctx = smartcard_call_context_new(pc->context.settings);
351 if (!scard->callctx)
352 goto fail;
353
354 scard->workObjects = ArrayList_New(TRUE);
355 if (!scard->workObjects)
356 goto fail;
357 obj = ArrayList_Object(scard->workObjects);
358 WINPR_ASSERT(obj);
359 obj->fnObjectFree = work_object_free;
360
361 scard->ThreadPool = CreateThreadpool(NULL);
362 if (!scard->ThreadPool)
363 goto fail;
364 InitializeThreadpoolEnvironment(&scard->ThreadPoolEnv);
365 SetThreadpoolCallbackPool(&scard->ThreadPoolEnv, scard->ThreadPool);
366
367 return HashTable_Insert(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME, scard);
368fail:
369 pf_channel_scard_client_context_free(&scard->base);
370 return FALSE;
371}
372
373void pf_channel_smartcard_client_free(pClientContext* pc)
374{
375 WINPR_ASSERT(pc);
376 WINPR_ASSERT(pc->interceptContextMap);
377 HashTable_Remove(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME);
378}
379
380BOOL pf_channel_smartcard_client_emulate(pClientContext* pc)
381{
382 pf_channel_client_context* scard = scard_get_client_context(pc);
383 if (!scard)
384 return FALSE;
385 return smartcard_call_is_configured(scard->callctx);
386}
387
388BOOL pf_channel_smartcard_client_reset(pClientContext* pc)
389{
390 pf_channel_client_context* scard = scard_get_client_context(pc);
391 if (!scard)
392 return TRUE;
393
394 channel_stop_and_wait(scard, TRUE);
395 return TRUE;
396}
This struct contains function pointer to initialize/free objects.
Definition collections.h:57