xref: /freebsd/contrib/wpa/src/utils/pcsc_funcs.c (revision 780fb4a2fa9a9aee5ac48a60b790f567c0dc13e9)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
3f05cddf9SRui Paulo  * Copyright (c) 2004-2007, 2012, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  *
839beb93cSSam Leffler  * This file implements wrapper functions for accessing GSM SIM and 3GPP USIM
939beb93cSSam Leffler  * cards through PC/SC smartcard library. These functions are used to implement
1039beb93cSSam Leffler  * authentication routines for EAP-SIM and EAP-AKA.
1139beb93cSSam Leffler  */
1239beb93cSSam Leffler 
1339beb93cSSam Leffler #include "includes.h"
14*780fb4a2SCy Schubert #ifdef __APPLE__
15*780fb4a2SCy Schubert #include <PCSC/winscard.h>
16*780fb4a2SCy Schubert #else
1739beb93cSSam Leffler #include <winscard.h>
18*780fb4a2SCy Schubert #endif
1939beb93cSSam Leffler 
2039beb93cSSam Leffler #include "common.h"
2139beb93cSSam Leffler #include "pcsc_funcs.h"
2239beb93cSSam Leffler 
2339beb93cSSam Leffler 
2439beb93cSSam Leffler /* See ETSI GSM 11.11 and ETSI TS 102 221 for details.
2539beb93cSSam Leffler  * SIM commands:
2639beb93cSSam Leffler  * Command APDU: CLA INS P1 P2 P3 Data
2739beb93cSSam Leffler  *   CLA (class of instruction): A0 for GSM, 00 for USIM
2839beb93cSSam Leffler  *   INS (instruction)
2939beb93cSSam Leffler  *   P1 P2 P3 (parameters, P3 = length of Data)
3039beb93cSSam Leffler  * Response APDU: Data SW1 SW2
3139beb93cSSam Leffler  *   SW1 SW2 (Status words)
3239beb93cSSam Leffler  * Commands (INS P1 P2 P3):
3339beb93cSSam Leffler  *   SELECT: A4 00 00 02 <file_id, 2 bytes>
3439beb93cSSam Leffler  *   GET RESPONSE: C0 00 00 <len>
3539beb93cSSam Leffler  *   RUN GSM ALG: 88 00 00 00 <RAND len = 10>
3639beb93cSSam Leffler  *   RUN UMTS ALG: 88 00 81 <len=0x22> data: 0x10 | RAND | 0x10 | AUTN
3739beb93cSSam Leffler  *	P1 = ID of alg in card
3839beb93cSSam Leffler  *	P2 = ID of secret key
3939beb93cSSam Leffler  *   READ BINARY: B0 <offset high> <offset low> <len>
4039beb93cSSam Leffler  *   READ RECORD: B2 <record number> <mode> <len>
4139beb93cSSam Leffler  *	P2 (mode) = '02' (next record), '03' (previous record),
4239beb93cSSam Leffler  *		    '04' (absolute mode)
4339beb93cSSam Leffler  *   VERIFY CHV: 20 00 <CHV number> 08
4439beb93cSSam Leffler  *   CHANGE CHV: 24 00 <CHV number> 10
4539beb93cSSam Leffler  *   DISABLE CHV: 26 00 01 08
4639beb93cSSam Leffler  *   ENABLE CHV: 28 00 01 08
4739beb93cSSam Leffler  *   UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10
4839beb93cSSam Leffler  *   SLEEP: FA 00 00 00
4939beb93cSSam Leffler  */
5039beb93cSSam Leffler 
5139beb93cSSam Leffler /* GSM SIM commands */
5239beb93cSSam Leffler #define SIM_CMD_SELECT			0xa0, 0xa4, 0x00, 0x00, 0x02
5339beb93cSSam Leffler #define SIM_CMD_RUN_GSM_ALG		0xa0, 0x88, 0x00, 0x00, 0x10
5439beb93cSSam Leffler #define SIM_CMD_GET_RESPONSE		0xa0, 0xc0, 0x00, 0x00
5539beb93cSSam Leffler #define SIM_CMD_READ_BIN		0xa0, 0xb0, 0x00, 0x00
5639beb93cSSam Leffler #define SIM_CMD_READ_RECORD		0xa0, 0xb2, 0x00, 0x00
5739beb93cSSam Leffler #define SIM_CMD_VERIFY_CHV1		0xa0, 0x20, 0x00, 0x01, 0x08
5839beb93cSSam Leffler 
5939beb93cSSam Leffler /* USIM commands */
6039beb93cSSam Leffler #define USIM_CLA			0x00
6139beb93cSSam Leffler #define USIM_CMD_RUN_UMTS_ALG		0x00, 0x88, 0x00, 0x81, 0x22
6239beb93cSSam Leffler #define USIM_CMD_GET_RESPONSE		0x00, 0xc0, 0x00, 0x00
6339beb93cSSam Leffler 
6439beb93cSSam Leffler #define SIM_RECORD_MODE_ABSOLUTE 0x04
6539beb93cSSam Leffler 
6639beb93cSSam Leffler #define USIM_FSP_TEMPL_TAG		0x62
6739beb93cSSam Leffler 
6839beb93cSSam Leffler #define USIM_TLV_FILE_DESC		0x82
6939beb93cSSam Leffler #define USIM_TLV_FILE_ID		0x83
7039beb93cSSam Leffler #define USIM_TLV_DF_NAME		0x84
7139beb93cSSam Leffler #define USIM_TLV_PROPR_INFO		0xA5
7239beb93cSSam Leffler #define USIM_TLV_LIFE_CYCLE_STATUS	0x8A
7339beb93cSSam Leffler #define USIM_TLV_FILE_SIZE		0x80
7439beb93cSSam Leffler #define USIM_TLV_TOTAL_FILE_SIZE	0x81
7539beb93cSSam Leffler #define USIM_TLV_PIN_STATUS_TEMPLATE	0xC6
7639beb93cSSam Leffler #define USIM_TLV_SHORT_FILE_ID		0x88
77f05cddf9SRui Paulo #define USIM_TLV_SECURITY_ATTR_8B	0x8B
78f05cddf9SRui Paulo #define USIM_TLV_SECURITY_ATTR_8C	0x8C
79f05cddf9SRui Paulo #define USIM_TLV_SECURITY_ATTR_AB	0xAB
8039beb93cSSam Leffler 
8139beb93cSSam Leffler #define USIM_PS_DO_TAG			0x90
8239beb93cSSam Leffler 
8339beb93cSSam Leffler #define AKA_RAND_LEN 16
8439beb93cSSam Leffler #define AKA_AUTN_LEN 16
8539beb93cSSam Leffler #define AKA_AUTS_LEN 14
8639beb93cSSam Leffler #define RES_MAX_LEN 16
8739beb93cSSam Leffler #define IK_LEN 16
8839beb93cSSam Leffler #define CK_LEN 16
8939beb93cSSam Leffler 
9039beb93cSSam Leffler 
91f05cddf9SRui Paulo /* GSM files
92f05cddf9SRui Paulo  * File type in first octet:
93f05cddf9SRui Paulo  * 3F = Master File
94f05cddf9SRui Paulo  * 7F = Dedicated File
95f05cddf9SRui Paulo  * 2F = Elementary File under the Master File
96f05cddf9SRui Paulo  * 6F = Elementary File under a Dedicated File
97f05cddf9SRui Paulo  */
98f05cddf9SRui Paulo #define SCARD_FILE_MF		0x3F00
99f05cddf9SRui Paulo #define SCARD_FILE_GSM_DF	0x7F20
100f05cddf9SRui Paulo #define SCARD_FILE_UMTS_DF	0x7F50
101f05cddf9SRui Paulo #define SCARD_FILE_GSM_EF_IMSI	0x6F07
102f05cddf9SRui Paulo #define SCARD_FILE_GSM_EF_AD	0x6FAD
103f05cddf9SRui Paulo #define SCARD_FILE_EF_DIR	0x2F00
104f05cddf9SRui Paulo #define SCARD_FILE_EF_ICCID	0x2FE2
105f05cddf9SRui Paulo #define SCARD_FILE_EF_CK	0x6FE1
106f05cddf9SRui Paulo #define SCARD_FILE_EF_IK	0x6FE2
107f05cddf9SRui Paulo 
108f05cddf9SRui Paulo #define SCARD_CHV1_OFFSET	13
109f05cddf9SRui Paulo #define SCARD_CHV1_FLAG		0x80
110f05cddf9SRui Paulo 
111f05cddf9SRui Paulo 
11239beb93cSSam Leffler typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types;
11339beb93cSSam Leffler 
11439beb93cSSam Leffler struct scard_data {
11539beb93cSSam Leffler 	SCARDCONTEXT ctx;
11639beb93cSSam Leffler 	SCARDHANDLE card;
117*780fb4a2SCy Schubert #ifdef __APPLE__
118*780fb4a2SCy Schubert 	uint32_t protocol;
119*780fb4a2SCy Schubert #else
12039beb93cSSam Leffler 	DWORD protocol;
121*780fb4a2SCy Schubert #endif
12239beb93cSSam Leffler 	sim_types sim_type;
12339beb93cSSam Leffler 	int pin1_required;
12439beb93cSSam Leffler };
12539beb93cSSam Leffler 
12639beb93cSSam Leffler #ifdef __MINGW32_VERSION
12739beb93cSSam Leffler /* MinGW does not yet support WinScard, so load the needed functions
12839beb93cSSam Leffler  * dynamically from winscard.dll for now. */
12939beb93cSSam Leffler 
13039beb93cSSam Leffler static HINSTANCE dll = NULL; /* winscard.dll */
13139beb93cSSam Leffler 
13239beb93cSSam Leffler static const SCARD_IO_REQUEST *dll_g_rgSCardT0Pci, *dll_g_rgSCardT1Pci;
13339beb93cSSam Leffler #undef SCARD_PCI_T0
13439beb93cSSam Leffler #define SCARD_PCI_T0 (dll_g_rgSCardT0Pci)
13539beb93cSSam Leffler #undef SCARD_PCI_T1
13639beb93cSSam Leffler #define SCARD_PCI_T1 (dll_g_rgSCardT1Pci)
13739beb93cSSam Leffler 
13839beb93cSSam Leffler 
13939beb93cSSam Leffler static WINSCARDAPI LONG WINAPI
14039beb93cSSam Leffler (*dll_SCardEstablishContext)(IN DWORD dwScope,
14139beb93cSSam Leffler 			     IN LPCVOID pvReserved1,
14239beb93cSSam Leffler 			     IN LPCVOID pvReserved2,
14339beb93cSSam Leffler 			     OUT LPSCARDCONTEXT phContext);
14439beb93cSSam Leffler #define SCardEstablishContext dll_SCardEstablishContext
14539beb93cSSam Leffler 
14639beb93cSSam Leffler static long (*dll_SCardReleaseContext)(long hContext);
14739beb93cSSam Leffler #define SCardReleaseContext dll_SCardReleaseContext
14839beb93cSSam Leffler 
14939beb93cSSam Leffler static WINSCARDAPI LONG WINAPI
15039beb93cSSam Leffler (*dll_SCardListReadersA)(IN SCARDCONTEXT hContext,
15139beb93cSSam Leffler 			 IN LPCSTR mszGroups,
15239beb93cSSam Leffler 			 OUT LPSTR mszReaders,
15339beb93cSSam Leffler 			 IN OUT LPDWORD pcchReaders);
15439beb93cSSam Leffler #undef SCardListReaders
15539beb93cSSam Leffler #define SCardListReaders dll_SCardListReadersA
15639beb93cSSam Leffler 
15739beb93cSSam Leffler static WINSCARDAPI LONG WINAPI
15839beb93cSSam Leffler (*dll_SCardConnectA)(IN SCARDCONTEXT hContext,
15939beb93cSSam Leffler 		     IN LPCSTR szReader,
16039beb93cSSam Leffler 		     IN DWORD dwShareMode,
16139beb93cSSam Leffler 		     IN DWORD dwPreferredProtocols,
16239beb93cSSam Leffler 		     OUT LPSCARDHANDLE phCard,
16339beb93cSSam Leffler 		     OUT LPDWORD pdwActiveProtocol);
16439beb93cSSam Leffler #undef SCardConnect
16539beb93cSSam Leffler #define SCardConnect dll_SCardConnectA
16639beb93cSSam Leffler 
16739beb93cSSam Leffler static WINSCARDAPI LONG WINAPI
16839beb93cSSam Leffler (*dll_SCardDisconnect)(IN SCARDHANDLE hCard,
16939beb93cSSam Leffler 		       IN DWORD dwDisposition);
17039beb93cSSam Leffler #define SCardDisconnect dll_SCardDisconnect
17139beb93cSSam Leffler 
17239beb93cSSam Leffler static WINSCARDAPI LONG WINAPI
17339beb93cSSam Leffler (*dll_SCardTransmit)(IN SCARDHANDLE hCard,
17439beb93cSSam Leffler 		     IN LPCSCARD_IO_REQUEST pioSendPci,
17539beb93cSSam Leffler 		     IN LPCBYTE pbSendBuffer,
17639beb93cSSam Leffler 		     IN DWORD cbSendLength,
17739beb93cSSam Leffler 		     IN OUT LPSCARD_IO_REQUEST pioRecvPci,
17839beb93cSSam Leffler 		     OUT LPBYTE pbRecvBuffer,
17939beb93cSSam Leffler 		     IN OUT LPDWORD pcbRecvLength);
18039beb93cSSam Leffler #define SCardTransmit dll_SCardTransmit
18139beb93cSSam Leffler 
18239beb93cSSam Leffler static WINSCARDAPI LONG WINAPI
18339beb93cSSam Leffler (*dll_SCardBeginTransaction)(IN SCARDHANDLE hCard);
18439beb93cSSam Leffler #define SCardBeginTransaction dll_SCardBeginTransaction
18539beb93cSSam Leffler 
18639beb93cSSam Leffler static WINSCARDAPI LONG WINAPI
18739beb93cSSam Leffler (*dll_SCardEndTransaction)(IN SCARDHANDLE hCard, IN DWORD dwDisposition);
18839beb93cSSam Leffler #define SCardEndTransaction dll_SCardEndTransaction
18939beb93cSSam Leffler 
19039beb93cSSam Leffler 
mingw_load_symbols(void)19139beb93cSSam Leffler static int mingw_load_symbols(void)
19239beb93cSSam Leffler {
19339beb93cSSam Leffler 	char *sym;
19439beb93cSSam Leffler 
19539beb93cSSam Leffler 	if (dll)
19639beb93cSSam Leffler 		return 0;
19739beb93cSSam Leffler 
19839beb93cSSam Leffler 	dll = LoadLibrary("winscard");
19939beb93cSSam Leffler 	if (dll == NULL) {
20039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WinSCard: Could not load winscard.dll "
20139beb93cSSam Leffler 			   "library");
20239beb93cSSam Leffler 		return -1;
20339beb93cSSam Leffler 	}
20439beb93cSSam Leffler 
20539beb93cSSam Leffler #define LOADSYM(s) \
20639beb93cSSam Leffler 	sym = #s; \
20739beb93cSSam Leffler 	dll_ ## s = (void *) GetProcAddress(dll, sym); \
20839beb93cSSam Leffler 	if (dll_ ## s == NULL) \
20939beb93cSSam Leffler 		goto fail;
21039beb93cSSam Leffler 
21139beb93cSSam Leffler 	LOADSYM(SCardEstablishContext);
21239beb93cSSam Leffler 	LOADSYM(SCardReleaseContext);
21339beb93cSSam Leffler 	LOADSYM(SCardListReadersA);
21439beb93cSSam Leffler 	LOADSYM(SCardConnectA);
21539beb93cSSam Leffler 	LOADSYM(SCardDisconnect);
21639beb93cSSam Leffler 	LOADSYM(SCardTransmit);
21739beb93cSSam Leffler 	LOADSYM(SCardBeginTransaction);
21839beb93cSSam Leffler 	LOADSYM(SCardEndTransaction);
21939beb93cSSam Leffler 	LOADSYM(g_rgSCardT0Pci);
22039beb93cSSam Leffler 	LOADSYM(g_rgSCardT1Pci);
22139beb93cSSam Leffler 
22239beb93cSSam Leffler #undef LOADSYM
22339beb93cSSam Leffler 
22439beb93cSSam Leffler 	return 0;
22539beb93cSSam Leffler 
22639beb93cSSam Leffler fail:
22739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WinSCard: Could not get address for %s from "
22839beb93cSSam Leffler 		   "winscard.dll", sym);
22939beb93cSSam Leffler 	FreeLibrary(dll);
23039beb93cSSam Leffler 	dll = NULL;
23139beb93cSSam Leffler 	return -1;
23239beb93cSSam Leffler }
23339beb93cSSam Leffler 
23439beb93cSSam Leffler 
mingw_unload_symbols(void)23539beb93cSSam Leffler static void mingw_unload_symbols(void)
23639beb93cSSam Leffler {
23739beb93cSSam Leffler 	if (dll == NULL)
23839beb93cSSam Leffler 		return;
23939beb93cSSam Leffler 
24039beb93cSSam Leffler 	FreeLibrary(dll);
24139beb93cSSam Leffler 	dll = NULL;
24239beb93cSSam Leffler }
24339beb93cSSam Leffler 
24439beb93cSSam Leffler #else /* __MINGW32_VERSION */
24539beb93cSSam Leffler 
24639beb93cSSam Leffler #define mingw_load_symbols() 0
24739beb93cSSam Leffler #define mingw_unload_symbols() do { } while (0)
24839beb93cSSam Leffler 
24939beb93cSSam Leffler #endif /* __MINGW32_VERSION */
25039beb93cSSam Leffler 
25139beb93cSSam Leffler 
25239beb93cSSam Leffler static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
25339beb93cSSam Leffler 			      unsigned char *buf, size_t *buf_len,
25439beb93cSSam Leffler 			      sim_types sim_type, unsigned char *aid,
25539beb93cSSam Leffler 			      size_t aidlen);
25639beb93cSSam Leffler static int scard_select_file(struct scard_data *scard, unsigned short file_id,
25739beb93cSSam Leffler 			     unsigned char *buf, size_t *buf_len);
25839beb93cSSam Leffler static int scard_verify_pin(struct scard_data *scard, const char *pin);
25939beb93cSSam Leffler static int scard_get_record_len(struct scard_data *scard,
26039beb93cSSam Leffler 				unsigned char recnum, unsigned char mode);
26139beb93cSSam Leffler static int scard_read_record(struct scard_data *scard,
26239beb93cSSam Leffler 			     unsigned char *data, size_t len,
26339beb93cSSam Leffler 			     unsigned char recnum, unsigned char mode);
26439beb93cSSam Leffler 
26539beb93cSSam Leffler 
scard_parse_fsp_templ(unsigned char * buf,size_t buf_len,int * ps_do,int * file_len)26639beb93cSSam Leffler static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len,
26739beb93cSSam Leffler 				 int *ps_do, int *file_len)
26839beb93cSSam Leffler {
26939beb93cSSam Leffler 	unsigned char *pos, *end;
27039beb93cSSam Leffler 
27139beb93cSSam Leffler 	if (ps_do)
27239beb93cSSam Leffler 		*ps_do = -1;
27339beb93cSSam Leffler 	if (file_len)
27439beb93cSSam Leffler 		*file_len = -1;
27539beb93cSSam Leffler 
27639beb93cSSam Leffler 	pos = buf;
27739beb93cSSam Leffler 	end = pos + buf_len;
27839beb93cSSam Leffler 	if (*pos != USIM_FSP_TEMPL_TAG) {
27939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: file header did not "
28039beb93cSSam Leffler 			   "start with FSP template tag");
28139beb93cSSam Leffler 		return -1;
28239beb93cSSam Leffler 	}
28339beb93cSSam Leffler 	pos++;
28439beb93cSSam Leffler 	if (pos >= end)
28539beb93cSSam Leffler 		return -1;
286*780fb4a2SCy Schubert 	if (pos[0] < end - pos)
28739beb93cSSam Leffler 		end = pos + 1 + pos[0];
28839beb93cSSam Leffler 	pos++;
28939beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
29039beb93cSSam Leffler 		    pos, end - pos);
29139beb93cSSam Leffler 
2925b9c547cSRui Paulo 	while (end - pos >= 2) {
2935b9c547cSRui Paulo 		unsigned char type, len;
2945b9c547cSRui Paulo 
2955b9c547cSRui Paulo 		type = pos[0];
2965b9c547cSRui Paulo 		len = pos[1];
297f05cddf9SRui Paulo 		wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d",
2985b9c547cSRui Paulo 			   type, len);
2995b9c547cSRui Paulo 		pos += 2;
3005b9c547cSRui Paulo 
3015b9c547cSRui Paulo 		if (len > (unsigned int) (end - pos))
30239beb93cSSam Leffler 			break;
30339beb93cSSam Leffler 
3045b9c547cSRui Paulo 		switch (type) {
305f05cddf9SRui Paulo 		case USIM_TLV_FILE_DESC:
306f05cddf9SRui Paulo 			wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV",
3075b9c547cSRui Paulo 				    pos, len);
308f05cddf9SRui Paulo 			break;
309f05cddf9SRui Paulo 		case USIM_TLV_FILE_ID:
310f05cddf9SRui Paulo 			wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV",
3115b9c547cSRui Paulo 				    pos, len);
312f05cddf9SRui Paulo 			break;
313f05cddf9SRui Paulo 		case USIM_TLV_DF_NAME:
314f05cddf9SRui Paulo 			wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV",
3155b9c547cSRui Paulo 				    pos, len);
316f05cddf9SRui Paulo 			break;
317f05cddf9SRui Paulo 		case USIM_TLV_PROPR_INFO:
318f05cddf9SRui Paulo 			wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary "
3195b9c547cSRui Paulo 				    "information TLV", pos, len);
320f05cddf9SRui Paulo 			break;
321f05cddf9SRui Paulo 		case USIM_TLV_LIFE_CYCLE_STATUS:
322f05cddf9SRui Paulo 			wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status "
3235b9c547cSRui Paulo 				    "Integer TLV", pos, len);
324f05cddf9SRui Paulo 			break;
325f05cddf9SRui Paulo 		case USIM_TLV_FILE_SIZE:
326f05cddf9SRui Paulo 			wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV",
3275b9c547cSRui Paulo 				    pos, len);
3285b9c547cSRui Paulo 			if ((len == 1 || len == 2) && file_len) {
3295b9c547cSRui Paulo 				if (len == 1)
3305b9c547cSRui Paulo 					*file_len = (int) pos[0];
33139beb93cSSam Leffler 				else
3325b9c547cSRui Paulo 					*file_len = WPA_GET_BE16(pos);
33339beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "SCARD: file_size=%d",
33439beb93cSSam Leffler 					   *file_len);
33539beb93cSSam Leffler 			}
336f05cddf9SRui Paulo 			break;
337f05cddf9SRui Paulo 		case USIM_TLV_TOTAL_FILE_SIZE:
338f05cddf9SRui Paulo 			wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV",
3395b9c547cSRui Paulo 				    pos, len);
340f05cddf9SRui Paulo 			break;
341f05cddf9SRui Paulo 		case USIM_TLV_PIN_STATUS_TEMPLATE:
342f05cddf9SRui Paulo 			wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template "
3435b9c547cSRui Paulo 				    "DO TLV", pos, len);
3445b9c547cSRui Paulo 			if (len >= 2 && pos[0] == USIM_PS_DO_TAG &&
3455b9c547cSRui Paulo 			    pos[1] >= 1 && ps_do) {
34639beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x",
3475b9c547cSRui Paulo 					   pos[2]);
3485b9c547cSRui Paulo 				*ps_do = (int) pos[2];
34939beb93cSSam Leffler 			}
350f05cddf9SRui Paulo 			break;
351f05cddf9SRui Paulo 		case USIM_TLV_SHORT_FILE_ID:
352f05cddf9SRui Paulo 			wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File "
3535b9c547cSRui Paulo 				    "Identifier (SFI) TLV", pos, len);
354f05cddf9SRui Paulo 			break;
355f05cddf9SRui Paulo 		case USIM_TLV_SECURITY_ATTR_8B:
356f05cddf9SRui Paulo 		case USIM_TLV_SECURITY_ATTR_8C:
357f05cddf9SRui Paulo 		case USIM_TLV_SECURITY_ATTR_AB:
358f05cddf9SRui Paulo 			wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute "
3595b9c547cSRui Paulo 				    "TLV", pos, len);
360f05cddf9SRui Paulo 			break;
361f05cddf9SRui Paulo 		default:
362f05cddf9SRui Paulo 			wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV",
3635b9c547cSRui Paulo 				    pos, len);
364f05cddf9SRui Paulo 			break;
365f05cddf9SRui Paulo 		}
36639beb93cSSam Leffler 
3675b9c547cSRui Paulo 		pos += len;
36839beb93cSSam Leffler 
36939beb93cSSam Leffler 		if (pos == end)
37039beb93cSSam Leffler 			return 0;
37139beb93cSSam Leffler 	}
37239beb93cSSam Leffler 	return -1;
37339beb93cSSam Leffler }
37439beb93cSSam Leffler 
37539beb93cSSam Leffler 
scard_pin_needed(struct scard_data * scard,unsigned char * hdr,size_t hlen)37639beb93cSSam Leffler static int scard_pin_needed(struct scard_data *scard,
37739beb93cSSam Leffler 			    unsigned char *hdr, size_t hlen)
37839beb93cSSam Leffler {
37939beb93cSSam Leffler 	if (scard->sim_type == SCARD_GSM_SIM) {
38039beb93cSSam Leffler 		if (hlen > SCARD_CHV1_OFFSET &&
38139beb93cSSam Leffler 		    !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG))
38239beb93cSSam Leffler 			return 1;
38339beb93cSSam Leffler 		return 0;
38439beb93cSSam Leffler 	}
38539beb93cSSam Leffler 
38639beb93cSSam Leffler 	if (scard->sim_type == SCARD_USIM) {
38739beb93cSSam Leffler 		int ps_do;
38839beb93cSSam Leffler 		if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL))
38939beb93cSSam Leffler 			return -1;
39039beb93cSSam Leffler 		/* TODO: there could be more than one PS_DO entry because of
39139beb93cSSam Leffler 		 * multiple PINs in key reference.. */
39239beb93cSSam Leffler 		if (ps_do > 0 && (ps_do & 0x80))
39339beb93cSSam Leffler 			return 1;
39439beb93cSSam Leffler 		return 0;
39539beb93cSSam Leffler 	}
39639beb93cSSam Leffler 
39739beb93cSSam Leffler 	return -1;
39839beb93cSSam Leffler }
39939beb93cSSam Leffler 
40039beb93cSSam Leffler 
scard_get_aid(struct scard_data * scard,unsigned char * aid,size_t maxlen)40139beb93cSSam Leffler static int scard_get_aid(struct scard_data *scard, unsigned char *aid,
40239beb93cSSam Leffler 			 size_t maxlen)
40339beb93cSSam Leffler {
40439beb93cSSam Leffler 	int rlen, rec;
40539beb93cSSam Leffler 	struct efdir {
40639beb93cSSam Leffler 		unsigned char appl_template_tag; /* 0x61 */
40739beb93cSSam Leffler 		unsigned char appl_template_len;
40839beb93cSSam Leffler 		unsigned char appl_id_tag; /* 0x4f */
40939beb93cSSam Leffler 		unsigned char aid_len;
41039beb93cSSam Leffler 		unsigned char rid[5];
41139beb93cSSam Leffler 		unsigned char appl_code[2]; /* 0x1002 for 3G USIM */
41239beb93cSSam Leffler 	} *efdir;
4135b9c547cSRui Paulo 	unsigned char buf[127], *aid_pos;
41439beb93cSSam Leffler 	size_t blen;
4155b9c547cSRui Paulo 	unsigned int aid_len = 0;
41639beb93cSSam Leffler 
41739beb93cSSam Leffler 	efdir = (struct efdir *) buf;
4185b9c547cSRui Paulo 	aid_pos = &buf[4];
41939beb93cSSam Leffler 	blen = sizeof(buf);
42039beb93cSSam Leffler 	if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) {
42139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR");
42239beb93cSSam Leffler 		return -1;
42339beb93cSSam Leffler 	}
42439beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR select", buf, blen);
42539beb93cSSam Leffler 
42639beb93cSSam Leffler 	for (rec = 1; rec < 10; rec++) {
42739beb93cSSam Leffler 		rlen = scard_get_record_len(scard, rec,
42839beb93cSSam Leffler 					    SIM_RECORD_MODE_ABSOLUTE);
42939beb93cSSam Leffler 		if (rlen < 0) {
43039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Failed to get EF_DIR "
43139beb93cSSam Leffler 				   "record length");
43239beb93cSSam Leffler 			return -1;
43339beb93cSSam Leffler 		}
43439beb93cSSam Leffler 		blen = sizeof(buf);
43539beb93cSSam Leffler 		if (rlen > (int) blen) {
43639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Too long EF_DIR record");
43739beb93cSSam Leffler 			return -1;
43839beb93cSSam Leffler 		}
43939beb93cSSam Leffler 		if (scard_read_record(scard, buf, rlen, rec,
44039beb93cSSam Leffler 				      SIM_RECORD_MODE_ABSOLUTE) < 0) {
44139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Failed to read "
44239beb93cSSam Leffler 				   "EF_DIR record %d", rec);
44339beb93cSSam Leffler 			return -1;
44439beb93cSSam Leffler 		}
44539beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR record", buf, rlen);
44639beb93cSSam Leffler 
44739beb93cSSam Leffler 		if (efdir->appl_template_tag != 0x61) {
44839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Unexpected application "
44939beb93cSSam Leffler 				   "template tag 0x%x",
45039beb93cSSam Leffler 				   efdir->appl_template_tag);
45139beb93cSSam Leffler 			continue;
45239beb93cSSam Leffler 		}
45339beb93cSSam Leffler 
45439beb93cSSam Leffler 		if (efdir->appl_template_len > rlen - 2) {
45539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Too long application "
45639beb93cSSam Leffler 				   "template (len=%d rlen=%d)",
45739beb93cSSam Leffler 				   efdir->appl_template_len, rlen);
45839beb93cSSam Leffler 			continue;
45939beb93cSSam Leffler 		}
46039beb93cSSam Leffler 
46139beb93cSSam Leffler 		if (efdir->appl_id_tag != 0x4f) {
46239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Unexpected application "
46339beb93cSSam Leffler 				   "identifier tag 0x%x", efdir->appl_id_tag);
46439beb93cSSam Leffler 			continue;
46539beb93cSSam Leffler 		}
46639beb93cSSam Leffler 
4675b9c547cSRui Paulo 		aid_len = efdir->aid_len;
4685b9c547cSRui Paulo 		if (aid_len < 1 || aid_len > 16) {
4695b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %u",
4705b9c547cSRui Paulo 				   aid_len);
47139beb93cSSam Leffler 			continue;
47239beb93cSSam Leffler 		}
47339beb93cSSam Leffler 
47439beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record",
4755b9c547cSRui Paulo 			    aid_pos, aid_len);
47639beb93cSSam Leffler 
47739beb93cSSam Leffler 		if (efdir->appl_code[0] == 0x10 &&
47839beb93cSSam Leffler 		    efdir->appl_code[1] == 0x02) {
47939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app found from "
48039beb93cSSam Leffler 				   "EF_DIR record %d", rec);
48139beb93cSSam Leffler 			break;
48239beb93cSSam Leffler 		}
48339beb93cSSam Leffler 	}
48439beb93cSSam Leffler 
48539beb93cSSam Leffler 	if (rec >= 10) {
48639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app not found "
48739beb93cSSam Leffler 			   "from EF_DIR records");
48839beb93cSSam Leffler 		return -1;
48939beb93cSSam Leffler 	}
49039beb93cSSam Leffler 
4915b9c547cSRui Paulo 	if (aid_len > maxlen) {
49239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: Too long AID");
49339beb93cSSam Leffler 		return -1;
49439beb93cSSam Leffler 	}
49539beb93cSSam Leffler 
4965b9c547cSRui Paulo 	os_memcpy(aid, aid_pos, aid_len);
49739beb93cSSam Leffler 
4985b9c547cSRui Paulo 	return aid_len;
49939beb93cSSam Leffler }
50039beb93cSSam Leffler 
50139beb93cSSam Leffler 
50239beb93cSSam Leffler /**
50339beb93cSSam Leffler  * scard_init - Initialize SIM/USIM connection using PC/SC
504f05cddf9SRui Paulo  * @reader: Reader name prefix to search for
50539beb93cSSam Leffler  * Returns: Pointer to private data structure, or %NULL on failure
50639beb93cSSam Leffler  *
50739beb93cSSam Leffler  * This function is used to initialize SIM/USIM connection. PC/SC is used to
5085b9c547cSRui Paulo  * open connection to the SIM/USIM card. In addition, local flag is set if a
5095b9c547cSRui Paulo  * PIN is needed to access some of the card functions. Once the connection is
5105b9c547cSRui Paulo  * not needed anymore, scard_deinit() can be used to close it.
51139beb93cSSam Leffler  */
scard_init(const char * reader)5125b9c547cSRui Paulo struct scard_data * scard_init(const char *reader)
51339beb93cSSam Leffler {
51439beb93cSSam Leffler 	long ret;
515*780fb4a2SCy Schubert #ifdef __APPLE__
516*780fb4a2SCy Schubert 	uint32_t len;
517*780fb4a2SCy Schubert #else
518*780fb4a2SCy Schubert 	unsigned long len;
519*780fb4a2SCy Schubert #endif
520*780fb4a2SCy Schubert 	unsigned long pos;
52139beb93cSSam Leffler 	struct scard_data *scard;
52239beb93cSSam Leffler #ifdef CONFIG_NATIVE_WINDOWS
52339beb93cSSam Leffler 	TCHAR *readers = NULL;
52439beb93cSSam Leffler #else /* CONFIG_NATIVE_WINDOWS */
52539beb93cSSam Leffler 	char *readers = NULL;
52639beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
52739beb93cSSam Leffler 	unsigned char buf[100];
52839beb93cSSam Leffler 	size_t blen;
52939beb93cSSam Leffler 	int transaction = 0;
53039beb93cSSam Leffler 	int pin_needed;
53139beb93cSSam Leffler 
53239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface");
53339beb93cSSam Leffler 	if (mingw_load_symbols())
53439beb93cSSam Leffler 		return NULL;
53539beb93cSSam Leffler 	scard = os_zalloc(sizeof(*scard));
53639beb93cSSam Leffler 	if (scard == NULL)
53739beb93cSSam Leffler 		return NULL;
53839beb93cSSam Leffler 
53939beb93cSSam Leffler 	ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
54039beb93cSSam Leffler 				    &scard->ctx);
54139beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS) {
54239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card "
54339beb93cSSam Leffler 			   "context (err=%ld)", ret);
54439beb93cSSam Leffler 		goto failed;
54539beb93cSSam Leffler 	}
54639beb93cSSam Leffler 
54739beb93cSSam Leffler 	ret = SCardListReaders(scard->ctx, NULL, NULL, &len);
54839beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS) {
54939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed "
55039beb93cSSam Leffler 			   "(err=%ld)", ret);
55139beb93cSSam Leffler 		goto failed;
55239beb93cSSam Leffler 	}
55339beb93cSSam Leffler 
55439beb93cSSam Leffler #ifdef UNICODE
55539beb93cSSam Leffler 	len *= 2;
55639beb93cSSam Leffler #endif /* UNICODE */
55739beb93cSSam Leffler 	readers = os_malloc(len);
55839beb93cSSam Leffler 	if (readers == NULL) {
55939beb93cSSam Leffler 		wpa_printf(MSG_INFO, "SCARD: malloc failed\n");
56039beb93cSSam Leffler 		goto failed;
56139beb93cSSam Leffler 	}
56239beb93cSSam Leffler 
56339beb93cSSam Leffler 	ret = SCardListReaders(scard->ctx, NULL, readers, &len);
56439beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS) {
56539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) "
56639beb93cSSam Leffler 			   "(err=%ld)", ret);
56739beb93cSSam Leffler 		goto failed;
56839beb93cSSam Leffler 	}
56939beb93cSSam Leffler 	if (len < 3) {
57039beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "SCARD: No smart card readers "
57139beb93cSSam Leffler 			   "available.");
57239beb93cSSam Leffler 		goto failed;
57339beb93cSSam Leffler 	}
574f05cddf9SRui Paulo 	wpa_hexdump_ascii(MSG_DEBUG, "SCARD: Readers", (u8 *) readers, len);
575f05cddf9SRui Paulo 	/*
576f05cddf9SRui Paulo 	 * readers is a list of available readers. The last entry is terminated
577f05cddf9SRui Paulo 	 * with double null.
578f05cddf9SRui Paulo 	 */
579f05cddf9SRui Paulo 	pos = 0;
58039beb93cSSam Leffler #ifdef UNICODE
581f05cddf9SRui Paulo 	/* TODO */
58239beb93cSSam Leffler #else /* UNICODE */
583f05cddf9SRui Paulo 	while (pos < len) {
584f05cddf9SRui Paulo 		if (reader == NULL ||
585f05cddf9SRui Paulo 		    os_strncmp(&readers[pos], reader, os_strlen(reader)) == 0)
586f05cddf9SRui Paulo 			break;
587f05cddf9SRui Paulo 		while (pos < len && readers[pos])
588f05cddf9SRui Paulo 			pos++;
589f05cddf9SRui Paulo 		pos++; /* skip separating null */
590f05cddf9SRui Paulo 		if (pos < len && readers[pos] == '\0')
591f05cddf9SRui Paulo 			pos = len; /* double null terminates list */
592f05cddf9SRui Paulo 	}
593f05cddf9SRui Paulo #endif /* UNICODE */
594f05cddf9SRui Paulo 	if (pos >= len) {
595f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "SCARD: No reader with prefix '%s' "
596f05cddf9SRui Paulo 			   "found", reader);
597f05cddf9SRui Paulo 		goto failed;
598f05cddf9SRui Paulo 	}
599f05cddf9SRui Paulo 
600f05cddf9SRui Paulo #ifdef UNICODE
601f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]);
602f05cddf9SRui Paulo #else /* UNICODE */
603f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]);
60439beb93cSSam Leffler #endif /* UNICODE */
60539beb93cSSam Leffler 
606f05cddf9SRui Paulo 	ret = SCardConnect(scard->ctx, &readers[pos], SCARD_SHARE_SHARED,
607f05cddf9SRui Paulo 			   SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
608f05cddf9SRui Paulo 			   &scard->card, &scard->protocol);
60939beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS) {
61039beb93cSSam Leffler 		if (ret == (long) SCARD_E_NO_SMARTCARD)
61139beb93cSSam Leffler 			wpa_printf(MSG_INFO, "No smart card inserted.");
61239beb93cSSam Leffler 		else
61339beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret);
61439beb93cSSam Leffler 		goto failed;
61539beb93cSSam Leffler 	}
61639beb93cSSam Leffler 
61739beb93cSSam Leffler 	os_free(readers);
61839beb93cSSam Leffler 	readers = NULL;
61939beb93cSSam Leffler 
62039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)",
621*780fb4a2SCy Schubert 		   (unsigned int) scard->card, (unsigned long) scard->protocol,
62239beb93cSSam Leffler 		   scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1");
62339beb93cSSam Leffler 
62439beb93cSSam Leffler 	ret = SCardBeginTransaction(scard->card);
62539beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS) {
62639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: Could not begin transaction: "
62739beb93cSSam Leffler 			   "0x%x", (unsigned int) ret);
62839beb93cSSam Leffler 		goto failed;
62939beb93cSSam Leffler 	}
63039beb93cSSam Leffler 	transaction = 1;
63139beb93cSSam Leffler 
63239beb93cSSam Leffler 	blen = sizeof(buf);
63339beb93cSSam Leffler 
63439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support");
63539beb93cSSam Leffler 	if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen,
63639beb93cSSam Leffler 			       SCARD_USIM, NULL, 0)) {
6375b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported. Trying to use GSM SIM");
63839beb93cSSam Leffler 		scard->sim_type = SCARD_GSM_SIM;
63939beb93cSSam Leffler 	} else {
64039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: USIM is supported");
64139beb93cSSam Leffler 		scard->sim_type = SCARD_USIM;
64239beb93cSSam Leffler 	}
64339beb93cSSam Leffler 
64439beb93cSSam Leffler 	if (scard->sim_type == SCARD_GSM_SIM) {
64539beb93cSSam Leffler 		blen = sizeof(buf);
64639beb93cSSam Leffler 		if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) {
64739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF");
64839beb93cSSam Leffler 			goto failed;
64939beb93cSSam Leffler 		}
65039beb93cSSam Leffler 
65139beb93cSSam Leffler 		blen = sizeof(buf);
65239beb93cSSam Leffler 		if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) {
65339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF");
65439beb93cSSam Leffler 			goto failed;
65539beb93cSSam Leffler 		}
65639beb93cSSam Leffler 	} else {
65739beb93cSSam Leffler 		unsigned char aid[32];
65839beb93cSSam Leffler 		int aid_len;
65939beb93cSSam Leffler 
66039beb93cSSam Leffler 		aid_len = scard_get_aid(scard, aid, sizeof(aid));
66139beb93cSSam Leffler 		if (aid_len < 0) {
66239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Failed to find AID for "
66339beb93cSSam Leffler 				   "3G USIM app - try to use standard 3G RID");
66439beb93cSSam Leffler 			os_memcpy(aid, "\xa0\x00\x00\x00\x87", 5);
66539beb93cSSam Leffler 			aid_len = 5;
66639beb93cSSam Leffler 		}
66739beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "SCARD: 3G USIM AID", aid, aid_len);
66839beb93cSSam Leffler 
66939beb93cSSam Leffler 		/* Select based on AID = 3G RID from EF_DIR. This is usually
67039beb93cSSam Leffler 		 * starting with A0 00 00 00 87. */
67139beb93cSSam Leffler 		blen = sizeof(buf);
67239beb93cSSam Leffler 		if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type,
67339beb93cSSam Leffler 				       aid, aid_len)) {
67439beb93cSSam Leffler 			wpa_printf(MSG_INFO, "SCARD: Failed to read 3G USIM "
67539beb93cSSam Leffler 				   "app");
67639beb93cSSam Leffler 			wpa_hexdump(MSG_INFO, "SCARD: 3G USIM AID",
67739beb93cSSam Leffler 				    aid, aid_len);
67839beb93cSSam Leffler 			goto failed;
67939beb93cSSam Leffler 		}
68039beb93cSSam Leffler 	}
68139beb93cSSam Leffler 
68239beb93cSSam Leffler 	/* Verify whether CHV1 (PIN1) is needed to access the card. */
68339beb93cSSam Leffler 	pin_needed = scard_pin_needed(scard, buf, blen);
68439beb93cSSam Leffler 	if (pin_needed < 0) {
68539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: Failed to determine whether PIN "
68639beb93cSSam Leffler 			   "is needed");
68739beb93cSSam Leffler 		goto failed;
68839beb93cSSam Leffler 	}
68939beb93cSSam Leffler 	if (pin_needed) {
69039beb93cSSam Leffler 		scard->pin1_required = 1;
691f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry "
692f05cddf9SRui Paulo 			   "counter=%d)", scard_get_pin_retry_counter(scard));
69339beb93cSSam Leffler 	}
69439beb93cSSam Leffler 
69539beb93cSSam Leffler 	ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
69639beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS) {
69739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: Could not end transaction: "
69839beb93cSSam Leffler 			   "0x%x", (unsigned int) ret);
69939beb93cSSam Leffler 	}
70039beb93cSSam Leffler 
70139beb93cSSam Leffler 	return scard;
70239beb93cSSam Leffler 
70339beb93cSSam Leffler failed:
70439beb93cSSam Leffler 	if (transaction)
70539beb93cSSam Leffler 		SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
70639beb93cSSam Leffler 	os_free(readers);
70739beb93cSSam Leffler 	scard_deinit(scard);
70839beb93cSSam Leffler 	return NULL;
70939beb93cSSam Leffler }
71039beb93cSSam Leffler 
71139beb93cSSam Leffler 
71239beb93cSSam Leffler /**
71339beb93cSSam Leffler  * scard_set_pin - Set PIN (CHV1/PIN1) code for accessing SIM/USIM commands
71439beb93cSSam Leffler  * @scard: Pointer to private data from scard_init()
71539beb93cSSam Leffler  * @pin: PIN code as an ASCII string (e.g., "1234")
71639beb93cSSam Leffler  * Returns: 0 on success, -1 on failure
71739beb93cSSam Leffler  */
scard_set_pin(struct scard_data * scard,const char * pin)71839beb93cSSam Leffler int scard_set_pin(struct scard_data *scard, const char *pin)
71939beb93cSSam Leffler {
72039beb93cSSam Leffler 	if (scard == NULL)
72139beb93cSSam Leffler 		return -1;
72239beb93cSSam Leffler 
72339beb93cSSam Leffler 	/* Verify whether CHV1 (PIN1) is needed to access the card. */
72439beb93cSSam Leffler 	if (scard->pin1_required) {
72539beb93cSSam Leffler 		if (pin == NULL) {
72639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "No PIN configured for SIM "
72739beb93cSSam Leffler 				   "access");
72839beb93cSSam Leffler 			return -1;
72939beb93cSSam Leffler 		}
73039beb93cSSam Leffler 		if (scard_verify_pin(scard, pin)) {
73139beb93cSSam Leffler 			wpa_printf(MSG_INFO, "PIN verification failed for "
73239beb93cSSam Leffler 				"SIM access");
73339beb93cSSam Leffler 			return -1;
73439beb93cSSam Leffler 		}
73539beb93cSSam Leffler 	}
73639beb93cSSam Leffler 
73739beb93cSSam Leffler 	return 0;
73839beb93cSSam Leffler }
73939beb93cSSam Leffler 
74039beb93cSSam Leffler 
74139beb93cSSam Leffler /**
74239beb93cSSam Leffler  * scard_deinit - Deinitialize SIM/USIM connection
74339beb93cSSam Leffler  * @scard: Pointer to private data from scard_init()
74439beb93cSSam Leffler  *
74539beb93cSSam Leffler  * This function closes the SIM/USIM connect opened with scard_init().
74639beb93cSSam Leffler  */
scard_deinit(struct scard_data * scard)74739beb93cSSam Leffler void scard_deinit(struct scard_data *scard)
74839beb93cSSam Leffler {
74939beb93cSSam Leffler 	long ret;
75039beb93cSSam Leffler 
75139beb93cSSam Leffler 	if (scard == NULL)
75239beb93cSSam Leffler 		return;
75339beb93cSSam Leffler 
75439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface");
75539beb93cSSam Leffler 	if (scard->card) {
75639beb93cSSam Leffler 		ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD);
75739beb93cSSam Leffler 		if (ret != SCARD_S_SUCCESS) {
75839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect "
75939beb93cSSam Leffler 				   "smart card (err=%ld)", ret);
76039beb93cSSam Leffler 		}
76139beb93cSSam Leffler 	}
76239beb93cSSam Leffler 
76339beb93cSSam Leffler 	if (scard->ctx) {
76439beb93cSSam Leffler 		ret = SCardReleaseContext(scard->ctx);
76539beb93cSSam Leffler 		if (ret != SCARD_S_SUCCESS) {
76639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "Failed to release smart card "
76739beb93cSSam Leffler 				   "context (err=%ld)", ret);
76839beb93cSSam Leffler 		}
76939beb93cSSam Leffler 	}
77039beb93cSSam Leffler 	os_free(scard);
77139beb93cSSam Leffler 	mingw_unload_symbols();
77239beb93cSSam Leffler }
77339beb93cSSam Leffler 
77439beb93cSSam Leffler 
scard_transmit(struct scard_data * scard,unsigned char * _send,size_t send_len,unsigned char * _recv,size_t * recv_len)77539beb93cSSam Leffler static long scard_transmit(struct scard_data *scard,
77639beb93cSSam Leffler 			   unsigned char *_send, size_t send_len,
77739beb93cSSam Leffler 			   unsigned char *_recv, size_t *recv_len)
77839beb93cSSam Leffler {
77939beb93cSSam Leffler 	long ret;
780*780fb4a2SCy Schubert #ifdef __APPLE__
781*780fb4a2SCy Schubert 	uint32_t rlen;
782*780fb4a2SCy Schubert #else
78339beb93cSSam Leffler 	unsigned long rlen;
784*780fb4a2SCy Schubert #endif
78539beb93cSSam Leffler 
78639beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send",
78739beb93cSSam Leffler 			_send, send_len);
78839beb93cSSam Leffler 	rlen = *recv_len;
78939beb93cSSam Leffler 	ret = SCardTransmit(scard->card,
79039beb93cSSam Leffler 			    scard->protocol == SCARD_PROTOCOL_T1 ?
79139beb93cSSam Leffler 			    SCARD_PCI_T1 : SCARD_PCI_T0,
79239beb93cSSam Leffler 			    _send, (unsigned long) send_len,
79339beb93cSSam Leffler 			    NULL, _recv, &rlen);
79439beb93cSSam Leffler 	*recv_len = rlen;
79539beb93cSSam Leffler 	if (ret == SCARD_S_SUCCESS) {
79639beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv",
79739beb93cSSam Leffler 			    _recv, rlen);
79839beb93cSSam Leffler 	} else {
79939beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
80039beb93cSSam Leffler 			   "(err=0x%lx)", ret);
80139beb93cSSam Leffler 	}
80239beb93cSSam Leffler 	return ret;
80339beb93cSSam Leffler }
80439beb93cSSam Leffler 
80539beb93cSSam Leffler 
_scard_select_file(struct scard_data * scard,unsigned short file_id,unsigned char * buf,size_t * buf_len,sim_types sim_type,unsigned char * aid,size_t aidlen)80639beb93cSSam Leffler static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
80739beb93cSSam Leffler 			      unsigned char *buf, size_t *buf_len,
80839beb93cSSam Leffler 			      sim_types sim_type, unsigned char *aid,
80939beb93cSSam Leffler 			      size_t aidlen)
81039beb93cSSam Leffler {
81139beb93cSSam Leffler 	long ret;
81239beb93cSSam Leffler 	unsigned char resp[3];
81339beb93cSSam Leffler 	unsigned char cmd[50] = { SIM_CMD_SELECT };
81439beb93cSSam Leffler 	int cmdlen;
81539beb93cSSam Leffler 	unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
81639beb93cSSam Leffler 	size_t len, rlen;
81739beb93cSSam Leffler 
81839beb93cSSam Leffler 	if (sim_type == SCARD_USIM) {
81939beb93cSSam Leffler 		cmd[0] = USIM_CLA;
82039beb93cSSam Leffler 		cmd[3] = 0x04;
82139beb93cSSam Leffler 		get_resp[0] = USIM_CLA;
82239beb93cSSam Leffler 	}
82339beb93cSSam Leffler 
82439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id);
82539beb93cSSam Leffler 	if (aid) {
82639beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "SCARD: select file by AID",
82739beb93cSSam Leffler 			    aid, aidlen);
82839beb93cSSam Leffler 		if (5 + aidlen > sizeof(cmd))
82939beb93cSSam Leffler 			return -1;
83039beb93cSSam Leffler 		cmd[2] = 0x04; /* Select by AID */
83139beb93cSSam Leffler 		cmd[4] = aidlen; /* len */
83239beb93cSSam Leffler 		os_memcpy(cmd + 5, aid, aidlen);
83339beb93cSSam Leffler 		cmdlen = 5 + aidlen;
83439beb93cSSam Leffler 	} else {
83539beb93cSSam Leffler 		cmd[5] = file_id >> 8;
83639beb93cSSam Leffler 		cmd[6] = file_id & 0xff;
83739beb93cSSam Leffler 		cmdlen = 7;
83839beb93cSSam Leffler 	}
83939beb93cSSam Leffler 	len = sizeof(resp);
84039beb93cSSam Leffler 	ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
84139beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS) {
84239beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
84339beb93cSSam Leffler 			   "(err=0x%lx)", ret);
84439beb93cSSam Leffler 		return -1;
84539beb93cSSam Leffler 	}
84639beb93cSSam Leffler 
84739beb93cSSam Leffler 	if (len != 2) {
84839beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "SCARD: unexpected resp len "
84939beb93cSSam Leffler 			   "%d (expected 2)", (int) len);
85039beb93cSSam Leffler 		return -1;
85139beb93cSSam Leffler 	}
85239beb93cSSam Leffler 
85339beb93cSSam Leffler 	if (resp[0] == 0x98 && resp[1] == 0x04) {
85439beb93cSSam Leffler 		/* Security status not satisfied (PIN_WLAN) */
85539beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied "
85639beb93cSSam Leffler 			   "(PIN_WLAN)");
85739beb93cSSam Leffler 		return -1;
85839beb93cSSam Leffler 	}
85939beb93cSSam Leffler 
86039beb93cSSam Leffler 	if (resp[0] == 0x6e) {
86139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported");
86239beb93cSSam Leffler 		return -1;
86339beb93cSSam Leffler 	}
86439beb93cSSam Leffler 
86539beb93cSSam Leffler 	if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) {
86639beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x "
86739beb93cSSam Leffler 			   "(expected 0x61, 0x6c, or 0x9f)", resp[0]);
86839beb93cSSam Leffler 		return -1;
86939beb93cSSam Leffler 	}
87039beb93cSSam Leffler 	/* Normal ending of command; resp[1] bytes available */
87139beb93cSSam Leffler 	get_resp[4] = resp[1];
87239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)",
87339beb93cSSam Leffler 		   resp[1]);
87439beb93cSSam Leffler 
87539beb93cSSam Leffler 	rlen = *buf_len;
87639beb93cSSam Leffler 	ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen);
87739beb93cSSam Leffler 	if (ret == SCARD_S_SUCCESS) {
87839beb93cSSam Leffler 		*buf_len = resp[1] < rlen ? resp[1] : rlen;
87939beb93cSSam Leffler 		return 0;
88039beb93cSSam Leffler 	}
88139beb93cSSam Leffler 
88239beb93cSSam Leffler 	wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret);
88339beb93cSSam Leffler 	return -1;
88439beb93cSSam Leffler }
88539beb93cSSam Leffler 
88639beb93cSSam Leffler 
scard_select_file(struct scard_data * scard,unsigned short file_id,unsigned char * buf,size_t * buf_len)88739beb93cSSam Leffler static int scard_select_file(struct scard_data *scard, unsigned short file_id,
88839beb93cSSam Leffler 			     unsigned char *buf, size_t *buf_len)
88939beb93cSSam Leffler {
89039beb93cSSam Leffler 	return _scard_select_file(scard, file_id, buf, buf_len,
89139beb93cSSam Leffler 				  scard->sim_type, NULL, 0);
89239beb93cSSam Leffler }
89339beb93cSSam Leffler 
89439beb93cSSam Leffler 
scard_get_record_len(struct scard_data * scard,unsigned char recnum,unsigned char mode)89539beb93cSSam Leffler static int scard_get_record_len(struct scard_data *scard, unsigned char recnum,
89639beb93cSSam Leffler 				unsigned char mode)
89739beb93cSSam Leffler {
89839beb93cSSam Leffler 	unsigned char buf[255];
89939beb93cSSam Leffler 	unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ };
90039beb93cSSam Leffler 	size_t blen;
90139beb93cSSam Leffler 	long ret;
90239beb93cSSam Leffler 
90339beb93cSSam Leffler 	if (scard->sim_type == SCARD_USIM)
90439beb93cSSam Leffler 		cmd[0] = USIM_CLA;
90539beb93cSSam Leffler 	cmd[2] = recnum;
90639beb93cSSam Leffler 	cmd[3] = mode;
90739beb93cSSam Leffler 	cmd[4] = sizeof(buf);
90839beb93cSSam Leffler 
90939beb93cSSam Leffler 	blen = sizeof(buf);
91039beb93cSSam Leffler 	ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
91139beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS) {
91239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: failed to determine file "
91339beb93cSSam Leffler 			   "length for record %d", recnum);
91439beb93cSSam Leffler 		return -1;
91539beb93cSSam Leffler 	}
91639beb93cSSam Leffler 
91739beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response",
91839beb93cSSam Leffler 		    buf, blen);
91939beb93cSSam Leffler 
920f05cddf9SRui Paulo 	if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) {
92139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file "
92239beb93cSSam Leffler 			   "length determination");
92339beb93cSSam Leffler 		return -1;
92439beb93cSSam Leffler 	}
92539beb93cSSam Leffler 
92639beb93cSSam Leffler 	return buf[1];
92739beb93cSSam Leffler }
92839beb93cSSam Leffler 
92939beb93cSSam Leffler 
scard_read_record(struct scard_data * scard,unsigned char * data,size_t len,unsigned char recnum,unsigned char mode)93039beb93cSSam Leffler static int scard_read_record(struct scard_data *scard,
93139beb93cSSam Leffler 			     unsigned char *data, size_t len,
93239beb93cSSam Leffler 			     unsigned char recnum, unsigned char mode)
93339beb93cSSam Leffler {
93439beb93cSSam Leffler 	unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ };
93539beb93cSSam Leffler 	size_t blen = len + 3;
93639beb93cSSam Leffler 	unsigned char *buf;
93739beb93cSSam Leffler 	long ret;
93839beb93cSSam Leffler 
93939beb93cSSam Leffler 	if (scard->sim_type == SCARD_USIM)
94039beb93cSSam Leffler 		cmd[0] = USIM_CLA;
94139beb93cSSam Leffler 	cmd[2] = recnum;
94239beb93cSSam Leffler 	cmd[3] = mode;
94339beb93cSSam Leffler 	cmd[4] = len;
94439beb93cSSam Leffler 
94539beb93cSSam Leffler 	buf = os_malloc(blen);
94639beb93cSSam Leffler 	if (buf == NULL)
94739beb93cSSam Leffler 		return -1;
94839beb93cSSam Leffler 
94939beb93cSSam Leffler 	ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
95039beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS) {
95139beb93cSSam Leffler 		os_free(buf);
95239beb93cSSam Leffler 		return -2;
95339beb93cSSam Leffler 	}
95439beb93cSSam Leffler 	if (blen != len + 2) {
95539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected "
95639beb93cSSam Leffler 			   "length %ld (expected %ld)",
95739beb93cSSam Leffler 			   (long) blen, (long) len + 2);
95839beb93cSSam Leffler 		os_free(buf);
95939beb93cSSam Leffler 		return -3;
96039beb93cSSam Leffler 	}
96139beb93cSSam Leffler 
96239beb93cSSam Leffler 	if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
96339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected "
96439beb93cSSam Leffler 			   "status %02x %02x (expected 90 00)",
96539beb93cSSam Leffler 			   buf[len], buf[len + 1]);
96639beb93cSSam Leffler 		os_free(buf);
96739beb93cSSam Leffler 		return -4;
96839beb93cSSam Leffler 	}
96939beb93cSSam Leffler 
97039beb93cSSam Leffler 	os_memcpy(data, buf, len);
97139beb93cSSam Leffler 	os_free(buf);
97239beb93cSSam Leffler 
97339beb93cSSam Leffler 	return 0;
97439beb93cSSam Leffler }
97539beb93cSSam Leffler 
97639beb93cSSam Leffler 
scard_read_file(struct scard_data * scard,unsigned char * data,size_t len)97739beb93cSSam Leffler static int scard_read_file(struct scard_data *scard,
97839beb93cSSam Leffler 			   unsigned char *data, size_t len)
97939beb93cSSam Leffler {
98039beb93cSSam Leffler 	unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ };
98139beb93cSSam Leffler 	size_t blen = len + 3;
98239beb93cSSam Leffler 	unsigned char *buf;
98339beb93cSSam Leffler 	long ret;
98439beb93cSSam Leffler 
98539beb93cSSam Leffler 	cmd[4] = len;
98639beb93cSSam Leffler 
98739beb93cSSam Leffler 	buf = os_malloc(blen);
98839beb93cSSam Leffler 	if (buf == NULL)
98939beb93cSSam Leffler 		return -1;
99039beb93cSSam Leffler 
99139beb93cSSam Leffler 	if (scard->sim_type == SCARD_USIM)
99239beb93cSSam Leffler 		cmd[0] = USIM_CLA;
99339beb93cSSam Leffler 	ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
99439beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS) {
99539beb93cSSam Leffler 		os_free(buf);
99639beb93cSSam Leffler 		return -2;
99739beb93cSSam Leffler 	}
99839beb93cSSam Leffler 	if (blen != len + 2) {
99939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
100039beb93cSSam Leffler 			   "length %ld (expected %ld)",
100139beb93cSSam Leffler 			   (long) blen, (long) len + 2);
100239beb93cSSam Leffler 		os_free(buf);
100339beb93cSSam Leffler 		return -3;
100439beb93cSSam Leffler 	}
100539beb93cSSam Leffler 
100639beb93cSSam Leffler 	if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
100739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
100839beb93cSSam Leffler 			   "status %02x %02x (expected 90 00)",
100939beb93cSSam Leffler 			   buf[len], buf[len + 1]);
101039beb93cSSam Leffler 		os_free(buf);
101139beb93cSSam Leffler 		return -4;
101239beb93cSSam Leffler 	}
101339beb93cSSam Leffler 
101439beb93cSSam Leffler 	os_memcpy(data, buf, len);
101539beb93cSSam Leffler 	os_free(buf);
101639beb93cSSam Leffler 
101739beb93cSSam Leffler 	return 0;
101839beb93cSSam Leffler }
101939beb93cSSam Leffler 
102039beb93cSSam Leffler 
scard_verify_pin(struct scard_data * scard,const char * pin)102139beb93cSSam Leffler static int scard_verify_pin(struct scard_data *scard, const char *pin)
102239beb93cSSam Leffler {
102339beb93cSSam Leffler 	long ret;
102439beb93cSSam Leffler 	unsigned char resp[3];
102539beb93cSSam Leffler 	unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 };
102639beb93cSSam Leffler 	size_t len;
102739beb93cSSam Leffler 
102839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "SCARD: verifying PIN");
102939beb93cSSam Leffler 
103039beb93cSSam Leffler 	if (pin == NULL || os_strlen(pin) > 8)
103139beb93cSSam Leffler 		return -1;
103239beb93cSSam Leffler 
103339beb93cSSam Leffler 	if (scard->sim_type == SCARD_USIM)
103439beb93cSSam Leffler 		cmd[0] = USIM_CLA;
103539beb93cSSam Leffler 	os_memcpy(cmd + 5, pin, os_strlen(pin));
103639beb93cSSam Leffler 	os_memset(cmd + 5 + os_strlen(pin), 0xff, 8 - os_strlen(pin));
103739beb93cSSam Leffler 
103839beb93cSSam Leffler 	len = sizeof(resp);
103939beb93cSSam Leffler 	ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
104039beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS)
104139beb93cSSam Leffler 		return -2;
104239beb93cSSam Leffler 
104339beb93cSSam Leffler 	if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) {
104439beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "SCARD: PIN verification failed");
104539beb93cSSam Leffler 		return -1;
104639beb93cSSam Leffler 	}
104739beb93cSSam Leffler 
104839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully");
104939beb93cSSam Leffler 	return 0;
105039beb93cSSam Leffler }
105139beb93cSSam Leffler 
105239beb93cSSam Leffler 
scard_get_pin_retry_counter(struct scard_data * scard)1053f05cddf9SRui Paulo int scard_get_pin_retry_counter(struct scard_data *scard)
1054f05cddf9SRui Paulo {
1055f05cddf9SRui Paulo 	long ret;
1056f05cddf9SRui Paulo 	unsigned char resp[3];
1057f05cddf9SRui Paulo 	unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 };
1058f05cddf9SRui Paulo 	size_t len;
1059f05cddf9SRui Paulo 	u16 val;
1060f05cddf9SRui Paulo 
1061f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "SCARD: fetching PIN retry counter");
1062f05cddf9SRui Paulo 
1063f05cddf9SRui Paulo 	if (scard->sim_type == SCARD_USIM)
1064f05cddf9SRui Paulo 		cmd[0] = USIM_CLA;
1065f05cddf9SRui Paulo 	cmd[4] = 0; /* Empty data */
1066f05cddf9SRui Paulo 
1067f05cddf9SRui Paulo 	len = sizeof(resp);
1068f05cddf9SRui Paulo 	ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
1069f05cddf9SRui Paulo 	if (ret != SCARD_S_SUCCESS)
1070f05cddf9SRui Paulo 		return -2;
1071f05cddf9SRui Paulo 
1072f05cddf9SRui Paulo 	if (len != 2) {
1073f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "SCARD: failed to fetch PIN retry "
1074f05cddf9SRui Paulo 			   "counter");
1075f05cddf9SRui Paulo 		return -1;
1076f05cddf9SRui Paulo 	}
1077f05cddf9SRui Paulo 
1078f05cddf9SRui Paulo 	val = WPA_GET_BE16(resp);
1079f05cddf9SRui Paulo 	if (val == 0x63c0 || val == 0x6983) {
1080f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "SCARD: PIN has been blocked");
1081f05cddf9SRui Paulo 		return 0;
1082f05cddf9SRui Paulo 	}
1083f05cddf9SRui Paulo 
1084f05cddf9SRui Paulo 	if (val >= 0x63c0 && val <= 0x63cf)
1085f05cddf9SRui Paulo 		return val & 0x000f;
1086f05cddf9SRui Paulo 
1087f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "SCARD: Unexpected PIN retry counter response "
1088f05cddf9SRui Paulo 		   "value 0x%x", val);
1089f05cddf9SRui Paulo 	return 0;
1090f05cddf9SRui Paulo }
1091f05cddf9SRui Paulo 
1092f05cddf9SRui Paulo 
109339beb93cSSam Leffler /**
109439beb93cSSam Leffler  * scard_get_imsi - Read IMSI from SIM/USIM card
109539beb93cSSam Leffler  * @scard: Pointer to private data from scard_init()
109639beb93cSSam Leffler  * @imsi: Buffer for IMSI
109739beb93cSSam Leffler  * @len: Length of imsi buffer; set to IMSI length on success
109839beb93cSSam Leffler  * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file
109939beb93cSSam Leffler  * selection returns invalid result code, -3 if parsing FSP template file fails
110039beb93cSSam Leffler  * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set
110139beb93cSSam Leffler  * to needed length), -5 if reading IMSI file fails.
110239beb93cSSam Leffler  *
110339beb93cSSam Leffler  * This function can be used to read IMSI from the SIM/USIM card. If the IMSI
110439beb93cSSam Leffler  * file is PIN protected, scard_set_pin() must have been used to set the
110539beb93cSSam Leffler  * correct PIN code before calling scard_get_imsi().
110639beb93cSSam Leffler  */
scard_get_imsi(struct scard_data * scard,char * imsi,size_t * len)110739beb93cSSam Leffler int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len)
110839beb93cSSam Leffler {
110939beb93cSSam Leffler 	unsigned char buf[100];
111039beb93cSSam Leffler 	size_t blen, imsilen, i;
111139beb93cSSam Leffler 	char *pos;
111239beb93cSSam Leffler 
111339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI");
111439beb93cSSam Leffler 	blen = sizeof(buf);
111539beb93cSSam Leffler 	if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen))
111639beb93cSSam Leffler 		return -1;
111739beb93cSSam Leffler 	if (blen < 4) {
111839beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI "
111939beb93cSSam Leffler 			   "header (len=%ld)", (long) blen);
112039beb93cSSam Leffler 		return -2;
112139beb93cSSam Leffler 	}
112239beb93cSSam Leffler 
112339beb93cSSam Leffler 	if (scard->sim_type == SCARD_GSM_SIM) {
11245b9c547cSRui Paulo 		blen = WPA_GET_BE16(&buf[2]);
112539beb93cSSam Leffler 	} else {
112639beb93cSSam Leffler 		int file_size;
112739beb93cSSam Leffler 		if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
112839beb93cSSam Leffler 			return -3;
112939beb93cSSam Leffler 		blen = file_size;
113039beb93cSSam Leffler 	}
113139beb93cSSam Leffler 	if (blen < 2 || blen > sizeof(buf)) {
113239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%ld",
113339beb93cSSam Leffler 			   (long) blen);
113439beb93cSSam Leffler 		return -3;
113539beb93cSSam Leffler 	}
113639beb93cSSam Leffler 
113739beb93cSSam Leffler 	imsilen = (blen - 2) * 2 + 1;
113839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%ld imsilen=%ld",
113939beb93cSSam Leffler 		   (long) blen, (long) imsilen);
114039beb93cSSam Leffler 	if (blen < 2 || imsilen > *len) {
114139beb93cSSam Leffler 		*len = imsilen;
114239beb93cSSam Leffler 		return -4;
114339beb93cSSam Leffler 	}
114439beb93cSSam Leffler 
114539beb93cSSam Leffler 	if (scard_read_file(scard, buf, blen))
114639beb93cSSam Leffler 		return -5;
114739beb93cSSam Leffler 
114839beb93cSSam Leffler 	pos = imsi;
114939beb93cSSam Leffler 	*pos++ = '0' + (buf[1] >> 4 & 0x0f);
115039beb93cSSam Leffler 	for (i = 2; i < blen; i++) {
115139beb93cSSam Leffler 		unsigned char digit;
115239beb93cSSam Leffler 
115339beb93cSSam Leffler 		digit = buf[i] & 0x0f;
115439beb93cSSam Leffler 		if (digit < 10)
115539beb93cSSam Leffler 			*pos++ = '0' + digit;
115639beb93cSSam Leffler 		else
115739beb93cSSam Leffler 			imsilen--;
115839beb93cSSam Leffler 
115939beb93cSSam Leffler 		digit = buf[i] >> 4 & 0x0f;
116039beb93cSSam Leffler 		if (digit < 10)
116139beb93cSSam Leffler 			*pos++ = '0' + digit;
116239beb93cSSam Leffler 		else
116339beb93cSSam Leffler 			imsilen--;
116439beb93cSSam Leffler 	}
116539beb93cSSam Leffler 	*len = imsilen;
116639beb93cSSam Leffler 
116739beb93cSSam Leffler 	return 0;
116839beb93cSSam Leffler }
116939beb93cSSam Leffler 
117039beb93cSSam Leffler 
117139beb93cSSam Leffler /**
1172f05cddf9SRui Paulo  * scard_get_mnc_len - Read length of MNC in the IMSI from SIM/USIM card
1173f05cddf9SRui Paulo  * @scard: Pointer to private data from scard_init()
1174f05cddf9SRui Paulo  * Returns: length (>0) on success, -1 if administrative data file cannot be
1175f05cddf9SRui Paulo  * selected, -2 if administrative data file selection returns invalid result
1176f05cddf9SRui Paulo  * code, -3 if parsing FSP template file fails (USIM only), -4 if length of
1177f05cddf9SRui Paulo  * the file is unexpected, -5 if reading file fails, -6 if MNC length is not
1178f05cddf9SRui Paulo  * in range (i.e. 2 or 3), -7 if MNC length is not available.
1179f05cddf9SRui Paulo  *
1180f05cddf9SRui Paulo  */
scard_get_mnc_len(struct scard_data * scard)1181f05cddf9SRui Paulo int scard_get_mnc_len(struct scard_data *scard)
1182f05cddf9SRui Paulo {
1183f05cddf9SRui Paulo 	unsigned char buf[100];
1184f05cddf9SRui Paulo 	size_t blen;
1185f05cddf9SRui Paulo 	int file_size;
1186f05cddf9SRui Paulo 
1187f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "SCARD: reading MNC len from (GSM) EF-AD");
1188f05cddf9SRui Paulo 	blen = sizeof(buf);
1189f05cddf9SRui Paulo 	if (scard_select_file(scard, SCARD_FILE_GSM_EF_AD, buf, &blen))
1190f05cddf9SRui Paulo 		return -1;
1191f05cddf9SRui Paulo 	if (blen < 4) {
1192f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-AD "
1193f05cddf9SRui Paulo 			   "header (len=%ld)", (long) blen);
1194f05cddf9SRui Paulo 		return -2;
1195f05cddf9SRui Paulo 	}
1196f05cddf9SRui Paulo 
1197f05cddf9SRui Paulo 	if (scard->sim_type == SCARD_GSM_SIM) {
11985b9c547cSRui Paulo 		file_size = WPA_GET_BE16(&buf[2]);
1199f05cddf9SRui Paulo 	} else {
1200f05cddf9SRui Paulo 		if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
1201f05cddf9SRui Paulo 			return -3;
1202f05cddf9SRui Paulo 	}
1203f05cddf9SRui Paulo 	if (file_size == 3) {
1204f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "SCARD: MNC length not available");
1205f05cddf9SRui Paulo 		return -7;
1206f05cddf9SRui Paulo 	}
1207f05cddf9SRui Paulo 	if (file_size < 4 || file_size > (int) sizeof(buf)) {
1208f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "SCARD: invalid file length=%ld",
1209f05cddf9SRui Paulo 			   (long) file_size);
1210f05cddf9SRui Paulo 		return -4;
1211f05cddf9SRui Paulo 	}
1212f05cddf9SRui Paulo 
1213f05cddf9SRui Paulo 	if (scard_read_file(scard, buf, file_size))
1214f05cddf9SRui Paulo 		return -5;
1215f05cddf9SRui Paulo 	buf[3] = buf[3] & 0x0f; /* upper nibble reserved for future use  */
1216f05cddf9SRui Paulo 	if (buf[3] < 2 || buf[3] > 3) {
1217f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "SCARD: invalid MNC length=%ld",
1218f05cddf9SRui Paulo 			   (long) buf[3]);
1219f05cddf9SRui Paulo 		return -6;
1220f05cddf9SRui Paulo 	}
1221f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "SCARD: MNC length=%ld", (long) buf[3]);
1222f05cddf9SRui Paulo 	return buf[3];
1223f05cddf9SRui Paulo }
1224f05cddf9SRui Paulo 
1225f05cddf9SRui Paulo 
1226f05cddf9SRui Paulo /**
122739beb93cSSam Leffler  * scard_gsm_auth - Run GSM authentication command on SIM card
122839beb93cSSam Leffler  * @scard: Pointer to private data from scard_init()
122939beb93cSSam Leffler  * @_rand: 16-byte RAND value from HLR/AuC
123039beb93cSSam Leffler  * @sres: 4-byte buffer for SRES
123139beb93cSSam Leffler  * @kc: 8-byte buffer for Kc
123239beb93cSSam Leffler  * Returns: 0 on success, -1 if SIM/USIM connection has not been initialized,
123339beb93cSSam Leffler  * -2 if authentication command execution fails, -3 if unknown response code
123439beb93cSSam Leffler  * for authentication command is received, -4 if reading of response fails,
123539beb93cSSam Leffler  * -5 if if response data is of unexpected length
123639beb93cSSam Leffler  *
123739beb93cSSam Leffler  * This function performs GSM authentication using SIM/USIM card and the
123839beb93cSSam Leffler  * provided RAND value from HLR/AuC. If authentication command can be completed
123939beb93cSSam Leffler  * successfully, SRES and Kc values will be written into sres and kc buffers.
124039beb93cSSam Leffler  */
scard_gsm_auth(struct scard_data * scard,const unsigned char * _rand,unsigned char * sres,unsigned char * kc)124139beb93cSSam Leffler int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
124239beb93cSSam Leffler 		   unsigned char *sres, unsigned char *kc)
124339beb93cSSam Leffler {
124439beb93cSSam Leffler 	unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG };
124539beb93cSSam Leffler 	int cmdlen;
124639beb93cSSam Leffler 	unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
124739beb93cSSam Leffler 	unsigned char resp[3], buf[12 + 3 + 2];
124839beb93cSSam Leffler 	size_t len;
124939beb93cSSam Leffler 	long ret;
125039beb93cSSam Leffler 
125139beb93cSSam Leffler 	if (scard == NULL)
125239beb93cSSam Leffler 		return -1;
125339beb93cSSam Leffler 
125439beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", _rand, 16);
125539beb93cSSam Leffler 	if (scard->sim_type == SCARD_GSM_SIM) {
125639beb93cSSam Leffler 		cmdlen = 5 + 16;
125739beb93cSSam Leffler 		os_memcpy(cmd + 5, _rand, 16);
125839beb93cSSam Leffler 	} else {
125939beb93cSSam Leffler 		cmdlen = 5 + 1 + 16;
126039beb93cSSam Leffler 		cmd[0] = USIM_CLA;
126139beb93cSSam Leffler 		cmd[3] = 0x80;
126239beb93cSSam Leffler 		cmd[4] = 17;
126339beb93cSSam Leffler 		cmd[5] = 16;
126439beb93cSSam Leffler 		os_memcpy(cmd + 6, _rand, 16);
12655b9c547cSRui Paulo 		get_resp[0] = USIM_CLA;
126639beb93cSSam Leffler 	}
126739beb93cSSam Leffler 	len = sizeof(resp);
126839beb93cSSam Leffler 	ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
126939beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS)
127039beb93cSSam Leffler 		return -2;
127139beb93cSSam Leffler 
127239beb93cSSam Leffler 	if ((scard->sim_type == SCARD_GSM_SIM &&
127339beb93cSSam Leffler 	     (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) ||
127439beb93cSSam Leffler 	    (scard->sim_type == SCARD_USIM &&
127539beb93cSSam Leffler 	     (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) {
127639beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM "
127739beb93cSSam Leffler 			   "auth request (len=%ld resp=%02x %02x)",
127839beb93cSSam Leffler 			   (long) len, resp[0], resp[1]);
127939beb93cSSam Leffler 		return -3;
128039beb93cSSam Leffler 	}
128139beb93cSSam Leffler 	get_resp[4] = resp[1];
128239beb93cSSam Leffler 
128339beb93cSSam Leffler 	len = sizeof(buf);
128439beb93cSSam Leffler 	ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
128539beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS)
128639beb93cSSam Leffler 		return -4;
128739beb93cSSam Leffler 
128839beb93cSSam Leffler 	if (scard->sim_type == SCARD_GSM_SIM) {
128939beb93cSSam Leffler 		if (len != 4 + 8 + 2) {
129039beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "SCARD: unexpected data "
129139beb93cSSam Leffler 				   "length for GSM auth (len=%ld, expected 14)",
129239beb93cSSam Leffler 				   (long) len);
129339beb93cSSam Leffler 			return -5;
129439beb93cSSam Leffler 		}
129539beb93cSSam Leffler 		os_memcpy(sres, buf, 4);
129639beb93cSSam Leffler 		os_memcpy(kc, buf + 4, 8);
129739beb93cSSam Leffler 	} else {
129839beb93cSSam Leffler 		if (len != 1 + 4 + 1 + 8 + 2) {
129939beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "SCARD: unexpected data "
130039beb93cSSam Leffler 				   "length for USIM auth (len=%ld, "
130139beb93cSSam Leffler 				   "expected 16)", (long) len);
130239beb93cSSam Leffler 			return -5;
130339beb93cSSam Leffler 		}
130439beb93cSSam Leffler 		if (buf[0] != 4 || buf[5] != 8) {
130539beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc "
130639beb93cSSam Leffler 				   "length (%d %d, expected 4 8)",
130739beb93cSSam Leffler 				   buf[0], buf[5]);
130839beb93cSSam Leffler 		}
130939beb93cSSam Leffler 		os_memcpy(sres, buf + 1, 4);
131039beb93cSSam Leffler 		os_memcpy(kc, buf + 6, 8);
131139beb93cSSam Leffler 	}
131239beb93cSSam Leffler 
131339beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4);
131439beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8);
131539beb93cSSam Leffler 
131639beb93cSSam Leffler 	return 0;
131739beb93cSSam Leffler }
131839beb93cSSam Leffler 
131939beb93cSSam Leffler 
132039beb93cSSam Leffler /**
132139beb93cSSam Leffler  * scard_umts_auth - Run UMTS authentication command on USIM card
132239beb93cSSam Leffler  * @scard: Pointer to private data from scard_init()
132339beb93cSSam Leffler  * @_rand: 16-byte RAND value from HLR/AuC
132439beb93cSSam Leffler  * @autn: 16-byte AUTN value from HLR/AuC
132539beb93cSSam Leffler  * @res: 16-byte buffer for RES
132639beb93cSSam Leffler  * @res_len: Variable that will be set to RES length
132739beb93cSSam Leffler  * @ik: 16-byte buffer for IK
132839beb93cSSam Leffler  * @ck: 16-byte buffer for CK
132939beb93cSSam Leffler  * @auts: 14-byte buffer for AUTS
133039beb93cSSam Leffler  * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization
133139beb93cSSam Leffler  * failure
133239beb93cSSam Leffler  *
133339beb93cSSam Leffler  * This function performs AKA authentication using USIM card and the provided
133439beb93cSSam Leffler  * RAND and AUTN values from HLR/AuC. If authentication command can be
133539beb93cSSam Leffler  * completed successfully, RES, IK, and CK values will be written into provided
133639beb93cSSam Leffler  * buffers and res_len is set to length of received RES value. If USIM reports
133739beb93cSSam Leffler  * synchronization failure, the received AUTS value will be written into auts
133839beb93cSSam Leffler  * buffer. In this case, RES, IK, and CK are not valid.
133939beb93cSSam Leffler  */
scard_umts_auth(struct scard_data * scard,const unsigned char * _rand,const unsigned char * autn,unsigned char * res,size_t * res_len,unsigned char * ik,unsigned char * ck,unsigned char * auts)134039beb93cSSam Leffler int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
134139beb93cSSam Leffler 		    const unsigned char *autn,
134239beb93cSSam Leffler 		    unsigned char *res, size_t *res_len,
134339beb93cSSam Leffler 		    unsigned char *ik, unsigned char *ck, unsigned char *auts)
134439beb93cSSam Leffler {
134539beb93cSSam Leffler 	unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] =
134639beb93cSSam Leffler 		{ USIM_CMD_RUN_UMTS_ALG };
134739beb93cSSam Leffler 	unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE };
134839beb93cSSam Leffler 	unsigned char resp[3], buf[64], *pos, *end;
134939beb93cSSam Leffler 	size_t len;
135039beb93cSSam Leffler 	long ret;
135139beb93cSSam Leffler 
135239beb93cSSam Leffler 	if (scard == NULL)
135339beb93cSSam Leffler 		return -1;
135439beb93cSSam Leffler 
135539beb93cSSam Leffler 	if (scard->sim_type == SCARD_GSM_SIM) {
135639beb93cSSam Leffler 		wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS "
135739beb93cSSam Leffler 			   "auth");
135839beb93cSSam Leffler 		return -1;
135939beb93cSSam Leffler 	}
136039beb93cSSam Leffler 
136139beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", _rand, AKA_RAND_LEN);
136239beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN);
136339beb93cSSam Leffler 	cmd[5] = AKA_RAND_LEN;
136439beb93cSSam Leffler 	os_memcpy(cmd + 6, _rand, AKA_RAND_LEN);
136539beb93cSSam Leffler 	cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN;
136639beb93cSSam Leffler 	os_memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN);
136739beb93cSSam Leffler 
136839beb93cSSam Leffler 	len = sizeof(resp);
136939beb93cSSam Leffler 	ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
137039beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS)
137139beb93cSSam Leffler 		return -1;
137239beb93cSSam Leffler 
137339beb93cSSam Leffler 	if (len <= sizeof(resp))
137439beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len);
137539beb93cSSam Leffler 
137639beb93cSSam Leffler 	if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) {
137739beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - "
137839beb93cSSam Leffler 			   "MAC != XMAC");
137939beb93cSSam Leffler 		return -1;
138039beb93cSSam Leffler 	} else if (len != 2 || resp[0] != 0x61) {
138139beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS "
138239beb93cSSam Leffler 			   "auth request (len=%ld resp=%02x %02x)",
138339beb93cSSam Leffler 			   (long) len, resp[0], resp[1]);
138439beb93cSSam Leffler 		return -1;
138539beb93cSSam Leffler 	}
138639beb93cSSam Leffler 	get_resp[4] = resp[1];
138739beb93cSSam Leffler 
138839beb93cSSam Leffler 	len = sizeof(buf);
138939beb93cSSam Leffler 	ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
139039beb93cSSam Leffler 	if (ret != SCARD_S_SUCCESS || len > sizeof(buf))
139139beb93cSSam Leffler 		return -1;
139239beb93cSSam Leffler 
139339beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len);
139439beb93cSSam Leffler 	if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc &&
139539beb93cSSam Leffler 	    buf[1] == AKA_AUTS_LEN) {
139639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure");
139739beb93cSSam Leffler 		os_memcpy(auts, buf + 2, AKA_AUTS_LEN);
139839beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN);
139939beb93cSSam Leffler 		return -2;
140039beb93cSSam Leffler 	} else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) {
140139beb93cSSam Leffler 		pos = buf + 1;
140239beb93cSSam Leffler 		end = buf + len;
140339beb93cSSam Leffler 
140439beb93cSSam Leffler 		/* RES */
1405*780fb4a2SCy Schubert 		if (pos[0] > RES_MAX_LEN || pos[0] > end - pos) {
140639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Invalid RES");
140739beb93cSSam Leffler 			return -1;
140839beb93cSSam Leffler 		}
140939beb93cSSam Leffler 		*res_len = *pos++;
141039beb93cSSam Leffler 		os_memcpy(res, pos, *res_len);
141139beb93cSSam Leffler 		pos += *res_len;
141239beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len);
141339beb93cSSam Leffler 
141439beb93cSSam Leffler 		/* CK */
1415*780fb4a2SCy Schubert 		if (pos[0] != CK_LEN || CK_LEN > end - pos) {
141639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Invalid CK");
141739beb93cSSam Leffler 			return -1;
141839beb93cSSam Leffler 		}
141939beb93cSSam Leffler 		pos++;
142039beb93cSSam Leffler 		os_memcpy(ck, pos, CK_LEN);
142139beb93cSSam Leffler 		pos += CK_LEN;
142239beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN);
142339beb93cSSam Leffler 
142439beb93cSSam Leffler 		/* IK */
1425*780fb4a2SCy Schubert 		if (pos[0] != IK_LEN || IK_LEN > end - pos) {
142639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "SCARD: Invalid IK");
142739beb93cSSam Leffler 			return -1;
142839beb93cSSam Leffler 		}
142939beb93cSSam Leffler 		pos++;
143039beb93cSSam Leffler 		os_memcpy(ik, pos, IK_LEN);
143139beb93cSSam Leffler 		pos += IK_LEN;
143239beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN);
143339beb93cSSam Leffler 
14345b9c547cSRui Paulo 		if (end > pos) {
14355b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG,
14365b9c547cSRui Paulo 				    "SCARD: Ignore extra data in end",
14375b9c547cSRui Paulo 				    pos, end - pos);
14385b9c547cSRui Paulo 		}
14395b9c547cSRui Paulo 
144039beb93cSSam Leffler 		return 0;
144139beb93cSSam Leffler 	}
144239beb93cSSam Leffler 
144339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response");
144439beb93cSSam Leffler 	return -1;
144539beb93cSSam Leffler }
1446f05cddf9SRui Paulo 
1447f05cddf9SRui Paulo 
scard_supports_umts(struct scard_data * scard)1448f05cddf9SRui Paulo int scard_supports_umts(struct scard_data *scard)
1449f05cddf9SRui Paulo {
1450f05cddf9SRui Paulo 	return scard->sim_type == SCARD_USIM;
1451f05cddf9SRui Paulo }
1452