bankd: regex matching of reader names

So far, bankd did a 1:1 string match of the CSV file line against
the reader name.  As pcsc-lite reader names unfortunately tend to
change at times, we introduce matching by regular expressions in
this patch.

Change-Id: I58b71f9562e152e7ed21b55d7b876bba481b01f8
This commit is contained in:
Harald Welte 2020-10-20 23:04:59 +02:00 committed by laforge
parent 4f5c79d627
commit 753c8aa87a
2 changed files with 115 additions and 13 deletions

View File

@ -108,6 +108,9 @@ bankd expects a CSV file `bankd_pcsc_slots.csv` in the current working directory
This CSV file specifies the mapping between the string names of the PCSC This CSV file specifies the mapping between the string names of the PCSC
readers and the RSPRO bandk/slot numbers. The format is as follows: readers and the RSPRO bandk/slot numbers. The format is as follows:
* first column: bankd number
* second column: slot number within bankd
* third column: extended POSIX regular expression matching the slot
.Example: CSV file mapping bankd slots 0..4 to an ACS ACR33U-A1 reader slots .Example: CSV file mapping bankd slots 0..4 to an ACS ACR33U-A1 reader slots
---- ----
@ -128,4 +131,24 @@ Scanning present readers...
---- ----
In this example, there's only a single PC/SC reader available, and it has a string of In this example, there's only a single PC/SC reader available, and it has a string of
"Alcor Micro AU9560 00 00" which needs to be copy-pasted into the CSV file. "Alcor Micro AU9560 00 00" which needs to be used in the CSV file.
NOTE:: If the reader name contains any special characters, they might need to be escaped according
to the extended POSIX regular expression syntax. See `man 7 regex` for a reference.
.Example: CSV file mapping bankd slots 0..7 to a sysmoOCTSIM:
----
"1","0","sysmocom sysmoOCTSIM \[CCID\] \(ab19180f3335355320202034463a15ff\) [0-9]{2} 00"
"1","1","sysmocom sysmoOCTSIM \[CCID\] \(ab19180f3335355320202034463a15ff\) [0-9]{2} 01"
"1","2","sysmocom sysmoOCTSIM \[CCID\] \(ab19180f3335355320202034463a15ff\) [0-9]{2} 02"
"1","3","sysmocom sysmoOCTSIM \[CCID\] \(ab19180f3335355320202034463a15ff\) [0-9]{2} 03"
"1","4","sysmocom sysmoOCTSIM \[CCID\] \(ab19180f3335355320202034463a15ff\) [0-9]{2} 04"
"1","5","sysmocom sysmoOCTSIM \[CCID\] \(ab19180f3335355320202034463a15ff\) [0-9]{2} 05"
"1","6","sysmocom sysmoOCTSIM \[CCID\] \(ab19180f3335355320202034463a15ff\) [0-9]{2} 06"
"1","7","sysmocom sysmoOCTSIM \[CCID\] \(ab19180f3335355320202034463a15ff\) [0-9]{2} 07"
----
In the above example, the +\[CCID\]+ and the +\(serialnumber\)+ both had to be escaped.
The +[0-9]\{2\}+ construct exists to perform wildcard matching, no matter which particular two-digit number
pcscd decides to use.

View File

@ -1,4 +1,4 @@
/* (C) 2018-2019 by Harald Welte <laforge@gnumonks.org> /* (C) 2018-2020 by Harald Welte <laforge@gnumonks.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@ -26,6 +26,8 @@
#include <osmocom/core/utils.h> #include <osmocom/core/utils.h>
#include <csv.h> #include <csv.h>
#include <regex.h>
#include <errno.h>
#include "bankd.h" #include "bankd.h"
@ -34,9 +36,19 @@ struct pcsc_slot_name {
/* RSPRO bank slot number */ /* RSPRO bank slot number */
struct bank_slot slot; struct bank_slot slot;
/* String name of the reader in PC/SC world */ /* String name of the reader in PC/SC world */
const char *name; const char *name_regex;
}; };
/* return a talloc-allocated string containing human-readable POSIX regex error */
static char *get_regerror(void *ctx, int errcode, regex_t *compiled)
{
size_t len = regerror(errcode, compiled, NULL, 0);
char *buffer = talloc_size(ctx, len);
OSMO_ASSERT(buffer);
regerror(errcode, compiled, buffer, len);
return buffer;
}
enum parser_state_name { enum parser_state_name {
ST_NONE, ST_NONE,
ST_BANK_NR, ST_BANK_NR,
@ -76,7 +88,7 @@ static void cb1(void *s, size_t len, void *data)
break; break;
case ST_PCSC_NAME: case ST_PCSC_NAME:
OSMO_ASSERT(ps->cur); OSMO_ASSERT(ps->cur);
ps->cur->name = talloc_strdup(ps->cur, field); ps->cur->name_regex = talloc_strdup(ps->cur, field);
break; break;
default: default:
OSMO_ASSERT(0); OSMO_ASSERT(0);
@ -87,9 +99,23 @@ static void cb2(int c, void *data)
{ {
struct parser_state *ps = data; struct parser_state *ps = data;
struct pcsc_slot_name *sn = ps->cur; struct pcsc_slot_name *sn = ps->cur;
regex_t compiled_name;
int rc;
printf("PC/SC slot name: %u/%u -> '%s'\n", sn->slot.bank_id, sn->slot.slot_nr, sn->name); printf("PC/SC slot name: %u/%u -> regex '%s'\n", sn->slot.bank_id, sn->slot.slot_nr, sn->name_regex);
llist_add_tail(&sn->list, &ps->bankd->pcsc_slot_names);
memset(&compiled_name, 0, sizeof(compiled_name));
rc = regcomp(&compiled_name, sn->name_regex, REG_EXTENDED);
if (rc != 0) {
char *errmsg = get_regerror(sn, rc, &compiled_name);
fprintf(stderr, "Error compiling regex '%s': %s - Ignoring\n", sn->name_regex, errmsg);
talloc_free(errmsg);
talloc_free(sn);
} else {
llist_add_tail(&sn->list, &ps->bankd->pcsc_slot_names);
}
regfree(&compiled_name);
ps->state = ST_BANK_NR; ps->state = ST_BANK_NR;
ps->cur = NULL; ps->cur = NULL;
@ -134,7 +160,7 @@ const char *bankd_pcsc_get_slot_name(struct bankd *bankd, const struct bank_slot
llist_for_each_entry(cur, &bankd->pcsc_slot_names, list) { llist_for_each_entry(cur, &bankd->pcsc_slot_names, list) {
if (bank_slot_equals(&cur->slot, slot)) if (bank_slot_equals(&cur->slot, slot))
return cur->name; return cur->name_regex;
} }
return NULL; return NULL;
} }
@ -144,9 +170,12 @@ const char *bankd_pcsc_get_slot_name(struct bankd *bankd, const struct bank_slot
#include <winscard.h> #include <winscard.h>
#include <pcsclite.h> #include <pcsclite.h>
#define LOGW_PCSC_ERROR(w, rv, text) \
LOGW((w), text ": %s (0x%lX)\n", pcsc_stringify_error(rv), rv)
#define PCSC_ERROR(w, rv, text) \ #define PCSC_ERROR(w, rv, text) \
if (rv != SCARD_S_SUCCESS) { \ if (rv != SCARD_S_SUCCESS) { \
LOGW((w), text ": %s (0x%lX)\n", pcsc_stringify_error(rv), rv); \ LOGW_PCSC_ERROR(w, rv, text); \
goto end; \ goto end; \
} else { \ } else { \
LOGW((w), ": OK\n"); \ LOGW((w), ": OK\n"); \
@ -170,6 +199,57 @@ end:
return rc; return rc;
} }
static int pcsc_connect_slot_regex(struct bankd_worker *worker)
{
DWORD dwReaders = SCARD_AUTOALLOCATE;
LPSTR mszReaders = NULL;
regex_t compiled_name;
int result = -1;
LONG rc;
char *p;
LOGW(worker, "Attempting to find card/slot using regex '%s'\n", worker->reader.name);
rc = regcomp(&compiled_name, worker->reader.name, REG_EXTENDED);
if (rc != 0) {
LOGW(worker, "Error compiling RegEx over name '%s'\n", worker->reader.name);
return -EINVAL;
}
rc = SCardListReaders(worker->reader.pcsc.hContext, NULL, (LPSTR)&mszReaders, &dwReaders);
if (rc != SCARD_S_SUCCESS) {
LOGW_PCSC_ERROR(worker, rc, "SCardListReaders");
goto out_regfree;
}
p = mszReaders;
while (*p) {
DWORD dwActiveProtocol;
int r = regexec(&compiled_name, p, 0, NULL, 0);
if (r == 0) {
LOGW(worker, "Attempting to open card/slot '%s'\n", p);
rc = SCardConnect(worker->reader.pcsc.hContext, p, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0, &worker->reader.pcsc.hCard,
&dwActiveProtocol);
if (rc == SCARD_S_SUCCESS)
result = 0;
else
LOGW_PCSC_ERROR(worker, rc, "SCardConnect");
break;
}
p += strlen(p) + 1;
}
SCardFreeMemory(worker->reader.pcsc.hContext, mszReaders);
out_regfree:
regfree(&compiled_name);
return result;
}
static int pcsc_open_card(struct bankd_worker *worker) static int pcsc_open_card(struct bankd_worker *worker)
{ {
long rc; long rc;
@ -182,14 +262,13 @@ static int pcsc_open_card(struct bankd_worker *worker)
} }
if (!worker->reader.pcsc.hCard) { if (!worker->reader.pcsc.hCard) {
LOGW(worker, "Attempting to open card/slot '%s'\n", worker->reader.name); rc = pcsc_connect_slot_regex(worker);
DWORD dwActiveProtocol; if (rc != 0)
rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name, SCARD_SHARE_SHARED, goto end;
SCARD_PROTOCOL_T0, &worker->reader.pcsc.hCard, &dwActiveProtocol);
PCSC_ERROR(worker, rc, "SCardConnect")
} }
rc = pcsc_get_atr(worker); rc = pcsc_get_atr(worker);
end: end:
return rc; return rc;
} }