blob: a3907824fdc48673a320402fedbcdf6c4ae82046 [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
155static int pcsc_open_card(struct bankd_worker *worker)
156{
157 long rc;
158
159 if (!worker->reader.pcsc.hContext) {
160 LOGW(worker, "Attempting to open PC/SC context\n");
161 /* The PC/SC context must be created inside the thread where we'll later use it */
162 rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &worker->reader.pcsc.hContext);
163 PCSC_ERROR(worker, rc, "SCardEstablishContext")
164 }
165
166 if (!worker->reader.pcsc.hCard) {
167 LOGW(worker, "Attempting to open card/slot '%s'\n", worker->reader.name);
168 DWORD dwActiveProtocol;
169 rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name, SCARD_SHARE_SHARED,
170 SCARD_PROTOCOL_T0, &worker->reader.pcsc.hCard, &dwActiveProtocol);
171 PCSC_ERROR(worker, rc, "SCardConnect")
172 }
173
174 /* use DWORD type as this is what the PC/SC API expects */
175 char pbReader[MAX_READERNAME];
176 DWORD dwReaderLen = sizeof(pbReader);
177 DWORD dwAtrLen = worker->card.atr_len = sizeof(worker->card.atr);
178 DWORD dwState, dwProt;
179 rc = SCardStatus(worker->reader.pcsc.hCard, pbReader, &dwReaderLen, &dwState, &dwProt,
180 worker->card.atr, &dwAtrLen);
181 PCSC_ERROR(worker, rc, "SCardStatus")
182 worker->card.atr_len = dwAtrLen;
183 LOGW(worker, "Card ATR: %s\n", osmo_hexdump_nospc(worker->card.atr, worker->card.atr_len));
184end:
185 return rc;
186}
187
188static int pcsc_transceive(struct bankd_worker *worker, const uint8_t *out, size_t out_len,
189 uint8_t *in, size_t *in_len)
190{
191 const SCARD_IO_REQUEST *pioSendPci = SCARD_PCI_T0;
192 SCARD_IO_REQUEST pioRecvPci;
193 long rc;
194
195 rc = SCardTransmit(worker->reader.pcsc.hCard, pioSendPci, out, out_len, &pioRecvPci, in, in_len);
196 PCSC_ERROR(worker, rc, "SCardTransmit");
197
198end:
199 return rc;
200}
201
202static void pcsc_cleanup(struct bankd_worker *worker)
203{
204 if (worker->reader.pcsc.hCard) {
205 SCardDisconnect(worker->reader.pcsc.hCard, SCARD_UNPOWER_CARD);
206 worker->reader.pcsc.hCard = 0;
207 }
208 if (worker->reader.pcsc.hContext) {
209 SCardReleaseContext(worker->reader.pcsc.hContext);
210 worker->reader.pcsc.hContext = 0;
211 }
212}
213
214const struct bankd_driver_ops pcsc_driver_ops = {
215 .open_card = pcsc_open_card,
216 .transceive = pcsc_transceive,
217 .cleanup = pcsc_cleanup,
218};