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
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
----
@ -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
"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
*
@ -26,6 +26,8 @@
#include <osmocom/core/utils.h>
#include <csv.h>
#include <regex.h>
#include <errno.h>
#include "bankd.h"
@ -34,9 +36,19 @@ struct pcsc_slot_name {
/* RSPRO bank slot number */
struct bank_slot slot;
/* 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 {
ST_NONE,
ST_BANK_NR,
@ -76,7 +88,7 @@ static void cb1(void *s, size_t len, void *data)
break;
case ST_PCSC_NAME:
OSMO_ASSERT(ps->cur);
ps->cur->name = talloc_strdup(ps->cur, field);
ps->cur->name_regex = talloc_strdup(ps->cur, field);
break;
default:
OSMO_ASSERT(0);
@ -87,9 +99,23 @@ static void cb2(int c, void *data)
{
struct parser_state *ps = data;
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);
llist_add_tail(&sn->list, &ps->bankd->pcsc_slot_names);
printf("PC/SC slot name: %u/%u -> regex '%s'\n", sn->slot.bank_id, sn->slot.slot_nr, sn->name_regex);
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->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) {
if (bank_slot_equals(&cur->slot, slot))
return cur->name;
return cur->name_regex;
}
return NULL;
}
@ -144,9 +170,12 @@ const char *bankd_pcsc_get_slot_name(struct bankd *bankd, const struct bank_slot
#include <winscard.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) \
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; \
} else { \
LOGW((w), ": OK\n"); \
@ -170,6 +199,57 @@ end:
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)
{
long rc;
@ -182,14 +262,13 @@ static int pcsc_open_card(struct bankd_worker *worker)
}
if (!worker->reader.pcsc.hCard) {
LOGW(worker, "Attempting to open card/slot '%s'\n", worker->reader.name);
DWORD dwActiveProtocol;
rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0, &worker->reader.pcsc.hCard, &dwActiveProtocol);
PCSC_ERROR(worker, rc, "SCardConnect")
rc = pcsc_connect_slot_regex(worker);
if (rc != 0)
goto end;
}
rc = pcsc_get_atr(worker);
end:
return rc;
}