blob: 551db00a1a89c7f3059ca5ee0839e750b0c233b0 [file] [log] [blame]
Harald Welte9fac4962020-02-14 21:01:23 +01001/* (C) 2020 by Harald Welte <laforge@gnumonks.org>
2 *
3 * All Rights Reserved
4 *
5 * SPDX-License-Identifier: GPL-2.0+
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
Harald Welte9fac4962020-02-14 21:01:23 +010017 */
18
19/* This is a remsim-client that provides an IFD_Handler (reader driver)
20 * towards the PC/SC services. This effectively allows any local PC/SC client
21 * application to use a remote smartcard via osmo-remsim.
22 *
23 * In order to use this, you will need an /etc/reader.conf.d/osmo-remsim-client
24 * file with the following content:
25 *
26 * FRIENDLYNAME "osmo-remsim-client"
27 * DEVICENAME 0:0:192.168.11.10:9998
28 * LIBPATH /usr/lib/pcsc/drivers/serial/libifd_remsim_client.so
29 *
30 * Where DEVICENAME has the following format:
31 * [ClientID:[SlotNr:[ServerIp:[ServerPort]]]]
32 *
33 */
34
35#include <errno.h>
36#include <unistd.h>
37#include <pthread.h>
38
39#include <osmocom/core/select.h>
40#include <osmocom/core/application.h>
41extern int osmo_ctx_init(const char *id);
42
43#include "client.h"
44
45/* ensure this current thread has an osmo_ctx and hence can use OTC_GLOBAL and friends */
46static void ensure_osmo_ctx(void)
47{
48 if (!osmo_ctx)
49 osmo_ctx_init("");
50}
51
52/* inter-thread messages between IFD thread and remsim-client thread */
53enum itmsg_type {
54 ITMSG_TYPE_NONE,
55
56 /* card present? */
57 ITMSG_TYPE_CARD_PRES_REQ,
58 ITMSG_TYPE_CARD_PRES_RESP,
59
60 /* obtain ATR */
61 ITMSG_TYPE_ATR_REQ,
62 ITMSG_TYPE_ATR_RESP,
63
64 /* transceive APDU: Send C-APDU, receive R-APDU */
65 ITMSG_TYPE_C_APDU_REQ,
66 ITMSG_TYPE_R_APDU_IND,
67
68 /* power off the card */
69 ITMSG_TYPE_POWER_OFF_REQ,
70 ITMSG_TYPE_POWER_OFF_RESP,
71
72 /* power on the card */
73 ITMSG_TYPE_POWER_ON_REQ,
74 ITMSG_TYPE_POWER_ON_RESP,
75
76 /* reset the card */
77 ITMSG_TYPE_RESET_REQ,
78 ITMSG_TYPE_RESET_RESP,
79};
80
81struct itmsg {
82 enum itmsg_type type;
83 uint16_t status; /* 0 == success */
84 uint16_t len; /* length of 'data' */
85 uint8_t data[0];
86};
87
88/* allocate + initialize msgb-wrapped inter-thread message (struct itmsg) */
89struct msgb *itmsg_alloc(enum itmsg_type type, uint16_t status, const uint8_t *data, uint16_t len)
90{
91 struct msgb *msg = msgb_alloc_c(OTC_GLOBAL, sizeof(struct itmsg)+len, "Tx itmsg");
92 struct itmsg *im;
93
94 if (!msg)
95 return NULL;
96
97 im = (struct itmsg *) msgb_put(msg, sizeof(struct itmsg) + len);
98 im->type = type;
99 im->status = status;
100 im->len = len;
101 if (len)
102 memcpy(im->data, data, len);
103
104 return msg;
105}
106
107/***********************************************************************
108 * remsim_client thread
109 ***********************************************************************/
110
111void __thread *talloc_asn1_ctx;
112
113struct client_thread {
114 /* bankd client runningi inside this thread */
115 struct bankd_client *bc;
116
117 /* inter-thread osmo-fd; communication with IFD/PCSC thread */
118 struct osmo_fd it_ofd;
119 struct llist_head it_msgq;
120
121 /* ATR as received from remsim-bankd */
122 uint8_t atr[ATR_SIZE_MAX];
123 uint8_t atr_len;
124};
125
126/* configuration of client thread; passed in from IFD thread */
127struct client_thread_cfg {
128 const char *name;
129 const char *server_host;
130 int server_port;
131 int client_id;
132 int client_slot;
133 int it_sock_fd;
134};
135
136/* enqueue a msgb (containg 'struct itmsg') towards the IFD-handler thread */
137static void enqueue_to_ifd(struct client_thread *ct, struct msgb *msg)
138{
139 if (!msg)
140 return;
141
142 msgb_enqueue(&ct->it_msgq, msg);
143 ct->it_ofd.when |= OSMO_FD_WRITE;
144}
145
146/***********************************************************************
Harald Welte0e968cc2020-02-22 18:16:16 +0100147 * frontend to remsim-client main FSM code
Harald Welte9fac4962020-02-14 21:01:23 +0100148 ***********************************************************************/
149
Harald Welte0e968cc2020-02-22 18:16:16 +0100150int frontend_request_card_insert(struct bankd_client *bc)
Harald Welte9fac4962020-02-14 21:01:23 +0100151{
Harald Welte0e968cc2020-02-22 18:16:16 +0100152 return 0;
153}
154
Harald Welte7b87ba12021-09-08 21:38:35 +0200155int frontend_request_card_remove(struct bankd_client *bc)
156{
157 return 0;
158}
159
Harald Welte0e968cc2020-02-22 18:16:16 +0100160int frontend_request_sim_remote(struct bankd_client *bc)
161{
162 return 0;
163}
164
Harald Welte7b87ba12021-09-08 21:38:35 +0200165int frontend_request_sim_local(struct bankd_client *bc)
166{
167 return 0;
168}
169
Harald Welte0e968cc2020-02-22 18:16:16 +0100170int frontend_request_modem_reset(struct bankd_client *bc)
171{
172 return 0;
173}
174
175int frontend_handle_card2modem(struct bankd_client *bc, const uint8_t *data, size_t len)
176{
Harald Welte9fac4962020-02-14 21:01:23 +0100177 struct client_thread *ct = bc->data;
178 struct msgb *msg;
179
Harald Welte0e968cc2020-02-22 18:16:16 +0100180 OSMO_ASSERT(data);
Harald Welte9fac4962020-02-14 21:01:23 +0100181
Harald Welte0e968cc2020-02-22 18:16:16 +0100182 DEBUGP(DMAIN, "R-APDU: %s\n", osmo_hexdump(data, len));
Harald Welte9fac4962020-02-14 21:01:23 +0100183 /* enqueue towards IFD thread */
Harald Welte0e968cc2020-02-22 18:16:16 +0100184 msg = itmsg_alloc(ITMSG_TYPE_R_APDU_IND, 0, data, len);
Harald Welte9fac4962020-02-14 21:01:23 +0100185 OSMO_ASSERT(msg);
186 enqueue_to_ifd(ct, msg);
187
188 return 0;
189}
190
Harald Welte0e968cc2020-02-22 18:16:16 +0100191int frontend_handle_set_atr(struct bankd_client *bc, const uint8_t *data, size_t len)
Harald Welte9fac4962020-02-14 21:01:23 +0100192{
193 struct client_thread *ct = bc->data;
Harald Welte9fac4962020-02-14 21:01:23 +0100194 unsigned int atr_len;
195
Harald Welte0e968cc2020-02-22 18:16:16 +0100196 OSMO_ASSERT(data);
Harald Welte9fac4962020-02-14 21:01:23 +0100197
Harald Welte0e968cc2020-02-22 18:16:16 +0100198 DEBUGP(DMAIN, "SET_ATR: %s\n", osmo_hexdump(data, len));
Harald Welte9fac4962020-02-14 21:01:23 +0100199
200 /* store ATR in local data structure until somebody needs it */
Harald Welte0e968cc2020-02-22 18:16:16 +0100201 atr_len = len;
Harald Welte9fac4962020-02-14 21:01:23 +0100202 if (atr_len > sizeof(ct->atr))
203 atr_len = sizeof(ct->atr);
Harald Welte0e968cc2020-02-22 18:16:16 +0100204 memcpy(ct->atr, data, atr_len);
Harald Welte9fac4962020-02-14 21:01:23 +0100205 ct->atr_len = atr_len;
206
Harald Welte9fac4962020-02-14 21:01:23 +0100207 return 0;
208}
209
Harald Welte0e968cc2020-02-22 18:16:16 +0100210int frontend_handle_slot_status(struct bankd_client *bc, const SlotPhysStatus_t *sts)
Harald Welte9fac4962020-02-14 21:01:23 +0100211{
Harald Welte0e968cc2020-02-22 18:16:16 +0100212 return 0;
213}
Harald Welte9fac4962020-02-14 21:01:23 +0100214
Harald Weltee580c932020-05-24 16:03:56 +0200215int frontend_append_script_env(struct bankd_client *bc, char **env, int idx, size_t max_env)
Harald Welte0e968cc2020-02-22 18:16:16 +0100216{
Harald Weltee580c932020-05-24 16:03:56 +0200217 return idx;
Harald Welte9fac4962020-02-14 21:01:23 +0100218}
219
220/***********************************************************************
221 * Incoming command from the user application
222 ***********************************************************************/
223
224/* handle a single msgb-wrapped 'struct itmsg' from the IFD-handler thread */
225static void handle_it_msg(struct client_thread *ct, struct itmsg *itmsg)
226{
227 struct bankd_client *bc = ct->bc;
228 struct msgb *tx = NULL;
229 RsproPDU_t *pdu;
230 BankSlot_t bslot;
231
232 bank_slot2rspro(&bslot, &ct->bc->bankd_slot);
233
234 switch (itmsg->type) {
235 case ITMSG_TYPE_CARD_PRES_REQ:
236 if (bc->bankd_conn.fi->state == 2 /*SRVC_ST_CONNECTED*/)
237 tx = itmsg_alloc(ITMSG_TYPE_CARD_PRES_RESP, 0, NULL, 0);
238 else
239 tx = itmsg_alloc(ITMSG_TYPE_CARD_PRES_RESP, 0xffff, NULL, 0);
240 OSMO_ASSERT(tx);
241 break;
242
243 case ITMSG_TYPE_ATR_REQ:
244 /* respond to IFD */
245 tx = itmsg_alloc(ITMSG_TYPE_ATR_RESP, 0, ct->atr, ct->atr_len);
246 OSMO_ASSERT(tx);
247 break;
248
249 case ITMSG_TYPE_POWER_OFF_REQ:
250 pdu = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot,
251 true, false, false, true);
252 server_conn_send_rspro(&bc->bankd_conn, pdu);
253 /* respond to IFD */
254 tx = itmsg_alloc(ITMSG_TYPE_POWER_OFF_RESP, 0, NULL, 0);
255 OSMO_ASSERT(tx);
256 break;
257
258 case ITMSG_TYPE_POWER_ON_REQ:
259 pdu = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot,
260 false, true, true, true);
261 server_conn_send_rspro(&bc->bankd_conn, pdu);
262 /* respond to IFD */
263 tx = itmsg_alloc(ITMSG_TYPE_POWER_ON_RESP, 0, NULL, 0);
264 OSMO_ASSERT(tx);
265 break;
266
267 case ITMSG_TYPE_RESET_REQ:
268 /* reset the [remote] card */
269 pdu = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot,
270 true, true, true, true);
271 server_conn_send_rspro(&bc->bankd_conn, pdu);
272 /* and take it out of reset again */
273 pdu = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot,
274 false, true, true, true);
275 server_conn_send_rspro(&bc->bankd_conn, pdu);
276 /* respond to IFD */
277 tx = itmsg_alloc(ITMSG_TYPE_RESET_RESP, 0, NULL, 0);
278 OSMO_ASSERT(tx);
279 break;
280 case ITMSG_TYPE_C_APDU_REQ:
281 if (!bc->srv_conn.clslot) {
282 LOGP(DMAIN, LOGL_ERROR, "Cannot send command; no client slot\n");
283 /* FIXME: Response? */
284 return;
285 }
286
287 /* Send CMD APDU to [remote] card */
288 pdu = rspro_gen_TpduModem2Card(bc->srv_conn.clslot, &bslot, itmsg->data, itmsg->len);
289 server_conn_send_rspro(&bc->bankd_conn, pdu);
290 /* response will come in asynchronously */
291 break;
292 default:
293 LOGP(DMAIN, LOGL_ERROR, "Unknown inter-thread msg type %u\n", itmsg->type);
294 break;
295 }
296
297 if (tx)
298 enqueue_to_ifd(ct, tx);
299
300}
301
302/* call-back function for inter-thread socket */
303static int it_sock_fd_cb(struct osmo_fd *ofd, unsigned int what)
304{
305 struct client_thread *ct = ofd->data;
306 int rc;
307
308 if (what & OSMO_FD_READ) {
309 struct msgb *msg = msgb_alloc_c(OTC_GLOBAL, 1024, "Rx it_fd");
310 struct itmsg *itmsg;
311
312 OSMO_ASSERT(msg);
313 rc = read(ofd->fd, msg->tail, msgb_tailroom(msg));
314 if (rc <= 0) {
315 LOGP(DMAIN, LOGL_ERROR, "Error reading from inter-thread fd: %d\n", rc);
316 pthread_exit(NULL);
317 }
318 msgb_put(msg, rc);
319 itmsg = (struct itmsg *) msgb_data(msg);
320 if (msgb_length(msg) < sizeof(*itmsg) ||
321 msgb_length(msg) < sizeof(*itmsg) + itmsg->len) {
322 LOGP(DMAIN, LOGL_ERROR, "Dropping short inter-thread message\n");
323 } else {
324 handle_it_msg(ct, itmsg);
325 }
326 msgb_free(msg);
327 }
328
329 if (what & OSMO_FD_WRITE) {
330 struct msgb *msg = msgb_dequeue(&ct->it_msgq);
331 if (!msg) {
332 /* last message: disable write events */
333 ofd->when &= ~OSMO_FD_WRITE;
334 } else {
335 unsigned int len = msgb_length(msg);
336 rc = write(ofd->fd, msgb_data(msg), len);
337 msgb_free(msg);
338 if (rc < len) {
339 LOGP(DMAIN, LOGL_ERROR, "Short write on inter-thread fd: %d < %d\n",
340 rc, len);
341 }
342 }
343 }
344
345
346 return 0;
347}
348
349/* release all resources allocated by thread */
350static void client_pthread_cleanup(void *arg)
351{
352 struct client_thread *ct = arg;
353
354 LOGP(DMAIN, LOGL_INFO, "Cleaning up remsim-client thread\n");
355 //FIXME remsim_client_destroy(ct->bc);
356 ct->bc = NULL;
357 msgb_queue_free(&ct->it_msgq);
358 osmo_fd_unregister(&ct->it_ofd);
359 close(ct->it_ofd.fd);
360 ct->it_ofd.fd = -1;
361 talloc_free(ct);
362}
363
364/* main function of remsim-client pthread */
365static void *client_pthread_main(void *arg)
366{
367 struct client_thread_cfg *cfg = arg;
Harald Welte0e968cc2020-02-22 18:16:16 +0100368 struct client_config *ccfg;
Harald Welte9fac4962020-02-14 21:01:23 +0100369 struct client_thread *ct;
370 int rc;
371
372 osmo_select_init();
373 rc = osmo_ctx_init("client");
374 OSMO_ASSERT(rc == 0);
375
376 ct = talloc_zero(OTC_GLOBAL, struct client_thread);
377 OSMO_ASSERT(ct);
378
Harald Welte0e968cc2020-02-22 18:16:16 +0100379 ccfg = client_config_init(ct);
380 OSMO_ASSERT(ccfg);
381 osmo_talloc_replace_string(ccfg, &ccfg->server_host, cfg->server_host);
382 if (cfg->server_port >= 0)
383 ccfg->server_port = cfg->server_port;
384 ccfg->client_id = cfg->client_id;
385 ccfg->client_slot = cfg->client_slot;
386
Harald Welte9fac4962020-02-14 21:01:23 +0100387 if (!talloc_asn1_ctx)
388 talloc_asn1_ctx= talloc_named_const(ct, 0, "asn1");
389
Harald Welte0e968cc2020-02-22 18:16:16 +0100390 ct->bc = remsim_client_create(ct, cfg->name, "remsim_ifdhandler", ccfg);
Harald Welte9fac4962020-02-14 21:01:23 +0100391 OSMO_ASSERT(ct->bc);
392 ct->bc->data = ct;
Harald Welte9fac4962020-02-14 21:01:23 +0100393
394 INIT_LLIST_HEAD(&ct->it_msgq);
395 osmo_fd_setup(&ct->it_ofd, cfg->it_sock_fd, OSMO_FD_READ, &it_sock_fd_cb, ct, 0);
396 osmo_fd_register(&ct->it_ofd);
397
398 /* ensure we get properly cleaned up if cancelled */
399 pthread_cleanup_push(client_pthread_cleanup, ct);
400
401 osmo_fsm_inst_dispatch(ct->bc->srv_conn.fi, SRVC_E_ESTABLISH, NULL);
402
403 while (1) {
404 osmo_select_main(0);
405 }
406
407 pthread_cleanup_pop(1);
408 return NULL;
409}
410
411/***********************************************************************
412 * PC/SC ifd_handler API functions
413 ***********************************************************************/
414
415#include <ifdhandler.h>
416#include <debuglog.h>
417
418#include <sys/types.h>
419#include <sys/socket.h>
420
421static const struct value_string ifd_status_names[] = {
422 OSMO_VALUE_STRING(IFD_SUCCESS),
423 OSMO_VALUE_STRING(IFD_ERROR_TAG),
424 OSMO_VALUE_STRING(IFD_ERROR_SET_FAILURE),
425 OSMO_VALUE_STRING(IFD_ERROR_VALUE_READ_ONLY),
426 OSMO_VALUE_STRING(IFD_ERROR_PTS_FAILURE),
427 OSMO_VALUE_STRING(IFD_ERROR_NOT_SUPPORTED),
428 OSMO_VALUE_STRING(IFD_PROTOCOL_NOT_SUPPORTED),
429 OSMO_VALUE_STRING(IFD_ERROR_POWER_ACTION),
430 OSMO_VALUE_STRING(IFD_ERROR_SWALLOW),
431 OSMO_VALUE_STRING(IFD_ERROR_EJECT),
432 OSMO_VALUE_STRING(IFD_ERROR_CONFISCATE),
433 OSMO_VALUE_STRING(IFD_COMMUNICATION_ERROR),
434 OSMO_VALUE_STRING(IFD_RESPONSE_TIMEOUT),
435 OSMO_VALUE_STRING(IFD_NOT_SUPPORTED),
436 OSMO_VALUE_STRING(IFD_ICC_PRESENT),
437 OSMO_VALUE_STRING(IFD_ICC_NOT_PRESENT),
438 OSMO_VALUE_STRING(IFD_NO_SUCH_DEVICE),
439 OSMO_VALUE_STRING(IFD_ERROR_INSUFFICIENT_BUFFER),
440 { 0, NULL }
441};
442
443static const struct value_string ifd_tag_names[] = {
444 OSMO_VALUE_STRING(TAG_IFD_ATR),
445 OSMO_VALUE_STRING(TAG_IFD_SLOTNUM),
446 OSMO_VALUE_STRING(TAG_IFD_SLOT_THREAD_SAFE),
447 OSMO_VALUE_STRING(TAG_IFD_THREAD_SAFE),
448 OSMO_VALUE_STRING(TAG_IFD_SLOTS_NUMBER),
449 OSMO_VALUE_STRING(TAG_IFD_SIMULTANEOUS_ACCESS),
450 OSMO_VALUE_STRING(TAG_IFD_POLLING_THREAD),
451 OSMO_VALUE_STRING(TAG_IFD_POLLING_THREAD_KILLABLE),
452 OSMO_VALUE_STRING(TAG_IFD_STOP_POLLING_THREAD),
453 OSMO_VALUE_STRING(TAG_IFD_POLLING_THREAD_WITH_TIMEOUT),
454 { 0, NULL }
455};
456
457#define LOG_EXIT(Lun, r) \
458 Log4(r == IFD_SUCCESS || r == IFD_ICC_NOT_PRESENT ? PCSC_LOG_DEBUG : PCSC_LOG_ERROR, \
459 "%s(0x%08lx) => %s\n", __func__, Lun, get_value_string(ifd_status_names, r))
460
461#define LOG_EXITF(Lun, r, fmt, args...) \
462 Log5(r == IFD_SUCCESS ? PCSC_LOG_DEBUG : PCSC_LOG_ERROR, \
463 "%s(0x%08lx) "fmt" => %s\n", __func__, Lun, ## args, get_value_string(ifd_status_names, r))
464
465/* IFD side handle for a remsim-client [thread] */
466struct ifd_client {
467 /* the client pthread itself */
468 pthread_t pthread;
469 /* socket to talk to thread */
470 int it_fd;
471 /* configuration passed into the thread */
472 struct client_thread_cfg cfg;
473};
474
475static struct msgb *ifd_xceive_client(struct ifd_client *ic, struct msgb *tx)
476{
477 struct msgb *rx = msgb_alloc_c(OTC_GLOBAL, 1024, "ifd_rx itmsg");
478 struct itmsg *rx_it;
479 int rc;
480
481 rc = write(ic->it_fd, msgb_data(tx), msgb_length(tx));
482 msgb_free(tx);
483 if (rc < msgb_length(tx)) {
484 Log2(PCSC_LOG_ERROR, "Short write IFD->client thread: %d\n", rc);
485 msgb_free(rx);
486 return NULL;
487 }
488 rc = read(ic->it_fd, rx->tail, msgb_tailroom(rx));
489 if (rc <= 0) {
490 Log2(PCSC_LOG_ERROR, "Short read IFD<-client thread: %d\n", rc);
491 msgb_free(rx);
492 return NULL;
493 }
494 msgb_put(rx, rc);
495 rx_it = (struct itmsg *) msgb_data(rx);
496 if (msgb_length(rx) < sizeof(*rx_it) + rx_it->len) {
497 Log2(PCSC_LOG_ERROR, "Short itmsg IFD<-client thread: %d\n", msgb_length(rx));
498 msgb_free(rx);
499 return NULL;
500 }
501 return rx;
502}
503
504/* function called on IFD side to create socketpair + start remsim-client thread */
505static struct ifd_client *create_ifd_client(const struct client_thread_cfg *cfg)
506{
507 struct ifd_client *ic = talloc_zero(OTC_GLOBAL, struct ifd_client);
508 int sp[2];
509 int rc;
510
511 /* copy over configuration */
512 ic->cfg = *cfg;
513
514 /* create socket pair for communication between threads */
515 rc = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sp);
516 if (rc != 0) {
517 talloc_free(ic);
518 return NULL;
519 }
520
521 ic->it_fd = sp[0];
522 ic->cfg.it_sock_fd = sp[1];
523
524 /* start the thread */
525 rc = pthread_create(&ic->pthread, NULL, client_pthread_main, &ic->cfg);
526 if (rc != 0) {
527 Log1(PCSC_LOG_ERROR, "Error creating remsim-client pthread\n");
528 close(sp[0]);
529 close(sp[1]);
530 talloc_free(ic);
531 return NULL;
532 }
533
534 return ic;
535}
536
537/* function called on IFD side to destroy (terminate) remsim-client thread */
538static void destroy_ifd_client(struct ifd_client *ic)
539{
540 if (!ic)
541 return;
542
543 pthread_cancel(ic->pthread);
544 pthread_join(ic->pthread, NULL);
545}
546
547#define MAX_SLOTS 256
548static struct ifd_client *ifd_client[MAX_SLOTS];
549
550#define LUN2SLOT(lun) ((lun) & 0xffff)
551#define LUN2RDR(lun) ((lun) >> 16)
552
553
554RESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel)
555{
556 return IFD_COMMUNICATION_ERROR;
557}
558
559RESPONSECODE IFDHCreateChannelByName(DWORD Lun, LPSTR DeviceName)
560{
561 struct ifd_client *ic;
562 struct client_thread_cfg cfg = {
563 .name = "fixme-name",
564 .server_host = "127.0.0.1",
565 .server_port = -1,
566 .client_id = 0,
567 .client_slot = 0,
568 };
569 char *r, *client_id, *slot_nr, *host, *port;
570
571 if (LUN2RDR(Lun) != 0)
572 return IFD_NO_SUCH_DEVICE;
573
574 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client))
575 return IFD_NO_SUCH_DEVICE;
576
577 ensure_osmo_ctx();
578
579 client_id = strtok_r(DeviceName, ":", &r);
580 if (!client_id)
581 goto end_parse;
582 cfg.client_id = atoi(client_id);
583
584 slot_nr = strtok_r(NULL, ":", &r);
585 if (!slot_nr)
586 goto end_parse;
587 cfg.client_slot = atoi(slot_nr);
588
589 host = strtok_r(NULL, ":", &r);
590 if (!host)
591 goto end_parse;
592 cfg.server_host = strdup(host);
593
594 port = strtok_r(NULL, ":", &r);
595 cfg.server_port = atoi(port);
596
597
598end_parse:
599 LOGP(DMAIN, LOGL_NOTICE, "remsim-client C%d:%d bankd=%s:%d\n",
600 cfg.client_id, cfg.client_slot, cfg.server_host, cfg.server_port);
601
602 ic = create_ifd_client(&cfg);
603 if (ic) {
604 ifd_client[LUN2SLOT(Lun)] = ic;
605 return IFD_SUCCESS;
606 } else
607 return IFD_COMMUNICATION_ERROR;
608}
609
610RESPONSECODE IFDHControl(DWORD Lun, DWORD dwControlCode, PUCHAR TxBuffer, DWORD TxLength,
611 PUCHAR RxBuffer, DWORD RxLength, LPDWORD pdwBytesReturned)
612{
613 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
614
615 ensure_osmo_ctx();
616
617 if (LUN2RDR(Lun) != 0) {
618 r = IFD_NO_SUCH_DEVICE;
619 goto err;
620 }
621
622 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
623 r = IFD_NO_SUCH_DEVICE;
624 goto err;
625 }
626
627 if (pdwBytesReturned)
628 *pdwBytesReturned = 0;
629
630 r = IFD_ERROR_NOT_SUPPORTED;
631err:
632 LOG_EXIT(Lun, r);
633 return r;
634}
635
636RESPONSECODE IFDHCloseChannel(DWORD Lun)
637{
638 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
639
640 ensure_osmo_ctx();
641
642 if (LUN2RDR(Lun) != 0) {
643 r = IFD_NO_SUCH_DEVICE;
644 goto err;
645 }
646
647 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
648 r = IFD_NO_SUCH_DEVICE;
649 goto err;
650 }
651
652 destroy_ifd_client(ifd_client[LUN2SLOT(Lun)]);
653 ifd_client[LUN2SLOT(Lun)] = NULL;
654
655 r = IFD_SUCCESS;
656err:
657 LOG_EXIT(Lun, r);
658 return r;
659}
660
661RESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value)
662{
663 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
664 struct ifd_client *ic;
665 struct msgb *rx, *tx;
666 struct itmsg *rx_it;
667
668 ensure_osmo_ctx();
669
670 if (LUN2RDR(Lun) != 0) {
671 r = IFD_NO_SUCH_DEVICE;
672 goto err;
673 }
674
675 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
676 r = IFD_NO_SUCH_DEVICE;
677 goto err;
678 }
679
680 ic = ifd_client[LUN2SLOT(Lun)];
681 if (!ic) {
682 r = IFD_NO_SUCH_DEVICE;
683 goto err;
684 }
685
686 if (!Length || !Value)
687 goto err;
688
689 switch (Tag) {
690 case TAG_IFD_ATR:
691 /* Return the ATR and its size */
692 tx = itmsg_alloc(ITMSG_TYPE_ATR_REQ, 0, NULL, 0);
693 OSMO_ASSERT(tx);
694 rx = ifd_xceive_client(ic, tx);
695 if (!rx) {
696 r = IFD_NO_SUCH_DEVICE;
697 goto err;
698 }
699 rx_it = (struct itmsg *)msgb_data(rx);
700 if (*Length > rx_it->len)
701 *Length = rx_it->len;
702 memcpy(Value, rx_it->data, *Length);
703 msgb_free(rx);
704 break;
705 case TAG_IFD_SIMULTANEOUS_ACCESS:
706 /* Return the number of sessions (readers) the driver
707 * can handle in Value[0]. This is used for multiple
708 * readers sharing the same driver. */
709 if (*Length < 1)
710 goto err;
711 *Value = 1;
712 *Length = 1;
713 break;
714 case TAG_IFD_SLOTS_NUMBER:
715 /* Return the number of slots in this reader in Value[0] */
716 if (*Length < 1)
717 goto err;
718 *Value = 1;
719 *Length = 1;
720 break;
721 case TAG_IFD_THREAD_SAFE:
722 /* If the driver supports more than one reader (see
723 * TAG_IFD_SIMULTANEOUS_ACCESS above) this tag indicates
724 * if the driver supports access to multiple readers at
725 * the same time. */
726 if (*Length < 1)
727 goto err;
728 *Value = 0;
729 *Length = 1;
730 break;
731 case TAG_IFD_SLOT_THREAD_SAFE:
732 /* If the reader has more than one slot (see
733 * TAG_IFD_SLOTS_NUMBER above) this tag indicates if the
734 * driver supports access to multiple slots of the same
735 * reader at the same time. */
736 if (*Length < 1)
737 goto err;
738 *Value = 0;
739 *Length = 1;
740 break;
741 default:
742 r = IFD_ERROR_TAG;
743 goto err;
744 }
745
746 r = IFD_SUCCESS;
747
748err:
749 if (r != IFD_SUCCESS && Length)
750 *Length = 0;
751
752 LOG_EXITF(Lun, r, "%s", get_value_string(ifd_tag_names, Tag));
753 return r;
754}
755
756RESPONSECODE IFDHSetCapabilities(DWORD Lun, DWORD Tag, DWORD Length, PUCHAR Value)
757{
758 ensure_osmo_ctx();
759
760 if (LUN2RDR(Lun) != 0)
761 return IFD_NO_SUCH_DEVICE;
762
763 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client))
764 return IFD_NO_SUCH_DEVICE;
765
766
767 LOG_EXIT(Lun, IFD_NOT_SUPPORTED);
768 return IFD_NOT_SUPPORTED;
769}
770
771RESPONSECODE IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol, UCHAR Flags, UCHAR PTS1,
772 UCHAR PTS2, UCHAR PTS3)
773{
774 ensure_osmo_ctx();
775
776 if (LUN2RDR(Lun) != 0)
777 return IFD_NO_SUCH_DEVICE;
778
779 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client))
780 return IFD_NO_SUCH_DEVICE;
781
782 LOG_EXIT(Lun, IFD_SUCCESS);
783 return IFD_SUCCESS;
784}
785
786RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength)
787{
788 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
789 struct ifd_client *ic;
790 struct msgb *rx, *tx;
791
792 ensure_osmo_ctx();
793
794 if (LUN2RDR(Lun) != 0) {
795 r = IFD_NO_SUCH_DEVICE;
796 goto err;
797 }
798
799 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
800 r = IFD_NO_SUCH_DEVICE;
801 goto err;
802 }
803
804 ic = ifd_client[LUN2SLOT(Lun)];
805 if (!ic) {
806 r = IFD_NO_SUCH_DEVICE;
807 goto err;
808 }
809
810 switch (Action) {
811 case IFD_POWER_DOWN:
812 tx = itmsg_alloc(ITMSG_TYPE_POWER_OFF_REQ, 0, NULL, 0);
813 break;
814 case IFD_POWER_UP:
815 tx = itmsg_alloc(ITMSG_TYPE_POWER_ON_REQ, 0, NULL, 0);
816 break;
817 case IFD_RESET:
818 tx = itmsg_alloc(ITMSG_TYPE_RESET_REQ, 0, NULL, 0);
819 break;
820 default:
821 r = IFD_NOT_SUPPORTED;
822 goto err;
823 }
824
825 rx = ifd_xceive_client(ic, tx);
826 if (!rx) {
827 r = IFD_NO_SUCH_DEVICE;
828 goto err;
829 }
830
831 r = IFD_SUCCESS;
832 msgb_free(rx);
833
834err:
835 if (r != IFD_SUCCESS && AtrLength)
836 *AtrLength = 0;
837 else
838 r = IFDHGetCapabilities(Lun, TAG_IFD_ATR, AtrLength, Atr);
839
840 LOG_EXIT(Lun, r);
841 return r;
842}
843
844RESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci, PUCHAR TxBuffer,
845 DWORD TxLength, PUCHAR RxBuffer, PDWORD RxLength,
846 PSCARD_IO_HEADER RecvPci)
847{
848 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
849 struct ifd_client *ic;
850 struct msgb *rx, *tx;
851 struct itmsg *rx_it;
852
853 ensure_osmo_ctx();
854
855 if (LUN2RDR(Lun) != 0) {
856 r = IFD_NO_SUCH_DEVICE;
857 goto err;
858 }
859
860 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
861 r = IFD_NO_SUCH_DEVICE;
862 goto err;
863 }
864
865 ic = ifd_client[LUN2SLOT(Lun)];
866 if (!ic) {
867 r = IFD_NO_SUCH_DEVICE;
868 goto err;
869 }
870
871 tx = itmsg_alloc(ITMSG_TYPE_C_APDU_REQ, 0, TxBuffer, TxLength);
872 OSMO_ASSERT(tx);
873 /* transmit C-APDU to remote reader + blocking wait for response from peer */
874 rx = ifd_xceive_client(ic, tx);
875 if (!rx) {
876 r = IFD_NO_SUCH_DEVICE;
877 goto err;
878 }
879 rx_it = (struct itmsg *) msgb_data(rx);
880 if (*RxLength > rx_it->len)
881 *RxLength = rx_it->len;
882 memcpy(RxBuffer, rx_it->data, *RxLength);
883 msgb_free(rx);
884
885 r = IFD_SUCCESS;
886err:
887 if (r != IFD_SUCCESS && RxLength)
888 *RxLength = 0;
889
890 LOG_EXIT(Lun, r);
891 return r;
892}
893
894RESPONSECODE IFDHICCPresence(DWORD Lun)
895{
896 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
897 struct ifd_client *ic;
898 struct msgb *rx, *tx;
899 struct itmsg *rx_it;
900
901 ensure_osmo_ctx();
902
903 if (LUN2RDR(Lun) != 0) {
904 r = IFD_NO_SUCH_DEVICE;
905 goto err;
906 }
907
908 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
909 r = IFD_NO_SUCH_DEVICE;
910 goto err;
911 }
912
913 ic = ifd_client[LUN2SLOT(Lun)];
914 if (!ic) {
915 r = IFD_NO_SUCH_DEVICE;
916 goto err;
917 }
918
919 tx = itmsg_alloc(ITMSG_TYPE_CARD_PRES_REQ, 0, NULL, 0);
920 OSMO_ASSERT(tx);
921 rx = ifd_xceive_client(ic, tx);
922 if (!rx) {
923 r = IFD_NO_SUCH_DEVICE;
924 goto err;
925 }
926 rx_it = (struct itmsg *) msgb_data(rx);
927 if (rx_it->status == 0)
928 r = IFD_SUCCESS;
929 else
930 r = IFD_ICC_NOT_PRESENT;
931
932err:
933 LOG_EXIT(Lun, r);
934 return r;
935}
936
937static __attribute__((constructor)) void on_dso_load_ifd(void)
938{
939 void *g_tall_ctx = NULL;
940 ensure_osmo_ctx();
941 osmo_init_logging2(g_tall_ctx, &log_info);
942}