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" 1439beb93cSSam Leffler #include <winscard.h> 1539beb93cSSam Leffler 1639beb93cSSam Leffler #include "common.h" 1739beb93cSSam Leffler #include "pcsc_funcs.h" 1839beb93cSSam Leffler 1939beb93cSSam Leffler 2039beb93cSSam Leffler /* See ETSI GSM 11.11 and ETSI TS 102 221 for details. 2139beb93cSSam Leffler * SIM commands: 2239beb93cSSam Leffler * Command APDU: CLA INS P1 P2 P3 Data 2339beb93cSSam Leffler * CLA (class of instruction): A0 for GSM, 00 for USIM 2439beb93cSSam Leffler * INS (instruction) 2539beb93cSSam Leffler * P1 P2 P3 (parameters, P3 = length of Data) 2639beb93cSSam Leffler * Response APDU: Data SW1 SW2 2739beb93cSSam Leffler * SW1 SW2 (Status words) 2839beb93cSSam Leffler * Commands (INS P1 P2 P3): 2939beb93cSSam Leffler * SELECT: A4 00 00 02 <file_id, 2 bytes> 3039beb93cSSam Leffler * GET RESPONSE: C0 00 00 <len> 3139beb93cSSam Leffler * RUN GSM ALG: 88 00 00 00 <RAND len = 10> 3239beb93cSSam Leffler * RUN UMTS ALG: 88 00 81 <len=0x22> data: 0x10 | RAND | 0x10 | AUTN 3339beb93cSSam Leffler * P1 = ID of alg in card 3439beb93cSSam Leffler * P2 = ID of secret key 3539beb93cSSam Leffler * READ BINARY: B0 <offset high> <offset low> <len> 3639beb93cSSam Leffler * READ RECORD: B2 <record number> <mode> <len> 3739beb93cSSam Leffler * P2 (mode) = '02' (next record), '03' (previous record), 3839beb93cSSam Leffler * '04' (absolute mode) 3939beb93cSSam Leffler * VERIFY CHV: 20 00 <CHV number> 08 4039beb93cSSam Leffler * CHANGE CHV: 24 00 <CHV number> 10 4139beb93cSSam Leffler * DISABLE CHV: 26 00 01 08 4239beb93cSSam Leffler * ENABLE CHV: 28 00 01 08 4339beb93cSSam Leffler * UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10 4439beb93cSSam Leffler * SLEEP: FA 00 00 00 4539beb93cSSam Leffler */ 4639beb93cSSam Leffler 4739beb93cSSam Leffler /* GSM SIM commands */ 4839beb93cSSam Leffler #define SIM_CMD_SELECT 0xa0, 0xa4, 0x00, 0x00, 0x02 4939beb93cSSam Leffler #define SIM_CMD_RUN_GSM_ALG 0xa0, 0x88, 0x00, 0x00, 0x10 5039beb93cSSam Leffler #define SIM_CMD_GET_RESPONSE 0xa0, 0xc0, 0x00, 0x00 5139beb93cSSam Leffler #define SIM_CMD_READ_BIN 0xa0, 0xb0, 0x00, 0x00 5239beb93cSSam Leffler #define SIM_CMD_READ_RECORD 0xa0, 0xb2, 0x00, 0x00 5339beb93cSSam Leffler #define SIM_CMD_VERIFY_CHV1 0xa0, 0x20, 0x00, 0x01, 0x08 5439beb93cSSam Leffler 5539beb93cSSam Leffler /* USIM commands */ 5639beb93cSSam Leffler #define USIM_CLA 0x00 5739beb93cSSam Leffler #define USIM_CMD_RUN_UMTS_ALG 0x00, 0x88, 0x00, 0x81, 0x22 5839beb93cSSam Leffler #define USIM_CMD_GET_RESPONSE 0x00, 0xc0, 0x00, 0x00 5939beb93cSSam Leffler 6039beb93cSSam Leffler #define SIM_RECORD_MODE_ABSOLUTE 0x04 6139beb93cSSam Leffler 6239beb93cSSam Leffler #define USIM_FSP_TEMPL_TAG 0x62 6339beb93cSSam Leffler 6439beb93cSSam Leffler #define USIM_TLV_FILE_DESC 0x82 6539beb93cSSam Leffler #define USIM_TLV_FILE_ID 0x83 6639beb93cSSam Leffler #define USIM_TLV_DF_NAME 0x84 6739beb93cSSam Leffler #define USIM_TLV_PROPR_INFO 0xA5 6839beb93cSSam Leffler #define USIM_TLV_LIFE_CYCLE_STATUS 0x8A 6939beb93cSSam Leffler #define USIM_TLV_FILE_SIZE 0x80 7039beb93cSSam Leffler #define USIM_TLV_TOTAL_FILE_SIZE 0x81 7139beb93cSSam Leffler #define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6 7239beb93cSSam Leffler #define USIM_TLV_SHORT_FILE_ID 0x88 73f05cddf9SRui Paulo #define USIM_TLV_SECURITY_ATTR_8B 0x8B 74f05cddf9SRui Paulo #define USIM_TLV_SECURITY_ATTR_8C 0x8C 75f05cddf9SRui Paulo #define USIM_TLV_SECURITY_ATTR_AB 0xAB 7639beb93cSSam Leffler 7739beb93cSSam Leffler #define USIM_PS_DO_TAG 0x90 7839beb93cSSam Leffler 7939beb93cSSam Leffler #define AKA_RAND_LEN 16 8039beb93cSSam Leffler #define AKA_AUTN_LEN 16 8139beb93cSSam Leffler #define AKA_AUTS_LEN 14 8239beb93cSSam Leffler #define RES_MAX_LEN 16 8339beb93cSSam Leffler #define IK_LEN 16 8439beb93cSSam Leffler #define CK_LEN 16 8539beb93cSSam Leffler 8639beb93cSSam Leffler 87f05cddf9SRui Paulo /* GSM files 88f05cddf9SRui Paulo * File type in first octet: 89f05cddf9SRui Paulo * 3F = Master File 90f05cddf9SRui Paulo * 7F = Dedicated File 91f05cddf9SRui Paulo * 2F = Elementary File under the Master File 92f05cddf9SRui Paulo * 6F = Elementary File under a Dedicated File 93f05cddf9SRui Paulo */ 94f05cddf9SRui Paulo #define SCARD_FILE_MF 0x3F00 95f05cddf9SRui Paulo #define SCARD_FILE_GSM_DF 0x7F20 96f05cddf9SRui Paulo #define SCARD_FILE_UMTS_DF 0x7F50 97f05cddf9SRui Paulo #define SCARD_FILE_GSM_EF_IMSI 0x6F07 98f05cddf9SRui Paulo #define SCARD_FILE_GSM_EF_AD 0x6FAD 99f05cddf9SRui Paulo #define SCARD_FILE_EF_DIR 0x2F00 100f05cddf9SRui Paulo #define SCARD_FILE_EF_ICCID 0x2FE2 101f05cddf9SRui Paulo #define SCARD_FILE_EF_CK 0x6FE1 102f05cddf9SRui Paulo #define SCARD_FILE_EF_IK 0x6FE2 103f05cddf9SRui Paulo 104f05cddf9SRui Paulo #define SCARD_CHV1_OFFSET 13 105f05cddf9SRui Paulo #define SCARD_CHV1_FLAG 0x80 106f05cddf9SRui Paulo 107f05cddf9SRui Paulo 10839beb93cSSam Leffler typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types; 10939beb93cSSam Leffler 11039beb93cSSam Leffler struct scard_data { 11139beb93cSSam Leffler SCARDCONTEXT ctx; 11239beb93cSSam Leffler SCARDHANDLE card; 11339beb93cSSam Leffler DWORD protocol; 11439beb93cSSam Leffler sim_types sim_type; 11539beb93cSSam Leffler int pin1_required; 11639beb93cSSam Leffler }; 11739beb93cSSam Leffler 11839beb93cSSam Leffler #ifdef __MINGW32_VERSION 11939beb93cSSam Leffler /* MinGW does not yet support WinScard, so load the needed functions 12039beb93cSSam Leffler * dynamically from winscard.dll for now. */ 12139beb93cSSam Leffler 12239beb93cSSam Leffler static HINSTANCE dll = NULL; /* winscard.dll */ 12339beb93cSSam Leffler 12439beb93cSSam Leffler static const SCARD_IO_REQUEST *dll_g_rgSCardT0Pci, *dll_g_rgSCardT1Pci; 12539beb93cSSam Leffler #undef SCARD_PCI_T0 12639beb93cSSam Leffler #define SCARD_PCI_T0 (dll_g_rgSCardT0Pci) 12739beb93cSSam Leffler #undef SCARD_PCI_T1 12839beb93cSSam Leffler #define SCARD_PCI_T1 (dll_g_rgSCardT1Pci) 12939beb93cSSam Leffler 13039beb93cSSam Leffler 13139beb93cSSam Leffler static WINSCARDAPI LONG WINAPI 13239beb93cSSam Leffler (*dll_SCardEstablishContext)(IN DWORD dwScope, 13339beb93cSSam Leffler IN LPCVOID pvReserved1, 13439beb93cSSam Leffler IN LPCVOID pvReserved2, 13539beb93cSSam Leffler OUT LPSCARDCONTEXT phContext); 13639beb93cSSam Leffler #define SCardEstablishContext dll_SCardEstablishContext 13739beb93cSSam Leffler 13839beb93cSSam Leffler static long (*dll_SCardReleaseContext)(long hContext); 13939beb93cSSam Leffler #define SCardReleaseContext dll_SCardReleaseContext 14039beb93cSSam Leffler 14139beb93cSSam Leffler static WINSCARDAPI LONG WINAPI 14239beb93cSSam Leffler (*dll_SCardListReadersA)(IN SCARDCONTEXT hContext, 14339beb93cSSam Leffler IN LPCSTR mszGroups, 14439beb93cSSam Leffler OUT LPSTR mszReaders, 14539beb93cSSam Leffler IN OUT LPDWORD pcchReaders); 14639beb93cSSam Leffler #undef SCardListReaders 14739beb93cSSam Leffler #define SCardListReaders dll_SCardListReadersA 14839beb93cSSam Leffler 14939beb93cSSam Leffler static WINSCARDAPI LONG WINAPI 15039beb93cSSam Leffler (*dll_SCardConnectA)(IN SCARDCONTEXT hContext, 15139beb93cSSam Leffler IN LPCSTR szReader, 15239beb93cSSam Leffler IN DWORD dwShareMode, 15339beb93cSSam Leffler IN DWORD dwPreferredProtocols, 15439beb93cSSam Leffler OUT LPSCARDHANDLE phCard, 15539beb93cSSam Leffler OUT LPDWORD pdwActiveProtocol); 15639beb93cSSam Leffler #undef SCardConnect 15739beb93cSSam Leffler #define SCardConnect dll_SCardConnectA 15839beb93cSSam Leffler 15939beb93cSSam Leffler static WINSCARDAPI LONG WINAPI 16039beb93cSSam Leffler (*dll_SCardDisconnect)(IN SCARDHANDLE hCard, 16139beb93cSSam Leffler IN DWORD dwDisposition); 16239beb93cSSam Leffler #define SCardDisconnect dll_SCardDisconnect 16339beb93cSSam Leffler 16439beb93cSSam Leffler static WINSCARDAPI LONG WINAPI 16539beb93cSSam Leffler (*dll_SCardTransmit)(IN SCARDHANDLE hCard, 16639beb93cSSam Leffler IN LPCSCARD_IO_REQUEST pioSendPci, 16739beb93cSSam Leffler IN LPCBYTE pbSendBuffer, 16839beb93cSSam Leffler IN DWORD cbSendLength, 16939beb93cSSam Leffler IN OUT LPSCARD_IO_REQUEST pioRecvPci, 17039beb93cSSam Leffler OUT LPBYTE pbRecvBuffer, 17139beb93cSSam Leffler IN OUT LPDWORD pcbRecvLength); 17239beb93cSSam Leffler #define SCardTransmit dll_SCardTransmit 17339beb93cSSam Leffler 17439beb93cSSam Leffler static WINSCARDAPI LONG WINAPI 17539beb93cSSam Leffler (*dll_SCardBeginTransaction)(IN SCARDHANDLE hCard); 17639beb93cSSam Leffler #define SCardBeginTransaction dll_SCardBeginTransaction 17739beb93cSSam Leffler 17839beb93cSSam Leffler static WINSCARDAPI LONG WINAPI 17939beb93cSSam Leffler (*dll_SCardEndTransaction)(IN SCARDHANDLE hCard, IN DWORD dwDisposition); 18039beb93cSSam Leffler #define SCardEndTransaction dll_SCardEndTransaction 18139beb93cSSam Leffler 18239beb93cSSam Leffler 18339beb93cSSam Leffler static int mingw_load_symbols(void) 18439beb93cSSam Leffler { 18539beb93cSSam Leffler char *sym; 18639beb93cSSam Leffler 18739beb93cSSam Leffler if (dll) 18839beb93cSSam Leffler return 0; 18939beb93cSSam Leffler 19039beb93cSSam Leffler dll = LoadLibrary("winscard"); 19139beb93cSSam Leffler if (dll == NULL) { 19239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WinSCard: Could not load winscard.dll " 19339beb93cSSam Leffler "library"); 19439beb93cSSam Leffler return -1; 19539beb93cSSam Leffler } 19639beb93cSSam Leffler 19739beb93cSSam Leffler #define LOADSYM(s) \ 19839beb93cSSam Leffler sym = #s; \ 19939beb93cSSam Leffler dll_ ## s = (void *) GetProcAddress(dll, sym); \ 20039beb93cSSam Leffler if (dll_ ## s == NULL) \ 20139beb93cSSam Leffler goto fail; 20239beb93cSSam Leffler 20339beb93cSSam Leffler LOADSYM(SCardEstablishContext); 20439beb93cSSam Leffler LOADSYM(SCardReleaseContext); 20539beb93cSSam Leffler LOADSYM(SCardListReadersA); 20639beb93cSSam Leffler LOADSYM(SCardConnectA); 20739beb93cSSam Leffler LOADSYM(SCardDisconnect); 20839beb93cSSam Leffler LOADSYM(SCardTransmit); 20939beb93cSSam Leffler LOADSYM(SCardBeginTransaction); 21039beb93cSSam Leffler LOADSYM(SCardEndTransaction); 21139beb93cSSam Leffler LOADSYM(g_rgSCardT0Pci); 21239beb93cSSam Leffler LOADSYM(g_rgSCardT1Pci); 21339beb93cSSam Leffler 21439beb93cSSam Leffler #undef LOADSYM 21539beb93cSSam Leffler 21639beb93cSSam Leffler return 0; 21739beb93cSSam Leffler 21839beb93cSSam Leffler fail: 21939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WinSCard: Could not get address for %s from " 22039beb93cSSam Leffler "winscard.dll", sym); 22139beb93cSSam Leffler FreeLibrary(dll); 22239beb93cSSam Leffler dll = NULL; 22339beb93cSSam Leffler return -1; 22439beb93cSSam Leffler } 22539beb93cSSam Leffler 22639beb93cSSam Leffler 22739beb93cSSam Leffler static void mingw_unload_symbols(void) 22839beb93cSSam Leffler { 22939beb93cSSam Leffler if (dll == NULL) 23039beb93cSSam Leffler return; 23139beb93cSSam Leffler 23239beb93cSSam Leffler FreeLibrary(dll); 23339beb93cSSam Leffler dll = NULL; 23439beb93cSSam Leffler } 23539beb93cSSam Leffler 23639beb93cSSam Leffler #else /* __MINGW32_VERSION */ 23739beb93cSSam Leffler 23839beb93cSSam Leffler #define mingw_load_symbols() 0 23939beb93cSSam Leffler #define mingw_unload_symbols() do { } while (0) 24039beb93cSSam Leffler 24139beb93cSSam Leffler #endif /* __MINGW32_VERSION */ 24239beb93cSSam Leffler 24339beb93cSSam Leffler 24439beb93cSSam Leffler static int _scard_select_file(struct scard_data *scard, unsigned short file_id, 24539beb93cSSam Leffler unsigned char *buf, size_t *buf_len, 24639beb93cSSam Leffler sim_types sim_type, unsigned char *aid, 24739beb93cSSam Leffler size_t aidlen); 24839beb93cSSam Leffler static int scard_select_file(struct scard_data *scard, unsigned short file_id, 24939beb93cSSam Leffler unsigned char *buf, size_t *buf_len); 25039beb93cSSam Leffler static int scard_verify_pin(struct scard_data *scard, const char *pin); 25139beb93cSSam Leffler static int scard_get_record_len(struct scard_data *scard, 25239beb93cSSam Leffler unsigned char recnum, unsigned char mode); 25339beb93cSSam Leffler static int scard_read_record(struct scard_data *scard, 25439beb93cSSam Leffler unsigned char *data, size_t len, 25539beb93cSSam Leffler unsigned char recnum, unsigned char mode); 25639beb93cSSam Leffler 25739beb93cSSam Leffler 25839beb93cSSam Leffler static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, 25939beb93cSSam Leffler int *ps_do, int *file_len) 26039beb93cSSam Leffler { 26139beb93cSSam Leffler unsigned char *pos, *end; 26239beb93cSSam Leffler 26339beb93cSSam Leffler if (ps_do) 26439beb93cSSam Leffler *ps_do = -1; 26539beb93cSSam Leffler if (file_len) 26639beb93cSSam Leffler *file_len = -1; 26739beb93cSSam Leffler 26839beb93cSSam Leffler pos = buf; 26939beb93cSSam Leffler end = pos + buf_len; 27039beb93cSSam Leffler if (*pos != USIM_FSP_TEMPL_TAG) { 27139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: file header did not " 27239beb93cSSam Leffler "start with FSP template tag"); 27339beb93cSSam Leffler return -1; 27439beb93cSSam Leffler } 27539beb93cSSam Leffler pos++; 27639beb93cSSam Leffler if (pos >= end) 27739beb93cSSam Leffler return -1; 27839beb93cSSam Leffler if ((pos + pos[0]) < end) 27939beb93cSSam Leffler end = pos + 1 + pos[0]; 28039beb93cSSam Leffler pos++; 28139beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", 28239beb93cSSam Leffler pos, end - pos); 28339beb93cSSam Leffler 284*5b9c547cSRui Paulo while (end - pos >= 2) { 285*5b9c547cSRui Paulo unsigned char type, len; 286*5b9c547cSRui Paulo 287*5b9c547cSRui Paulo type = pos[0]; 288*5b9c547cSRui Paulo len = pos[1]; 289f05cddf9SRui Paulo wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d", 290*5b9c547cSRui Paulo type, len); 291*5b9c547cSRui Paulo pos += 2; 292*5b9c547cSRui Paulo 293*5b9c547cSRui Paulo if (len > (unsigned int) (end - pos)) 29439beb93cSSam Leffler break; 29539beb93cSSam Leffler 296*5b9c547cSRui Paulo switch (type) { 297f05cddf9SRui Paulo case USIM_TLV_FILE_DESC: 298f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV", 299*5b9c547cSRui Paulo pos, len); 300f05cddf9SRui Paulo break; 301f05cddf9SRui Paulo case USIM_TLV_FILE_ID: 302f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV", 303*5b9c547cSRui Paulo pos, len); 304f05cddf9SRui Paulo break; 305f05cddf9SRui Paulo case USIM_TLV_DF_NAME: 306f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV", 307*5b9c547cSRui Paulo pos, len); 308f05cddf9SRui Paulo break; 309f05cddf9SRui Paulo case USIM_TLV_PROPR_INFO: 310f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary " 311*5b9c547cSRui Paulo "information TLV", pos, len); 312f05cddf9SRui Paulo break; 313f05cddf9SRui Paulo case USIM_TLV_LIFE_CYCLE_STATUS: 314f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status " 315*5b9c547cSRui Paulo "Integer TLV", pos, len); 316f05cddf9SRui Paulo break; 317f05cddf9SRui Paulo case USIM_TLV_FILE_SIZE: 318f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV", 319*5b9c547cSRui Paulo pos, len); 320*5b9c547cSRui Paulo if ((len == 1 || len == 2) && file_len) { 321*5b9c547cSRui Paulo if (len == 1) 322*5b9c547cSRui Paulo *file_len = (int) pos[0]; 32339beb93cSSam Leffler else 324*5b9c547cSRui Paulo *file_len = WPA_GET_BE16(pos); 32539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: file_size=%d", 32639beb93cSSam Leffler *file_len); 32739beb93cSSam Leffler } 328f05cddf9SRui Paulo break; 329f05cddf9SRui Paulo case USIM_TLV_TOTAL_FILE_SIZE: 330f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV", 331*5b9c547cSRui Paulo pos, len); 332f05cddf9SRui Paulo break; 333f05cddf9SRui Paulo case USIM_TLV_PIN_STATUS_TEMPLATE: 334f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template " 335*5b9c547cSRui Paulo "DO TLV", pos, len); 336*5b9c547cSRui Paulo if (len >= 2 && pos[0] == USIM_PS_DO_TAG && 337*5b9c547cSRui Paulo pos[1] >= 1 && ps_do) { 33839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x", 339*5b9c547cSRui Paulo pos[2]); 340*5b9c547cSRui Paulo *ps_do = (int) pos[2]; 34139beb93cSSam Leffler } 342f05cddf9SRui Paulo break; 343f05cddf9SRui Paulo case USIM_TLV_SHORT_FILE_ID: 344f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File " 345*5b9c547cSRui Paulo "Identifier (SFI) TLV", pos, len); 346f05cddf9SRui Paulo break; 347f05cddf9SRui Paulo case USIM_TLV_SECURITY_ATTR_8B: 348f05cddf9SRui Paulo case USIM_TLV_SECURITY_ATTR_8C: 349f05cddf9SRui Paulo case USIM_TLV_SECURITY_ATTR_AB: 350f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute " 351*5b9c547cSRui Paulo "TLV", pos, len); 352f05cddf9SRui Paulo break; 353f05cddf9SRui Paulo default: 354f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV", 355*5b9c547cSRui Paulo pos, len); 356f05cddf9SRui Paulo break; 357f05cddf9SRui Paulo } 35839beb93cSSam Leffler 359*5b9c547cSRui Paulo pos += len; 36039beb93cSSam Leffler 36139beb93cSSam Leffler if (pos == end) 36239beb93cSSam Leffler return 0; 36339beb93cSSam Leffler } 36439beb93cSSam Leffler return -1; 36539beb93cSSam Leffler } 36639beb93cSSam Leffler 36739beb93cSSam Leffler 36839beb93cSSam Leffler static int scard_pin_needed(struct scard_data *scard, 36939beb93cSSam Leffler unsigned char *hdr, size_t hlen) 37039beb93cSSam Leffler { 37139beb93cSSam Leffler if (scard->sim_type == SCARD_GSM_SIM) { 37239beb93cSSam Leffler if (hlen > SCARD_CHV1_OFFSET && 37339beb93cSSam Leffler !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG)) 37439beb93cSSam Leffler return 1; 37539beb93cSSam Leffler return 0; 37639beb93cSSam Leffler } 37739beb93cSSam Leffler 37839beb93cSSam Leffler if (scard->sim_type == SCARD_USIM) { 37939beb93cSSam Leffler int ps_do; 38039beb93cSSam Leffler if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL)) 38139beb93cSSam Leffler return -1; 38239beb93cSSam Leffler /* TODO: there could be more than one PS_DO entry because of 38339beb93cSSam Leffler * multiple PINs in key reference.. */ 38439beb93cSSam Leffler if (ps_do > 0 && (ps_do & 0x80)) 38539beb93cSSam Leffler return 1; 38639beb93cSSam Leffler return 0; 38739beb93cSSam Leffler } 38839beb93cSSam Leffler 38939beb93cSSam Leffler return -1; 39039beb93cSSam Leffler } 39139beb93cSSam Leffler 39239beb93cSSam Leffler 39339beb93cSSam Leffler static int scard_get_aid(struct scard_data *scard, unsigned char *aid, 39439beb93cSSam Leffler size_t maxlen) 39539beb93cSSam Leffler { 39639beb93cSSam Leffler int rlen, rec; 39739beb93cSSam Leffler struct efdir { 39839beb93cSSam Leffler unsigned char appl_template_tag; /* 0x61 */ 39939beb93cSSam Leffler unsigned char appl_template_len; 40039beb93cSSam Leffler unsigned char appl_id_tag; /* 0x4f */ 40139beb93cSSam Leffler unsigned char aid_len; 40239beb93cSSam Leffler unsigned char rid[5]; 40339beb93cSSam Leffler unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ 40439beb93cSSam Leffler } *efdir; 405*5b9c547cSRui Paulo unsigned char buf[127], *aid_pos; 40639beb93cSSam Leffler size_t blen; 407*5b9c547cSRui Paulo unsigned int aid_len = 0; 40839beb93cSSam Leffler 40939beb93cSSam Leffler efdir = (struct efdir *) buf; 410*5b9c547cSRui Paulo aid_pos = &buf[4]; 41139beb93cSSam Leffler blen = sizeof(buf); 41239beb93cSSam Leffler if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) { 41339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR"); 41439beb93cSSam Leffler return -1; 41539beb93cSSam Leffler } 41639beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR select", buf, blen); 41739beb93cSSam Leffler 41839beb93cSSam Leffler for (rec = 1; rec < 10; rec++) { 41939beb93cSSam Leffler rlen = scard_get_record_len(scard, rec, 42039beb93cSSam Leffler SIM_RECORD_MODE_ABSOLUTE); 42139beb93cSSam Leffler if (rlen < 0) { 42239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Failed to get EF_DIR " 42339beb93cSSam Leffler "record length"); 42439beb93cSSam Leffler return -1; 42539beb93cSSam Leffler } 42639beb93cSSam Leffler blen = sizeof(buf); 42739beb93cSSam Leffler if (rlen > (int) blen) { 42839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Too long EF_DIR record"); 42939beb93cSSam Leffler return -1; 43039beb93cSSam Leffler } 43139beb93cSSam Leffler if (scard_read_record(scard, buf, rlen, rec, 43239beb93cSSam Leffler SIM_RECORD_MODE_ABSOLUTE) < 0) { 43339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Failed to read " 43439beb93cSSam Leffler "EF_DIR record %d", rec); 43539beb93cSSam Leffler return -1; 43639beb93cSSam Leffler } 43739beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR record", buf, rlen); 43839beb93cSSam Leffler 43939beb93cSSam Leffler if (efdir->appl_template_tag != 0x61) { 44039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Unexpected application " 44139beb93cSSam Leffler "template tag 0x%x", 44239beb93cSSam Leffler efdir->appl_template_tag); 44339beb93cSSam Leffler continue; 44439beb93cSSam Leffler } 44539beb93cSSam Leffler 44639beb93cSSam Leffler if (efdir->appl_template_len > rlen - 2) { 44739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Too long application " 44839beb93cSSam Leffler "template (len=%d rlen=%d)", 44939beb93cSSam Leffler efdir->appl_template_len, rlen); 45039beb93cSSam Leffler continue; 45139beb93cSSam Leffler } 45239beb93cSSam Leffler 45339beb93cSSam Leffler if (efdir->appl_id_tag != 0x4f) { 45439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Unexpected application " 45539beb93cSSam Leffler "identifier tag 0x%x", efdir->appl_id_tag); 45639beb93cSSam Leffler continue; 45739beb93cSSam Leffler } 45839beb93cSSam Leffler 459*5b9c547cSRui Paulo aid_len = efdir->aid_len; 460*5b9c547cSRui Paulo if (aid_len < 1 || aid_len > 16) { 461*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %u", 462*5b9c547cSRui Paulo aid_len); 46339beb93cSSam Leffler continue; 46439beb93cSSam Leffler } 46539beb93cSSam Leffler 46639beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record", 467*5b9c547cSRui Paulo aid_pos, aid_len); 46839beb93cSSam Leffler 46939beb93cSSam Leffler if (efdir->appl_code[0] == 0x10 && 47039beb93cSSam Leffler efdir->appl_code[1] == 0x02) { 47139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app found from " 47239beb93cSSam Leffler "EF_DIR record %d", rec); 47339beb93cSSam Leffler break; 47439beb93cSSam Leffler } 47539beb93cSSam Leffler } 47639beb93cSSam Leffler 47739beb93cSSam Leffler if (rec >= 10) { 47839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app not found " 47939beb93cSSam Leffler "from EF_DIR records"); 48039beb93cSSam Leffler return -1; 48139beb93cSSam Leffler } 48239beb93cSSam Leffler 483*5b9c547cSRui Paulo if (aid_len > maxlen) { 48439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Too long AID"); 48539beb93cSSam Leffler return -1; 48639beb93cSSam Leffler } 48739beb93cSSam Leffler 488*5b9c547cSRui Paulo os_memcpy(aid, aid_pos, aid_len); 48939beb93cSSam Leffler 490*5b9c547cSRui Paulo return aid_len; 49139beb93cSSam Leffler } 49239beb93cSSam Leffler 49339beb93cSSam Leffler 49439beb93cSSam Leffler /** 49539beb93cSSam Leffler * scard_init - Initialize SIM/USIM connection using PC/SC 496f05cddf9SRui Paulo * @reader: Reader name prefix to search for 49739beb93cSSam Leffler * Returns: Pointer to private data structure, or %NULL on failure 49839beb93cSSam Leffler * 49939beb93cSSam Leffler * This function is used to initialize SIM/USIM connection. PC/SC is used to 500*5b9c547cSRui Paulo * open connection to the SIM/USIM card. In addition, local flag is set if a 501*5b9c547cSRui Paulo * PIN is needed to access some of the card functions. Once the connection is 502*5b9c547cSRui Paulo * not needed anymore, scard_deinit() can be used to close it. 50339beb93cSSam Leffler */ 504*5b9c547cSRui Paulo struct scard_data * scard_init(const char *reader) 50539beb93cSSam Leffler { 50639beb93cSSam Leffler long ret; 507f05cddf9SRui Paulo unsigned long len, pos; 50839beb93cSSam Leffler struct scard_data *scard; 50939beb93cSSam Leffler #ifdef CONFIG_NATIVE_WINDOWS 51039beb93cSSam Leffler TCHAR *readers = NULL; 51139beb93cSSam Leffler #else /* CONFIG_NATIVE_WINDOWS */ 51239beb93cSSam Leffler char *readers = NULL; 51339beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */ 51439beb93cSSam Leffler unsigned char buf[100]; 51539beb93cSSam Leffler size_t blen; 51639beb93cSSam Leffler int transaction = 0; 51739beb93cSSam Leffler int pin_needed; 51839beb93cSSam Leffler 51939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface"); 52039beb93cSSam Leffler if (mingw_load_symbols()) 52139beb93cSSam Leffler return NULL; 52239beb93cSSam Leffler scard = os_zalloc(sizeof(*scard)); 52339beb93cSSam Leffler if (scard == NULL) 52439beb93cSSam Leffler return NULL; 52539beb93cSSam Leffler 52639beb93cSSam Leffler ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, 52739beb93cSSam Leffler &scard->ctx); 52839beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) { 52939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card " 53039beb93cSSam Leffler "context (err=%ld)", ret); 53139beb93cSSam Leffler goto failed; 53239beb93cSSam Leffler } 53339beb93cSSam Leffler 53439beb93cSSam Leffler ret = SCardListReaders(scard->ctx, NULL, NULL, &len); 53539beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) { 53639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed " 53739beb93cSSam Leffler "(err=%ld)", ret); 53839beb93cSSam Leffler goto failed; 53939beb93cSSam Leffler } 54039beb93cSSam Leffler 54139beb93cSSam Leffler #ifdef UNICODE 54239beb93cSSam Leffler len *= 2; 54339beb93cSSam Leffler #endif /* UNICODE */ 54439beb93cSSam Leffler readers = os_malloc(len); 54539beb93cSSam Leffler if (readers == NULL) { 54639beb93cSSam Leffler wpa_printf(MSG_INFO, "SCARD: malloc failed\n"); 54739beb93cSSam Leffler goto failed; 54839beb93cSSam Leffler } 54939beb93cSSam Leffler 55039beb93cSSam Leffler ret = SCardListReaders(scard->ctx, NULL, readers, &len); 55139beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) { 55239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) " 55339beb93cSSam Leffler "(err=%ld)", ret); 55439beb93cSSam Leffler goto failed; 55539beb93cSSam Leffler } 55639beb93cSSam Leffler if (len < 3) { 55739beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: No smart card readers " 55839beb93cSSam Leffler "available."); 55939beb93cSSam Leffler goto failed; 56039beb93cSSam Leffler } 561f05cddf9SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "SCARD: Readers", (u8 *) readers, len); 562f05cddf9SRui Paulo /* 563f05cddf9SRui Paulo * readers is a list of available readers. The last entry is terminated 564f05cddf9SRui Paulo * with double null. 565f05cddf9SRui Paulo */ 566f05cddf9SRui Paulo pos = 0; 56739beb93cSSam Leffler #ifdef UNICODE 568f05cddf9SRui Paulo /* TODO */ 56939beb93cSSam Leffler #else /* UNICODE */ 570f05cddf9SRui Paulo while (pos < len) { 571f05cddf9SRui Paulo if (reader == NULL || 572f05cddf9SRui Paulo os_strncmp(&readers[pos], reader, os_strlen(reader)) == 0) 573f05cddf9SRui Paulo break; 574f05cddf9SRui Paulo while (pos < len && readers[pos]) 575f05cddf9SRui Paulo pos++; 576f05cddf9SRui Paulo pos++; /* skip separating null */ 577f05cddf9SRui Paulo if (pos < len && readers[pos] == '\0') 578f05cddf9SRui Paulo pos = len; /* double null terminates list */ 579f05cddf9SRui Paulo } 580f05cddf9SRui Paulo #endif /* UNICODE */ 581f05cddf9SRui Paulo if (pos >= len) { 582f05cddf9SRui Paulo wpa_printf(MSG_WARNING, "SCARD: No reader with prefix '%s' " 583f05cddf9SRui Paulo "found", reader); 584f05cddf9SRui Paulo goto failed; 585f05cddf9SRui Paulo } 586f05cddf9SRui Paulo 587f05cddf9SRui Paulo #ifdef UNICODE 588f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]); 589f05cddf9SRui Paulo #else /* UNICODE */ 590f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]); 59139beb93cSSam Leffler #endif /* UNICODE */ 59239beb93cSSam Leffler 593f05cddf9SRui Paulo ret = SCardConnect(scard->ctx, &readers[pos], SCARD_SHARE_SHARED, 594f05cddf9SRui Paulo SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, 595f05cddf9SRui Paulo &scard->card, &scard->protocol); 59639beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) { 59739beb93cSSam Leffler if (ret == (long) SCARD_E_NO_SMARTCARD) 59839beb93cSSam Leffler wpa_printf(MSG_INFO, "No smart card inserted."); 59939beb93cSSam Leffler else 60039beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret); 60139beb93cSSam Leffler goto failed; 60239beb93cSSam Leffler } 60339beb93cSSam Leffler 60439beb93cSSam Leffler os_free(readers); 60539beb93cSSam Leffler readers = NULL; 60639beb93cSSam Leffler 60739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)", 60839beb93cSSam Leffler (unsigned int) scard->card, scard->protocol, 60939beb93cSSam Leffler scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1"); 61039beb93cSSam Leffler 61139beb93cSSam Leffler ret = SCardBeginTransaction(scard->card); 61239beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) { 61339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Could not begin transaction: " 61439beb93cSSam Leffler "0x%x", (unsigned int) ret); 61539beb93cSSam Leffler goto failed; 61639beb93cSSam Leffler } 61739beb93cSSam Leffler transaction = 1; 61839beb93cSSam Leffler 61939beb93cSSam Leffler blen = sizeof(buf); 62039beb93cSSam Leffler 62139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support"); 62239beb93cSSam Leffler if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen, 62339beb93cSSam Leffler SCARD_USIM, NULL, 0)) { 624*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported. Trying to use GSM SIM"); 62539beb93cSSam Leffler scard->sim_type = SCARD_GSM_SIM; 62639beb93cSSam Leffler } else { 62739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: USIM is supported"); 62839beb93cSSam Leffler scard->sim_type = SCARD_USIM; 62939beb93cSSam Leffler } 63039beb93cSSam Leffler 63139beb93cSSam Leffler if (scard->sim_type == SCARD_GSM_SIM) { 63239beb93cSSam Leffler blen = sizeof(buf); 63339beb93cSSam Leffler if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) { 63439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF"); 63539beb93cSSam Leffler goto failed; 63639beb93cSSam Leffler } 63739beb93cSSam Leffler 63839beb93cSSam Leffler blen = sizeof(buf); 63939beb93cSSam Leffler if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) { 64039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF"); 64139beb93cSSam Leffler goto failed; 64239beb93cSSam Leffler } 64339beb93cSSam Leffler } else { 64439beb93cSSam Leffler unsigned char aid[32]; 64539beb93cSSam Leffler int aid_len; 64639beb93cSSam Leffler 64739beb93cSSam Leffler aid_len = scard_get_aid(scard, aid, sizeof(aid)); 64839beb93cSSam Leffler if (aid_len < 0) { 64939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Failed to find AID for " 65039beb93cSSam Leffler "3G USIM app - try to use standard 3G RID"); 65139beb93cSSam Leffler os_memcpy(aid, "\xa0\x00\x00\x00\x87", 5); 65239beb93cSSam Leffler aid_len = 5; 65339beb93cSSam Leffler } 65439beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: 3G USIM AID", aid, aid_len); 65539beb93cSSam Leffler 65639beb93cSSam Leffler /* Select based on AID = 3G RID from EF_DIR. This is usually 65739beb93cSSam Leffler * starting with A0 00 00 00 87. */ 65839beb93cSSam Leffler blen = sizeof(buf); 65939beb93cSSam Leffler if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type, 66039beb93cSSam Leffler aid, aid_len)) { 66139beb93cSSam Leffler wpa_printf(MSG_INFO, "SCARD: Failed to read 3G USIM " 66239beb93cSSam Leffler "app"); 66339beb93cSSam Leffler wpa_hexdump(MSG_INFO, "SCARD: 3G USIM AID", 66439beb93cSSam Leffler aid, aid_len); 66539beb93cSSam Leffler goto failed; 66639beb93cSSam Leffler } 66739beb93cSSam Leffler } 66839beb93cSSam Leffler 66939beb93cSSam Leffler /* Verify whether CHV1 (PIN1) is needed to access the card. */ 67039beb93cSSam Leffler pin_needed = scard_pin_needed(scard, buf, blen); 67139beb93cSSam Leffler if (pin_needed < 0) { 67239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Failed to determine whether PIN " 67339beb93cSSam Leffler "is needed"); 67439beb93cSSam Leffler goto failed; 67539beb93cSSam Leffler } 67639beb93cSSam Leffler if (pin_needed) { 67739beb93cSSam Leffler scard->pin1_required = 1; 678f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry " 679f05cddf9SRui Paulo "counter=%d)", scard_get_pin_retry_counter(scard)); 68039beb93cSSam Leffler } 68139beb93cSSam Leffler 68239beb93cSSam Leffler ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); 68339beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) { 68439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Could not end transaction: " 68539beb93cSSam Leffler "0x%x", (unsigned int) ret); 68639beb93cSSam Leffler } 68739beb93cSSam Leffler 68839beb93cSSam Leffler return scard; 68939beb93cSSam Leffler 69039beb93cSSam Leffler failed: 69139beb93cSSam Leffler if (transaction) 69239beb93cSSam Leffler SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); 69339beb93cSSam Leffler os_free(readers); 69439beb93cSSam Leffler scard_deinit(scard); 69539beb93cSSam Leffler return NULL; 69639beb93cSSam Leffler } 69739beb93cSSam Leffler 69839beb93cSSam Leffler 69939beb93cSSam Leffler /** 70039beb93cSSam Leffler * scard_set_pin - Set PIN (CHV1/PIN1) code for accessing SIM/USIM commands 70139beb93cSSam Leffler * @scard: Pointer to private data from scard_init() 70239beb93cSSam Leffler * @pin: PIN code as an ASCII string (e.g., "1234") 70339beb93cSSam Leffler * Returns: 0 on success, -1 on failure 70439beb93cSSam Leffler */ 70539beb93cSSam Leffler int scard_set_pin(struct scard_data *scard, const char *pin) 70639beb93cSSam Leffler { 70739beb93cSSam Leffler if (scard == NULL) 70839beb93cSSam Leffler return -1; 70939beb93cSSam Leffler 71039beb93cSSam Leffler /* Verify whether CHV1 (PIN1) is needed to access the card. */ 71139beb93cSSam Leffler if (scard->pin1_required) { 71239beb93cSSam Leffler if (pin == NULL) { 71339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "No PIN configured for SIM " 71439beb93cSSam Leffler "access"); 71539beb93cSSam Leffler return -1; 71639beb93cSSam Leffler } 71739beb93cSSam Leffler if (scard_verify_pin(scard, pin)) { 71839beb93cSSam Leffler wpa_printf(MSG_INFO, "PIN verification failed for " 71939beb93cSSam Leffler "SIM access"); 72039beb93cSSam Leffler return -1; 72139beb93cSSam Leffler } 72239beb93cSSam Leffler } 72339beb93cSSam Leffler 72439beb93cSSam Leffler return 0; 72539beb93cSSam Leffler } 72639beb93cSSam Leffler 72739beb93cSSam Leffler 72839beb93cSSam Leffler /** 72939beb93cSSam Leffler * scard_deinit - Deinitialize SIM/USIM connection 73039beb93cSSam Leffler * @scard: Pointer to private data from scard_init() 73139beb93cSSam Leffler * 73239beb93cSSam Leffler * This function closes the SIM/USIM connect opened with scard_init(). 73339beb93cSSam Leffler */ 73439beb93cSSam Leffler void scard_deinit(struct scard_data *scard) 73539beb93cSSam Leffler { 73639beb93cSSam Leffler long ret; 73739beb93cSSam Leffler 73839beb93cSSam Leffler if (scard == NULL) 73939beb93cSSam Leffler return; 74039beb93cSSam Leffler 74139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface"); 74239beb93cSSam Leffler if (scard->card) { 74339beb93cSSam Leffler ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD); 74439beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) { 74539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect " 74639beb93cSSam Leffler "smart card (err=%ld)", ret); 74739beb93cSSam Leffler } 74839beb93cSSam Leffler } 74939beb93cSSam Leffler 75039beb93cSSam Leffler if (scard->ctx) { 75139beb93cSSam Leffler ret = SCardReleaseContext(scard->ctx); 75239beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) { 75339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "Failed to release smart card " 75439beb93cSSam Leffler "context (err=%ld)", ret); 75539beb93cSSam Leffler } 75639beb93cSSam Leffler } 75739beb93cSSam Leffler os_free(scard); 75839beb93cSSam Leffler mingw_unload_symbols(); 75939beb93cSSam Leffler } 76039beb93cSSam Leffler 76139beb93cSSam Leffler 76239beb93cSSam Leffler static long scard_transmit(struct scard_data *scard, 76339beb93cSSam Leffler unsigned char *_send, size_t send_len, 76439beb93cSSam Leffler unsigned char *_recv, size_t *recv_len) 76539beb93cSSam Leffler { 76639beb93cSSam Leffler long ret; 76739beb93cSSam Leffler unsigned long rlen; 76839beb93cSSam Leffler 76939beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send", 77039beb93cSSam Leffler _send, send_len); 77139beb93cSSam Leffler rlen = *recv_len; 77239beb93cSSam Leffler ret = SCardTransmit(scard->card, 77339beb93cSSam Leffler scard->protocol == SCARD_PROTOCOL_T1 ? 77439beb93cSSam Leffler SCARD_PCI_T1 : SCARD_PCI_T0, 77539beb93cSSam Leffler _send, (unsigned long) send_len, 77639beb93cSSam Leffler NULL, _recv, &rlen); 77739beb93cSSam Leffler *recv_len = rlen; 77839beb93cSSam Leffler if (ret == SCARD_S_SUCCESS) { 77939beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv", 78039beb93cSSam Leffler _recv, rlen); 78139beb93cSSam Leffler } else { 78239beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " 78339beb93cSSam Leffler "(err=0x%lx)", ret); 78439beb93cSSam Leffler } 78539beb93cSSam Leffler return ret; 78639beb93cSSam Leffler } 78739beb93cSSam Leffler 78839beb93cSSam Leffler 78939beb93cSSam Leffler static int _scard_select_file(struct scard_data *scard, unsigned short file_id, 79039beb93cSSam Leffler unsigned char *buf, size_t *buf_len, 79139beb93cSSam Leffler sim_types sim_type, unsigned char *aid, 79239beb93cSSam Leffler size_t aidlen) 79339beb93cSSam Leffler { 79439beb93cSSam Leffler long ret; 79539beb93cSSam Leffler unsigned char resp[3]; 79639beb93cSSam Leffler unsigned char cmd[50] = { SIM_CMD_SELECT }; 79739beb93cSSam Leffler int cmdlen; 79839beb93cSSam Leffler unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; 79939beb93cSSam Leffler size_t len, rlen; 80039beb93cSSam Leffler 80139beb93cSSam Leffler if (sim_type == SCARD_USIM) { 80239beb93cSSam Leffler cmd[0] = USIM_CLA; 80339beb93cSSam Leffler cmd[3] = 0x04; 80439beb93cSSam Leffler get_resp[0] = USIM_CLA; 80539beb93cSSam Leffler } 80639beb93cSSam Leffler 80739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id); 80839beb93cSSam Leffler if (aid) { 80939beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: select file by AID", 81039beb93cSSam Leffler aid, aidlen); 81139beb93cSSam Leffler if (5 + aidlen > sizeof(cmd)) 81239beb93cSSam Leffler return -1; 81339beb93cSSam Leffler cmd[2] = 0x04; /* Select by AID */ 81439beb93cSSam Leffler cmd[4] = aidlen; /* len */ 81539beb93cSSam Leffler os_memcpy(cmd + 5, aid, aidlen); 81639beb93cSSam Leffler cmdlen = 5 + aidlen; 81739beb93cSSam Leffler } else { 81839beb93cSSam Leffler cmd[5] = file_id >> 8; 81939beb93cSSam Leffler cmd[6] = file_id & 0xff; 82039beb93cSSam Leffler cmdlen = 7; 82139beb93cSSam Leffler } 82239beb93cSSam Leffler len = sizeof(resp); 82339beb93cSSam Leffler ret = scard_transmit(scard, cmd, cmdlen, resp, &len); 82439beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) { 82539beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " 82639beb93cSSam Leffler "(err=0x%lx)", ret); 82739beb93cSSam Leffler return -1; 82839beb93cSSam Leffler } 82939beb93cSSam Leffler 83039beb93cSSam Leffler if (len != 2) { 83139beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: unexpected resp len " 83239beb93cSSam Leffler "%d (expected 2)", (int) len); 83339beb93cSSam Leffler return -1; 83439beb93cSSam Leffler } 83539beb93cSSam Leffler 83639beb93cSSam Leffler if (resp[0] == 0x98 && resp[1] == 0x04) { 83739beb93cSSam Leffler /* Security status not satisfied (PIN_WLAN) */ 83839beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied " 83939beb93cSSam Leffler "(PIN_WLAN)"); 84039beb93cSSam Leffler return -1; 84139beb93cSSam Leffler } 84239beb93cSSam Leffler 84339beb93cSSam Leffler if (resp[0] == 0x6e) { 84439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported"); 84539beb93cSSam Leffler return -1; 84639beb93cSSam Leffler } 84739beb93cSSam Leffler 84839beb93cSSam Leffler if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) { 84939beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x " 85039beb93cSSam Leffler "(expected 0x61, 0x6c, or 0x9f)", resp[0]); 85139beb93cSSam Leffler return -1; 85239beb93cSSam Leffler } 85339beb93cSSam Leffler /* Normal ending of command; resp[1] bytes available */ 85439beb93cSSam Leffler get_resp[4] = resp[1]; 85539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)", 85639beb93cSSam Leffler resp[1]); 85739beb93cSSam Leffler 85839beb93cSSam Leffler rlen = *buf_len; 85939beb93cSSam Leffler ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen); 86039beb93cSSam Leffler if (ret == SCARD_S_SUCCESS) { 86139beb93cSSam Leffler *buf_len = resp[1] < rlen ? resp[1] : rlen; 86239beb93cSSam Leffler return 0; 86339beb93cSSam Leffler } 86439beb93cSSam Leffler 86539beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret); 86639beb93cSSam Leffler return -1; 86739beb93cSSam Leffler } 86839beb93cSSam Leffler 86939beb93cSSam Leffler 87039beb93cSSam Leffler static int scard_select_file(struct scard_data *scard, unsigned short file_id, 87139beb93cSSam Leffler unsigned char *buf, size_t *buf_len) 87239beb93cSSam Leffler { 87339beb93cSSam Leffler return _scard_select_file(scard, file_id, buf, buf_len, 87439beb93cSSam Leffler scard->sim_type, NULL, 0); 87539beb93cSSam Leffler } 87639beb93cSSam Leffler 87739beb93cSSam Leffler 87839beb93cSSam Leffler static int scard_get_record_len(struct scard_data *scard, unsigned char recnum, 87939beb93cSSam Leffler unsigned char mode) 88039beb93cSSam Leffler { 88139beb93cSSam Leffler unsigned char buf[255]; 88239beb93cSSam Leffler unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; 88339beb93cSSam Leffler size_t blen; 88439beb93cSSam Leffler long ret; 88539beb93cSSam Leffler 88639beb93cSSam Leffler if (scard->sim_type == SCARD_USIM) 88739beb93cSSam Leffler cmd[0] = USIM_CLA; 88839beb93cSSam Leffler cmd[2] = recnum; 88939beb93cSSam Leffler cmd[3] = mode; 89039beb93cSSam Leffler cmd[4] = sizeof(buf); 89139beb93cSSam Leffler 89239beb93cSSam Leffler blen = sizeof(buf); 89339beb93cSSam Leffler ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); 89439beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) { 89539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: failed to determine file " 89639beb93cSSam Leffler "length for record %d", recnum); 89739beb93cSSam Leffler return -1; 89839beb93cSSam Leffler } 89939beb93cSSam Leffler 90039beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response", 90139beb93cSSam Leffler buf, blen); 90239beb93cSSam Leffler 903f05cddf9SRui Paulo if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) { 90439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file " 90539beb93cSSam Leffler "length determination"); 90639beb93cSSam Leffler return -1; 90739beb93cSSam Leffler } 90839beb93cSSam Leffler 90939beb93cSSam Leffler return buf[1]; 91039beb93cSSam Leffler } 91139beb93cSSam Leffler 91239beb93cSSam Leffler 91339beb93cSSam Leffler static int scard_read_record(struct scard_data *scard, 91439beb93cSSam Leffler unsigned char *data, size_t len, 91539beb93cSSam Leffler unsigned char recnum, unsigned char mode) 91639beb93cSSam Leffler { 91739beb93cSSam Leffler unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; 91839beb93cSSam Leffler size_t blen = len + 3; 91939beb93cSSam Leffler unsigned char *buf; 92039beb93cSSam Leffler long ret; 92139beb93cSSam Leffler 92239beb93cSSam Leffler if (scard->sim_type == SCARD_USIM) 92339beb93cSSam Leffler cmd[0] = USIM_CLA; 92439beb93cSSam Leffler cmd[2] = recnum; 92539beb93cSSam Leffler cmd[3] = mode; 92639beb93cSSam Leffler cmd[4] = len; 92739beb93cSSam Leffler 92839beb93cSSam Leffler buf = os_malloc(blen); 92939beb93cSSam Leffler if (buf == NULL) 93039beb93cSSam Leffler return -1; 93139beb93cSSam Leffler 93239beb93cSSam Leffler ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); 93339beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) { 93439beb93cSSam Leffler os_free(buf); 93539beb93cSSam Leffler return -2; 93639beb93cSSam Leffler } 93739beb93cSSam Leffler if (blen != len + 2) { 93839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected " 93939beb93cSSam Leffler "length %ld (expected %ld)", 94039beb93cSSam Leffler (long) blen, (long) len + 2); 94139beb93cSSam Leffler os_free(buf); 94239beb93cSSam Leffler return -3; 94339beb93cSSam Leffler } 94439beb93cSSam Leffler 94539beb93cSSam Leffler if (buf[len] != 0x90 || buf[len + 1] != 0x00) { 94639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected " 94739beb93cSSam Leffler "status %02x %02x (expected 90 00)", 94839beb93cSSam Leffler buf[len], buf[len + 1]); 94939beb93cSSam Leffler os_free(buf); 95039beb93cSSam Leffler return -4; 95139beb93cSSam Leffler } 95239beb93cSSam Leffler 95339beb93cSSam Leffler os_memcpy(data, buf, len); 95439beb93cSSam Leffler os_free(buf); 95539beb93cSSam Leffler 95639beb93cSSam Leffler return 0; 95739beb93cSSam Leffler } 95839beb93cSSam Leffler 95939beb93cSSam Leffler 96039beb93cSSam Leffler static int scard_read_file(struct scard_data *scard, 96139beb93cSSam Leffler unsigned char *data, size_t len) 96239beb93cSSam Leffler { 96339beb93cSSam Leffler unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ }; 96439beb93cSSam Leffler size_t blen = len + 3; 96539beb93cSSam Leffler unsigned char *buf; 96639beb93cSSam Leffler long ret; 96739beb93cSSam Leffler 96839beb93cSSam Leffler cmd[4] = len; 96939beb93cSSam Leffler 97039beb93cSSam Leffler buf = os_malloc(blen); 97139beb93cSSam Leffler if (buf == NULL) 97239beb93cSSam Leffler return -1; 97339beb93cSSam Leffler 97439beb93cSSam Leffler if (scard->sim_type == SCARD_USIM) 97539beb93cSSam Leffler cmd[0] = USIM_CLA; 97639beb93cSSam Leffler ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); 97739beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) { 97839beb93cSSam Leffler os_free(buf); 97939beb93cSSam Leffler return -2; 98039beb93cSSam Leffler } 98139beb93cSSam Leffler if (blen != len + 2) { 98239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " 98339beb93cSSam Leffler "length %ld (expected %ld)", 98439beb93cSSam Leffler (long) blen, (long) len + 2); 98539beb93cSSam Leffler os_free(buf); 98639beb93cSSam Leffler return -3; 98739beb93cSSam Leffler } 98839beb93cSSam Leffler 98939beb93cSSam Leffler if (buf[len] != 0x90 || buf[len + 1] != 0x00) { 99039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " 99139beb93cSSam Leffler "status %02x %02x (expected 90 00)", 99239beb93cSSam Leffler buf[len], buf[len + 1]); 99339beb93cSSam Leffler os_free(buf); 99439beb93cSSam Leffler return -4; 99539beb93cSSam Leffler } 99639beb93cSSam Leffler 99739beb93cSSam Leffler os_memcpy(data, buf, len); 99839beb93cSSam Leffler os_free(buf); 99939beb93cSSam Leffler 100039beb93cSSam Leffler return 0; 100139beb93cSSam Leffler } 100239beb93cSSam Leffler 100339beb93cSSam Leffler 100439beb93cSSam Leffler static int scard_verify_pin(struct scard_data *scard, const char *pin) 100539beb93cSSam Leffler { 100639beb93cSSam Leffler long ret; 100739beb93cSSam Leffler unsigned char resp[3]; 100839beb93cSSam Leffler unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 }; 100939beb93cSSam Leffler size_t len; 101039beb93cSSam Leffler 101139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: verifying PIN"); 101239beb93cSSam Leffler 101339beb93cSSam Leffler if (pin == NULL || os_strlen(pin) > 8) 101439beb93cSSam Leffler return -1; 101539beb93cSSam Leffler 101639beb93cSSam Leffler if (scard->sim_type == SCARD_USIM) 101739beb93cSSam Leffler cmd[0] = USIM_CLA; 101839beb93cSSam Leffler os_memcpy(cmd + 5, pin, os_strlen(pin)); 101939beb93cSSam Leffler os_memset(cmd + 5 + os_strlen(pin), 0xff, 8 - os_strlen(pin)); 102039beb93cSSam Leffler 102139beb93cSSam Leffler len = sizeof(resp); 102239beb93cSSam Leffler ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); 102339beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) 102439beb93cSSam Leffler return -2; 102539beb93cSSam Leffler 102639beb93cSSam Leffler if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) { 102739beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: PIN verification failed"); 102839beb93cSSam Leffler return -1; 102939beb93cSSam Leffler } 103039beb93cSSam Leffler 103139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully"); 103239beb93cSSam Leffler return 0; 103339beb93cSSam Leffler } 103439beb93cSSam Leffler 103539beb93cSSam Leffler 1036f05cddf9SRui Paulo int scard_get_pin_retry_counter(struct scard_data *scard) 1037f05cddf9SRui Paulo { 1038f05cddf9SRui Paulo long ret; 1039f05cddf9SRui Paulo unsigned char resp[3]; 1040f05cddf9SRui Paulo unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 }; 1041f05cddf9SRui Paulo size_t len; 1042f05cddf9SRui Paulo u16 val; 1043f05cddf9SRui Paulo 1044f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "SCARD: fetching PIN retry counter"); 1045f05cddf9SRui Paulo 1046f05cddf9SRui Paulo if (scard->sim_type == SCARD_USIM) 1047f05cddf9SRui Paulo cmd[0] = USIM_CLA; 1048f05cddf9SRui Paulo cmd[4] = 0; /* Empty data */ 1049f05cddf9SRui Paulo 1050f05cddf9SRui Paulo len = sizeof(resp); 1051f05cddf9SRui Paulo ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); 1052f05cddf9SRui Paulo if (ret != SCARD_S_SUCCESS) 1053f05cddf9SRui Paulo return -2; 1054f05cddf9SRui Paulo 1055f05cddf9SRui Paulo if (len != 2) { 1056f05cddf9SRui Paulo wpa_printf(MSG_WARNING, "SCARD: failed to fetch PIN retry " 1057f05cddf9SRui Paulo "counter"); 1058f05cddf9SRui Paulo return -1; 1059f05cddf9SRui Paulo } 1060f05cddf9SRui Paulo 1061f05cddf9SRui Paulo val = WPA_GET_BE16(resp); 1062f05cddf9SRui Paulo if (val == 0x63c0 || val == 0x6983) { 1063f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "SCARD: PIN has been blocked"); 1064f05cddf9SRui Paulo return 0; 1065f05cddf9SRui Paulo } 1066f05cddf9SRui Paulo 1067f05cddf9SRui Paulo if (val >= 0x63c0 && val <= 0x63cf) 1068f05cddf9SRui Paulo return val & 0x000f; 1069f05cddf9SRui Paulo 1070f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "SCARD: Unexpected PIN retry counter response " 1071f05cddf9SRui Paulo "value 0x%x", val); 1072f05cddf9SRui Paulo return 0; 1073f05cddf9SRui Paulo } 1074f05cddf9SRui Paulo 1075f05cddf9SRui Paulo 107639beb93cSSam Leffler /** 107739beb93cSSam Leffler * scard_get_imsi - Read IMSI from SIM/USIM card 107839beb93cSSam Leffler * @scard: Pointer to private data from scard_init() 107939beb93cSSam Leffler * @imsi: Buffer for IMSI 108039beb93cSSam Leffler * @len: Length of imsi buffer; set to IMSI length on success 108139beb93cSSam Leffler * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file 108239beb93cSSam Leffler * selection returns invalid result code, -3 if parsing FSP template file fails 108339beb93cSSam Leffler * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set 108439beb93cSSam Leffler * to needed length), -5 if reading IMSI file fails. 108539beb93cSSam Leffler * 108639beb93cSSam Leffler * This function can be used to read IMSI from the SIM/USIM card. If the IMSI 108739beb93cSSam Leffler * file is PIN protected, scard_set_pin() must have been used to set the 108839beb93cSSam Leffler * correct PIN code before calling scard_get_imsi(). 108939beb93cSSam Leffler */ 109039beb93cSSam Leffler int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len) 109139beb93cSSam Leffler { 109239beb93cSSam Leffler unsigned char buf[100]; 109339beb93cSSam Leffler size_t blen, imsilen, i; 109439beb93cSSam Leffler char *pos; 109539beb93cSSam Leffler 109639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI"); 109739beb93cSSam Leffler blen = sizeof(buf); 109839beb93cSSam Leffler if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen)) 109939beb93cSSam Leffler return -1; 110039beb93cSSam Leffler if (blen < 4) { 110139beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI " 110239beb93cSSam Leffler "header (len=%ld)", (long) blen); 110339beb93cSSam Leffler return -2; 110439beb93cSSam Leffler } 110539beb93cSSam Leffler 110639beb93cSSam Leffler if (scard->sim_type == SCARD_GSM_SIM) { 1107*5b9c547cSRui Paulo blen = WPA_GET_BE16(&buf[2]); 110839beb93cSSam Leffler } else { 110939beb93cSSam Leffler int file_size; 111039beb93cSSam Leffler if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) 111139beb93cSSam Leffler return -3; 111239beb93cSSam Leffler blen = file_size; 111339beb93cSSam Leffler } 111439beb93cSSam Leffler if (blen < 2 || blen > sizeof(buf)) { 111539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%ld", 111639beb93cSSam Leffler (long) blen); 111739beb93cSSam Leffler return -3; 111839beb93cSSam Leffler } 111939beb93cSSam Leffler 112039beb93cSSam Leffler imsilen = (blen - 2) * 2 + 1; 112139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%ld imsilen=%ld", 112239beb93cSSam Leffler (long) blen, (long) imsilen); 112339beb93cSSam Leffler if (blen < 2 || imsilen > *len) { 112439beb93cSSam Leffler *len = imsilen; 112539beb93cSSam Leffler return -4; 112639beb93cSSam Leffler } 112739beb93cSSam Leffler 112839beb93cSSam Leffler if (scard_read_file(scard, buf, blen)) 112939beb93cSSam Leffler return -5; 113039beb93cSSam Leffler 113139beb93cSSam Leffler pos = imsi; 113239beb93cSSam Leffler *pos++ = '0' + (buf[1] >> 4 & 0x0f); 113339beb93cSSam Leffler for (i = 2; i < blen; i++) { 113439beb93cSSam Leffler unsigned char digit; 113539beb93cSSam Leffler 113639beb93cSSam Leffler digit = buf[i] & 0x0f; 113739beb93cSSam Leffler if (digit < 10) 113839beb93cSSam Leffler *pos++ = '0' + digit; 113939beb93cSSam Leffler else 114039beb93cSSam Leffler imsilen--; 114139beb93cSSam Leffler 114239beb93cSSam Leffler digit = buf[i] >> 4 & 0x0f; 114339beb93cSSam Leffler if (digit < 10) 114439beb93cSSam Leffler *pos++ = '0' + digit; 114539beb93cSSam Leffler else 114639beb93cSSam Leffler imsilen--; 114739beb93cSSam Leffler } 114839beb93cSSam Leffler *len = imsilen; 114939beb93cSSam Leffler 115039beb93cSSam Leffler return 0; 115139beb93cSSam Leffler } 115239beb93cSSam Leffler 115339beb93cSSam Leffler 115439beb93cSSam Leffler /** 1155f05cddf9SRui Paulo * scard_get_mnc_len - Read length of MNC in the IMSI from SIM/USIM card 1156f05cddf9SRui Paulo * @scard: Pointer to private data from scard_init() 1157f05cddf9SRui Paulo * Returns: length (>0) on success, -1 if administrative data file cannot be 1158f05cddf9SRui Paulo * selected, -2 if administrative data file selection returns invalid result 1159f05cddf9SRui Paulo * code, -3 if parsing FSP template file fails (USIM only), -4 if length of 1160f05cddf9SRui Paulo * the file is unexpected, -5 if reading file fails, -6 if MNC length is not 1161f05cddf9SRui Paulo * in range (i.e. 2 or 3), -7 if MNC length is not available. 1162f05cddf9SRui Paulo * 1163f05cddf9SRui Paulo */ 1164f05cddf9SRui Paulo int scard_get_mnc_len(struct scard_data *scard) 1165f05cddf9SRui Paulo { 1166f05cddf9SRui Paulo unsigned char buf[100]; 1167f05cddf9SRui Paulo size_t blen; 1168f05cddf9SRui Paulo int file_size; 1169f05cddf9SRui Paulo 1170f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "SCARD: reading MNC len from (GSM) EF-AD"); 1171f05cddf9SRui Paulo blen = sizeof(buf); 1172f05cddf9SRui Paulo if (scard_select_file(scard, SCARD_FILE_GSM_EF_AD, buf, &blen)) 1173f05cddf9SRui Paulo return -1; 1174f05cddf9SRui Paulo if (blen < 4) { 1175f05cddf9SRui Paulo wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-AD " 1176f05cddf9SRui Paulo "header (len=%ld)", (long) blen); 1177f05cddf9SRui Paulo return -2; 1178f05cddf9SRui Paulo } 1179f05cddf9SRui Paulo 1180f05cddf9SRui Paulo if (scard->sim_type == SCARD_GSM_SIM) { 1181*5b9c547cSRui Paulo file_size = WPA_GET_BE16(&buf[2]); 1182f05cddf9SRui Paulo } else { 1183f05cddf9SRui Paulo if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) 1184f05cddf9SRui Paulo return -3; 1185f05cddf9SRui Paulo } 1186f05cddf9SRui Paulo if (file_size == 3) { 1187f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "SCARD: MNC length not available"); 1188f05cddf9SRui Paulo return -7; 1189f05cddf9SRui Paulo } 1190f05cddf9SRui Paulo if (file_size < 4 || file_size > (int) sizeof(buf)) { 1191f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "SCARD: invalid file length=%ld", 1192f05cddf9SRui Paulo (long) file_size); 1193f05cddf9SRui Paulo return -4; 1194f05cddf9SRui Paulo } 1195f05cddf9SRui Paulo 1196f05cddf9SRui Paulo if (scard_read_file(scard, buf, file_size)) 1197f05cddf9SRui Paulo return -5; 1198f05cddf9SRui Paulo buf[3] = buf[3] & 0x0f; /* upper nibble reserved for future use */ 1199f05cddf9SRui Paulo if (buf[3] < 2 || buf[3] > 3) { 1200f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "SCARD: invalid MNC length=%ld", 1201f05cddf9SRui Paulo (long) buf[3]); 1202f05cddf9SRui Paulo return -6; 1203f05cddf9SRui Paulo } 1204f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "SCARD: MNC length=%ld", (long) buf[3]); 1205f05cddf9SRui Paulo return buf[3]; 1206f05cddf9SRui Paulo } 1207f05cddf9SRui Paulo 1208f05cddf9SRui Paulo 1209f05cddf9SRui Paulo /** 121039beb93cSSam Leffler * scard_gsm_auth - Run GSM authentication command on SIM card 121139beb93cSSam Leffler * @scard: Pointer to private data from scard_init() 121239beb93cSSam Leffler * @_rand: 16-byte RAND value from HLR/AuC 121339beb93cSSam Leffler * @sres: 4-byte buffer for SRES 121439beb93cSSam Leffler * @kc: 8-byte buffer for Kc 121539beb93cSSam Leffler * Returns: 0 on success, -1 if SIM/USIM connection has not been initialized, 121639beb93cSSam Leffler * -2 if authentication command execution fails, -3 if unknown response code 121739beb93cSSam Leffler * for authentication command is received, -4 if reading of response fails, 121839beb93cSSam Leffler * -5 if if response data is of unexpected length 121939beb93cSSam Leffler * 122039beb93cSSam Leffler * This function performs GSM authentication using SIM/USIM card and the 122139beb93cSSam Leffler * provided RAND value from HLR/AuC. If authentication command can be completed 122239beb93cSSam Leffler * successfully, SRES and Kc values will be written into sres and kc buffers. 122339beb93cSSam Leffler */ 122439beb93cSSam Leffler int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand, 122539beb93cSSam Leffler unsigned char *sres, unsigned char *kc) 122639beb93cSSam Leffler { 122739beb93cSSam Leffler unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG }; 122839beb93cSSam Leffler int cmdlen; 122939beb93cSSam Leffler unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; 123039beb93cSSam Leffler unsigned char resp[3], buf[12 + 3 + 2]; 123139beb93cSSam Leffler size_t len; 123239beb93cSSam Leffler long ret; 123339beb93cSSam Leffler 123439beb93cSSam Leffler if (scard == NULL) 123539beb93cSSam Leffler return -1; 123639beb93cSSam Leffler 123739beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", _rand, 16); 123839beb93cSSam Leffler if (scard->sim_type == SCARD_GSM_SIM) { 123939beb93cSSam Leffler cmdlen = 5 + 16; 124039beb93cSSam Leffler os_memcpy(cmd + 5, _rand, 16); 124139beb93cSSam Leffler } else { 124239beb93cSSam Leffler cmdlen = 5 + 1 + 16; 124339beb93cSSam Leffler cmd[0] = USIM_CLA; 124439beb93cSSam Leffler cmd[3] = 0x80; 124539beb93cSSam Leffler cmd[4] = 17; 124639beb93cSSam Leffler cmd[5] = 16; 124739beb93cSSam Leffler os_memcpy(cmd + 6, _rand, 16); 1248*5b9c547cSRui Paulo get_resp[0] = USIM_CLA; 124939beb93cSSam Leffler } 125039beb93cSSam Leffler len = sizeof(resp); 125139beb93cSSam Leffler ret = scard_transmit(scard, cmd, cmdlen, resp, &len); 125239beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) 125339beb93cSSam Leffler return -2; 125439beb93cSSam Leffler 125539beb93cSSam Leffler if ((scard->sim_type == SCARD_GSM_SIM && 125639beb93cSSam Leffler (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) || 125739beb93cSSam Leffler (scard->sim_type == SCARD_USIM && 125839beb93cSSam Leffler (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) { 125939beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM " 126039beb93cSSam Leffler "auth request (len=%ld resp=%02x %02x)", 126139beb93cSSam Leffler (long) len, resp[0], resp[1]); 126239beb93cSSam Leffler return -3; 126339beb93cSSam Leffler } 126439beb93cSSam Leffler get_resp[4] = resp[1]; 126539beb93cSSam Leffler 126639beb93cSSam Leffler len = sizeof(buf); 126739beb93cSSam Leffler ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); 126839beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) 126939beb93cSSam Leffler return -4; 127039beb93cSSam Leffler 127139beb93cSSam Leffler if (scard->sim_type == SCARD_GSM_SIM) { 127239beb93cSSam Leffler if (len != 4 + 8 + 2) { 127339beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: unexpected data " 127439beb93cSSam Leffler "length for GSM auth (len=%ld, expected 14)", 127539beb93cSSam Leffler (long) len); 127639beb93cSSam Leffler return -5; 127739beb93cSSam Leffler } 127839beb93cSSam Leffler os_memcpy(sres, buf, 4); 127939beb93cSSam Leffler os_memcpy(kc, buf + 4, 8); 128039beb93cSSam Leffler } else { 128139beb93cSSam Leffler if (len != 1 + 4 + 1 + 8 + 2) { 128239beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: unexpected data " 128339beb93cSSam Leffler "length for USIM auth (len=%ld, " 128439beb93cSSam Leffler "expected 16)", (long) len); 128539beb93cSSam Leffler return -5; 128639beb93cSSam Leffler } 128739beb93cSSam Leffler if (buf[0] != 4 || buf[5] != 8) { 128839beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc " 128939beb93cSSam Leffler "length (%d %d, expected 4 8)", 129039beb93cSSam Leffler buf[0], buf[5]); 129139beb93cSSam Leffler } 129239beb93cSSam Leffler os_memcpy(sres, buf + 1, 4); 129339beb93cSSam Leffler os_memcpy(kc, buf + 6, 8); 129439beb93cSSam Leffler } 129539beb93cSSam Leffler 129639beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4); 129739beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8); 129839beb93cSSam Leffler 129939beb93cSSam Leffler return 0; 130039beb93cSSam Leffler } 130139beb93cSSam Leffler 130239beb93cSSam Leffler 130339beb93cSSam Leffler /** 130439beb93cSSam Leffler * scard_umts_auth - Run UMTS authentication command on USIM card 130539beb93cSSam Leffler * @scard: Pointer to private data from scard_init() 130639beb93cSSam Leffler * @_rand: 16-byte RAND value from HLR/AuC 130739beb93cSSam Leffler * @autn: 16-byte AUTN value from HLR/AuC 130839beb93cSSam Leffler * @res: 16-byte buffer for RES 130939beb93cSSam Leffler * @res_len: Variable that will be set to RES length 131039beb93cSSam Leffler * @ik: 16-byte buffer for IK 131139beb93cSSam Leffler * @ck: 16-byte buffer for CK 131239beb93cSSam Leffler * @auts: 14-byte buffer for AUTS 131339beb93cSSam Leffler * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization 131439beb93cSSam Leffler * failure 131539beb93cSSam Leffler * 131639beb93cSSam Leffler * This function performs AKA authentication using USIM card and the provided 131739beb93cSSam Leffler * RAND and AUTN values from HLR/AuC. If authentication command can be 131839beb93cSSam Leffler * completed successfully, RES, IK, and CK values will be written into provided 131939beb93cSSam Leffler * buffers and res_len is set to length of received RES value. If USIM reports 132039beb93cSSam Leffler * synchronization failure, the received AUTS value will be written into auts 132139beb93cSSam Leffler * buffer. In this case, RES, IK, and CK are not valid. 132239beb93cSSam Leffler */ 132339beb93cSSam Leffler int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, 132439beb93cSSam Leffler const unsigned char *autn, 132539beb93cSSam Leffler unsigned char *res, size_t *res_len, 132639beb93cSSam Leffler unsigned char *ik, unsigned char *ck, unsigned char *auts) 132739beb93cSSam Leffler { 132839beb93cSSam Leffler unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] = 132939beb93cSSam Leffler { USIM_CMD_RUN_UMTS_ALG }; 133039beb93cSSam Leffler unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE }; 133139beb93cSSam Leffler unsigned char resp[3], buf[64], *pos, *end; 133239beb93cSSam Leffler size_t len; 133339beb93cSSam Leffler long ret; 133439beb93cSSam Leffler 133539beb93cSSam Leffler if (scard == NULL) 133639beb93cSSam Leffler return -1; 133739beb93cSSam Leffler 133839beb93cSSam Leffler if (scard->sim_type == SCARD_GSM_SIM) { 133939beb93cSSam Leffler wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS " 134039beb93cSSam Leffler "auth"); 134139beb93cSSam Leffler return -1; 134239beb93cSSam Leffler } 134339beb93cSSam Leffler 134439beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", _rand, AKA_RAND_LEN); 134539beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN); 134639beb93cSSam Leffler cmd[5] = AKA_RAND_LEN; 134739beb93cSSam Leffler os_memcpy(cmd + 6, _rand, AKA_RAND_LEN); 134839beb93cSSam Leffler cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN; 134939beb93cSSam Leffler os_memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN); 135039beb93cSSam Leffler 135139beb93cSSam Leffler len = sizeof(resp); 135239beb93cSSam Leffler ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); 135339beb93cSSam Leffler if (ret != SCARD_S_SUCCESS) 135439beb93cSSam Leffler return -1; 135539beb93cSSam Leffler 135639beb93cSSam Leffler if (len <= sizeof(resp)) 135739beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len); 135839beb93cSSam Leffler 135939beb93cSSam Leffler if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) { 136039beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - " 136139beb93cSSam Leffler "MAC != XMAC"); 136239beb93cSSam Leffler return -1; 136339beb93cSSam Leffler } else if (len != 2 || resp[0] != 0x61) { 136439beb93cSSam Leffler wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS " 136539beb93cSSam Leffler "auth request (len=%ld resp=%02x %02x)", 136639beb93cSSam Leffler (long) len, resp[0], resp[1]); 136739beb93cSSam Leffler return -1; 136839beb93cSSam Leffler } 136939beb93cSSam Leffler get_resp[4] = resp[1]; 137039beb93cSSam Leffler 137139beb93cSSam Leffler len = sizeof(buf); 137239beb93cSSam Leffler ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); 137339beb93cSSam Leffler if (ret != SCARD_S_SUCCESS || len > sizeof(buf)) 137439beb93cSSam Leffler return -1; 137539beb93cSSam Leffler 137639beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len); 137739beb93cSSam Leffler if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc && 137839beb93cSSam Leffler buf[1] == AKA_AUTS_LEN) { 137939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure"); 138039beb93cSSam Leffler os_memcpy(auts, buf + 2, AKA_AUTS_LEN); 138139beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN); 138239beb93cSSam Leffler return -2; 138339beb93cSSam Leffler } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) { 138439beb93cSSam Leffler pos = buf + 1; 138539beb93cSSam Leffler end = buf + len; 138639beb93cSSam Leffler 138739beb93cSSam Leffler /* RES */ 138839beb93cSSam Leffler if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) { 138939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Invalid RES"); 139039beb93cSSam Leffler return -1; 139139beb93cSSam Leffler } 139239beb93cSSam Leffler *res_len = *pos++; 139339beb93cSSam Leffler os_memcpy(res, pos, *res_len); 139439beb93cSSam Leffler pos += *res_len; 139539beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len); 139639beb93cSSam Leffler 139739beb93cSSam Leffler /* CK */ 139839beb93cSSam Leffler if (pos[0] != CK_LEN || pos + CK_LEN > end) { 139939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Invalid CK"); 140039beb93cSSam Leffler return -1; 140139beb93cSSam Leffler } 140239beb93cSSam Leffler pos++; 140339beb93cSSam Leffler os_memcpy(ck, pos, CK_LEN); 140439beb93cSSam Leffler pos += CK_LEN; 140539beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN); 140639beb93cSSam Leffler 140739beb93cSSam Leffler /* IK */ 140839beb93cSSam Leffler if (pos[0] != IK_LEN || pos + IK_LEN > end) { 140939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Invalid IK"); 141039beb93cSSam Leffler return -1; 141139beb93cSSam Leffler } 141239beb93cSSam Leffler pos++; 141339beb93cSSam Leffler os_memcpy(ik, pos, IK_LEN); 141439beb93cSSam Leffler pos += IK_LEN; 141539beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN); 141639beb93cSSam Leffler 1417*5b9c547cSRui Paulo if (end > pos) { 1418*5b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, 1419*5b9c547cSRui Paulo "SCARD: Ignore extra data in end", 1420*5b9c547cSRui Paulo pos, end - pos); 1421*5b9c547cSRui Paulo } 1422*5b9c547cSRui Paulo 142339beb93cSSam Leffler return 0; 142439beb93cSSam Leffler } 142539beb93cSSam Leffler 142639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response"); 142739beb93cSSam Leffler return -1; 142839beb93cSSam Leffler } 1429f05cddf9SRui Paulo 1430f05cddf9SRui Paulo 1431f05cddf9SRui Paulo int scard_supports_umts(struct scard_data *scard) 1432f05cddf9SRui Paulo { 1433f05cddf9SRui Paulo return scard->sim_type == SCARD_USIM; 1434f05cddf9SRui Paulo } 1435