blob: c6a93922c1add7033db38f5ce55ef626e678a6ff [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 {
Harald Weltea9bb9a92022-05-03 15:28:02 +0200114 /* bankd client running inside this thread */
Harald Welte9fac4962020-02-14 21:01:23 +0100115 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 {
Harald Welte9fac4962020-02-14 21:01:23 +0100128 const char *server_host;
129 int server_port;
130 int client_id;
131 int client_slot;
132 int it_sock_fd;
133};
134
Harald Weltea9bb9a92022-05-03 15:28:02 +0200135/* enqueue a msgb (containing 'struct itmsg') towards the IFD-handler thread */
Harald Welte9fac4962020-02-14 21:01:23 +0100136static void enqueue_to_ifd(struct client_thread *ct, struct msgb *msg)
137{
138 if (!msg)
139 return;
140
141 msgb_enqueue(&ct->it_msgq, msg);
142 ct->it_ofd.when |= OSMO_FD_WRITE;
143}
144
145/***********************************************************************
Harald Welte0e968cc2020-02-22 18:16:16 +0100146 * frontend to remsim-client main FSM code
Harald Welte9fac4962020-02-14 21:01:23 +0100147 ***********************************************************************/
148
Harald Welte0e968cc2020-02-22 18:16:16 +0100149int frontend_request_card_insert(struct bankd_client *bc)
Harald Welte9fac4962020-02-14 21:01:23 +0100150{
Harald Welte0e968cc2020-02-22 18:16:16 +0100151 return 0;
152}
153
Harald Welte7b87ba12021-09-08 21:38:35 +0200154int frontend_request_card_remove(struct bankd_client *bc)
155{
156 return 0;
157}
158
Harald Welte0e968cc2020-02-22 18:16:16 +0100159int frontend_request_sim_remote(struct bankd_client *bc)
160{
161 return 0;
162}
163
Harald Welte7b87ba12021-09-08 21:38:35 +0200164int frontend_request_sim_local(struct bankd_client *bc)
165{
166 return 0;
167}
168
Harald Welte0e968cc2020-02-22 18:16:16 +0100169int frontend_request_modem_reset(struct bankd_client *bc)
170{
171 return 0;
172}
173
174int frontend_handle_card2modem(struct bankd_client *bc, const uint8_t *data, size_t len)
175{
Harald Welte9fac4962020-02-14 21:01:23 +0100176 struct client_thread *ct = bc->data;
177 struct msgb *msg;
178
Harald Welte0e968cc2020-02-22 18:16:16 +0100179 OSMO_ASSERT(data);
Harald Welte9fac4962020-02-14 21:01:23 +0100180
Harald Welte0e968cc2020-02-22 18:16:16 +0100181 DEBUGP(DMAIN, "R-APDU: %s\n", osmo_hexdump(data, len));
Harald Welte9fac4962020-02-14 21:01:23 +0100182 /* enqueue towards IFD thread */
Harald Welte0e968cc2020-02-22 18:16:16 +0100183 msg = itmsg_alloc(ITMSG_TYPE_R_APDU_IND, 0, data, len);
Harald Welte9fac4962020-02-14 21:01:23 +0100184 OSMO_ASSERT(msg);
185 enqueue_to_ifd(ct, msg);
186
187 return 0;
188}
189
Harald Welte0e968cc2020-02-22 18:16:16 +0100190int frontend_handle_set_atr(struct bankd_client *bc, const uint8_t *data, size_t len)
Harald Welte9fac4962020-02-14 21:01:23 +0100191{
192 struct client_thread *ct = bc->data;
Harald Welte9fac4962020-02-14 21:01:23 +0100193 unsigned int atr_len;
194
Harald Welte0e968cc2020-02-22 18:16:16 +0100195 OSMO_ASSERT(data);
Harald Welte9fac4962020-02-14 21:01:23 +0100196
Harald Welte0e968cc2020-02-22 18:16:16 +0100197 DEBUGP(DMAIN, "SET_ATR: %s\n", osmo_hexdump(data, len));
Harald Welte9fac4962020-02-14 21:01:23 +0100198
199 /* store ATR in local data structure until somebody needs it */
Harald Welte0e968cc2020-02-22 18:16:16 +0100200 atr_len = len;
Harald Welte9fac4962020-02-14 21:01:23 +0100201 if (atr_len > sizeof(ct->atr))
202 atr_len = sizeof(ct->atr);
Harald Welte0e968cc2020-02-22 18:16:16 +0100203 memcpy(ct->atr, data, atr_len);
Harald Welte9fac4962020-02-14 21:01:23 +0100204 ct->atr_len = atr_len;
205
Harald Welte9fac4962020-02-14 21:01:23 +0100206 return 0;
207}
208
Harald Welte0e968cc2020-02-22 18:16:16 +0100209int frontend_handle_slot_status(struct bankd_client *bc, const SlotPhysStatus_t *sts)
Harald Welte9fac4962020-02-14 21:01:23 +0100210{
Harald Welte0e968cc2020-02-22 18:16:16 +0100211 return 0;
212}
Harald Welte9fac4962020-02-14 21:01:23 +0100213
Harald Weltee580c932020-05-24 16:03:56 +0200214int frontend_append_script_env(struct bankd_client *bc, char **env, int idx, size_t max_env)
Harald Welte0e968cc2020-02-22 18:16:16 +0100215{
Harald Weltee580c932020-05-24 16:03:56 +0200216 return idx;
Harald Welte9fac4962020-02-14 21:01:23 +0100217}
218
219/***********************************************************************
220 * Incoming command from the user application
221 ***********************************************************************/
222
223/* handle a single msgb-wrapped 'struct itmsg' from the IFD-handler thread */
224static void handle_it_msg(struct client_thread *ct, struct itmsg *itmsg)
225{
226 struct bankd_client *bc = ct->bc;
227 struct msgb *tx = NULL;
228 RsproPDU_t *pdu;
229 BankSlot_t bslot;
230
231 bank_slot2rspro(&bslot, &ct->bc->bankd_slot);
232
233 switch (itmsg->type) {
234 case ITMSG_TYPE_CARD_PRES_REQ:
235 if (bc->bankd_conn.fi->state == 2 /*SRVC_ST_CONNECTED*/)
236 tx = itmsg_alloc(ITMSG_TYPE_CARD_PRES_RESP, 0, NULL, 0);
237 else
238 tx = itmsg_alloc(ITMSG_TYPE_CARD_PRES_RESP, 0xffff, NULL, 0);
239 OSMO_ASSERT(tx);
240 break;
241
242 case ITMSG_TYPE_ATR_REQ:
243 /* respond to IFD */
244 tx = itmsg_alloc(ITMSG_TYPE_ATR_RESP, 0, ct->atr, ct->atr_len);
245 OSMO_ASSERT(tx);
246 break;
247
248 case ITMSG_TYPE_POWER_OFF_REQ:
249 pdu = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot,
250 true, false, false, true);
251 server_conn_send_rspro(&bc->bankd_conn, pdu);
252 /* respond to IFD */
253 tx = itmsg_alloc(ITMSG_TYPE_POWER_OFF_RESP, 0, NULL, 0);
254 OSMO_ASSERT(tx);
255 break;
256
257 case ITMSG_TYPE_POWER_ON_REQ:
258 pdu = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot,
259 false, true, true, true);
260 server_conn_send_rspro(&bc->bankd_conn, pdu);
261 /* respond to IFD */
262 tx = itmsg_alloc(ITMSG_TYPE_POWER_ON_RESP, 0, NULL, 0);
263 OSMO_ASSERT(tx);
264 break;
265
266 case ITMSG_TYPE_RESET_REQ:
267 /* reset the [remote] card */
268 pdu = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot,
269 true, true, true, true);
270 server_conn_send_rspro(&bc->bankd_conn, pdu);
271 /* and take it out of reset again */
272 pdu = rspro_gen_ClientSlotStatusInd(bc->srv_conn.clslot, &bslot,
273 false, true, true, true);
274 server_conn_send_rspro(&bc->bankd_conn, pdu);
275 /* respond to IFD */
276 tx = itmsg_alloc(ITMSG_TYPE_RESET_RESP, 0, NULL, 0);
277 OSMO_ASSERT(tx);
278 break;
279 case ITMSG_TYPE_C_APDU_REQ:
280 if (!bc->srv_conn.clslot) {
281 LOGP(DMAIN, LOGL_ERROR, "Cannot send command; no client slot\n");
282 /* FIXME: Response? */
283 return;
284 }
285
286 /* Send CMD APDU to [remote] card */
287 pdu = rspro_gen_TpduModem2Card(bc->srv_conn.clslot, &bslot, itmsg->data, itmsg->len);
288 server_conn_send_rspro(&bc->bankd_conn, pdu);
289 /* response will come in asynchronously */
290 break;
291 default:
292 LOGP(DMAIN, LOGL_ERROR, "Unknown inter-thread msg type %u\n", itmsg->type);
293 break;
294 }
295
296 if (tx)
297 enqueue_to_ifd(ct, tx);
298
299}
300
301/* call-back function for inter-thread socket */
302static int it_sock_fd_cb(struct osmo_fd *ofd, unsigned int what)
303{
304 struct client_thread *ct = ofd->data;
305 int rc;
306
307 if (what & OSMO_FD_READ) {
308 struct msgb *msg = msgb_alloc_c(OTC_GLOBAL, 1024, "Rx it_fd");
309 struct itmsg *itmsg;
310
311 OSMO_ASSERT(msg);
312 rc = read(ofd->fd, msg->tail, msgb_tailroom(msg));
313 if (rc <= 0) {
314 LOGP(DMAIN, LOGL_ERROR, "Error reading from inter-thread fd: %d\n", rc);
315 pthread_exit(NULL);
316 }
317 msgb_put(msg, rc);
318 itmsg = (struct itmsg *) msgb_data(msg);
319 if (msgb_length(msg) < sizeof(*itmsg) ||
320 msgb_length(msg) < sizeof(*itmsg) + itmsg->len) {
321 LOGP(DMAIN, LOGL_ERROR, "Dropping short inter-thread message\n");
322 } else {
323 handle_it_msg(ct, itmsg);
324 }
325 msgb_free(msg);
326 }
327
328 if (what & OSMO_FD_WRITE) {
329 struct msgb *msg = msgb_dequeue(&ct->it_msgq);
330 if (!msg) {
331 /* last message: disable write events */
332 ofd->when &= ~OSMO_FD_WRITE;
333 } else {
334 unsigned int len = msgb_length(msg);
335 rc = write(ofd->fd, msgb_data(msg), len);
336 msgb_free(msg);
337 if (rc < len) {
338 LOGP(DMAIN, LOGL_ERROR, "Short write on inter-thread fd: %d < %d\n",
339 rc, len);
340 }
341 }
342 }
343
344
345 return 0;
346}
347
348/* release all resources allocated by thread */
349static void client_pthread_cleanup(void *arg)
350{
351 struct client_thread *ct = arg;
352
353 LOGP(DMAIN, LOGL_INFO, "Cleaning up remsim-client thread\n");
354 //FIXME remsim_client_destroy(ct->bc);
355 ct->bc = NULL;
356 msgb_queue_free(&ct->it_msgq);
357 osmo_fd_unregister(&ct->it_ofd);
358 close(ct->it_ofd.fd);
359 ct->it_ofd.fd = -1;
360 talloc_free(ct);
361}
362
363/* main function of remsim-client pthread */
364static void *client_pthread_main(void *arg)
365{
366 struct client_thread_cfg *cfg = arg;
Harald Welte0e968cc2020-02-22 18:16:16 +0100367 struct client_config *ccfg;
Harald Welte9fac4962020-02-14 21:01:23 +0100368 struct client_thread *ct;
Harald Welteeea631b2022-05-03 15:14:19 +0200369 char hostname[256];
Harald Welte9fac4962020-02-14 21:01:23 +0100370 int rc;
371
Harald Welteeea631b2022-05-03 15:14:19 +0200372 if (gethostname(hostname, sizeof(hostname)) < 0)
373 OSMO_STRLCPY_ARRAY(hostname, "unknown");
374
Harald Welte9fac4962020-02-14 21:01:23 +0100375 osmo_select_init();
376 rc = osmo_ctx_init("client");
377 OSMO_ASSERT(rc == 0);
378
379 ct = talloc_zero(OTC_GLOBAL, struct client_thread);
380 OSMO_ASSERT(ct);
381
Harald Welte0e968cc2020-02-22 18:16:16 +0100382 ccfg = client_config_init(ct);
383 OSMO_ASSERT(ccfg);
384 osmo_talloc_replace_string(ccfg, &ccfg->server_host, cfg->server_host);
385 if (cfg->server_port >= 0)
386 ccfg->server_port = cfg->server_port;
387 ccfg->client_id = cfg->client_id;
388 ccfg->client_slot = cfg->client_slot;
389
Harald Welte9fac4962020-02-14 21:01:23 +0100390 if (!talloc_asn1_ctx)
391 talloc_asn1_ctx= talloc_named_const(ct, 0, "asn1");
392
Harald Welteeea631b2022-05-03 15:14:19 +0200393 ct->bc = remsim_client_create(ct, hostname, "remsim_ifdhandler", ccfg);
Harald Welte9fac4962020-02-14 21:01:23 +0100394 OSMO_ASSERT(ct->bc);
395 ct->bc->data = ct;
Harald Welte9fac4962020-02-14 21:01:23 +0100396
397 INIT_LLIST_HEAD(&ct->it_msgq);
398 osmo_fd_setup(&ct->it_ofd, cfg->it_sock_fd, OSMO_FD_READ, &it_sock_fd_cb, ct, 0);
Harald Welte58eebe12023-07-18 13:36:05 +0200399 rc = osmo_fd_register(&ct->it_ofd);
400 OSMO_ASSERT(rc == 0);
Harald Welte9fac4962020-02-14 21:01:23 +0100401
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 = {
Harald Welte9fac4962020-02-14 21:01:23 +0100567 .server_host = "127.0.0.1",
568 .server_port = -1,
569 .client_id = 0,
570 .client_slot = 0,
571 };
572 char *r, *client_id, *slot_nr, *host, *port;
573
574 if (LUN2RDR(Lun) != 0)
575 return IFD_NO_SUCH_DEVICE;
576
577 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client))
578 return IFD_NO_SUCH_DEVICE;
579
580 ensure_osmo_ctx();
581
582 client_id = strtok_r(DeviceName, ":", &r);
583 if (!client_id)
584 goto end_parse;
585 cfg.client_id = atoi(client_id);
586
587 slot_nr = strtok_r(NULL, ":", &r);
588 if (!slot_nr)
589 goto end_parse;
590 cfg.client_slot = atoi(slot_nr);
591
592 host = strtok_r(NULL, ":", &r);
593 if (!host)
594 goto end_parse;
595 cfg.server_host = strdup(host);
596
597 port = strtok_r(NULL, ":", &r);
598 cfg.server_port = atoi(port);
599
600
601end_parse:
602 LOGP(DMAIN, LOGL_NOTICE, "remsim-client C%d:%d bankd=%s:%d\n",
603 cfg.client_id, cfg.client_slot, cfg.server_host, cfg.server_port);
604
605 ic = create_ifd_client(&cfg);
606 if (ic) {
607 ifd_client[LUN2SLOT(Lun)] = ic;
608 return IFD_SUCCESS;
609 } else
610 return IFD_COMMUNICATION_ERROR;
611}
612
613RESPONSECODE IFDHControl(DWORD Lun, DWORD dwControlCode, PUCHAR TxBuffer, DWORD TxLength,
614 PUCHAR RxBuffer, DWORD RxLength, LPDWORD pdwBytesReturned)
615{
616 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
617
618 ensure_osmo_ctx();
619
620 if (LUN2RDR(Lun) != 0) {
621 r = IFD_NO_SUCH_DEVICE;
622 goto err;
623 }
624
625 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
626 r = IFD_NO_SUCH_DEVICE;
627 goto err;
628 }
629
630 if (pdwBytesReturned)
631 *pdwBytesReturned = 0;
632
633 r = IFD_ERROR_NOT_SUPPORTED;
634err:
635 LOG_EXIT(Lun, r);
636 return r;
637}
638
639RESPONSECODE IFDHCloseChannel(DWORD Lun)
640{
641 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
642
643 ensure_osmo_ctx();
644
645 if (LUN2RDR(Lun) != 0) {
646 r = IFD_NO_SUCH_DEVICE;
647 goto err;
648 }
649
650 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
651 r = IFD_NO_SUCH_DEVICE;
652 goto err;
653 }
654
655 destroy_ifd_client(ifd_client[LUN2SLOT(Lun)]);
656 ifd_client[LUN2SLOT(Lun)] = NULL;
657
658 r = IFD_SUCCESS;
659err:
660 LOG_EXIT(Lun, r);
661 return r;
662}
663
664RESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value)
665{
666 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
667 struct ifd_client *ic;
668 struct msgb *rx, *tx;
669 struct itmsg *rx_it;
670
671 ensure_osmo_ctx();
672
673 if (LUN2RDR(Lun) != 0) {
674 r = IFD_NO_SUCH_DEVICE;
675 goto err;
676 }
677
678 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
679 r = IFD_NO_SUCH_DEVICE;
680 goto err;
681 }
682
683 ic = ifd_client[LUN2SLOT(Lun)];
684 if (!ic) {
685 r = IFD_NO_SUCH_DEVICE;
686 goto err;
687 }
688
689 if (!Length || !Value)
690 goto err;
691
692 switch (Tag) {
693 case TAG_IFD_ATR:
694 /* Return the ATR and its size */
695 tx = itmsg_alloc(ITMSG_TYPE_ATR_REQ, 0, NULL, 0);
696 OSMO_ASSERT(tx);
697 rx = ifd_xceive_client(ic, tx);
698 if (!rx) {
699 r = IFD_NO_SUCH_DEVICE;
700 goto err;
701 }
702 rx_it = (struct itmsg *)msgb_data(rx);
703 if (*Length > rx_it->len)
704 *Length = rx_it->len;
705 memcpy(Value, rx_it->data, *Length);
706 msgb_free(rx);
707 break;
708 case TAG_IFD_SIMULTANEOUS_ACCESS:
709 /* Return the number of sessions (readers) the driver
710 * can handle in Value[0]. This is used for multiple
711 * readers sharing the same driver. */
712 if (*Length < 1)
713 goto err;
714 *Value = 1;
715 *Length = 1;
716 break;
717 case TAG_IFD_SLOTS_NUMBER:
718 /* Return the number of slots in this reader in Value[0] */
719 if (*Length < 1)
720 goto err;
721 *Value = 1;
722 *Length = 1;
723 break;
724 case TAG_IFD_THREAD_SAFE:
725 /* If the driver supports more than one reader (see
726 * TAG_IFD_SIMULTANEOUS_ACCESS above) this tag indicates
727 * if the driver supports access to multiple readers at
728 * the same time. */
729 if (*Length < 1)
730 goto err;
731 *Value = 0;
732 *Length = 1;
733 break;
734 case TAG_IFD_SLOT_THREAD_SAFE:
735 /* If the reader has more than one slot (see
736 * TAG_IFD_SLOTS_NUMBER above) this tag indicates if the
737 * driver supports access to multiple slots of the same
738 * reader at the same time. */
739 if (*Length < 1)
740 goto err;
741 *Value = 0;
742 *Length = 1;
743 break;
744 default:
745 r = IFD_ERROR_TAG;
746 goto err;
747 }
748
749 r = IFD_SUCCESS;
750
751err:
752 if (r != IFD_SUCCESS && Length)
753 *Length = 0;
754
755 LOG_EXITF(Lun, r, "%s", get_value_string(ifd_tag_names, Tag));
756 return r;
757}
758
759RESPONSECODE IFDHSetCapabilities(DWORD Lun, DWORD Tag, DWORD Length, PUCHAR Value)
760{
761 ensure_osmo_ctx();
762
763 if (LUN2RDR(Lun) != 0)
764 return IFD_NO_SUCH_DEVICE;
765
766 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client))
767 return IFD_NO_SUCH_DEVICE;
768
769
770 LOG_EXIT(Lun, IFD_NOT_SUPPORTED);
771 return IFD_NOT_SUPPORTED;
772}
773
774RESPONSECODE IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol, UCHAR Flags, UCHAR PTS1,
775 UCHAR PTS2, UCHAR PTS3)
776{
777 ensure_osmo_ctx();
778
779 if (LUN2RDR(Lun) != 0)
780 return IFD_NO_SUCH_DEVICE;
781
782 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client))
783 return IFD_NO_SUCH_DEVICE;
784
785 LOG_EXIT(Lun, IFD_SUCCESS);
786 return IFD_SUCCESS;
787}
788
789RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength)
790{
791 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
792 struct ifd_client *ic;
793 struct msgb *rx, *tx;
794
795 ensure_osmo_ctx();
796
797 if (LUN2RDR(Lun) != 0) {
798 r = IFD_NO_SUCH_DEVICE;
799 goto err;
800 }
801
802 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
803 r = IFD_NO_SUCH_DEVICE;
804 goto err;
805 }
806
807 ic = ifd_client[LUN2SLOT(Lun)];
808 if (!ic) {
809 r = IFD_NO_SUCH_DEVICE;
810 goto err;
811 }
812
813 switch (Action) {
814 case IFD_POWER_DOWN:
815 tx = itmsg_alloc(ITMSG_TYPE_POWER_OFF_REQ, 0, NULL, 0);
816 break;
817 case IFD_POWER_UP:
818 tx = itmsg_alloc(ITMSG_TYPE_POWER_ON_REQ, 0, NULL, 0);
819 break;
820 case IFD_RESET:
821 tx = itmsg_alloc(ITMSG_TYPE_RESET_REQ, 0, NULL, 0);
822 break;
823 default:
824 r = IFD_NOT_SUPPORTED;
825 goto err;
826 }
827
828 rx = ifd_xceive_client(ic, tx);
829 if (!rx) {
830 r = IFD_NO_SUCH_DEVICE;
831 goto err;
832 }
833
834 r = IFD_SUCCESS;
835 msgb_free(rx);
836
837err:
838 if (r != IFD_SUCCESS && AtrLength)
839 *AtrLength = 0;
840 else
841 r = IFDHGetCapabilities(Lun, TAG_IFD_ATR, AtrLength, Atr);
842
843 LOG_EXIT(Lun, r);
844 return r;
845}
846
847RESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci, PUCHAR TxBuffer,
848 DWORD TxLength, PUCHAR RxBuffer, PDWORD RxLength,
849 PSCARD_IO_HEADER RecvPci)
850{
851 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
852 struct ifd_client *ic;
853 struct msgb *rx, *tx;
854 struct itmsg *rx_it;
855
856 ensure_osmo_ctx();
857
858 if (LUN2RDR(Lun) != 0) {
859 r = IFD_NO_SUCH_DEVICE;
860 goto err;
861 }
862
863 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
864 r = IFD_NO_SUCH_DEVICE;
865 goto err;
866 }
867
868 ic = ifd_client[LUN2SLOT(Lun)];
869 if (!ic) {
870 r = IFD_NO_SUCH_DEVICE;
871 goto err;
872 }
873
874 tx = itmsg_alloc(ITMSG_TYPE_C_APDU_REQ, 0, TxBuffer, TxLength);
875 OSMO_ASSERT(tx);
876 /* transmit C-APDU to remote reader + blocking wait for response from peer */
877 rx = ifd_xceive_client(ic, tx);
878 if (!rx) {
879 r = IFD_NO_SUCH_DEVICE;
880 goto err;
881 }
882 rx_it = (struct itmsg *) msgb_data(rx);
883 if (*RxLength > rx_it->len)
884 *RxLength = rx_it->len;
885 memcpy(RxBuffer, rx_it->data, *RxLength);
886 msgb_free(rx);
887
888 r = IFD_SUCCESS;
889err:
890 if (r != IFD_SUCCESS && RxLength)
891 *RxLength = 0;
892
893 LOG_EXIT(Lun, r);
894 return r;
895}
896
897RESPONSECODE IFDHICCPresence(DWORD Lun)
898{
899 RESPONSECODE r = IFD_COMMUNICATION_ERROR;
900 struct ifd_client *ic;
901 struct msgb *rx, *tx;
902 struct itmsg *rx_it;
903
904 ensure_osmo_ctx();
905
906 if (LUN2RDR(Lun) != 0) {
907 r = IFD_NO_SUCH_DEVICE;
908 goto err;
909 }
910
911 if (LUN2SLOT(Lun) >= ARRAY_SIZE(ifd_client)) {
912 r = IFD_NO_SUCH_DEVICE;
913 goto err;
914 }
915
916 ic = ifd_client[LUN2SLOT(Lun)];
917 if (!ic) {
918 r = IFD_NO_SUCH_DEVICE;
919 goto err;
920 }
921
922 tx = itmsg_alloc(ITMSG_TYPE_CARD_PRES_REQ, 0, NULL, 0);
923 OSMO_ASSERT(tx);
924 rx = ifd_xceive_client(ic, tx);
925 if (!rx) {
926 r = IFD_NO_SUCH_DEVICE;
927 goto err;
928 }
929 rx_it = (struct itmsg *) msgb_data(rx);
930 if (rx_it->status == 0)
931 r = IFD_SUCCESS;
932 else
933 r = IFD_ICC_NOT_PRESENT;
934
935err:
936 LOG_EXIT(Lun, r);
937 return r;
938}
939
940static __attribute__((constructor)) void on_dso_load_ifd(void)
941{
942 void *g_tall_ctx = NULL;
943 ensure_osmo_ctx();
944 osmo_init_logging2(g_tall_ctx, &log_info);
945}