FreeRDP
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 
43 typedef 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 
52 typedef 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 
61 static 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 
74 static BOOL pf_channel_client_write_iostatus(wStream* out, const SMARTCARD_OPERATION* op,
75  UINT32 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_UINT32(out, ioStatus);
103  Stream_SetPosition(out, pos);
104  return TRUE;
105 }
106 
107 struct thread_arg
108 {
109  pf_channel_client_context* scard;
110  pf_channel_client_queue_element* e;
111 };
112 
113 static void queue_free(void* obj);
114 static void* queue_copy(const void* obj);
115 
116 static VOID irp_thread(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work)
117 {
118  struct thread_arg* arg = Context;
119  pf_channel_client_context* scard = arg->scard;
120  {
121  UINT32 ioStatus = 0;
122  LONG rc = smartcard_irp_device_control_call(arg->scard->callctx, arg->e->out, &ioStatus,
123  &arg->e->op);
124  if (rc == CHANNEL_RC_OK)
125  {
126  if (pf_channel_client_write_iostatus(arg->e->out, &arg->e->op, ioStatus))
127  arg->e->send_fkt(arg->e->log, arg->e->pc, arg->e->out);
128  }
129  }
130  queue_free(arg->e);
131  free(arg);
132  ArrayList_Remove(scard->workObjects, Work);
133 }
134 
135 static BOOL start_irp_thread(pf_channel_client_context* scard,
136  const pf_channel_client_queue_element* e)
137 {
138  PTP_WORK work = NULL;
139  struct thread_arg* arg = calloc(1, sizeof(struct thread_arg));
140  if (!arg)
141  return FALSE;
142  arg->scard = scard;
143  arg->e = queue_copy(e);
144  if (!arg->e)
145  goto fail;
146 
147  work = CreateThreadpoolWork(irp_thread, arg, &scard->ThreadPoolEnv);
148  if (!work)
149  goto fail;
150  ArrayList_Append(scard->workObjects, work);
151  SubmitThreadpoolWork(work);
152 
153  return TRUE;
154 
155 fail:
156  if (arg)
157  queue_free(arg->e);
158  free(arg);
159  return FALSE;
160 }
161 
162 BOOL pf_channel_smartcard_client_handle(wLog* log, pClientContext* pc, wStream* s, wStream* out,
163  pf_scard_send_fkt_t send_fkt)
164 {
165  BOOL rc = FALSE;
166  LONG status = 0;
167  UINT32 FileId = 0;
168  UINT32 CompletionId = 0;
169  UINT32 ioStatus = 0;
170  pf_channel_client_queue_element e = { 0 };
171  pf_channel_client_context* scard = scard_get_client_context(pc);
172 
173  WINPR_ASSERT(log);
174  WINPR_ASSERT(send_fkt);
175  WINPR_ASSERT(s);
176 
177  if (!scard)
178  return FALSE;
179 
180  e.log = log;
181  e.pc = pc;
182  e.out = out;
183  e.send_fkt = send_fkt;
184 
185  /* Skip IRP header */
186  if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
187  return FALSE;
188  else
189  {
190  UINT32 DeviceId = 0;
191  UINT32 MajorFunction = 0;
192  UINT32 MinorFunction = 0;
193 
194  Stream_Read_UINT32(s, DeviceId); /* DeviceId (4 bytes) */
195  Stream_Read_UINT32(s, FileId); /* FileId (4 bytes) */
196  Stream_Read_UINT32(s, CompletionId); /* CompletionId (4 bytes) */
197  Stream_Read_UINT32(s, MajorFunction); /* MajorFunction (4 bytes) */
198  Stream_Read_UINT32(s, MinorFunction); /* MinorFunction (4 bytes) */
199 
200  if (MajorFunction != IRP_MJ_DEVICE_CONTROL)
201  {
202  WLog_WARN(TAG, "[%s] Invalid IRP received, expected %s, got %2", SCARD_SVC_CHANNEL_NAME,
203  rdpdr_irp_string(IRP_MJ_DEVICE_CONTROL), rdpdr_irp_string(MajorFunction));
204  return FALSE;
205  }
206  e.op.completionID = CompletionId;
207  e.op.deviceID = DeviceId;
208 
209  if (!rdpdr_write_iocompletion_header(out, DeviceId, CompletionId, 0))
210  return FALSE;
211  }
212 
213  status = smartcard_irp_device_control_decode(s, CompletionId, FileId, &e.op);
214  if (status != 0)
215  goto fail;
216 
217  switch (e.op.ioControlCode)
218  {
219  case SCARD_IOCTL_LISTREADERGROUPSA:
220  case SCARD_IOCTL_LISTREADERGROUPSW:
221  case SCARD_IOCTL_LISTREADERSA:
222  case SCARD_IOCTL_LISTREADERSW:
223  case SCARD_IOCTL_LOCATECARDSA:
224  case SCARD_IOCTL_LOCATECARDSW:
225  case SCARD_IOCTL_LOCATECARDSBYATRA:
226  case SCARD_IOCTL_LOCATECARDSBYATRW:
227  case SCARD_IOCTL_GETSTATUSCHANGEA:
228  case SCARD_IOCTL_GETSTATUSCHANGEW:
229  case SCARD_IOCTL_CONNECTA:
230  case SCARD_IOCTL_CONNECTW:
231  case SCARD_IOCTL_RECONNECT:
232  case SCARD_IOCTL_DISCONNECT:
233  case SCARD_IOCTL_BEGINTRANSACTION:
234  case SCARD_IOCTL_ENDTRANSACTION:
235  case SCARD_IOCTL_STATE:
236  case SCARD_IOCTL_STATUSA:
237  case SCARD_IOCTL_STATUSW:
238  case SCARD_IOCTL_TRANSMIT:
239  case SCARD_IOCTL_CONTROL:
240  case SCARD_IOCTL_GETATTRIB:
241  case SCARD_IOCTL_SETATTRIB:
242  if (!start_irp_thread(scard, &e))
243  goto fail;
244  return TRUE;
245 
246  default:
247  status = smartcard_irp_device_control_call(scard->callctx, out, &ioStatus, &e.op);
248  if (status != 0)
249  goto fail;
250  if (!pf_channel_client_write_iostatus(out, &e.op, ioStatus))
251  goto fail;
252  break;
253  }
254 
255  rc = send_fkt(log, pc, out) == CHANNEL_RC_OK;
256 
257 fail:
258  smartcard_operation_free(&e.op, FALSE);
259  return rc;
260 }
261 
262 BOOL pf_channel_smartcard_server_handle(pServerContext* ps, wStream* s)
263 {
264  WLog_ERR(TAG, "TODO: unimplemented");
265  return TRUE;
266 }
267 
268 static void channel_stop_and_wait(pf_channel_client_context* scard, BOOL reset)
269 {
270  WINPR_ASSERT(scard);
271  smartcard_call_context_signal_stop(scard->callctx, FALSE);
272 
273  while (ArrayList_Count(scard->workObjects) > 0)
274  {
275  PTP_WORK work = ArrayList_GetItem(scard->workObjects, 0);
276  if (!work)
277  continue;
278  WaitForThreadpoolWorkCallbacks(work, TRUE);
279  }
280 
281  smartcard_call_context_signal_stop(scard->callctx, reset);
282 }
283 
284 static void pf_channel_scard_client_context_free(InterceptContextMapEntry* base)
285 {
286  pf_channel_client_context* entry = (pf_channel_client_context*)base;
287  if (!entry)
288  return;
289 
290  /* Set the stop event.
291  * All threads waiting in blocking operations will abort at the next
292  * available polling slot */
293  channel_stop_and_wait(entry, FALSE);
294  ArrayList_Free(entry->workObjects);
295  CloseThreadpool(entry->ThreadPool);
296  DestroyThreadpoolEnvironment(&entry->ThreadPoolEnv);
297 
298  smartcard_call_context_free(entry->callctx);
299  free(entry);
300 }
301 
302 static void queue_free(void* obj)
303 {
304  pf_channel_client_queue_element* element = obj;
305  if (!element)
306  return;
307  smartcard_operation_free(&element->op, FALSE);
308  Stream_Free(element->out, TRUE);
309  free(element);
310 }
311 
312 static void* queue_copy(const void* obj)
313 {
314  const pf_channel_client_queue_element* other = obj;
315  pf_channel_client_queue_element* copy = NULL;
316  if (!other)
317  return NULL;
318  copy = calloc(1, sizeof(pf_channel_client_queue_element));
319  if (!copy)
320  return NULL;
321 
322  *copy = *other;
323  copy->out = Stream_New(NULL, Stream_Capacity(other->out));
324  if (!copy->out)
325  goto fail;
326  Stream_Write(copy->out, Stream_Buffer(other->out), Stream_GetPosition(other->out));
327  return copy;
328 fail:
329  queue_free(copy);
330  return NULL;
331 }
332 
333 static void work_object_free(void* arg)
334 {
335  PTP_WORK work = arg;
336  CloseThreadpoolWork(work);
337 }
338 
339 BOOL pf_channel_smartcard_client_new(pClientContext* pc)
340 {
341  pf_channel_client_context* scard = NULL;
342  wObject* obj = NULL;
343 
344  WINPR_ASSERT(pc);
345  WINPR_ASSERT(pc->interceptContextMap);
346 
347  scard = calloc(1, sizeof(pf_channel_client_context));
348  if (!scard)
349  return FALSE;
350  scard->base.free = pf_channel_scard_client_context_free;
351  scard->callctx = smartcard_call_context_new(pc->context.settings);
352  if (!scard->callctx)
353  goto fail;
354 
355  scard->workObjects = ArrayList_New(TRUE);
356  if (!scard->workObjects)
357  goto fail;
358  obj = ArrayList_Object(scard->workObjects);
359  WINPR_ASSERT(obj);
360  obj->fnObjectFree = work_object_free;
361 
362  scard->ThreadPool = CreateThreadpool(NULL);
363  if (!scard->ThreadPool)
364  goto fail;
365  InitializeThreadpoolEnvironment(&scard->ThreadPoolEnv);
366  SetThreadpoolCallbackPool(&scard->ThreadPoolEnv, scard->ThreadPool);
367 
368  return HashTable_Insert(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME, scard);
369 fail:
370  pf_channel_scard_client_context_free(&scard->base);
371  return FALSE;
372 }
373 
374 void pf_channel_smartcard_client_free(pClientContext* pc)
375 {
376  WINPR_ASSERT(pc);
377  WINPR_ASSERT(pc->interceptContextMap);
378  HashTable_Remove(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME);
379 }
380 
381 BOOL pf_channel_smartcard_client_emulate(pClientContext* pc)
382 {
383  pf_channel_client_context* scard = scard_get_client_context(pc);
384  if (!scard)
385  return FALSE;
386  return smartcard_call_is_configured(scard->callctx);
387 }
388 
389 BOOL pf_channel_smartcard_client_reset(pClientContext* pc)
390 {
391  pf_channel_client_context* scard = scard_get_client_context(pc);
392  if (!scard)
393  return TRUE;
394 
395  channel_stop_and_wait(scard, TRUE);
396  return TRUE;
397 }
This struct contains function pointer to initialize/free objects.
Definition: collections.h:57