|
|
|
@ -2,10 +2,17 @@
|
|
|
|
|
#include <osmocom/core/linuxlist.h> |
|
|
|
|
#include <osmocom/core/talloc.h> |
|
|
|
|
#include <osmocom/core/utils.h> |
|
|
|
|
#include <osmocom/core/fsm.h> |
|
|
|
|
#include <osmocom/core/logging.h> |
|
|
|
|
|
|
|
|
|
#include <csv.h> |
|
|
|
|
|
|
|
|
|
#include "bankd.h" |
|
|
|
|
#include "rspro_util.h" |
|
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
|
* RSPRO bank/slot-id <-> PCSC Reader name mapping |
|
|
|
|
***********************************************************************/ |
|
|
|
|
|
|
|
|
|
struct pcsc_slot_name { |
|
|
|
|
struct llist_head list; |
|
|
|
@ -116,3 +123,184 @@ const char *bankd_pcsc_get_slot_name(struct bankd *bankd, const struct bank_slot
|
|
|
|
|
} |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
|
* SCard related FSM |
|
|
|
|
***********************************************************************/ |
|
|
|
|
|
|
|
|
|
#define S(x) (1 << (x)) |
|
|
|
|
|
|
|
|
|
#define T2_TIMEOUT_SECS 10 |
|
|
|
|
#define T1_TIMEOUT_SECS 10 |
|
|
|
|
|
|
|
|
|
enum sc_fsm_states { |
|
|
|
|
SC_ST_CARD_ABSENT, |
|
|
|
|
SC_ST_CARD_PRESENT, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static const struct value_string sc_fsm_event_names[] = { |
|
|
|
|
{ SC_E_CONNECT_CMD, "CONNECT_CMD" }, |
|
|
|
|
{ SC_E_DISCONNECT_CMD, "DISCONNECT_CMD" }, |
|
|
|
|
{ SC_E_TPDU_CMD, "TPDU_CMD" }, |
|
|
|
|
{ 0, NULL } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/* an attempt at SCardConnect */ |
|
|
|
|
static void attempt_sc_connect(struct osmo_fsm_inst *fi) |
|
|
|
|
{ |
|
|
|
|
struct bankd_worker *worker = fi->priv; |
|
|
|
|
LONG rc; |
|
|
|
|
DWORD protocol; |
|
|
|
|
|
|
|
|
|
/* another attempt at SCardConnect */ |
|
|
|
|
rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name, |
|
|
|
|
SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0, |
|
|
|
|
&worker->reader.pcsc.hCard, &protocol); |
|
|
|
|
if (rc == SCARD_S_SUCCESS) { |
|
|
|
|
osmo_fsm_inst_state_chg(fi, SC_ST_CARD_PRESENT, T2_TIMEOUT_SECS, 2); |
|
|
|
|
/* FIXME: inform client */ |
|
|
|
|
} else { |
|
|
|
|
/* schedule the next SCardConnect request */ |
|
|
|
|
osmo_timer_schedule(&fi->timer, T1_TIMEOUT_SECS, 1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* no card currently present; attempt to re-connect via timer if asked to */ |
|
|
|
|
static void sc_st_card_absent(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
|
|
|
|
{ |
|
|
|
|
struct bankd_worker *worker = fi->priv; |
|
|
|
|
const struct TpduModemToCard *mdm2sim; |
|
|
|
|
const RsproPDU_t *pdu, *pdu_resp; |
|
|
|
|
|
|
|
|
|
switch (event) { |
|
|
|
|
case SC_E_CONNECT_CMD: |
|
|
|
|
attempt_sc_connect(fi); |
|
|
|
|
break; |
|
|
|
|
case SC_E_TPDU_CMD: |
|
|
|
|
pdu = data; |
|
|
|
|
mdm2sim = &pdu->msg.choice.tpduModemToCard; |
|
|
|
|
/* reject transceiving the PDU; we're not connected */ |
|
|
|
|
#if 0 |
|
|
|
|
pdu_resp = rspro_gen_TpduCard2Modem(&mdm2sim->toBankSlot, &mdm2sim->fromClientSlot, |
|
|
|
|
rx_buf, rx_buf_len); |
|
|
|
|
worker_send_rspro(worker, pdu_resp); |
|
|
|
|
#endif |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
OSMO_ASSERT(0); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void sc_st_card_present(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
|
|
|
|
{ |
|
|
|
|
struct bankd_worker *worker = fi->priv; |
|
|
|
|
const RsproPDU_t *pdu; |
|
|
|
|
LONG rc; |
|
|
|
|
|
|
|
|
|
switch (event) { |
|
|
|
|
case SC_E_TPDU_CMD: |
|
|
|
|
/* transceive an APDU */ |
|
|
|
|
pdu = data; |
|
|
|
|
worker_handle_tpduModemToCard(worker, pdu); |
|
|
|
|
break; |
|
|
|
|
case SC_E_DISCONNECT_CMD: |
|
|
|
|
rc = SCardDisconnect(worker->reader.pcsc.hCard, SCARD_UNPOWER_CARD); |
|
|
|
|
/* FIXME: evaluate rc */ |
|
|
|
|
osmo_fsm_inst_state_chg(fi, SC_ST_CARD_ABSENT, 0, 0); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
OSMO_ASSERT(0); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int sc_timer_cb(struct osmo_fsm_inst *fi) |
|
|
|
|
{ |
|
|
|
|
struct bankd_worker *worker = fi->priv; |
|
|
|
|
char reader_name[32]; |
|
|
|
|
uint8_t atr[32]; |
|
|
|
|
DWORD reader_state, protocol; |
|
|
|
|
DWORD atr_len = sizeof(atr); |
|
|
|
|
DWORD reader_name_len = sizeof(atr); |
|
|
|
|
LONG rc; |
|
|
|
|
|
|
|
|
|
switch (fi->T) { |
|
|
|
|
case 1: |
|
|
|
|
attempt_sc_connect(fi); |
|
|
|
|
break; |
|
|
|
|
case 2: |
|
|
|
|
/* another iteration of SCardStatus */ |
|
|
|
|
rc = SCardStatus(worker->reader.pcsc.hCard, reader_name, &reader_name_len, |
|
|
|
|
&reader_state, &protocol, atr, &atr_len); |
|
|
|
|
if (rc == SCARD_S_SUCCESS) { |
|
|
|
|
RsproPDU_t *pdu = NULL; |
|
|
|
|
/* Determine any changes in state, and if so, report to client */ |
|
|
|
|
if (reader_state != worker->reader.pcsc.dwState) { |
|
|
|
|
worker->reader.pcsc.dwState = reader_state; |
|
|
|
|
/* FIXME: inform client */ |
|
|
|
|
//pdu = rspro_gen_SetAtrReq(foo, bar, worker->atr, worker->atr_len);
|
|
|
|
|
//worker_send_rspro(worker, pdu);
|
|
|
|
|
} |
|
|
|
|
if (atr_len != worker->atr_len || memcmp(atr, worker->atr, atr_len)) { |
|
|
|
|
ClientSlot_t clslot = client_slot2asn(&worker->client.clslot); |
|
|
|
|
OSMO_ASSERT(atr_len < sizeof(worker->atr)); |
|
|
|
|
memcpy(worker->atr, atr, atr_len); |
|
|
|
|
worker->atr_len = atr_len; |
|
|
|
|
/* inform client */ |
|
|
|
|
pdu = rspro_gen_SetAtrReq(&clslot, worker->atr, worker->atr_len); |
|
|
|
|
worker_send_rspro(worker, pdu); |
|
|
|
|
} |
|
|
|
|
/* schedule the next SCardStatus request */ |
|
|
|
|
osmo_timer_schedule(&fi->timer, T2_TIMEOUT_SECS, 0); |
|
|
|
|
} else |
|
|
|
|
osmo_fsm_inst_state_chg(fi, SC_ST_CARD_ABSENT, T1_TIMEOUT_SECS, 1); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
OSMO_ASSERT(0); |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const struct osmo_fsm_state sc_fsm_states[] = { |
|
|
|
|
[SC_ST_CARD_ABSENT] = { |
|
|
|
|
.in_event_mask = S(SC_E_CONNECT_CMD) | S(SC_E_DISCONNECT_CMD) | S(SC_E_TPDU_CMD), |
|
|
|
|
.out_state_mask = S(SC_ST_CARD_PRESENT) | S(SC_ST_CARD_ABSENT), |
|
|
|
|
.name = "CARD_ABSENT", |
|
|
|
|
.action = sc_st_card_absent, |
|
|
|
|
}, |
|
|
|
|
[SC_ST_CARD_PRESENT] = { |
|
|
|
|
.in_event_mask = S(SC_E_DISCONNECT_CMD) | S(SC_E_TPDU_CMD), |
|
|
|
|
.out_state_mask = S(SC_ST_CARD_PRESENT) | S(SC_ST_CARD_ABSENT), |
|
|
|
|
.name = "CART_PRESENT", |
|
|
|
|
.action = sc_st_card_present, |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static struct osmo_fsm sc_fsm = { |
|
|
|
|
.name = "SC", |
|
|
|
|
.states = sc_fsm_states, |
|
|
|
|
.num_states = ARRAY_SIZE(sc_fsm_states), |
|
|
|
|
.timer_cb = sc_timer_cb, |
|
|
|
|
.event_names = sc_fsm_event_names, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static bool fsm_initialized = false; |
|
|
|
|
|
|
|
|
|
struct osmo_fsm_inst *sc_fsm_alloc(struct bankd_worker *worker) |
|
|
|
|
{ |
|
|
|
|
struct osmo_fsm_inst *fi; |
|
|
|
|
char num[8]; |
|
|
|
|
|
|
|
|
|
if (!fsm_initialized) { |
|
|
|
|
osmo_fsm_register(&sc_fsm); |
|
|
|
|
fsm_initialized = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
snprintf(num, 8, "%d", worker->num); |
|
|
|
|
|
|
|
|
|
fi = osmo_fsm_inst_alloc(&sc_fsm, worker, worker, LOGL_DEBUG, num); |
|
|
|
|
|
|
|
|
|
osmo_fsm_inst_dispatch(fi, SC_E_CONNECT_CMD, NULL); |
|
|
|
|
|
|
|
|
|
return fi; |
|
|
|
|
} |
|
|
|
|