Check-in [9f8b1347d9]
Overview
SHA1:9f8b1347d98f3b40475e1dbb4780b18e3cc6a9f6
Date: 2016-02-16 16:06:27
User: rkeene
Comment:Better handling of loss of connection to the PCSC daemon or card resetting
Timelines: family | ancestors | descendants | both | trunk
Downloads: Tarball | ZIP archive
Other Links: files | file ages | folders | manifest
Tags And Properties
Context
2016-02-16
16:06
[54d3a148ef] Fixed an issue where the mutex released slightly too early (user: rkeene, tags: trunk)
16:06
[9f8b1347d9] Better handling of loss of connection to the PCSC daemon or card resetting (user: rkeene, tags: trunk)
15:25
[3fe401d585] Updated AppFS build script to set the PIN entry program (user: rkeene, tags: trunk)
Changes

Modified cackey.c from [6bf4e65a64] to [ef984b4aeb].

   723    723    * functions and not include any symbols in the output shared object.
   724    724    */
   725    725   #include "asn1-x509.c"
   726    726   #include "sha1.c"
   727    727   #include "md5.c"
   728    728   
   729    729   typedef enum {
          730  +	CACKEY_ID_TYPE_ERROR,
          731  +	CACKEY_ID_TYPE_UNKNOWN,
   730    732   	CACKEY_ID_TYPE_CAC,
   731    733   	CACKEY_ID_TYPE_PIV,
   732    734   	CACKEY_ID_TYPE_CERT_ONLY
   733    735   } cackey_pcsc_id_type;
   734    736   
   735    737   struct cackey_pcsc_identity {
   736    738   	cackey_pcsc_id_type id_type;
................................................................................
  1115   1117    *
  1116   1118    * NOTES
  1117   1119    *     This function marks a slot has having been reset, to later be cleaned up.
  1118   1120    *     Cleanup only happens when a PKCS#11 client calls C_FindObjectsInit.
  1119   1121    *
  1120   1122    */
  1121   1123   static void cackey_mark_slot_reset(struct cackey_slot *slot) {
         1124  +	struct cackey_pcsc_identity *pcsc_identities;
         1125  +	unsigned long num_certs;
         1126  +
  1122   1127   	if (slot == NULL) {
  1123   1128   		return;
  1124   1129   	}
  1125   1130   
  1126   1131   	CACKEY_DEBUG_PRINTF("Called.");
  1127   1132   
  1128   1133   	if (slot->pcsc_card_connected) {
................................................................................
  1133   1138   	slot->pcsc_card_connected = 0;
  1134   1139   	if (cackey_pin_command == NULL) {
  1135   1140   		slot->token_flags = CKF_LOGIN_REQUIRED;
  1136   1141   	} else {
  1137   1142   		slot->token_flags = 0;
  1138   1143   	}
  1139   1144   
  1140         -	CACKEY_DEBUG_PRINTF("Returning.");
  1141         -
  1142   1145   	return;
  1143   1146   }
  1144   1147   
  1145   1148   /*
  1146   1149    * SYNPOSIS
  1147   1150    *     LONG cackey_reconnect_card(struct cackey_slot *slot, DWORD default_protocol);
  1148   1151    *
................................................................................
  1254   1257   					CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=1")
  1255   1258   					scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, &slot->pcsc_card, &protocol);
  1256   1259   				}
  1257   1260   			}
  1258   1261   
  1259   1262   			scard_conn_ret = cackey_reconnect_card(slot, protocol);
  1260   1263   		}
         1264  +
         1265  +		if (scard_conn_ret == SCARD_E_NO_SERVICE) {
         1266  +			CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_NO_SERVICE -- which could mean our handle is invalid, will try to reconnect to PC/SC service");
         1267  +
         1268  +			cackey_pcsc_disconnect();
         1269  +
         1270  +			cackey_pcsc_connect();
         1271  +
         1272  +			cackey_mark_slot_reset(slot);
         1273  +
         1274  +			return(cackey_connect_card(slot));
         1275  +		}
  1261   1276   
  1262   1277   		if (scard_conn_ret != SCARD_S_SUCCESS) {
  1263   1278   			CACKEY_DEBUG_PRINTF("Connection to card failed, returning in failure (SCardConnect() = %s/%li)", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_conn_ret), (long) scard_conn_ret);
  1264   1279   
  1265   1280   			return(CACKEY_PCSC_E_GENERIC);
  1266   1281   		}
  1267   1282   
................................................................................
  1553   1568   
  1554   1569   	recv_len = sizeof(recv_buf);
  1555   1570   	scard_xmit_ret = SCardTransmit(slot->pcsc_card, pioSendPci, xmit_buf, xmit_len, NULL, recv_buf, &recv_len);
  1556   1571   
  1557   1572   	if (scard_xmit_ret == SCARD_E_NOT_TRANSACTED) {
  1558   1573   		CACKEY_DEBUG_PRINTF("Failed to send APDU to card (SCardTransmit() = %s/%lx), will ask calling function to retry (not resetting card)...", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_xmit_ret), (unsigned long) scard_xmit_ret);
  1559   1574   
  1560         -		/* Begin Smartcard Transaction */
         1575  +		/* End Smartcard Transaction */
  1561   1576   		cackey_end_transaction(slot);
  1562   1577   
  1563   1578   		cackey_reconnect_card(slot, slot->protocol);
  1564   1579   
         1580  +		return(CACKEY_PCSC_E_RETRY);
         1581  +	}
         1582  +
         1583  +	if (scard_xmit_ret == SCARD_E_NO_SERVICE) {
         1584  +		CACKEY_DEBUG_PRINTF("Failed to send APDU to card, possibly due to PC/SC handle being invalid (SCardTransmit() = %s/%lx), will ask calling function to retry...", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_xmit_ret), (unsigned long) scard_xmit_ret);
         1585  +
         1586  +		cackey_mark_slot_reset(slot);
         1587  +
  1565   1588   		return(CACKEY_PCSC_E_RETRY);
  1566   1589   	}
  1567   1590   
  1568   1591   	if (scard_xmit_ret != SCARD_S_SUCCESS) {
  1569   1592   		CACKEY_DEBUG_PRINTF("Failed to send APDU to card (SCardTransmit() = %s/%lx)", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_xmit_ret), (unsigned long) scard_xmit_ret);
  1570   1593   
  1571   1594   		CACKEY_DEBUG_PRINTF("Marking slot as having been reset");
................................................................................
  2082   2105   		return(CACKEY_PCSC_E_GENERIC);
  2083   2106   	}
  2084   2107   
  2085   2108   	CACKEY_DEBUG_PRINTF("Successfully selected file");
  2086   2109   
  2087   2110   	return(CACKEY_PCSC_S_OK);
  2088   2111   }
         2112  +
         2113  +/*
         2114  + * SYNPOSIS
         2115  + *     cackey_pcsc_id_type cackey_detect_and_select_root_applet(struct cackey_slot *slot, cackey_pcsc_id_type type_hint);
         2116  + *
         2117  + * ARGUMENTS
         2118  + *     struct cackey_slot *slot
         2119  + *         Slot to send commands to
         2120  + *
         2121  + *     cackey_pcsc_id_type type_hint
         2122  + *         A hint as to which type of card might be in this slot (CAC or PIV)
         2123  + *
         2124  + * RETURN VALUE
         2125  + *     CACKEY_ID_TYPE_PIV       If the card connected is a PIV
         2126  + *     CACKEY_ID_TYPE_CAC       If the card connected is a CAC with the CCC
         2127  + *                              applet
         2128  + *     CACKEY_ID_TYPE_ERROR     If we are unable to determine what type of card
         2129  + *                              is connected
         2130  + *
         2131  + * NOTES
         2132  + *     This function reselects the "root" applet, after this function is called
         2133  + *     the user may be required to login again
         2134  + *
         2135  + */
         2136  +static cackey_pcsc_id_type cackey_detect_and_select_root_applet(struct cackey_slot *slot, cackey_pcsc_id_type type_hint) {
         2137  +	unsigned char ccc_aid[] = {GSCIS_AID_CCC}, piv_aid[] = {NISTSP800_73_3_PIV_AID};
         2138  +	cackey_pcsc_id_type try_types[2], try_type;
         2139  +	int send_ret;
         2140  +	int idx;
         2141  +
         2142  +	CACKEY_DEBUG_PRINTF("Reselecting the root applet");
         2143  +
         2144  +	if (type_hint == CACKEY_ID_TYPE_UNKNOWN) {
         2145  +		if (slot->cached_certs && slot->cached_certs_count > 0) {
         2146  +			type_hint = slot->cached_certs[0].id_type;
         2147  +		}
         2148  +	}
         2149  +
         2150  +	switch (type_hint) {
         2151  +		case CACKEY_ID_TYPE_PIV:
         2152  +			CACKEY_DEBUG_PRINTF("Trying to reselect the PIV root applet first");
         2153  +
         2154  +			try_types[0] = CACKEY_ID_TYPE_PIV;
         2155  +			try_types[1] = CACKEY_ID_TYPE_CAC;
         2156  +
         2157  +			break;
         2158  +		default:
         2159  +			CACKEY_DEBUG_PRINTF("Trying to reselect the CAC CCC applet first");
         2160  +
         2161  +			try_types[0] = CACKEY_ID_TYPE_CAC;
         2162  +			try_types[1] = CACKEY_ID_TYPE_PIV;
         2163  +
         2164  +			break;
         2165  +	}
         2166  +
         2167  +	for (idx = 0; idx < (sizeof(try_types) / sizeof(try_types[0])); idx++) {
         2168  +		try_type = try_types[idx];
         2169  +
         2170  +		switch (try_type) {
         2171  +			case CACKEY_ID_TYPE_CAC:
         2172  +				CACKEY_DEBUG_PRINTF("Trying to select the CAC CCC applet");
         2173  +
         2174  +				send_ret = cackey_select_applet(slot, ccc_aid, sizeof(ccc_aid));
         2175  +
         2176  +				break;
         2177  +			case CACKEY_ID_TYPE_PIV:
         2178  +				CACKEY_DEBUG_PRINTF("Trying to select the PIV root applet");
         2179  +
         2180  +				send_ret = cackey_select_applet(slot, piv_aid, sizeof(piv_aid));
         2181  +
         2182  +				break;
         2183  +		}
         2184  +
         2185  +		if (send_ret == CACKEY_PCSC_S_OK) {
         2186  +			CACKEY_DEBUG_PRINTF("Successfully selected the %s applet -- setting the \"LOGIN REQUIRED\" flag on the token",
         2187  +				try_type == CACKEY_ID_TYPE_CAC ? "CAC" : "PIV"
         2188  +			);
         2189  +
         2190  +			slot->token_flags = CKF_LOGIN_REQUIRED;
         2191  +
         2192  +			return(try_type);
         2193  +		}
         2194  +	}
         2195  +
         2196  +	CACKEY_DEBUG_PRINTF("Unable to select any applet, returning in failure");
         2197  +
         2198  +	return(CACKEY_ID_TYPE_ERROR);
         2199  +}
  2089   2200   
  2090   2201   /*
  2091   2202    * SYNPOSIS
  2092   2203    *     cackey_ret cackey_select_file(struct cackey_slot *slot, uint16_t ef);
  2093   2204    *
  2094   2205    * ARGUMENTS
  2095   2206    *     struct cackey_slot *slot
................................................................................
  2446   2557    *     ...
  2447   2558    *
  2448   2559    * NOTES
  2449   2560    *     ...
  2450   2561    *
  2451   2562    */
  2452   2563   static struct cackey_pcsc_identity *cackey_read_certs(struct cackey_slot *slot, struct cackey_pcsc_identity *certs, unsigned long *count) {
         2564  +	cackey_pcsc_id_type check_id_type;
  2453   2565   	struct cackey_pcsc_identity *curr_id;
  2454   2566   	struct cackey_tlv_entity *ccc_tlv, *ccc_curr, *app_tlv, *app_curr;
  2455         -	unsigned char ccc_aid[] = {GSCIS_AID_CCC}, piv_aid[] = {NISTSP800_73_3_PIV_AID};
  2456   2567   	unsigned char *piv_oid, piv_oid_pivauth[] = {NISTSP800_73_3_OID_PIVAUTH}, piv_oid_signature[] = {NISTSP800_73_3_OID_SIGNATURE}, piv_oid_keymgt[] = {NISTSP800_73_3_OID_KEYMGT};
  2457   2568   	unsigned char curr_aid[7];
  2458   2569   	unsigned char buffer[8192], *buffer_p, *tmpbuf;
  2459   2570   	unsigned long outidx = 0;
  2460   2571   	char *piv_label;
  2461   2572   	cackey_ret transaction_ret;
  2462   2573   	ssize_t read_ret;
  2463   2574   	size_t buffer_len, tmpbuflen;
  2464   2575   	int certs_resizable;
  2465   2576   	int send_ret, select_ret;
  2466   2577   	int piv_key, piv = 0;
         2578  +	int cached_certs_valid;
  2467   2579   	int idx;
         2580  +	cackey_pcsc_id_type id_type;
  2468   2581   #ifdef HAVE_LIBZ
  2469   2582   	int uncompress_ret;
  2470   2583   	z_stream gzip_stream;
  2471   2584   #endif
  2472   2585   
  2473   2586   	CACKEY_DEBUG_PRINTF("Called.");
  2474   2587   
................................................................................
  2482   2595   		if (*count == 0) {
  2483   2596   			CACKEY_DEBUG_PRINTF("Requested we return 0 objects, short-circuit");
  2484   2597   
  2485   2598   			return(certs);
  2486   2599   		}
  2487   2600   	}
  2488   2601   
         2602  +	cached_certs_valid = 0;
  2489   2603   	if (!slot->slot_reset) {
  2490   2604   		if (slot->cached_certs) {
  2491         -			if (certs == NULL) {
  2492         -				certs = malloc(sizeof(*certs) * slot->cached_certs_count);
         2605  +			cached_certs_valid = 1;
         2606  +
         2607  +			if (slot->cached_certs_count > 0) {
         2608  +				cached_certs_valid = 1;
         2609  +			}
         2610  +		}
         2611  +	}
         2612  +
         2613  +	if (cached_certs_valid) {
         2614  +		if (certs == NULL) {
         2615  +			certs = malloc(sizeof(*certs) * slot->cached_certs_count);
         2616  +			*count = slot->cached_certs_count;
         2617  +		} else {
         2618  +			if (*count > slot->cached_certs_count) {
  2493   2619   				*count = slot->cached_certs_count;
  2494         -			} else {
  2495         -				if (*count > slot->cached_certs_count) {
  2496         -					*count = slot->cached_certs_count;
  2497         -				}
  2498   2620   			}
         2621  +		}
  2499   2622   
  2500         -			cackey_copy_certs(certs, slot->cached_certs, *count);
         2623  +		cackey_copy_certs(certs, slot->cached_certs, *count);
  2501   2624   
  2502         -			return(certs);
  2503         -		}
         2625  +		CACKEY_DEBUG_PRINTF("Returning cached certificates for this slot (card has not been reset and there are cached certs available)");
         2626  +
         2627  +		return(certs);
  2504   2628   	}
  2505   2629   
  2506   2630   	if (slot->cached_certs) {
  2507   2631   		cackey_free_certs(slot->cached_certs, slot->cached_certs_count, 1);
  2508   2632   
  2509   2633   		slot->cached_certs = NULL;
  2510   2634   	}
................................................................................
  2513   2637   	transaction_ret = cackey_begin_transaction(slot);
  2514   2638   	if (transaction_ret != CACKEY_PCSC_S_OK) {
  2515   2639   		CACKEY_DEBUG_PRINTF("Unable begin transaction, returning in failure");
  2516   2640   
  2517   2641   		return(NULL);
  2518   2642   	}
  2519   2643   
  2520         -	/* Select the CCC Applet */
  2521         -	send_ret = cackey_select_applet(slot, ccc_aid, sizeof(ccc_aid));
  2522         -	if (send_ret != CACKEY_PCSC_S_OK) {
  2523         -		/* Try PIV application */
  2524         -		send_ret = cackey_select_applet(slot, piv_aid, sizeof(piv_aid));
  2525         -		if (send_ret == CACKEY_PCSC_S_OK) {
  2526         -			CACKEY_DEBUG_PRINTF("We have a PIV card -- not using the CCC, pulling pre-selected keys");
         2644  +	id_type = cackey_detect_and_select_root_applet(slot, CACKEY_ID_TYPE_UNKNOWN);
  2527   2645   
         2646  +	switch (id_type) {
         2647  +		case CACKEY_ID_TYPE_CAC:
         2648  +			piv = 0;
         2649  +
         2650  +			break;
         2651  +		case CACKEY_ID_TYPE_PIV:
  2528   2652   			piv = 1;
  2529         -		} else {
  2530         -			CACKEY_DEBUG_PRINTF("Unable to select CCC Applet, returning in failure");
  2531   2653   
         2654  +			break;
         2655  +		case CACKEY_ID_TYPE_ERROR:
  2532   2656   			/* Terminate SmartCard Transaction */
  2533   2657   			cackey_end_transaction(slot);
  2534   2658   
  2535   2659   			if (certs == NULL) {
  2536   2660   				*count = 0;
  2537   2661   			}
  2538   2662   
  2539   2663   			return(NULL);
  2540         -		}
         2664  +
         2665  +			break;
         2666  +		case CACKEY_ID_TYPE_CERT_ONLY:
         2667  +			CACKEY_DEBUG_PRINTF("Error.  Impossible condition reached.");
         2668  +
         2669  +			break;
  2541   2670   	}
  2542   2671   
  2543   2672   	if (certs == NULL) {
  2544   2673   		certs = malloc(sizeof(*certs) * 5);
  2545   2674   		*count = 5;
  2546   2675   		certs_resizable = 1;
  2547   2676   	} else {
................................................................................
  2788   2917    *     ...
  2789   2918    *
  2790   2919    * NOTES
  2791   2920    *     ...
  2792   2921    *
  2793   2922    */
  2794   2923   static ssize_t cackey_signdecrypt(struct cackey_slot *slot, struct cackey_identity *identity, unsigned char *buf, size_t buflen, unsigned char *outbuf, size_t outbuflen, int padInput, int unpadOutput) {
  2795         -	cackey_pcsc_id_type id_type;
         2924  +	cackey_pcsc_id_type id_type, check_id_type;
  2796   2925   	unsigned char dyn_auth_template[10], *dyn_auth_tmpbuf;
  2797   2926   	unsigned char *tmpbuf, *tmpbuf_s, *outbuf_s, *outbuf_p;
  2798   2927   	unsigned char bytes_to_send, p1, class;
  2799   2928   	unsigned char blocktype;
  2800   2929   	cackey_ret send_ret;
  2801   2930   	uint16_t respcode;
  2802   2931   	ssize_t retval = 0, unpadoffset;
................................................................................
  3000   3129   				CACKEY_DEBUG_PRINTF("ADPU Sending Failed -- retrying.");
  3001   3130   
  3002   3131   				return(cackey_signdecrypt(slot, identity, buf, buflen, outbuf, outbuflen, padInput, unpadOutput));
  3003   3132   			}
  3004   3133   
  3005   3134   			CACKEY_DEBUG_PRINTF("ADPU Sending Failed -- returning in error.");
  3006   3135   
  3007         -			if (respcode == 0x6982 || respcode == 0x6e00) {
  3008         -				if (respcode == 0x6E00) {
         3136  +			if (respcode == 0x6982 || respcode == 0x6e00 || respcode == 0x6d00) {
         3137  +				if (respcode == 0x6e00) {
  3009   3138   					CACKEY_DEBUG_PRINTF("Got \"WRONG CLASS\", this means we are talking to the wrong object (likely because the card went away) -- resetting");
         3139  +				} else if (respcode == 0x6d00) {
         3140  +					CACKEY_DEBUG_PRINTF("Got \"INVALID INSTRUCTION\", this means we are talking to the wrong object (likely because the card went away) -- resetting");
  3010   3141   				} else {
  3011   3142   					CACKEY_DEBUG_PRINTF("Security status not satisified (respcode = 0x%04x).  Returning NEEDLOGIN", (int) respcode);
  3012   3143   				}
  3013   3144   
  3014   3145   				cackey_mark_slot_reset(slot);
         3146  +
         3147  +				cackey_detect_and_select_root_applet(slot, CACKEY_ID_TYPE_UNKNOWN);
  3015   3148   
  3016   3149   				slot->token_flags = CKF_LOGIN_REQUIRED;
  3017   3150   
  3018   3151   				return(CACKEY_PCSC_E_NEEDLOGIN);
  3019   3152   			}
  3020   3153   
  3021   3154   			if (send_ret == CACKEY_PCSC_E_TOKENABSENT) {
................................................................................
  3024   3157   				cackey_mark_slot_reset(slot);
  3025   3158   
  3026   3159   				return(CACKEY_PCSC_E_TOKENABSENT);
  3027   3160   			}
  3028   3161   
  3029   3162   			CACKEY_DEBUG_PRINTF("Something went wrong during signing, resetting the slot and hoping for the best.");
  3030   3163   
  3031         -			cackey_mark_slot_reset(slot);
         3164  +			cackey_pcsc_disconnect();
         3165  +			cackey_pcsc_connect();
  3032   3166   
  3033   3167   			return(CACKEY_PCSC_E_GENERIC);
  3034   3168   		}
  3035   3169   
  3036   3170   		tmpbuf += bytes_to_send;
  3037   3171   		tmpbuflen -= bytes_to_send;
  3038   3172   
................................................................................
  3459   3593   		}
  3460   3594   
  3461   3595   		if (response_code == 0x6d00) {
  3462   3596   			if (retries > 0) {
  3463   3597   				CACKEY_DEBUG_PRINTF("Got ISO 7816 Response \"6D 00\" in response to a VERIFY request.");
  3464   3598   				CACKEY_DEBUG_PRINTF("We did not expect this because it is not mentioned in NIST SP 800-73-3 Part 2 Section 3.2.1 or GSC-IS v2.1");
  3465   3599   				CACKEY_DEBUG_PRINTF("We are going to try to reset the card and select the applet again.");
         3600  +
         3601  +				if (num_certs > 0 && pcsc_identities != NULL) {
         3602  +					cackey_detect_and_select_root_applet(slot, pcsc_identities[0].id_type);
         3603  +				}
  3466   3604   
  3467   3605   				cackey_mark_slot_reset(slot);
  3468   3606   
  3469   3607   				connect_ret = cackey_connect_card(slot);
  3470   3608   				if (connect_ret != CACKEY_PCSC_S_OK) {
  3471   3609   					CACKEY_DEBUG_PRINTF("Unable to reconnect after resetting the card, returning in error.");
  3472   3610   
................................................................................
  3476   3614   				CACKEY_DEBUG_PRINTF("Verifying we still have a token.");
  3477   3615   				token_ret = cackey_token_present(slot);
  3478   3616   				if (token_ret != CACKEY_PCSC_S_TOKENPRESENT) {
  3479   3617   					CACKEY_DEBUG_PRINTF("Token not present, returning in error.");
  3480   3618   
  3481   3619   					return(token_ret);
  3482   3620   				}
  3483         -
  3484   3621   
  3485   3622   				CACKEY_DEBUG_PRINTF("Trying to login again");
  3486   3623   				return(cackey_login(slot, pin, pin_len, tries_remaining_p, retries - 1));
  3487   3624   			}
  3488   3625   		}
  3489   3626   
  3490   3627   		return(CACKEY_PCSC_E_GENERIC);