blob: a416e6e9d012435b3014c7c227309f004209e947 [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
Harald Welte7b87ba12021-09-08 21:38:35 +0200159int frontend_request_card_remove(struct bankd_client *bc)
160{
161 return 0;
162}
163
Harald Welte0e968cc2020-02-22 18:16:16 +0100164int frontend_request_sim_remote(struct bankd_client *bc)
165{
166 return 0;
167}
168
Harald Welte7b87ba12021-09-08 21:38:35 +0200169int frontend_request_sim_local(struct bankd_client *bc)
170{
171 return 0;
172}
173
Harald Welte0e968cc2020-02-22 18:16:16 +0100174int frontend_request_modem_reset(struct bankd_client *bc)
175{
176 return 0;
177}
178
179int frontend_handle_card2modem(struct bankd_client *bc, const uint8_t *data, size_t len)
180{
Harald Welte9fac4962020-02-14 21:01:23 +0100181 struct client_thread *ct = bc->data;
182 struct msgb *msg;
183
Harald Welte0e968cc2020-02-22 18:16:16 +0100184 OSMO_ASSERT(data);
Harald Welte9fac4962020-02-14 21:01:23 +0100185
Harald Welte0e968cc2020-02-22 18:16:16 +0100186 DEBUGP(DMAIN, "R-APDU: %s\n", osmo_hexdump(data, len));
Harald Welte9fac4962020-02-14 21:01:23 +0100187 /* enqueue towards IFD thread */
Harald Welte0e968cc2020-02-22 18:16:16 +0100188 msg = itmsg_alloc(ITMSG_TYPE_R_APDU_IND, 0, data, len);
Harald Welte9fac4962020-02-14 21:01:23 +0100189 OSMO_ASSERT(msg);
190 enqueue_to_ifd(ct, msg);
191
192 return 0;
193}
194
Harald Welte0e968cc2020-02-22 18:16:16 +0100195int frontend_handle_set_atr(struct bankd_client *bc, const uint8_t *data, size_t len)
Harald Welte9fac4962020-02-14 21:01:23 +0100196{
197 struct client_thread *ct = bc->data;
Harald Welte9fac4962020-02-14 21:01:23 +0100198 unsigned int atr_len;
199
Harald Welte0e968cc2020-02-22 18:16:16 +0100200 OSMO_ASSERT(data);
Harald Welte9fac4962020-02-14 21:01:23 +0100201
Harald Welte0e968cc2020-02-22 18:16:16 +0100202 DEBUGP(DMAIN, "SET_ATR: %s\n", osmo_hexdump(data, len));
Harald Welte9fac4962020-02-14 21:01:23 +0100203
204 /* store ATR in local data structure until somebody needs it */
Harald Welte0e968cc2020-02-22 18:16:16 +0100205 atr_len = len;
Harald Welte9fac4962020-02-14 21:01:23 +0100206 if (atr_len > sizeof(ct->atr))
207 atr_len = sizeof(ct->atr);
Harald Welte0e968cc2020-02-22 18:16:16 +0100208 memcpy(ct->atr, data, atr_len);
Harald Welte9fac4962020-02-14 21:01:23 +0100209 ct->atr_len = atr_len;
210
Harald Welte9fac4962020-02-14 21:01:23 +0100211 return 0;
212}
213
Harald Welte0e968cc2020-02-22 18:16:16 +0100214int frontend_handle_slot_status(struct bankd_client *bc, const SlotPhysStatus_t *sts)
Harald Welte9fac4962020-02-14 21:01:23 +0100215{
Harald Welte0e968cc2020-02-22 18:16:16 +0100216 return 0;
217}
Harald Welte9fac4962020-02-14 21:01:23 +0100218
Harald Weltee580c932020-05-24 16:03:56 +0200219int frontend_append_script_env(struct bankd_client *bc, char **env, int idx, size_t max_env)
Harald Welte0e968cc2020-02-22 18:16:16 +0100220{
Harald Weltee580c932020-05-24 16:03:56 +0200221 return idx;
Harald Welte9fac4962020-02-14 21:01:23 +0100222}
223
224/***********************************************************************
225 * Incoming command from the user application
226 ***********************************************************************/
227
228/* handle a single msgb-wrapped 'struct itmsg' from the IFD-handler thread */
229static void handle_it_msg(struct client_thread *ct, struct itmsg *itmsg)
230{
231 struct bankd_client *bc = ct->bc;
232 struct msgb *tx = NULL;
233 RsproPDU_t *pdu;
234 BankSlot_t bslot;
235
236 bank_slot2rspro(&bslot, &ct->bc->bankd_slot);
237
238 switch (itmsg->type) {
239 case ITMSG_TYPE_CARD_PRES_REQ:
240 if (bc->bankd_conn.fi->state == 2 /*SRVC_ST_CONNECTED*/)
241 tx = itmsg_alloc(ITMSG_TYPE_CARD_PRES_RESP, 0, NULL, 0);
242 else
243 tx = itmsg_alloc(ITMSG_TYPE_CARD_PRES_RESP, 0xffff, NULL, 0);
244 OSMO_ASSERT(tx);
245 break;
246
247 case ITMSG_TYPE_ATR_REQ:
248 /* respond to IFD */
249 tx = itmsg_alloc(ITMSG_TYPE_ATR_RESP, 0, ct->atr, ct->atr_len);
250 OSMO_ASSERT(tx);
251 break;
252
253 case ITMSG_TYPE_POWER_OFF_REQ:
254 pdu = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot,
255 true, false, false, true);
256 server_conn_send_rspro(&bc->bankd_conn, pdu);
257 /* respond to IFD */
258 tx = itmsg_alloc(ITMSG_TYPE_POWER_OFF_RESP, 0, NULL, 0);
259 OSMO_ASSERT(tx);
260 break;
261
262 case ITMSG_TYPE_POWER_ON_REQ:
263 pdu = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot,
264 false, true, true, true);
265 server_conn_send_rspro(&bc->bankd_conn, pdu);
266 /* respond to IFD */
267 tx = itmsg_alloc(ITMSG_TYPE_POWER_ON_RESP, 0, NULL, 0);
268 OSMO_ASSERT(tx);
269 break;
270
271 case ITMSG_TYPE_RESET_REQ:
272 /* reset the [remote] card */
273 pdu = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot,
274 true, true, true, true);
275 server_conn_send_rspro(&bc->bankd_conn, pdu);
276 /* and take it out of reset again */
277 pdu = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot,
278 false, true, true, true);
279 server_conn_send_rspro(&bc->bankd_conn, pdu);
280 /* respond to IFD */
281 tx = itmsg_alloc(ITMSG_TYPE_RESET_RESP, 0, NULL, 0);
282 OSMO_ASSERT(tx);
283 break;
284 case ITMSG_TYPE_C_APDU_REQ:
285 if (!bc->srv_conn.clslot) {
286 LOGP(DMAIN, LOGL_ERROR, "Cannot send command; no client slot\n");
287 /* FIXME: Response? */
288 return;
289 }
290
291 /* Send CMD APDU to [remote] card */
292 pdu = rspro_gen_TpduModem2Card(bc->srv_conn.clslot, &bslot, itmsg->data, itmsg->len);
293 server_conn_send_rspro(&bc->bankd_conn, pdu);
294 /* response will come in asynchronously */
295 break;
296 default:
297 LOGP(DMAIN, LOGL_ERROR, "Unknown inter-thread msg type %u\n", itmsg->type);
298 break;
299 }
300
301 if (tx)
302 enqueue_to_ifd(ct, tx);
303
304}
305
306/* call-back function for inter-thread socket */
307static int it_sock_fd_cb(struct osmo_fd *ofd, unsigned int what)
308{
309 struct client_thread *ct = ofd->data;
310 int rc;
311
312 if (what & OSMO_FD_READ) {
313 struct msgb *msg = msgb_alloc_c(OTC_GLOBAL, 1024, "Rx it_fd");
314 struct itmsg *itmsg;
315
316 OSMO_ASSERT(msg);
317 rc = read(ofd->fd, msg->tail, msgb_tailroom(msg));
318 if (rc <= 0) {
319 LOGP(DMAIN, LOGL_ERROR, "Error reading from inter-thread fd: %d\n", rc);
320 pthread_exit(NULL);
321 }
322 msgb_put(msg, rc);
323 itmsg = (struct itmsg *) msgb_data(msg);
324 if (msgb_length(msg) < sizeof(*itmsg) ||
325 msgb_length(msg) < sizeof(*itmsg) + itmsg->len) {
326 LOGP(DMAIN, LOGL_ERROR, "Dropping short inter-thread message\n");
327 } else {
328 handle_it_msg(ct, itmsg);
329 }
330 msgb_free(msg);
331 }
332
333 if (what & OSMO_FD_WRITE) {
334 struct msgb *msg = msgb_dequeue(&ct->it_msgq);
335 if (!msg) {
336 /* last message: disable write events */
337 ofd->when &= ~OSMO_FD_WRITE;
338 } else {
339 unsigned int len = msgb_length(msg);
340 rc = write(ofd->fd, msgb_data(msg), len);
341 msgb_free(msg);
342 if (rc < len) {
343 LOGP(DMAIN, LOGL_ERROR, "Short write on inter-thread fd: %d < %d\n",
344 rc, len);
345 }
346 }
347 }
348
349
350 return 0;
351}
352
353/* release all resources allocated by thread */
354static void client_pthread_cleanup(void *arg)
355{
356 struct client_thread *ct = arg;
357
358 LOGP(DMAIN, LOGL_INFO, "Cleaning up remsim-client thread\n");
359 //FIXME remsim_client_destroy(ct->bc);
360 ct->bc = NULL;
361 msgb_queue_free(&ct->it_msgq);
362 osmo_fd_unregister(&ct->it_ofd);
363 close(ct->it_ofd.fd);
364 ct->it_ofd.fd = -1;
365 talloc_free(ct);
366}
367
368/* main function of remsim-client pthread */
369static void *client_pthread_main(void *arg)
370{
371 struct client_thread_cfg *cfg = arg;
Harald Welte0e968cc2020-02-22 18:16:16 +0100372 struct client_config *ccfg;
Harald Welte9fac4962020-02-14 21:01:23 +0100373 struct client_thread *ct;
374 int rc;
375
376 osmo_select_init();
377 rc = osmo_ctx_init("client");
378 OSMO_ASSERT(rc == 0);
379
380 ct = talloc_zero(OTC_GLOBAL, struct client_thread);
381 OSMO_ASSERT(ct);
382
Harald Welte0e968cc2020-02-22 18:16:16 +0100383 ccfg = client_config_init(ct);
384 OSMO_ASSERT(ccfg);
385 osmo_talloc_replace_string(ccfg, &ccfg->server_host, cfg->server_host);
386 if (cfg->server_port >= 0)
387 ccfg->server_port = cfg->server_port;
388 ccfg->client_id = cfg->client_id;
389 ccfg->client_slot = cfg->client_slot;
390
Harald Welte9fac4962020-02-14 21:01:23 +0100391 if (!talloc_asn1_ctx)
392 talloc_asn1_ctx= talloc_named_const(ct, 0, "asn1");
393
Harald Welte0e968cc2020-02-22 18:16:16 +0100394 ct->bc = remsim_client_create(ct, cfg->name, "remsim_ifdhandler", ccfg);
Harald Welte9fac4962020-02-14 21:01:23 +0100395 OSMO_ASSERT(ct->bc);
396 ct->bc->data = ct;
Harald Welte9fac4962020-02-14 21:01:23 +0100397
398 INIT_LLIST_HEAD(&ct->it_msgq);
399 osmo_fd_setup(&ct->it_ofd, cfg->it_sock_fd, OSMO_FD_READ, &it_sock_fd_cb, ct, 0);
400 osmo_fd_register(&ct->it_ofd);
401
402 /* ensure we get properly cleaned up if cancelled */
403 pthread_cleanup_push(client_pthread_cleanup, ct);
404
405 osmo_fsm_inst_dispatch(ct->bc->srv_conn.fi, SRVC_E_ESTABLISH, NULL);
406
407 while (1) {
408 osmo_select_main(0);
409 }
410
411 pthread_cleanup_pop(1);
412 return NULL;
413}
414
415/***********************************************************************
416 * PC/SC ifd_handler API functions
417 ***********************************************************************/
418
419#include <ifdhandler.h>
420#include <debuglog.h>
421
422#include <sys/types.h>
423#include <sys/socket.h>
424
425static const struct value_string ifd_status_names[] = {
426 OSMO_VALUE_STRING(IFD_SUCCESS),
427 OSMO_VALUE_STRING(IFD_ERROR_TAG),
428 OSMO_VALUE_STRING(IFD_ERROR_SET_FAILURE),
429 OSMO_VALUE_STRING(IFD_ERROR_VALUE_READ_ONLY),
430 OSMO_VALUE_STRING(IFD_ERROR_PTS_FAILURE),
431 OSMO_VALUE_STRING(IFD_ERROR_NOT_SUPPORTED),
432 OSMO_VALUE_STRING(IFD_PROTOCOL_NOT_SUPPORTED),
433 OSMO_VALUE_STRING(IFD_ERROR_POWER_ACTION),
434 OSMO_VALUE_STRING(IFD_ERROR_SWALLOW),
435 OSMO_VALUE_STRING(IFD_ERROR_EJECT),
436 OSMO_VALUE_STRING(IFD_ERROR_CONFISCATE),
437 OSMO_VALUE_STRING(IFD_COMMUNICATION_ERROR),
438 OSMO_VALUE_STRING(IFD_RESPONSE_TIMEOUT),
439 OSMO_VALUE_STRING(IFD_NOT_SUPPORTED),
440 OSMO_VALUE_STRING(IFD_ICC_PRESENT),
441 OSMO_VALUE_STRING(IFD_ICC_NOT_PRESENT),
442 OSMO_VALUE_STRING(IFD_NO_SUCH_DEVICE),
443 OSMO_VALUE_STRING(IFD_ERROR_INSUFFICIENT_BUFFER),
444 { 0, NULL }
445};
446
447static const struct value_string ifd_tag_names[] = {
448 OSMO_VALUE_STRING(TAG_IFD_ATR),
449 OSMO_VALUE_STRING(TAG_IFD_SLOTNUM),
450 OSMO_VALUE_STRING(TAG_IFD_SLOT_THREAD_SAFE),
451 OSMO_VALUE_STRING(TAG_IFD_THREAD_SAFE),
452 OSMO_VALUE_STRING(TAG_IFD_SLOTS_NUMBER),
453 OSMO_VALUE_STRING(TAG_IFD_SIMULTANEOUS_ACCESS),
454 OSMO_VALUE_STRING(TAG_IFD_POLLING_THREAD),
455 OSMO_VALUE_STRING(TAG_IFD_POLLING_THREAD_KILLABLE),
456 OSMO_VALUE_STRING(TAG_IFD_STOP_POLLING_THREAD),
457 OSMO_VALUE_STRING(TAG_IFD_POLLING_THREAD_WITH_TIMEOUT),
458 { 0, NULL }
459};
460
461#define LOG_EXIT(Lun, r) \
462 Log4(r == IFD_SUCCESS || r == IFD_ICC_NOT_PRESENT ? PCSC_LOG_DEBUG : PCSC_LOG_ERROR, \
463 "%s(0x%08lx) => %s\n", __func__, Lun, get_value_string(ifd_status_names, r))
464
465#define LOG_EXITF(Lun, r, fmt, args...) \
466 Log5(r == IFD_SUCCESS ? PCSC_LOG_DEBUG : PCSC_LOG_ERROR, \
467 "%s(0x%08lx) "fmt" => %s\n", __func__, Lun, ## args, get_value_string(ifd_status_names, r))
468
469/* IFD side handle for a remsim-client [thread] */
470struct ifd_client {
471 /* the client pthread itself */
472 pthread_t pthread;
473 /* socket to talk to thread */
474 int it_fd;
475 /* configuration passed into the thread */
476 struct client_thread_cfg cfg;
477};
478
479static struct msgb *ifd_xceive_client(struct ifd_client *ic, struct msgb *tx)
480{
481 struct msgb *rx = msgb_alloc_c(OTC_GLOBAL, 1024, "ifd_rx itmsg");
482 struct itmsg *rx_it;
483 int rc;
484
485 rc = write(ic->it_fd, msgb_data(tx), msgb_length(tx));
486 msgb_free(tx);
487 if (rc < msgb_length(tx)) {
488 Log2(PCSC_LOG_ERROR, "Short write IFD->client thread: %d\n", rc);
489 msgb_free(rx);
490 return NULL;
491 }
492 rc = read(ic->it_fd, rx->tail, msgb_tailroom(rx));
493 if (rc <= 0) {
494 Log2(PCSC_LOG_ERROR, "Short read IFD<-client thread: %d\n", rc);
495 msgb_free(rx);
496 return NULL;
497 }
498 msgb_put(rx, rc);
499 rx_it = (struct itmsg *) msgb_data(rx);
500 if (msgb_length(rx) < sizeof(*rx_it) + rx_it->len) {
501 Log2(PCSC_LOG_ERROR, "Short itmsg IFD<-client thread: %d\n", msgb_length(rx));
502 msgb_free(rx);
503 return NULL;
504 }
505 return rx;
506}
507
508/* function called on IFD side to create socketpair + start remsim-client thread */
509static struct ifd_client *create_ifd_client(const struct client_thread_cfg *cfg)
510{
511 struct ifd_client *ic = talloc_zero(OTC_GLOBAL, struct ifd_client);
512 int sp[2];
513 int rc;
514
515 /* copy over configuration */
516 ic->cfg = *cfg;
517
518 /* create socket pair for communication between threads */
519 rc = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sp);
520 if (rc != 0) {
521 talloc_free(ic);
522 return NULL;
523 }
524
525 ic->it_fd = sp[0];
526 ic->cfg.it_sock_fd = sp[1];
527
528 /* start the thread */
529 rc = pthread_create(&ic->pthread, NULL, client_pthread_main, &ic->cfg);
530 if (rc != 0) {
531 Log1(PCSC_LOG_ERROR, "Error creating remsim-client pthread\n");
532 close(sp[0]);
533 close(sp[1]);
534 talloc_free(ic);
535 return NULL;
536 }
537
538 return ic;
539}
540
541/* function called on IFD side to destroy (terminate) remsim-client thread */
542static void destroy_ifd_client(struct ifd_client *ic)
543{
544 if (!ic)
545 return;
546
547 pthread_cancel(ic->pthread);
548 pthread_join(ic->pthread, NULL);
549}
550
551#define MAX_SLOTS 256
552static struct ifd_client *ifd_client[MAX_SLOTS];
553
554#define LUN2SLOT(lun) ((lun) & 0xffff)
555#define LUN2RDR(lun) ((lun) >> 16)
556
557
558RESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel)
559{
560 return IFD_COMMUNICATION_ERROR;
561}
562
563RESPONSECODE IFDHCreateChannelByName(DWORD Lun, LPSTR DeviceName)
564{
565 struct ifd_client *ic;
566 struct client_thread_cfg cfg = {
567 .name = "fixme-name",
568 .server_host = "127.0.0.1",
569 .server_port = -1,
570 .client_id = 0,
571 .client_slot = 0,
572 };
573 char *r, *client_id, *slot_nr, *host, *port;
574
575 if (LUN2RDR(Lun) != 0)
576 return IFD_NO_SUCH_DEVICE;
577
578 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client))
579 return IFD_NO_SUCH_DEVICE;
580
581 ensure_osmo_ctx();
582
583 client_id = strtok_r(DeviceName, ":", &r);
584 if (!client_id)
585 goto end_parse;
586 cfg.client_id = atoi(client_id);
587
588 slot_nr = strtok_r(NULL, ":", &r);
589 if (!slot_nr)
590 goto end_parse;
591 cfg.client_slot = atoi(slot_nr);
592
593 host = strtok_r(NULL, ":", &r);
594 if (!host)
595 goto end_parse;
596 cfg.server_host = strdup(host);
597
598 port = strtok_r(NULL, ":", &r);
599 cfg.server_port = atoi(port);
600
601
602end_parse:
603 LOGP(DMAIN, LOGL_NOTICE, "remsim-client C%d:%d bankd=%s:%d\n",
604 cfg.client_id, cfg.client_slot, cfg.server_host, cfg.server_port);
605
606 ic = create_ifd_client(&cfg);
607 if (ic) {
608 ifd_client[LUN2SLOT(Lun)] = ic;
609 return IFD_SUCCESS;
610 } else
611 return IFD_COMMUNICATION_ERROR;
612}
613
614RESPONSECODE IFDHControl(DWORD Lun, DWORD dwControlCode, PUCHAR TxBuffer, DWORD TxLength,
615 PUCHAR RxBuffer, DWORD RxLength, LPDWORD pdwBytesReturned)
616{
617 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
618
619 ensure_osmo_ctx();
620
621 if (LUN2RDR(Lun) != 0) {
622 r = IFD_NO_SUCH_DEVICE;
623 goto err;
624 }
625
626 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
627 r = IFD_NO_SUCH_DEVICE;
628 goto err;
629 }
630
631 if (pdwBytesReturned)
632 *pdwBytesReturned = 0;
633
634 r = IFD_ERROR_NOT_SUPPORTED;
635err:
636 LOG_EXIT(Lun, r);
637 return r;
638}
639
640RESPONSECODE IFDHCloseChannel(DWORD Lun)
641{
642 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
643
644 ensure_osmo_ctx();
645
646 if (LUN2RDR(Lun) != 0) {
647 r = IFD_NO_SUCH_DEVICE;
648 goto err;
649 }
650
651 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
652 r = IFD_NO_SUCH_DEVICE;
653 goto err;
654 }
655
656 destroy_ifd_client(ifd_client[LUN2SLOT(Lun)]);
657 ifd_client[LUN2SLOT(Lun)] = NULL;
658
659 r = IFD_SUCCESS;
660err:
661 LOG_EXIT(Lun, r);
662 return r;
663}
664
665RESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value)
666{
667 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
668 struct ifd_client *ic;
669 struct msgb *rx, *tx;
670 struct itmsg *rx_it;
671
672 ensure_osmo_ctx();
673
674 if (LUN2RDR(Lun) != 0) {
675 r = IFD_NO_SUCH_DEVICE;
676 goto err;
677 }
678
679 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
680 r = IFD_NO_SUCH_DEVICE;
681 goto err;
682 }
683
684 ic = ifd_client[LUN2SLOT(Lun)];
685 if (!ic) {
686 r = IFD_NO_SUCH_DEVICE;
687 goto err;
688 }
689
690 if (!Length || !Value)
691 goto err;
692
693 switch (Tag) {
694 case TAG_IFD_ATR:
695 /* Return the ATR and its size */
696 tx = itmsg_alloc(ITMSG_TYPE_ATR_REQ, 0, NULL, 0);
697 OSMO_ASSERT(tx);
698 rx = ifd_xceive_client(ic, tx);
699 if (!rx) {
700 r = IFD_NO_SUCH_DEVICE;
701 goto err;
702 }
703 rx_it = (struct itmsg *)msgb_data(rx);
704 if (*Length > rx_it->len)
705 *Length = rx_it->len;
706 memcpy(Value, rx_it->data, *Length);
707 msgb_free(rx);
708 break;
709 case TAG_IFD_SIMULTANEOUS_ACCESS:
710 /* Return the number of sessions (readers) the driver
711 * can handle in Value[0]. This is used for multiple
712 * readers sharing the same driver. */
713 if (*Length < 1)
714 goto err;
715 *Value = 1;
716 *Length = 1;
717 break;
718 case TAG_IFD_SLOTS_NUMBER:
719 /* Return the number of slots in this reader in Value[0] */
720 if (*Length < 1)
721 goto err;
722 *Value = 1;
723 *Length = 1;
724 break;
725 case TAG_IFD_THREAD_SAFE:
726 /* If the driver supports more than one reader (see
727 * TAG_IFD_SIMULTANEOUS_ACCESS above) this tag indicates
728 * if the driver supports access to multiple readers at
729 * the same time. */
730 if (*Length < 1)
731 goto err;
732 *Value = 0;
733 *Length = 1;
734 break;
735 case TAG_IFD_SLOT_THREAD_SAFE:
736 /* If the reader has more than one slot (see
737 * TAG_IFD_SLOTS_NUMBER above) this tag indicates if the
738 * driver supports access to multiple slots of the same
739 * reader at the same time. */
740 if (*Length < 1)
741 goto err;
742 *Value = 0;
743 *Length = 1;
744 break;
745 default:
746 r = IFD_ERROR_TAG;
747 goto err;
748 }
749
750 r = IFD_SUCCESS;
751
752err:
753 if (r != IFD_SUCCESS && Length)
754 *Length = 0;
755
756 LOG_EXITF(Lun, r, "%s", get_value_string(ifd_tag_names, Tag));
757 return r;
758}
759
760RESPONSECODE IFDHSetCapabilities(DWORD Lun, DWORD Tag, DWORD Length, PUCHAR Value)
761{
762 ensure_osmo_ctx();
763
764 if (LUN2RDR(Lun) != 0)
765 return IFD_NO_SUCH_DEVICE;
766
767 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client))
768 return IFD_NO_SUCH_DEVICE;
769
770
771 LOG_EXIT(Lun, IFD_NOT_SUPPORTED);
772 return IFD_NOT_SUPPORTED;
773}
774
775RESPONSECODE IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol, UCHAR Flags, UCHAR PTS1,
776 UCHAR PTS2, UCHAR PTS3)
777{
778 ensure_osmo_ctx();
779
780 if (LUN2RDR(Lun) != 0)
781 return IFD_NO_SUCH_DEVICE;
782
783 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client))
784 return IFD_NO_SUCH_DEVICE;
785
786 LOG_EXIT(Lun, IFD_SUCCESS);
787 return IFD_SUCCESS;
788}
789
790RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength)
791{
792 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
793 struct ifd_client *ic;
794 struct msgb *rx, *tx;
795
796 ensure_osmo_ctx();
797
798 if (LUN2RDR(Lun) != 0) {
799 r = IFD_NO_SUCH_DEVICE;
800 goto err;
801 }
802
803 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
804 r = IFD_NO_SUCH_DEVICE;
805 goto err;
806 }
807
808 ic = ifd_client[LUN2SLOT(Lun)];
809 if (!ic) {
810 r = IFD_NO_SUCH_DEVICE;
811 goto err;
812 }
813
814 switch (Action) {
815 case IFD_POWER_DOWN:
816 tx = itmsg_alloc(ITMSG_TYPE_POWER_OFF_REQ, 0, NULL, 0);
817 break;
818 case IFD_POWER_UP:
819 tx = itmsg_alloc(ITMSG_TYPE_POWER_ON_REQ, 0, NULL, 0);
820 break;
821 case IFD_RESET:
822 tx = itmsg_alloc(ITMSG_TYPE_RESET_REQ, 0, NULL, 0);
823 break;
824 default:
825 r = IFD_NOT_SUPPORTED;
826 goto err;
827 }
828
829 rx = ifd_xceive_client(ic, tx);
830 if (!rx) {
831 r = IFD_NO_SUCH_DEVICE;
832 goto err;
833 }
834
835 r = IFD_SUCCESS;
836 msgb_free(rx);
837
838err:
839 if (r != IFD_SUCCESS && AtrLength)
840 *AtrLength = 0;
841 else
842 r = IFDHGetCapabilities(Lun, TAG_IFD_ATR, AtrLength, Atr);
843
844 LOG_EXIT(Lun, r);
845 return r;
846}
847
848RESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci, PUCHAR TxBuffer,
849 DWORD TxLength, PUCHAR RxBuffer, PDWORD RxLength,
850 PSCARD_IO_HEADER RecvPci)
851{
852 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
853 struct ifd_client *ic;
854 struct msgb *rx, *tx;
855 struct itmsg *rx_it;
856
857 ensure_osmo_ctx();
858
859 if (LUN2RDR(Lun) != 0) {
860 r = IFD_NO_SUCH_DEVICE;
861 goto err;
862 }
863
864 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
865 r = IFD_NO_SUCH_DEVICE;
866 goto err;
867 }
868
869 ic = ifd_client[LUN2SLOT(Lun)];
870 if (!ic) {
871 r = IFD_NO_SUCH_DEVICE;
872 goto err;
873 }
874
875 tx = itmsg_alloc(ITMSG_TYPE_C_APDU_REQ, 0, TxBuffer, TxLength);
876 OSMO_ASSERT(tx);
877 /* transmit C-APDU to remote reader + blocking wait for response from peer */
878 rx = ifd_xceive_client(ic, tx);
879 if (!rx) {
880 r = IFD_NO_SUCH_DEVICE;
881 goto err;
882 }
883 rx_it = (struct itmsg *) msgb_data(rx);
884 if (*RxLength > rx_it->len)
885 *RxLength = rx_it->len;
886 memcpy(RxBuffer, rx_it->data, *RxLength);
887 msgb_free(rx);
888
889 r = IFD_SUCCESS;
890err:
891 if (r != IFD_SUCCESS && RxLength)
892 *RxLength = 0;
893
894 LOG_EXIT(Lun, r);
895 return r;
896}
897
898RESPONSECODE IFDHICCPresence(DWORD Lun)
899{
900 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
901 struct ifd_client *ic;
902 struct msgb *rx, *tx;
903 struct itmsg *rx_it;
904
905 ensure_osmo_ctx();
906
907 if (LUN2RDR(Lun) != 0) {
908 r = IFD_NO_SUCH_DEVICE;
909 goto err;
910 }
911
912 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
913 r = IFD_NO_SUCH_DEVICE;
914 goto err;
915 }
916
917 ic = ifd_client[LUN2SLOT(Lun)];
918 if (!ic) {
919 r = IFD_NO_SUCH_DEVICE;
920 goto err;
921 }
922
923 tx = itmsg_alloc(ITMSG_TYPE_CARD_PRES_REQ, 0, NULL, 0);
924 OSMO_ASSERT(tx);
925 rx = ifd_xceive_client(ic, tx);
926 if (!rx) {
927 r = IFD_NO_SUCH_DEVICE;
928 goto err;
929 }
930 rx_it = (struct itmsg *) msgb_data(rx);
931 if (rx_it->status == 0)
932 r = IFD_SUCCESS;
933 else
934 r = IFD_ICC_NOT_PRESENT;
935
936err:
937 LOG_EXIT(Lun, r);
938 return r;
939}
940
941static __attribute__((constructor)) void on_dso_load_ifd(void)
942{
943 void *g_tall_ctx = NULL;
944 ensure_osmo_ctx();
945 osmo_init_logging2(g_tall_ctx, &log_info);
946}