bankd: Open PC/SC by default in EXCLUSIVE mode

Let's open the cards in EXCLUSIVE mode, we don't want other applications
tinkering with the card state while we have a bankd worker running on
it.  This change also means that no two bankd workers can trip on
each other accidentially anymore.

Related: OS#5527
Change-Id: I43a1c8c7bd1c0124ee5f605e2e5b04ed8f7836ab
diff --git a/doc/manuals/chapters/remsim-bankd.adoc b/doc/manuals/chapters/remsim-bankd.adoc
index 7c102de..cb3061c 100644
--- a/doc/manuals/chapters/remsim-bankd.adoc
+++ b/doc/manuals/chapters/remsim-bankd.adoc
@@ -89,6 +89,15 @@
 *-P, --bind-port <1-65535>*::
   Specify the local TCP port to which the socket for incoming connections
   from `osmo-remsim-client`s is bound to.
+*-s, --permit-shared-pcsc*::
+  Specify whether the PC/SC readers should be accessed in SCARD_SHARE_SHARED
+  mode, instead of the default (SCARD_SHARE_EXCLUSIVE).  Shared mode would
+  permit multiple application programs to access a single reader/slot/card
+  concurrently.  This is potentially dangerous as the two programs operate
+  without knowledge of each other, and either of them might modify the card
+  state (such as the currently selected file, validated PIN, etc.) in a
+  way not expected by the other application.
+
 
 ==== Examples
 .remsim-server is on 10.2.3.4, cardreader has 5 slots:
diff --git a/src/bankd/bankd.h b/src/bankd/bankd.h
index 9bf9bc9..0f94818 100644
--- a/src/bankd/bankd.h
+++ b/src/bankd/bankd.h
@@ -130,6 +130,10 @@
 	pthread_mutex_t workers_mutex;
 
 	struct llist_head pcsc_slot_names;
+
+	struct {
+		bool permit_shared_pcsc;
+	} cfg;
 };
 
 int bankd_pcsc_read_slotnames(struct bankd *bankd, const char *csv_file);
diff --git a/src/bankd/bankd_main.c b/src/bankd/bankd_main.c
index b28eec9..c2d6e69 100644
--- a/src/bankd/bankd_main.c
+++ b/src/bankd/bankd_main.c
@@ -98,6 +98,8 @@
 	/* FIXME: other members of app_comp_id */
 
 	INIT_LLIST_HEAD(&bankd->pcsc_slot_names);
+
+	bankd->cfg.permit_shared_pcsc = false;
 }
 
 /* create + start a new bankd_worker thread */
@@ -291,6 +293,7 @@
 "				connections (default: INADDR_ANY)\n"
 "  -P --bind-port <1-65535>	Local TCP port to bind for incoming client\n"
 "				connectionss (default: 9999)\n"
+"  -s --permit-shared-pcsc	Permit SHARED access to PC/SC readers (default: exclusive)\n"
 	      );
 }
 
@@ -312,10 +315,11 @@
 			{ "component-name", 1, 0, 'N' },
 			{ "bind-ip", 1, 0, 'I' },
 			{ "bind-port", 1, 0, 'P' },
+			{ "permit-shared-pcsc", 0, 0, 's' },
 			{ 0, 0, 0, 0 }
 		};
 
-		c = getopt_long(argc, argv, "hVd:i:o:b:n:N:I:P:", long_options, &option_index);
+		c = getopt_long(argc, argv, "hVd:i:o:b:n:N:I:P:s", long_options, &option_index);
 		if (c == -1)
 			break;
 
@@ -352,6 +356,9 @@
 		case 'P':
 			g_bind_port = atoi(optarg);
 			break;
+		case 's':
+			g_bankd->cfg.permit_shared_pcsc = true;
+			break;
 		}
 	}
 }
diff --git a/src/bankd/bankd_pcsc.c b/src/bankd/bankd_pcsc.c
index ee01c93..e1477dd 100644
--- a/src/bankd/bankd_pcsc.c
+++ b/src/bankd/bankd_pcsc.c
@@ -184,6 +184,14 @@
 	LOGW((w), text ": OK\n"); \
 }
 
+static DWORD bankd_share_mode(struct bankd *bankd)
+{
+	if (bankd->cfg.permit_shared_pcsc)
+		return SCARD_SHARE_SHARED;
+	else
+		return SCARD_SHARE_EXCLUSIVE;
+}
+
 static int pcsc_get_atr(struct bankd_worker *worker)
 {
 	long rc;
@@ -232,7 +240,7 @@
 		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,
+			rc = SCardConnect(worker->reader.pcsc.hContext, p, bankd_share_mode(worker->bankd),
 					  SCARD_PROTOCOL_T0, &worker->reader.pcsc.hCard,
 					  &dwActiveProtocol);
 			if (rc == SCARD_S_SUCCESS)
@@ -289,7 +297,7 @@
 
 	LOGW(worker, "Resetting card in '%s' (%s)\n", worker->reader.name,
 		cold_reset ? "cold reset" : "warm reset");
-	rc = SCardReconnect(worker->reader.pcsc.hCard, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0,
+	rc = SCardReconnect(worker->reader.pcsc.hCard, bankd_share_mode(worker->bankd), SCARD_PROTOCOL_T0,
 			    cold_reset ? SCARD_UNPOWER_CARD : SCARD_RESET_CARD, &dwActiveProtocol);
 	PCSC_ERROR(worker, rc, "SCardReconnect");