blob: 91eeadd6c49fd74a69bc4761e1430f5197e96726 [file] [log] [blame]
Harald Welte753c8aa2020-10-20 23:04:59 +02001/* (C) 2018-2020 by Harald Welte <laforge@gnumonks.org>
Harald Welte3dcdd202019-03-09 13:06:46 +01002 *
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>
Harald Welte753c8aa2020-10-20 23:04:59 +020029#include <regex.h>
30#include <errno.h>
Harald Welte45c948c2018-09-23 19:26:52 +020031
32#include "bankd.h"
33
34struct pcsc_slot_name {
35 struct llist_head list;
36 /* RSPRO bank slot number */
37 struct bank_slot slot;
38 /* String name of the reader in PC/SC world */
Harald Welte753c8aa2020-10-20 23:04:59 +020039 const char *name_regex;
Harald Welte45c948c2018-09-23 19:26:52 +020040};
41
Harald Welte753c8aa2020-10-20 23:04:59 +020042/* return a talloc-allocated string containing human-readable POSIX regex error */
43static char *get_regerror(void *ctx, int errcode, regex_t *compiled)
44{
45 size_t len = regerror(errcode, compiled, NULL, 0);
46 char *buffer = talloc_size(ctx, len);
47 OSMO_ASSERT(buffer);
48 regerror(errcode, compiled, buffer, len);
49 return buffer;
50}
51
Harald Welte45c948c2018-09-23 19:26:52 +020052enum parser_state_name {
53 ST_NONE,
54 ST_BANK_NR,
55 ST_SLOT_NR,
56 ST_PCSC_NAME,
57};
58struct parser_state {
59 struct bankd *bankd;
60 enum parser_state_name state;
61 struct pcsc_slot_name *cur;
62};
63
64
65static void parser_state_init(struct parser_state *ps)
66{
67 ps->state = ST_BANK_NR;
68 ps->cur = NULL;
69}
70
71static void cb1(void *s, size_t len, void *data)
72{
73 char *field = (char *) s;
74 struct parser_state *ps = data;
75
76 switch (ps->state) {
77 case ST_BANK_NR:
78 OSMO_ASSERT(!ps->cur);
79 ps->cur = talloc_zero(ps->bankd, struct pcsc_slot_name);
80 OSMO_ASSERT(ps->cur);
81 ps->cur->slot.bank_id = atoi(field);
82 ps->state = ST_SLOT_NR;
83 break;
84 case ST_SLOT_NR:
85 OSMO_ASSERT(ps->cur);
86 ps->cur->slot.slot_nr = atoi(field);
87 ps->state = ST_PCSC_NAME;
88 break;
89 case ST_PCSC_NAME:
90 OSMO_ASSERT(ps->cur);
Harald Welte753c8aa2020-10-20 23:04:59 +020091 ps->cur->name_regex = talloc_strdup(ps->cur, field);
Harald Welte45c948c2018-09-23 19:26:52 +020092 break;
93 default:
94 OSMO_ASSERT(0);
95 }
96}
97
98static void cb2(int c, void *data)
99{
100 struct parser_state *ps = data;
101 struct pcsc_slot_name *sn = ps->cur;
Harald Welte753c8aa2020-10-20 23:04:59 +0200102 regex_t compiled_name;
103 int rc;
Harald Welte45c948c2018-09-23 19:26:52 +0200104
Harald Welte753c8aa2020-10-20 23:04:59 +0200105 printf("PC/SC slot name: %u/%u -> regex '%s'\n", sn->slot.bank_id, sn->slot.slot_nr, sn->name_regex);
106
107 memset(&compiled_name, 0, sizeof(compiled_name));
108
109 rc = regcomp(&compiled_name, sn->name_regex, REG_EXTENDED);
110 if (rc != 0) {
111 char *errmsg = get_regerror(sn, rc, &compiled_name);
112 fprintf(stderr, "Error compiling regex '%s': %s - Ignoring\n", sn->name_regex, errmsg);
113 talloc_free(errmsg);
114 talloc_free(sn);
115 } else {
116 llist_add_tail(&sn->list, &ps->bankd->pcsc_slot_names);
117 }
118 regfree(&compiled_name);
Harald Welte45c948c2018-09-23 19:26:52 +0200119
120 ps->state = ST_BANK_NR;
121 ps->cur = NULL;
122}
123
124int bankd_pcsc_read_slotnames(struct bankd *bankd, const char *csv_file)
125{
126 FILE *fp;
127 struct csv_parser p;
128 char buf[1024];
129 size_t bytes_read;
130 struct parser_state ps;
131
132 if (csv_init(&p, CSV_APPEND_NULL) != 0)
133 return -1;
134
135 fp = fopen(csv_file, "rb");
136 if (!fp)
137 return -1;
138
139 parser_state_init(&ps);
140 ps.bankd = bankd;
141
142 while ((bytes_read = fread(buf, 1, sizeof(buf), fp)) > 0) {
143 if (csv_parse(&p, buf, bytes_read, cb1, cb2, &ps) != bytes_read) {
144 fprintf(stderr, "Error parsing CSV: %s\n", csv_strerror(csv_error(&p)));
145 fclose(fp);
146 return -1;
147 }
148 }
149
150 csv_fini(&p, cb1, cb2, &ps);
151 fclose(fp);
152 csv_free(&p);
153
154 return 0;
155}
156
157const char *bankd_pcsc_get_slot_name(struct bankd *bankd, const struct bank_slot *slot)
158{
159 struct pcsc_slot_name *cur;
160
161 llist_for_each_entry(cur, &bankd->pcsc_slot_names, list) {
162 if (bank_slot_equals(&cur->slot, slot))
Harald Welte753c8aa2020-10-20 23:04:59 +0200163 return cur->name_regex;
Harald Welte45c948c2018-09-23 19:26:52 +0200164 }
165 return NULL;
166}
Harald Welte297d72e2019-03-28 18:42:35 +0100167
168
169#include <wintypes.h>
170#include <winscard.h>
171#include <pcsclite.h>
172
Harald Welte753c8aa2020-10-20 23:04:59 +0200173#define LOGW_PCSC_ERROR(w, rv, text) \
174 LOGW((w), text ": %s (0x%lX)\n", pcsc_stringify_error(rv), rv)
175
Harald Welte297d72e2019-03-28 18:42:35 +0100176#define PCSC_ERROR(w, rv, text) \
177if (rv != SCARD_S_SUCCESS) { \
Harald Welte753c8aa2020-10-20 23:04:59 +0200178 LOGW_PCSC_ERROR(w, rv, text); \
Harald Welte297d72e2019-03-28 18:42:35 +0100179 goto end; \
180} else { \
Harald Welte168d7242021-12-08 15:25:42 +0100181 LOGW((w), text ": OK\n"); \
Harald Welte297d72e2019-03-28 18:42:35 +0100182}
183
Harald Weltebbd18bd2019-12-16 13:11:50 +0100184static int pcsc_get_atr(struct bankd_worker *worker)
185{
186 long rc;
187 char pbReader[MAX_READERNAME];
188 /* use DWORD type as this is what the PC/SC API expects */
189 DWORD dwReaderLen = sizeof(pbReader);
190 DWORD dwAtrLen = worker->card.atr_len = sizeof(worker->card.atr);
191 DWORD dwState, dwProt;
192
193 rc = SCardStatus(worker->reader.pcsc.hCard, pbReader, &dwReaderLen, &dwState, &dwProt,
194 worker->card.atr, &dwAtrLen);
195 PCSC_ERROR(worker, rc, "SCardStatus")
196 worker->card.atr_len = dwAtrLen;
197 LOGW(worker, "Card ATR: %s\n", osmo_hexdump_nospc(worker->card.atr, worker->card.atr_len));
198end:
199 return rc;
200}
201
Harald Welte753c8aa2020-10-20 23:04:59 +0200202
203static int pcsc_connect_slot_regex(struct bankd_worker *worker)
204{
205 DWORD dwReaders = SCARD_AUTOALLOCATE;
206 LPSTR mszReaders = NULL;
207 regex_t compiled_name;
208 int result = -1;
209 LONG rc;
210 char *p;
211
212 LOGW(worker, "Attempting to find card/slot using regex '%s'\n", worker->reader.name);
213
214 rc = regcomp(&compiled_name, worker->reader.name, REG_EXTENDED);
215 if (rc != 0) {
216 LOGW(worker, "Error compiling RegEx over name '%s'\n", worker->reader.name);
217 return -EINVAL;
218 }
219
220 rc = SCardListReaders(worker->reader.pcsc.hContext, NULL, (LPSTR)&mszReaders, &dwReaders);
221 if (rc != SCARD_S_SUCCESS) {
222 LOGW_PCSC_ERROR(worker, rc, "SCardListReaders");
223 goto out_regfree;
224 }
225
226 p = mszReaders;
227 while (*p) {
228 DWORD dwActiveProtocol;
229 int r = regexec(&compiled_name, p, 0, NULL, 0);
230 if (r == 0) {
231 LOGW(worker, "Attempting to open card/slot '%s'\n", p);
232 rc = SCardConnect(worker->reader.pcsc.hContext, p, SCARD_SHARE_SHARED,
233 SCARD_PROTOCOL_T0, &worker->reader.pcsc.hCard,
234 &dwActiveProtocol);
235 if (rc == SCARD_S_SUCCESS)
236 result = 0;
237 else
238 LOGW_PCSC_ERROR(worker, rc, "SCardConnect");
239 break;
240 }
241 p += strlen(p) + 1;
242 }
243
244 SCardFreeMemory(worker->reader.pcsc.hContext, mszReaders);
245
246out_regfree:
247 regfree(&compiled_name);
248
249 return result;
250}
251
252
Harald Welte297d72e2019-03-28 18:42:35 +0100253static int pcsc_open_card(struct bankd_worker *worker)
254{
255 long rc;
256
257 if (!worker->reader.pcsc.hContext) {
258 LOGW(worker, "Attempting to open PC/SC context\n");
259 /* The PC/SC context must be created inside the thread where we'll later use it */
260 rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &worker->reader.pcsc.hContext);
261 PCSC_ERROR(worker, rc, "SCardEstablishContext")
262 }
263
264 if (!worker->reader.pcsc.hCard) {
Harald Welte753c8aa2020-10-20 23:04:59 +0200265 rc = pcsc_connect_slot_regex(worker);
266 if (rc != 0)
267 goto end;
Harald Welte297d72e2019-03-28 18:42:35 +0100268 }
269
Harald Weltebbd18bd2019-12-16 13:11:50 +0100270 rc = pcsc_get_atr(worker);
Harald Welte753c8aa2020-10-20 23:04:59 +0200271
Harald Weltebbd18bd2019-12-16 13:11:50 +0100272end:
273 return rc;
274}
275
276static int pcsc_reset_card(struct bankd_worker *worker, bool cold_reset)
277{
278 long rc;
279 DWORD dwActiveProtocol;
280
281 LOGW(worker, "Resetting card in '%s' (%s)\n", worker->reader.name,
282 cold_reset ? "cold reset" : "warm reset");
283 rc = SCardReconnect(worker->reader.pcsc.hCard, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0,
284 cold_reset ? SCARD_UNPOWER_CARD : SCARD_RESET_CARD, &dwActiveProtocol);
285 PCSC_ERROR(worker, rc, "SCardReconnect");
286
287 rc = pcsc_get_atr(worker);
Harald Welte297d72e2019-03-28 18:42:35 +0100288end:
289 return rc;
290}
291
292static int pcsc_transceive(struct bankd_worker *worker, const uint8_t *out, size_t out_len,
293 uint8_t *in, size_t *in_len)
294{
295 const SCARD_IO_REQUEST *pioSendPci = SCARD_PCI_T0;
296 SCARD_IO_REQUEST pioRecvPci;
297 long rc;
298
299 rc = SCardTransmit(worker->reader.pcsc.hCard, pioSendPci, out, out_len, &pioRecvPci, in, in_len);
Harald Welte50a09722021-12-08 15:33:12 +0100300 /* don't use PCSC_ERROR here as we don't want to log every successful SCardTransmit */
301 if (rc != SCARD_S_SUCCESS)
302 LOGW_PCSC_ERROR(worker, rc, "SCardTransmit");
Harald Welte297d72e2019-03-28 18:42:35 +0100303
Harald Welte297d72e2019-03-28 18:42:35 +0100304 return rc;
305}
306
307static void pcsc_cleanup(struct bankd_worker *worker)
308{
309 if (worker->reader.pcsc.hCard) {
310 SCardDisconnect(worker->reader.pcsc.hCard, SCARD_UNPOWER_CARD);
311 worker->reader.pcsc.hCard = 0;
312 }
313 if (worker->reader.pcsc.hContext) {
314 SCardReleaseContext(worker->reader.pcsc.hContext);
315 worker->reader.pcsc.hContext = 0;
316 }
317}
318
319const struct bankd_driver_ops pcsc_driver_ops = {
320 .open_card = pcsc_open_card,
Harald Weltebbd18bd2019-12-16 13:11:50 +0100321 .reset_card = pcsc_reset_card,
Harald Welte297d72e2019-03-28 18:42:35 +0100322 .transceive = pcsc_transceive,
323 .cleanup = pcsc_cleanup,
324};