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-cac1.c       |   10 ++++++---
 src/libopensc/card-coolkey.c    |    3 --
 src/libopensc/card-edo.c        |    7 ++++++
 src/libopensc/card-epass2003.c  |   18 ++++++++++++++++
 src/libopensc/card-esteid2018.c |    5 ++++
 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-starcos.c    |   11 ----------
 src/libopensc/card-westcos.c    |   44 ++++++++++++++++++++++++----------------
 16 files changed, 155 insertions(+), 49 deletions(-)

--- a/src/libopensc/card-asepcos.c
+++ b/src/libopensc/card-asepcos.c
@@ -1050,6 +1050,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)
@@ -1066,6 +1080,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
@@ -2311,6 +2311,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
@@ -1831,9 +1831,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 */
@@ -1862,6 +1859,12 @@ static int cac_init(sc_card_t *card)
 	LOG_FUNC_RETURN(card->ctx, 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
@@ -1933,6 +1936,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-cac1.c
+++ b/src/libopensc/card-cac1.c
@@ -498,9 +498,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 */
@@ -529,6 +526,12 @@ static int cac_init(sc_card_t *card)
 	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
 }
 
+static int cac_logout(sc_card_t *card)
+{
+	int index;
+	return cac_find_first_pki_applet(card, &index);
+}
+
 static struct sc_card_operations cac_ops;
 
 static struct sc_card_driver cac1_drv = {
@@ -550,6 +553,7 @@ static struct sc_card_driver * sc_get_dr
 
 	cac_ops.select_file =  cac_select_file; /* need to record object type */
 	cac_ops.read_binary = cac_read_binary;
+	cac_ops.logout = cac_logout;
 
 	return &cac1_drv;
 }
--- a/src/libopensc/card-coolkey.c
+++ b/src/libopensc/card-coolkey.c
@@ -2264,9 +2264,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);
 	if (r == SC_SUCCESS) {
--- a/src/libopensc/card-edo.c
+++ b/src/libopensc/card-edo.c
@@ -302,6 +302,12 @@ static int edo_init(sc_card_t* card) {
 }
 
 
+static int edo_logout(sc_card_t* card) {
+	sc_sm_stop(card);
+	return edo_unlock(card);
+}
+
+
 struct sc_card_driver* sc_get_edo_driver(void) {
 	edo_ops = *sc_get_iso7816_driver()->ops;
 	edo_ops.match_card = edo_match_card;
@@ -309,6 +315,7 @@ struct sc_card_driver* sc_get_edo_driver
 	edo_ops.select_file = edo_select_file;
 	edo_ops.set_security_env = edo_set_security_env;
 	edo_ops.compute_signature = edo_compute_signature;
+	edo_ops.logout = edo_logout;
 
 	return &edo_drv;
 }
--- a/src/libopensc/card-epass2003.c
+++ b/src/libopensc/card-epass2003.c
@@ -3278,6 +3278,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();
@@ -3307,6 +3324,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-esteid2018.c
+++ b/src/libopensc/card-esteid2018.c
@@ -306,6 +306,10 @@ static int esteid_finish(sc_card_t *card
 	return 0;
 }
 
+static int esteid_logout(sc_card_t *card) {
+	return gp_select_aid(card, &IASECC_AID);
+}
+
 struct sc_card_driver *sc_get_esteid2018_driver(void) {
 	struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
 
@@ -323,6 +327,7 @@ struct sc_card_driver *sc_get_esteid2018
 	esteid_ops.set_security_env = esteid_set_security_env;
 	esteid_ops.compute_signature = esteid_compute_signature;
 	esteid_ops.pin_cmd = esteid_pin_cmd;
+	esteid_ops.logout = esteid_logout;
 
 	return &esteid2018_driver;
 }
--- a/src/libopensc/card-gemsafeV1.c
+++ b/src/libopensc/card-gemsafeV1.c
@@ -582,6 +582,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();
@@ -602,6 +609,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
@@ -1244,6 +1244,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();
@@ -1267,6 +1274,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
@@ -361,6 +361,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)
 {
@@ -375,6 +380,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
@@ -1174,6 +1174,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)
 {
@@ -1190,6 +1199,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);
@@ -853,6 +849,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)
 {
@@ -881,6 +890,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
@@ -2183,11 +2183,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);
 }
 
@@ -3082,10 +3082,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) {
@@ -3747,12 +3743,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-starcos.c
+++ b/src/libopensc/card-starcos.c
@@ -2150,18 +2150,9 @@ static int starcos_card_ctl(sc_card_t *c
 	}
 }
 
-/**
- * starcos_logout_v3_x()
- * StarCOS 3.x cards will not clear the security status by selecting MF.
- * Returning NOT_SUPPORTED would cause card reset, effectively invalidating
- * the security status.
- */
 static int starcos_logout_v3_x(sc_card_t *card)
 {
-	int r = SC_ERROR_NOT_SUPPORTED;
-	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
-
-	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
+	return SC_ERROR_NOT_SUPPORTED;
 }
 
 static int starcos_logout(sc_card_t *card)
--- a/src/libopensc/card-westcos.c
+++ b/src/libopensc/card-westcos.c
@@ -166,6 +166,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;
@@ -176,23 +196,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;
 	}
 	
@@ -1257,6 +1261,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)
@@ -1288,6 +1297,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;
 }
