diff --git a/doc/manuals/chapters/remsim-bankd.adoc b/doc/manuals/chapters/remsim-bankd.adoc index e76afd5..87dafa1 100644 --- a/doc/manuals/chapters/remsim-bankd.adoc +++ b/doc/manuals/chapters/remsim-bankd.adoc @@ -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. diff --git a/src/bankd/bankd_pcsc.c b/src/bankd/bankd_pcsc.c index f6e3683..e0b349c 100644 --- a/src/bankd/bankd_pcsc.c +++ b/src/bankd/bankd_pcsc.c @@ -1,4 +1,4 @@ -/* (C) 2018-2019 by Harald Welte +/* (C) 2018-2020 by Harald Welte * * All Rights Reserved * @@ -26,6 +26,8 @@ #include #include +#include +#include #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 #include +#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; }