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