blob: f6e36834447dc241e22fea0bc1f5c4907a38670d [file] [log] [blame]
Harald Welte3dcdd202019-03-09 13:06:46 +01001/* (C) 2018-2019 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
Harald Welte45c948c2018-09-23 19:26:52 +020023
24#include <osmocom/core/linuxlist.h>
25#include <osmocom/core/talloc.h>
26#include <osmocom/core/utils.h>
27
28#include <csv.h>
29
30#include "bankd.h"
31
32struct pcsc_slot_name {
33 struct llist_head list;
34 /* RSPRO bank slot number */
35 struct bank_slot slot;
36 /* String name of the reader in PC/SC world */
37 const char *name;
38};
39
40enum parser_state_name {
41 ST_NONE,
42 ST_BANK_NR,
43 ST_SLOT_NR,
44 ST_PCSC_NAME,
45};
46struct parser_state {
47 struct bankd *bankd;
48 enum parser_state_name state;
49 struct pcsc_slot_name *cur;
50};
51
52
53static void parser_state_init(struct parser_state *ps)
54{
55 ps->state = ST_BANK_NR;
56 ps->cur = NULL;
57}
58
59static void cb1(void *s, size_t len, void *data)
60{
61 char *field = (char *) s;
62 struct parser_state *ps = data;
63
64 switch (ps->state) {
65 case ST_BANK_NR:
66 OSMO_ASSERT(!ps->cur);
67 ps->cur = talloc_zero(ps->bankd, struct pcsc_slot_name);
68 OSMO_ASSERT(ps->cur);
69 ps->cur->slot.bank_id = atoi(field);
70 ps->state = ST_SLOT_NR;
71 break;
72 case ST_SLOT_NR:
73 OSMO_ASSERT(ps->cur);
74 ps->cur->slot.slot_nr = atoi(field);
75 ps->state = ST_PCSC_NAME;
76 break;
77 case ST_PCSC_NAME:
78 OSMO_ASSERT(ps->cur);
79 ps->cur->name = talloc_strdup(ps->cur, field);
80 break;
81 default:
82 OSMO_ASSERT(0);
83 }
84}
85
86static void cb2(int c, void *data)
87{
88 struct parser_state *ps = data;
89 struct pcsc_slot_name *sn = ps->cur;
90
91 printf("PC/SC slot name: %u/%u -> '%s'\n", sn->slot.bank_id, sn->slot.slot_nr, sn->name);
92 llist_add_tail(&sn->list, &ps->bankd->pcsc_slot_names);
93
94 ps->state = ST_BANK_NR;
95 ps->cur = NULL;
96}
97
98int bankd_pcsc_read_slotnames(struct bankd *bankd, const char *csv_file)
99{
100 FILE *fp;
101 struct csv_parser p;
102 char buf[1024];
103 size_t bytes_read;
104 struct parser_state ps;
105
106 if (csv_init(&p, CSV_APPEND_NULL) != 0)
107 return -1;
108
109 fp = fopen(csv_file, "rb");
110 if (!fp)
111 return -1;
112
113 parser_state_init(&ps);
114 ps.bankd = bankd;
115
116 while ((bytes_read = fread(buf, 1, sizeof(buf), fp)) > 0) {
117 if (csv_parse(&p, buf, bytes_read, cb1, cb2, &ps) != bytes_read) {
118 fprintf(stderr, "Error parsing CSV: %s\n", csv_strerror(csv_error(&p)));
119 fclose(fp);
120 return -1;
121 }
122 }
123
124 csv_fini(&p, cb1, cb2, &ps);
125 fclose(fp);
126 csv_free(&p);
127
128 return 0;
129}
130
131const char *bankd_pcsc_get_slot_name(struct bankd *bankd, const struct bank_slot *slot)
132{
133 struct pcsc_slot_name *cur;
134
135 llist_for_each_entry(cur, &bankd->pcsc_slot_names, list) {
136 if (bank_slot_equals(&cur->slot, slot))
137 return cur->name;
138 }
139 return NULL;
140}
Harald Welte297d72e2019-03-28 18:42:35 +0100141
142
143#include <wintypes.h>
144#include <winscard.h>
145#include <pcsclite.h>
146
147#define PCSC_ERROR(w, rv, text) \
148if (rv != SCARD_S_SUCCESS) { \
149 LOGW((w), text ": %s (0x%lX)\n", pcsc_stringify_error(rv), rv); \
150 goto end; \
151} else { \
152 LOGW((w), ": OK\n"); \
153}
154
Harald Weltebbd18bd2019-12-16 13:11:50 +0100155static int pcsc_get_atr(struct bankd_worker *worker)
156{
157 long rc;
158 char pbReader[MAX_READERNAME];
159 /* use DWORD type as this is what the PC/SC API expects */
160 DWORD dwReaderLen = sizeof(pbReader);
161 DWORD dwAtrLen = worker->card.atr_len = sizeof(worker->card.atr);
162 DWORD dwState, dwProt;
163
164 rc = SCardStatus(worker->reader.pcsc.hCard, pbReader, &dwReaderLen, &dwState, &dwProt,
165 worker->card.atr, &dwAtrLen);
166 PCSC_ERROR(worker, rc, "SCardStatus")
167 worker->card.atr_len = dwAtrLen;
168 LOGW(worker, "Card ATR: %s\n", osmo_hexdump_nospc(worker->card.atr, worker->card.atr_len));
169end:
170 return rc;
171}
172
Harald Welte297d72e2019-03-28 18:42:35 +0100173static int pcsc_open_card(struct bankd_worker *worker)
174{
175 long rc;
176
177 if (!worker->reader.pcsc.hContext) {
178 LOGW(worker, "Attempting to open PC/SC context\n");
179 /* The PC/SC context must be created inside the thread where we'll later use it */
180 rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &worker->reader.pcsc.hContext);
181 PCSC_ERROR(worker, rc, "SCardEstablishContext")
182 }
183
184 if (!worker->reader.pcsc.hCard) {
185 LOGW(worker, "Attempting to open card/slot '%s'\n", worker->reader.name);
186 DWORD dwActiveProtocol;
187 rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name, SCARD_SHARE_SHARED,
188 SCARD_PROTOCOL_T0, &worker->reader.pcsc.hCard, &dwActiveProtocol);
189 PCSC_ERROR(worker, rc, "SCardConnect")
190 }
191
Harald Weltebbd18bd2019-12-16 13:11:50 +0100192 rc = pcsc_get_atr(worker);
193end:
194 return rc;
195}
196
197static int pcsc_reset_card(struct bankd_worker *worker, bool cold_reset)
198{
199 long rc;
200 DWORD dwActiveProtocol;
201
202 LOGW(worker, "Resetting card in '%s' (%s)\n", worker->reader.name,
203 cold_reset ? "cold reset" : "warm reset");
204 rc = SCardReconnect(worker->reader.pcsc.hCard, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0,
205 cold_reset ? SCARD_UNPOWER_CARD : SCARD_RESET_CARD, &dwActiveProtocol);
206 PCSC_ERROR(worker, rc, "SCardReconnect");
207
208 rc = pcsc_get_atr(worker);
Harald Welte297d72e2019-03-28 18:42:35 +0100209end:
210 return rc;
211}
212
213static int pcsc_transceive(struct bankd_worker *worker, const uint8_t *out, size_t out_len,
214 uint8_t *in, size_t *in_len)
215{
216 const SCARD_IO_REQUEST *pioSendPci = SCARD_PCI_T0;
217 SCARD_IO_REQUEST pioRecvPci;
218 long rc;
219
220 rc = SCardTransmit(worker->reader.pcsc.hCard, pioSendPci, out, out_len, &pioRecvPci, in, in_len);
221 PCSC_ERROR(worker, rc, "SCardTransmit");
222
223end:
224 return rc;
225}
226
227static void pcsc_cleanup(struct bankd_worker *worker)
228{
229 if (worker->reader.pcsc.hCard) {
230 SCardDisconnect(worker->reader.pcsc.hCard, SCARD_UNPOWER_CARD);
231 worker->reader.pcsc.hCard = 0;
232 }
233 if (worker->reader.pcsc.hContext) {
234 SCardReleaseContext(worker->reader.pcsc.hContext);
235 worker->reader.pcsc.hContext = 0;
236 }
237}
238
239const struct bankd_driver_ops pcsc_driver_ops = {
240 .open_card = pcsc_open_card,
Harald Weltebbd18bd2019-12-16 13:11:50 +0100241 .reset_card = pcsc_reset_card,
Harald Welte297d72e2019-03-28 18:42:35 +0100242 .transceive = pcsc_transceive,
243 .cleanup = pcsc_cleanup,
244};