From d7fadae950f6d33b32f979759c06ab78a3475c22 Mon Sep 17 00:00:00 2001
From: Frank Morgner <frankmorgner@gmail.com>
Date: Wed, 21 Jun 2023 13:49:40 +0200
Subject: [PATCH 01/15] PIV: implemented logout

---
 src/libopensc/card-asepcos.c   |   15 +++++++++++++
 src/libopensc/card-authentic.c |   11 ++++++++++
 src/libopensc/card-cac.c       |   10 ++++++---
 src/libopensc/card-coolkey.c   |    3 --
 src/libopensc/card-epass2003.c |   18 ++++++++++++++++
 src/libopensc/card-gemsafeV1.c |    8 +++++++
 src/libopensc/card-isoApplet.c |    8 +++++++
 src/libopensc/card-jpki.c      |    6 +++++
 src/libopensc/card-mcrd.c      |   10 +++++++++
 src/libopensc/card-muscle.c    |   18 +++++++++++++---
 src/libopensc/card-piv.c       |   20 ++++++++++--------
 src/libopensc/card-westcos.c   |   44 +++++++++++++++++++++++++----------------
 12 files changed, 135 insertions(+), 36 deletions(-)

--- a/src/libopensc/card-asepcos.c
+++ b/src/libopensc/card-asepcos.c
@@ -1041,6 +1041,20 @@ static int asepcos_card_reader_lock_obta
 	LOG_FUNC_RETURN(card->ctx, r);
 }
 
+static int asepcos_logout(sc_card_t *card)
+{
+	int r = SC_ERROR_NOT_SUPPORTED;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	if (card->type == SC_CARD_TYPE_ASEPCOS_JAVA) {
+		/* in case of a Java card try to select the ASEPCOS applet */
+		r = asepcos_select_asepcos_applet(card);
+	}
+
+	LOG_FUNC_RETURN(card->ctx, r);
+}
+
 static struct sc_card_driver * sc_get_driver(void)
 {
 	if (iso_ops == NULL)
@@ -1057,6 +1071,7 @@ static struct sc_card_driver * sc_get_dr
 	asepcos_ops.list_files        = asepcos_list_files;
 	asepcos_ops.card_ctl          = asepcos_card_ctl;
 	asepcos_ops.pin_cmd           = asepcos_pin_cmd;
+	asepcos_ops.logout            = asepcos_logout;
 	asepcos_ops.card_reader_lock_obtained = asepcos_card_reader_lock_obtained;
 
 	return &asepcos_drv;
--- a/src/libopensc/card-authentic.c
+++ b/src/libopensc/card-authentic.c
@@ -2278,6 +2278,17 @@ authentic_sm_get_wrapped_apdu(struct sc_
 }
 #endif
 
+int authentic_logout(sc_card_t *card)
+{
+	int r = SC_ERROR_NOT_SUPPORTED;
+
+	if (card->type == SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2) {
+		r = authentic_select_aid(card, aid_AuthentIC_3_2, sizeof(aid_AuthentIC_3_2), NULL, NULL);
+	}
+
+	return r;
+}
+
 static struct sc_card_driver *
 sc_get_driver(void)
 {
--- a/src/libopensc/card-cac.c
+++ b/src/libopensc/card-cac.c
@@ -1948,9 +1948,6 @@ static int cac_match_card(sc_card_t *car
 {
 	int r;
 	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
-	/* Since we send an APDU, the card's logout function may be called...
-	 * however it may be in dirty memory */
-	card->ops->logout = NULL;
 
 	r = cac_find_and_initialize(card, 0);
 	return (r == SC_SUCCESS); /* never match */
@@ -1979,6 +1976,12 @@ static int cac_init(sc_card_t *card)
 	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
 }
 
+static int cac_logout(sc_card_t *card)
+{
+	int index;
+	return cac_find_first_pki_applet(card, &index);
+}
+
 static int cac_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
 {
 	/* CAC, like PIV needs Extra validation of (new) PIN during
@@ -2031,6 +2034,7 @@ static struct sc_card_driver * sc_get_dr
 	cac_ops.decipher =  cac_decipher;
 	cac_ops.card_ctl = cac_card_ctl;
 	cac_ops.pin_cmd = cac_pin_cmd;
+	cac_ops.logout = cac_logout;
 
 	return &cac_drv;
 }
--- a/src/libopensc/card-coolkey.c
+++ b/src/libopensc/card-coolkey.c
@@ -2231,9 +2231,6 @@ static int coolkey_match_card(sc_card_t
 {
 	int r;
 	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
-	/* Since we send an APDU, the card's logout function may be called...
-	 * however it may be in dirty memory */
-	card->ops->logout = NULL;
 
 	r = coolkey_select_applet(card);
 	return (r >= SC_SUCCESS);
--- a/src/libopensc/card-epass2003.c
+++ b/src/libopensc/card-epass2003.c
@@ -2723,6 +2723,23 @@ epass2003_pin_cmd(struct sc_card *card,
 	return r;
 }
 
+static int
+epass2003_logout(struct sc_card *card)
+{
+	epass2003_exdata *exdata = NULL;
+
+	if (!card->drv_data) 
+		return SC_ERROR_INVALID_ARGUMENTS;
+
+	exdata = (epass2003_exdata *)card->drv_data;
+	if (exdata->sm) {
+		sc_sm_stop(card);
+		return epass2003_refresh(card);
+	}
+
+	return SC_ERROR_NOT_SUPPORTED;
+}
+
 static struct sc_card_driver *sc_get_driver(void)
 {
 	struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
@@ -2752,6 +2769,7 @@ static struct sc_card_driver *sc_get_dri
 	epass2003_ops.pin_cmd = epass2003_pin_cmd;
 	epass2003_ops.check_sw = epass2003_check_sw;
 	epass2003_ops.get_challenge = epass2003_get_challenge;
+	epass2003_ops.logout = epass2003_logout;
 	return &epass2003_drv;
 }
 
--- a/src/libopensc/card-gemsafeV1.c
+++ b/src/libopensc/card-gemsafeV1.c
@@ -580,6 +580,13 @@ static int gemsafe_card_reader_lock_obta
 	LOG_FUNC_RETURN(card->ctx, r);
 }
 
+static int gemsafe_logout(sc_card_t *card)
+{
+	gemsafe_exdata *exdata = (gemsafe_exdata *)card->drv_data;
+
+	return gp_select_applet(card, exdata->aid, exdata->aid_len);
+}
+
 static struct sc_card_driver *sc_get_driver(void)
 {
 	struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
@@ -600,6 +607,7 @@ static struct sc_card_driver *sc_get_dri
 	gemsafe_ops.process_fci	= gemsafe_process_fci;
 	gemsafe_ops.pin_cmd		 = iso_ops->pin_cmd;
 	gemsafe_ops.card_reader_lock_obtained = gemsafe_card_reader_lock_obtained;
+	gemsafe_ops.logout = gemsafe_logout;
 
 	return &gemsafe_drv;
 }
--- a/src/libopensc/card-isoApplet.c
+++ b/src/libopensc/card-isoApplet.c
@@ -1239,6 +1239,13 @@ static int isoApplet_card_reader_lock_ob
 	LOG_FUNC_RETURN(card->ctx, r);
 }
 
+static int isoApplet_logout(sc_card_t *card)
+{
+	size_t rlen = SC_MAX_APDU_BUFFER_SIZE;
+	u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
+	return isoApplet_select_applet(card, isoApplet_aid, sizeof(isoApplet_aid), rbuf, &rlen);
+}
+
 static struct sc_card_driver *sc_get_driver(void)
 {
 	sc_card_driver_t *iso_drv = sc_get_iso7816_driver();
@@ -1262,6 +1269,7 @@ static struct sc_card_driver *sc_get_dri
 	isoApplet_ops.compute_signature = isoApplet_compute_signature;
 	isoApplet_ops.get_challenge = isoApplet_get_challenge;
 	isoApplet_ops.card_reader_lock_obtained = isoApplet_card_reader_lock_obtained;
+	isoApplet_ops.logout = isoApplet_logout;
 
 	/* unsupported functions */
 	isoApplet_ops.write_binary = NULL;
--- a/src/libopensc/card-jpki.c
+++ b/src/libopensc/card-jpki.c
@@ -375,6 +375,11 @@ static int jpki_card_reader_lock_obtaine
 	LOG_FUNC_RETURN(card->ctx, r);
 }
 
+static int jpki_logout(sc_card_t *card)
+{
+	return jpki_select_ap(card);
+}
+
 static struct sc_card_driver *
 sc_get_driver(void)
 {
@@ -390,6 +395,7 @@ sc_get_driver(void)
 	jpki_ops.set_security_env = jpki_set_security_env;
 	jpki_ops.compute_signature = jpki_compute_signature;
 	jpki_ops.card_reader_lock_obtained = jpki_card_reader_lock_obtained;
+	jpki_ops.logout = jpki_logout;
 
 	return &jpki_drv;
 }
--- a/src/libopensc/card-mcrd.c
+++ b/src/libopensc/card-mcrd.c
@@ -1497,6 +1497,15 @@ static int mcrd_pin_cmd(sc_card_t * card
 	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, iso_ops->pin_cmd(card, data, tries_left));
 }
 
+static int mcrd_logout(sc_card_t * card)
+{
+	if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V30) {
+		return gp_select_aid(card, &EstEID_v35_AID);
+	} else {
+		return SC_ERROR_NOT_SUPPORTED;
+	}
+}
+
 /* Driver binding */
 static struct sc_card_driver *sc_get_driver(void)
 {
@@ -1513,6 +1522,7 @@ static struct sc_card_driver *sc_get_dri
 	mcrd_ops.compute_signature = mcrd_compute_signature;
 	mcrd_ops.decipher = mcrd_decipher;
 	mcrd_ops.pin_cmd = mcrd_pin_cmd;
+	mcrd_ops.logout = mcrd_logout;
 
 	return &mcrd_drv;
 }
--- a/src/libopensc/card-muscle.c
+++ b/src/libopensc/card-muscle.c
@@ -81,10 +81,6 @@ static int muscle_match_card(sc_card_t *
 	u8 response[64];
 	int r;
 
-	/* Since we send an APDU, the card's logout function may be called...
-	 * however it's not always properly nulled out... */
-	card->ops->logout = NULL;
-
 	if (msc_select_applet(card, muscleAppletId, sizeof muscleAppletId) == 1) {
 		/* Muscle applet is present, check the protocol version to be sure */
 		sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x3C, 0x00, 0x00);
@@ -833,6 +829,19 @@ static int muscle_card_reader_lock_obtai
 	LOG_FUNC_RETURN(card->ctx, r);
 }
 
+static int muscle_logout(sc_card_t *card)
+{
+	int r = SC_ERROR_NOT_SUPPORTED;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	if (msc_select_applet(card, muscleAppletId, sizeof muscleAppletId) == 1) {
+		r = SC_SUCCESS;
+	}
+
+	LOG_FUNC_RETURN(card->ctx, r);
+}
+
 
 static struct sc_card_driver * sc_get_driver(void)
 {
@@ -861,6 +870,7 @@ static struct sc_card_driver * sc_get_dr
 	muscle_ops.delete_file = muscle_delete_file;
 	muscle_ops.list_files = muscle_list_files;
 	muscle_ops.card_reader_lock_obtained = muscle_card_reader_lock_obtained;
+	muscle_ops.logout = muscle_logout;
 
 	return &muscle_drv;
 }
--- a/src/libopensc/card-piv.c
+++ b/src/libopensc/card-piv.c
@@ -2171,11 +2171,11 @@ static int piv_is_object_present(sc_card
  * or the global pin for the card 0x00. Look at Discovery object to get this.
  * called by pkcs15-piv.c  via cardctl when setting up the pins.
  */
-static int piv_get_pin_preference(sc_card_t *card, int *ptr)
+static int piv_get_pin_preference(sc_card_t *card, int *pin_ref)
 {
 	piv_private_data_t * priv = PIV_DATA(card);
 
-	*ptr = priv->pin_preference;
+	*pin_ref = priv->pin_preference;
 	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
 }
 
@@ -2994,10 +2994,6 @@ static int piv_match_card_continued(sc_c
 	piv_private_data_t *priv = NULL;
 	int saved_type = card->type;
 
-	/* Since we send an APDU, the card's logout function may be called...
-	 * however it may be in dirty memory */
-	card->ops->logout = NULL;
-
 	/* piv_match_card may be called with card->type, set by opensc.conf */
 	/* user provide card type must be one we know */
 	switch (card->type) {
@@ -3539,12 +3535,18 @@ piv_pin_cmd(sc_card_t *card, struct sc_p
 
 static int piv_logout(sc_card_t *card)
 {
-	int r = SC_ERROR_NOT_SUPPORTED; /* TODO Some PIV cards may support a logout */
-	/* piv_private_data_t * priv = PIV_DATA(card); */
+	int r = SC_ERROR_NOT_SUPPORTED;
+	piv_private_data_t * priv = PIV_DATA(card);
 
 	LOG_FUNC_CALLED(card->ctx);
 
-	/* TODO 800-73-3 does not define a logout, 800-73-4 does */
+	if (priv) {
+		/* logout defined since 800-73-4 */
+		r = iso7816_logout(card, priv->pin_preference);
+		if (r == SC_SUCCESS) {
+			priv->logged_in = SC_PIN_STATE_LOGGED_OUT;
+		}
+	}
 
 	LOG_FUNC_RETURN(card->ctx, r);
 }
--- a/src/libopensc/card-westcos.c
+++ b/src/libopensc/card-westcos.c
@@ -171,6 +171,26 @@ static int westcos_finish(sc_card_t * ca
 	return 0;
 }
 
+static int select_westcos_applet(sc_card_t *card)
+{
+	int r;
+	sc_apdu_t apdu;
+	u8 aid[] = {
+		0xA0, 0x00, 0xCE, 0x00, 0x07, 0x01
+	};
+	sc_format_apdu(card, &apdu,
+			SC_APDU_CASE_3_SHORT, 0xA4, 0x04,
+			0);
+	apdu.cla = 0x00;
+	apdu.lc = sizeof(aid);
+	apdu.datalen = sizeof(aid);
+	apdu.data = aid;
+	r = sc_transmit_apdu(card, &apdu);
+	if (r)
+		return r;
+	return sc_check_sw(card, apdu.sw1, apdu.sw2);
+}
+
 static int westcos_match_card(sc_card_t * card)
 {
 	int i;
@@ -181,23 +201,7 @@ static int westcos_match_card(sc_card_t
 	
 	/* JAVACARD, look for westcos applet */
 	if (i == 1) { 
-		int r;
-		sc_apdu_t apdu;
-		u8 aid[] = {
-			0xA0, 0x00, 0xCE, 0x00, 0x07, 0x01
-		};
-		sc_format_apdu(card, &apdu,
-				SC_APDU_CASE_3_SHORT, 0xA4, 0x04,
-				0);
-		apdu.cla = 0x00;
-		apdu.lc = sizeof(aid);
-		apdu.datalen = sizeof(aid);
-		apdu.data = aid;
-		r = sc_transmit_apdu(card, &apdu);
-		if (r)
-			return 0;
-		r = sc_check_sw(card, apdu.sw1, apdu.sw2);
-		if (r)
+		if (select_westcos_applet(card))
 			return 0;
 	}
 	
@@ -1250,6 +1254,11 @@ static int westcos_decipher(sc_card_t *c
 	return westcos_sign_decipher(1, card, crgram, crgram_len, out, outlen);
 }
 
+static int westcos_logout(sc_card_t *card)
+{
+	return select_westcos_applet(card);
+}
+
 struct sc_card_driver *sc_get_westcos_driver(void)
 {
 	if (iso_ops == NULL)
@@ -1281,6 +1290,7 @@ struct sc_card_driver *sc_get_westcos_dr
 	westcos_ops.process_fci = westcos_process_fci;
 	westcos_ops.construct_fci = NULL;
 	westcos_ops.pin_cmd = westcos_pin_cmd;
+	westcos_ops.logout = westcos_logout;
 
 	return &westcos_drv;
 }
