blob: 01fe877689a7682b38b1da0f73a4a92c71be393f [file] [log] [blame]
Harald Welte45c948c2018-09-23 19:26:52 +02001
2#include <osmocom/core/linuxlist.h>
3#include <osmocom/core/talloc.h>
4#include <osmocom/core/utils.h>
Harald Welted08d5052018-10-03 20:43:31 +02005#include <osmocom/core/fsm.h>
6#include <osmocom/core/logging.h>
Harald Welte45c948c2018-09-23 19:26:52 +02007
8#include <csv.h>
9
10#include "bankd.h"
Harald Welted08d5052018-10-03 20:43:31 +020011#include "rspro_util.h"
12
13/***********************************************************************
14 * RSPRO bank/slot-id <-> PCSC Reader name mapping
15 ***********************************************************************/
Harald Welte45c948c2018-09-23 19:26:52 +020016
17struct pcsc_slot_name {
18 struct llist_head list;
19 /* RSPRO bank slot number */
20 struct bank_slot slot;
21 /* String name of the reader in PC/SC world */
22 const char *name;
23};
24
25enum parser_state_name {
26 ST_NONE,
27 ST_BANK_NR,
28 ST_SLOT_NR,
29 ST_PCSC_NAME,
30};
31struct parser_state {
32 struct bankd *bankd;
33 enum parser_state_name state;
34 struct pcsc_slot_name *cur;
35};
36
37
38static void parser_state_init(struct parser_state *ps)
39{
40 ps->state = ST_BANK_NR;
41 ps->cur = NULL;
42}
43
44static void cb1(void *s, size_t len, void *data)
45{
46 char *field = (char *) s;
47 struct parser_state *ps = data;
48
49 switch (ps->state) {
50 case ST_BANK_NR:
51 OSMO_ASSERT(!ps->cur);
52 ps->cur = talloc_zero(ps->bankd, struct pcsc_slot_name);
53 OSMO_ASSERT(ps->cur);
54 ps->cur->slot.bank_id = atoi(field);
55 ps->state = ST_SLOT_NR;
56 break;
57 case ST_SLOT_NR:
58 OSMO_ASSERT(ps->cur);
59 ps->cur->slot.slot_nr = atoi(field);
60 ps->state = ST_PCSC_NAME;
61 break;
62 case ST_PCSC_NAME:
63 OSMO_ASSERT(ps->cur);
64 ps->cur->name = talloc_strdup(ps->cur, field);
65 break;
66 default:
67 OSMO_ASSERT(0);
68 }
69}
70
71static void cb2(int c, void *data)
72{
73 struct parser_state *ps = data;
74 struct pcsc_slot_name *sn = ps->cur;
75
76 printf("PC/SC slot name: %u/%u -> '%s'\n", sn->slot.bank_id, sn->slot.slot_nr, sn->name);
77 llist_add_tail(&sn->list, &ps->bankd->pcsc_slot_names);
78
79 ps->state = ST_BANK_NR;
80 ps->cur = NULL;
81}
82
83int bankd_pcsc_read_slotnames(struct bankd *bankd, const char *csv_file)
84{
85 FILE *fp;
86 struct csv_parser p;
87 char buf[1024];
88 size_t bytes_read;
89 struct parser_state ps;
90
91 if (csv_init(&p, CSV_APPEND_NULL) != 0)
92 return -1;
93
94 fp = fopen(csv_file, "rb");
95 if (!fp)
96 return -1;
97
98 parser_state_init(&ps);
99 ps.bankd = bankd;
100
101 while ((bytes_read = fread(buf, 1, sizeof(buf), fp)) > 0) {
102 if (csv_parse(&p, buf, bytes_read, cb1, cb2, &ps) != bytes_read) {
103 fprintf(stderr, "Error parsing CSV: %s\n", csv_strerror(csv_error(&p)));
104 fclose(fp);
105 return -1;
106 }
107 }
108
109 csv_fini(&p, cb1, cb2, &ps);
110 fclose(fp);
111 csv_free(&p);
112
113 return 0;
114}
115
116const char *bankd_pcsc_get_slot_name(struct bankd *bankd, const struct bank_slot *slot)
117{
118 struct pcsc_slot_name *cur;
119
120 llist_for_each_entry(cur, &bankd->pcsc_slot_names, list) {
121 if (bank_slot_equals(&cur->slot, slot))
122 return cur->name;
123 }
124 return NULL;
125}
Harald Welted08d5052018-10-03 20:43:31 +0200126
127
128/***********************************************************************
129 * SCard related FSM
130 ***********************************************************************/
131
132#define S(x) (1 << (x))
133
134#define T2_TIMEOUT_SECS 10
135#define T1_TIMEOUT_SECS 10
136
137enum sc_fsm_states {
138 SC_ST_CARD_ABSENT,
139 SC_ST_CARD_PRESENT,
140};
141
142static const struct value_string sc_fsm_event_names[] = {
143 { SC_E_CONNECT_CMD, "CONNECT_CMD" },
144 { SC_E_DISCONNECT_CMD, "DISCONNECT_CMD" },
145 { SC_E_TPDU_CMD, "TPDU_CMD" },
146 { 0, NULL }
147};
148
149/* an attempt at SCardConnect */
150static void attempt_sc_connect(struct osmo_fsm_inst *fi)
151{
152 struct bankd_worker *worker = fi->priv;
153 LONG rc;
154 DWORD protocol;
155
156 /* another attempt at SCardConnect */
157 rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name,
158 SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0,
159 &worker->reader.pcsc.hCard, &protocol);
160 if (rc == SCARD_S_SUCCESS) {
161 osmo_fsm_inst_state_chg(fi, SC_ST_CARD_PRESENT, T2_TIMEOUT_SECS, 2);
162 /* FIXME: inform client */
163 } else {
164 /* schedule the next SCardConnect request */
165 osmo_timer_schedule(&fi->timer, T1_TIMEOUT_SECS, 1);
166 }
167}
168
169/* no card currently present; attempt to re-connect via timer if asked to */
170static void sc_st_card_absent(struct osmo_fsm_inst *fi, uint32_t event, void *data)
171{
172 struct bankd_worker *worker = fi->priv;
173 const struct TpduModemToCard *mdm2sim;
174 const RsproPDU_t *pdu, *pdu_resp;
175
176 switch (event) {
177 case SC_E_CONNECT_CMD:
178 attempt_sc_connect(fi);
179 break;
180 case SC_E_TPDU_CMD:
181 pdu = data;
182 mdm2sim = &pdu->msg.choice.tpduModemToCard;
183 /* reject transceiving the PDU; we're not connected */
184#if 0
185 pdu_resp = rspro_gen_TpduCard2Modem(&mdm2sim->toBankSlot, &mdm2sim->fromClientSlot,
186 rx_buf, rx_buf_len);
187 worker_send_rspro(worker, pdu_resp);
188#endif
189 break;
190 default:
191 OSMO_ASSERT(0);
192 }
193}
194
195static void sc_st_card_present(struct osmo_fsm_inst *fi, uint32_t event, void *data)
196{
197 struct bankd_worker *worker = fi->priv;
198 const RsproPDU_t *pdu;
199 LONG rc;
200
201 switch (event) {
202 case SC_E_TPDU_CMD:
203 /* transceive an APDU */
204 pdu = data;
205 worker_handle_tpduModemToCard(worker, pdu);
206 break;
207 case SC_E_DISCONNECT_CMD:
208 rc = SCardDisconnect(worker->reader.pcsc.hCard, SCARD_UNPOWER_CARD);
209 /* FIXME: evaluate rc */
210 osmo_fsm_inst_state_chg(fi, SC_ST_CARD_ABSENT, 0, 0);
211 break;
212 default:
213 OSMO_ASSERT(0);
214 }
215}
216
217static int sc_timer_cb(struct osmo_fsm_inst *fi)
218{
219 struct bankd_worker *worker = fi->priv;
220 char reader_name[32];
221 uint8_t atr[32];
222 DWORD reader_state, protocol;
223 DWORD atr_len = sizeof(atr);
224 DWORD reader_name_len = sizeof(atr);
225 LONG rc;
226
227 switch (fi->T) {
228 case 1:
229 attempt_sc_connect(fi);
230 break;
231 case 2:
232 /* another iteration of SCardStatus */
233 rc = SCardStatus(worker->reader.pcsc.hCard, reader_name, &reader_name_len,
234 &reader_state, &protocol, atr, &atr_len);
235 if (rc == SCARD_S_SUCCESS) {
236 RsproPDU_t *pdu = NULL;
237 /* Determine any changes in state, and if so, report to client */
238 if (reader_state != worker->reader.pcsc.dwState) {
239 worker->reader.pcsc.dwState = reader_state;
240 /* FIXME: inform client */
241 //pdu = rspro_gen_SetAtrReq(foo, bar, worker->atr, worker->atr_len);
242 //worker_send_rspro(worker, pdu);
243 }
244 if (atr_len != worker->atr_len || memcmp(atr, worker->atr, atr_len)) {
245 ClientSlot_t clslot = client_slot2asn(&worker->client.clslot);
246 OSMO_ASSERT(atr_len < sizeof(worker->atr));
247 memcpy(worker->atr, atr, atr_len);
248 worker->atr_len = atr_len;
249 /* inform client */
250 pdu = rspro_gen_SetAtrReq(&clslot, worker->atr, worker->atr_len);
251 worker_send_rspro(worker, pdu);
252 }
253 /* schedule the next SCardStatus request */
254 osmo_timer_schedule(&fi->timer, T2_TIMEOUT_SECS, 0);
255 } else
256 osmo_fsm_inst_state_chg(fi, SC_ST_CARD_ABSENT, T1_TIMEOUT_SECS, 1);
257 break;
258 default:
259 OSMO_ASSERT(0);
260 }
261 return 0;
262}
263
264static const struct osmo_fsm_state sc_fsm_states[] = {
265 [SC_ST_CARD_ABSENT] = {
266 .in_event_mask = S(SC_E_CONNECT_CMD) | S(SC_E_DISCONNECT_CMD) | S(SC_E_TPDU_CMD),
267 .out_state_mask = S(SC_ST_CARD_PRESENT) | S(SC_ST_CARD_ABSENT),
268 .name = "CARD_ABSENT",
269 .action = sc_st_card_absent,
270 },
271 [SC_ST_CARD_PRESENT] = {
272 .in_event_mask = S(SC_E_DISCONNECT_CMD) | S(SC_E_TPDU_CMD),
273 .out_state_mask = S(SC_ST_CARD_PRESENT) | S(SC_ST_CARD_ABSENT),
274 .name = "CART_PRESENT",
275 .action = sc_st_card_present,
276 },
277};
278
279static struct osmo_fsm sc_fsm = {
280 .name = "SC",
281 .states = sc_fsm_states,
282 .num_states = ARRAY_SIZE(sc_fsm_states),
283 .timer_cb = sc_timer_cb,
284 .event_names = sc_fsm_event_names,
285};
286
287static bool fsm_initialized = false;
288
289struct osmo_fsm_inst *sc_fsm_alloc(struct bankd_worker *worker)
290{
291 struct osmo_fsm_inst *fi;
292 char num[8];
293
294 if (!fsm_initialized) {
295 osmo_fsm_register(&sc_fsm);
296 fsm_initialized = true;
297 }
298
299 snprintf(num, 8, "%d", worker->num);
300
301 fi = osmo_fsm_inst_alloc(&sc_fsm, worker, worker, LOGL_DEBUG, num);
302
303 osmo_fsm_inst_dispatch(fi, SC_E_CONNECT_CMD, NULL);
304
305 return fi;
306}