Diff

Differences From Artifact [6bf4e65a64]:

To Artifact [ef984b4aeb]:


723
724
725
726
727
728
729


730
731
732
733
734
735
736
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738







+
+







 * functions and not include any symbols in the output shared object.
 */
#include "asn1-x509.c"
#include "sha1.c"
#include "md5.c"

typedef enum {
	CACKEY_ID_TYPE_ERROR,
	CACKEY_ID_TYPE_UNKNOWN,
	CACKEY_ID_TYPE_CAC,
	CACKEY_ID_TYPE_PIV,
	CACKEY_ID_TYPE_CERT_ONLY
} cackey_pcsc_id_type;

struct cackey_pcsc_identity {
	cackey_pcsc_id_type id_type;
1115
1116
1117
1118
1119
1120
1121



1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144


1145
1146
1147
1148
1149
1150
1151







+
+
+


















-
-







 *
 * NOTES
 *     This function marks a slot has having been reset, to later be cleaned up.
 *     Cleanup only happens when a PKCS#11 client calls C_FindObjectsInit.
 *
 */
static void cackey_mark_slot_reset(struct cackey_slot *slot) {
	struct cackey_pcsc_identity *pcsc_identities;
	unsigned long num_certs;

	if (slot == NULL) {
		return;
	}

	CACKEY_DEBUG_PRINTF("Called.");

	if (slot->pcsc_card_connected) {
		SCardDisconnect(slot->pcsc_card, SCARD_LEAVE_CARD);
	}

	slot->slot_reset = 1;
	slot->pcsc_card_connected = 0;
	if (cackey_pin_command == NULL) {
		slot->token_flags = CKF_LOGIN_REQUIRED;
	} else {
		slot->token_flags = 0;
	}

	CACKEY_DEBUG_PRINTF("Returning.");

	return;
}

/*
 * SYNPOSIS
 *     LONG cackey_reconnect_card(struct cackey_slot *slot, DWORD default_protocol);
 *
1254
1255
1256
1257
1258
1259
1260












1261
1262
1263
1264
1265
1266
1267
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282







+
+
+
+
+
+
+
+
+
+
+
+







					CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=1")
					scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, &slot->pcsc_card, &protocol);
				}
			}

			scard_conn_ret = cackey_reconnect_card(slot, protocol);
		}

		if (scard_conn_ret == SCARD_E_NO_SERVICE) {
			CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_NO_SERVICE -- which could mean our handle is invalid, will try to reconnect to PC/SC service");

			cackey_pcsc_disconnect();

			cackey_pcsc_connect();

			cackey_mark_slot_reset(slot);

			return(cackey_connect_card(slot));
		}

		if (scard_conn_ret != SCARD_S_SUCCESS) {
			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);

			return(CACKEY_PCSC_E_GENERIC);
		}

1553
1554
1555
1556
1557
1558
1559
1560

1561
1562
1563
1564








1565
1566
1567
1568
1569
1570
1571
1568
1569
1570
1571
1572
1573
1574

1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594







-
+




+
+
+
+
+
+
+
+








	recv_len = sizeof(recv_buf);
	scard_xmit_ret = SCardTransmit(slot->pcsc_card, pioSendPci, xmit_buf, xmit_len, NULL, recv_buf, &recv_len);

	if (scard_xmit_ret == SCARD_E_NOT_TRANSACTED) {
		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);

		/* Begin Smartcard Transaction */
		/* End Smartcard Transaction */
		cackey_end_transaction(slot);

		cackey_reconnect_card(slot, slot->protocol);

		return(CACKEY_PCSC_E_RETRY);
	}

	if (scard_xmit_ret == SCARD_E_NO_SERVICE) {
		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);

		cackey_mark_slot_reset(slot);

		return(CACKEY_PCSC_E_RETRY);
	}

	if (scard_xmit_ret != SCARD_S_SUCCESS) {
		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);

		CACKEY_DEBUG_PRINTF("Marking slot as having been reset");
2082
2083
2084
2085
2086
2087
2088
























































































2089
2090
2091
2092
2093
2094
2095
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







		return(CACKEY_PCSC_E_GENERIC);
	}

	CACKEY_DEBUG_PRINTF("Successfully selected file");

	return(CACKEY_PCSC_S_OK);
}

/*
 * SYNPOSIS
 *     cackey_pcsc_id_type cackey_detect_and_select_root_applet(struct cackey_slot *slot, cackey_pcsc_id_type type_hint);
 *
 * ARGUMENTS
 *     struct cackey_slot *slot
 *         Slot to send commands to
 *
 *     cackey_pcsc_id_type type_hint
 *         A hint as to which type of card might be in this slot (CAC or PIV)
 *
 * RETURN VALUE
 *     CACKEY_ID_TYPE_PIV       If the card connected is a PIV
 *     CACKEY_ID_TYPE_CAC       If the card connected is a CAC with the CCC
 *                              applet
 *     CACKEY_ID_TYPE_ERROR     If we are unable to determine what type of card
 *                              is connected
 *
 * NOTES
 *     This function reselects the "root" applet, after this function is called
 *     the user may be required to login again
 *
 */
static cackey_pcsc_id_type cackey_detect_and_select_root_applet(struct cackey_slot *slot, cackey_pcsc_id_type type_hint) {
	unsigned char ccc_aid[] = {GSCIS_AID_CCC}, piv_aid[] = {NISTSP800_73_3_PIV_AID};
	cackey_pcsc_id_type try_types[2], try_type;
	int send_ret;
	int idx;

	CACKEY_DEBUG_PRINTF("Reselecting the root applet");

	if (type_hint == CACKEY_ID_TYPE_UNKNOWN) {
		if (slot->cached_certs && slot->cached_certs_count > 0) {
			type_hint = slot->cached_certs[0].id_type;
		}
	}

	switch (type_hint) {
		case CACKEY_ID_TYPE_PIV:
			CACKEY_DEBUG_PRINTF("Trying to reselect the PIV root applet first");

			try_types[0] = CACKEY_ID_TYPE_PIV;
			try_types[1] = CACKEY_ID_TYPE_CAC;

			break;
		default:
			CACKEY_DEBUG_PRINTF("Trying to reselect the CAC CCC applet first");

			try_types[0] = CACKEY_ID_TYPE_CAC;
			try_types[1] = CACKEY_ID_TYPE_PIV;

			break;
	}

	for (idx = 0; idx < (sizeof(try_types) / sizeof(try_types[0])); idx++) {
		try_type = try_types[idx];

		switch (try_type) {
			case CACKEY_ID_TYPE_CAC:
				CACKEY_DEBUG_PRINTF("Trying to select the CAC CCC applet");

				send_ret = cackey_select_applet(slot, ccc_aid, sizeof(ccc_aid));

				break;
			case CACKEY_ID_TYPE_PIV:
				CACKEY_DEBUG_PRINTF("Trying to select the PIV root applet");

				send_ret = cackey_select_applet(slot, piv_aid, sizeof(piv_aid));

				break;
		}

		if (send_ret == CACKEY_PCSC_S_OK) {
			CACKEY_DEBUG_PRINTF("Successfully selected the %s applet -- setting the \"LOGIN REQUIRED\" flag on the token",
				try_type == CACKEY_ID_TYPE_CAC ? "CAC" : "PIV"
			);

			slot->token_flags = CKF_LOGIN_REQUIRED;

			return(try_type);
		}
	}

	CACKEY_DEBUG_PRINTF("Unable to select any applet, returning in failure");

	return(CACKEY_ID_TYPE_ERROR);
}

/*
 * SYNPOSIS
 *     cackey_ret cackey_select_file(struct cackey_slot *slot, uint16_t ef);
 *
 * ARGUMENTS
 *     struct cackey_slot *slot
2446
2447
2448
2449
2450
2451
2452

2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466

2467

2468
2469
2470
2471
2472
2473
2474
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566

2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587







+


-











+

+







 *     ...
 *
 * NOTES
 *     ...
 *
 */
static struct cackey_pcsc_identity *cackey_read_certs(struct cackey_slot *slot, struct cackey_pcsc_identity *certs, unsigned long *count) {
	cackey_pcsc_id_type check_id_type;
	struct cackey_pcsc_identity *curr_id;
	struct cackey_tlv_entity *ccc_tlv, *ccc_curr, *app_tlv, *app_curr;
	unsigned char ccc_aid[] = {GSCIS_AID_CCC}, piv_aid[] = {NISTSP800_73_3_PIV_AID};
	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};
	unsigned char curr_aid[7];
	unsigned char buffer[8192], *buffer_p, *tmpbuf;
	unsigned long outidx = 0;
	char *piv_label;
	cackey_ret transaction_ret;
	ssize_t read_ret;
	size_t buffer_len, tmpbuflen;
	int certs_resizable;
	int send_ret, select_ret;
	int piv_key, piv = 0;
	int cached_certs_valid;
	int idx;
	cackey_pcsc_id_type id_type;
#ifdef HAVE_LIBZ
	int uncompress_ret;
	z_stream gzip_stream;
#endif

	CACKEY_DEBUG_PRINTF("Called.");

2482
2483
2484
2485
2486
2487
2488

2489
2490









2491
2492
2493
2494
2495
2496
2497
2498








2499
2500

2501
2502
2503



2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522




2523
2524
2525

2526
2527


2528
2529
2530
2531


2532
2533
2534
2535
2536
2537
2538
2539
2540






2541
2542
2543
2544
2545
2546
2547
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613








2614
2615
2616
2617
2618
2619
2620
2621
2622

2623
2624


2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643



2644
2645
2646
2647



2648

2649
2650
2651
2652


2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663

2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676







+


+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
+

-
-
+
+
+
















-
-
-
+
+
+
+
-
-
-
+
-

+
+

-
-

+
+








-
+
+
+
+
+
+







		if (*count == 0) {
			CACKEY_DEBUG_PRINTF("Requested we return 0 objects, short-circuit");

			return(certs);
		}
	}

	cached_certs_valid = 0;
	if (!slot->slot_reset) {
		if (slot->cached_certs) {
			cached_certs_valid = 1;

			if (slot->cached_certs_count > 0) {
				cached_certs_valid = 1;
			}
		}
	}

	if (cached_certs_valid) {
			if (certs == NULL) {
				certs = malloc(sizeof(*certs) * slot->cached_certs_count);
				*count = slot->cached_certs_count;
			} else {
				if (*count > slot->cached_certs_count) {
					*count = slot->cached_certs_count;
				}
			}
		if (certs == NULL) {
			certs = malloc(sizeof(*certs) * slot->cached_certs_count);
			*count = slot->cached_certs_count;
		} else {
			if (*count > slot->cached_certs_count) {
				*count = slot->cached_certs_count;
			}
		}

			cackey_copy_certs(certs, slot->cached_certs, *count);
		cackey_copy_certs(certs, slot->cached_certs, *count);

			return(certs);
		}
		CACKEY_DEBUG_PRINTF("Returning cached certificates for this slot (card has not been reset and there are cached certs available)");

		return(certs);
	}

	if (slot->cached_certs) {
		cackey_free_certs(slot->cached_certs, slot->cached_certs_count, 1);

		slot->cached_certs = NULL;
	}

	/* Begin a SmartCard transaction */
	transaction_ret = cackey_begin_transaction(slot);
	if (transaction_ret != CACKEY_PCSC_S_OK) {
		CACKEY_DEBUG_PRINTF("Unable begin transaction, returning in failure");

		return(NULL);
	}

	/* Select the CCC Applet */
	send_ret = cackey_select_applet(slot, ccc_aid, sizeof(ccc_aid));
	if (send_ret != CACKEY_PCSC_S_OK) {
	id_type = cackey_detect_and_select_root_applet(slot, CACKEY_ID_TYPE_UNKNOWN);

	switch (id_type) {
		case CACKEY_ID_TYPE_CAC:
		/* Try PIV application */
		send_ret = cackey_select_applet(slot, piv_aid, sizeof(piv_aid));
		if (send_ret == CACKEY_PCSC_S_OK) {
			piv = 0;
			CACKEY_DEBUG_PRINTF("We have a PIV card -- not using the CCC, pulling pre-selected keys");

			break;
		case CACKEY_ID_TYPE_PIV:
			piv = 1;
		} else {
			CACKEY_DEBUG_PRINTF("Unable to select CCC Applet, returning in failure");

			break;
		case CACKEY_ID_TYPE_ERROR:
			/* Terminate SmartCard Transaction */
			cackey_end_transaction(slot);

			if (certs == NULL) {
				*count = 0;
			}

			return(NULL);
		}

			break;
		case CACKEY_ID_TYPE_CERT_ONLY:
			CACKEY_DEBUG_PRINTF("Error.  Impossible condition reached.");

			break;
	}

	if (certs == NULL) {
		certs = malloc(sizeof(*certs) * 5);
		*count = 5;
		certs_resizable = 1;
	} else {
2788
2789
2790
2791
2792
2793
2794
2795

2796
2797
2798
2799
2800
2801
2802
2917
2918
2919
2920
2921
2922
2923

2924
2925
2926
2927
2928
2929
2930
2931







-
+







 *     ...
 *
 * NOTES
 *     ...
 *
 */
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) {
	cackey_pcsc_id_type id_type;
	cackey_pcsc_id_type id_type, check_id_type;
	unsigned char dyn_auth_template[10], *dyn_auth_tmpbuf;
	unsigned char *tmpbuf, *tmpbuf_s, *outbuf_s, *outbuf_p;
	unsigned char bytes_to_send, p1, class;
	unsigned char blocktype;
	cackey_ret send_ret;
	uint16_t respcode;
	ssize_t retval = 0, unpadoffset;
3000
3001
3002
3003
3004
3005
3006
3007
3008


3009


3010
3011
3012
3013
3014


3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030

3031

3032
3033
3034
3035
3036
3037
3038
3129
3130
3131
3132
3133
3134
3135


3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164

3165
3166
3167
3168
3169
3170
3171
3172







-
-
+
+

+
+





+
+
















+
-
+







				CACKEY_DEBUG_PRINTF("ADPU Sending Failed -- retrying.");

				return(cackey_signdecrypt(slot, identity, buf, buflen, outbuf, outbuflen, padInput, unpadOutput));
			}

			CACKEY_DEBUG_PRINTF("ADPU Sending Failed -- returning in error.");

			if (respcode == 0x6982 || respcode == 0x6e00) {
				if (respcode == 0x6E00) {
			if (respcode == 0x6982 || respcode == 0x6e00 || respcode == 0x6d00) {
				if (respcode == 0x6e00) {
					CACKEY_DEBUG_PRINTF("Got \"WRONG CLASS\", this means we are talking to the wrong object (likely because the card went away) -- resetting");
				} else if (respcode == 0x6d00) {
					CACKEY_DEBUG_PRINTF("Got \"INVALID INSTRUCTION\", this means we are talking to the wrong object (likely because the card went away) -- resetting");
				} else {
					CACKEY_DEBUG_PRINTF("Security status not satisified (respcode = 0x%04x).  Returning NEEDLOGIN", (int) respcode);
				}

				cackey_mark_slot_reset(slot);

				cackey_detect_and_select_root_applet(slot, CACKEY_ID_TYPE_UNKNOWN);

				slot->token_flags = CKF_LOGIN_REQUIRED;

				return(CACKEY_PCSC_E_NEEDLOGIN);
			}

			if (send_ret == CACKEY_PCSC_E_TOKENABSENT) {
				CACKEY_DEBUG_PRINTF("Token absent.  Returning TOKENABSENT");

				cackey_mark_slot_reset(slot);

				return(CACKEY_PCSC_E_TOKENABSENT);
			}

			CACKEY_DEBUG_PRINTF("Something went wrong during signing, resetting the slot and hoping for the best.");

			cackey_pcsc_disconnect();
			cackey_mark_slot_reset(slot);
			cackey_pcsc_connect();

			return(CACKEY_PCSC_E_GENERIC);
		}

		tmpbuf += bytes_to_send;
		tmpbuflen -= bytes_to_send;

3459
3460
3461
3462
3463
3464
3465




3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620

3621
3622
3623
3624
3625
3626
3627







+
+
+
+

















-







		}

		if (response_code == 0x6d00) {
			if (retries > 0) {
				CACKEY_DEBUG_PRINTF("Got ISO 7816 Response \"6D 00\" in response to a VERIFY request.");
				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");
				CACKEY_DEBUG_PRINTF("We are going to try to reset the card and select the applet again.");

				if (num_certs > 0 && pcsc_identities != NULL) {
					cackey_detect_and_select_root_applet(slot, pcsc_identities[0].id_type);
				}

				cackey_mark_slot_reset(slot);

				connect_ret = cackey_connect_card(slot);
				if (connect_ret != CACKEY_PCSC_S_OK) {
					CACKEY_DEBUG_PRINTF("Unable to reconnect after resetting the card, returning in error.");

					return(connect_ret);
				}

				CACKEY_DEBUG_PRINTF("Verifying we still have a token.");
				token_ret = cackey_token_present(slot);
				if (token_ret != CACKEY_PCSC_S_TOKENPRESENT) {
					CACKEY_DEBUG_PRINTF("Token not present, returning in error.");

					return(token_ret);
				}


				CACKEY_DEBUG_PRINTF("Trying to login again");
				return(cackey_login(slot, pin, pin_len, tries_remaining_p, retries - 1));
			}
		}

		return(CACKEY_PCSC_E_GENERIC);