1189251Ssam/* 2189251Ssam * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM 3252726Srpaulo * Copyright (c) 2004-2007, 2012, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam * 8189251Ssam * This file implements wrapper functions for accessing GSM SIM and 3GPP USIM 9189251Ssam * cards through PC/SC smartcard library. These functions are used to implement 10189251Ssam * authentication routines for EAP-SIM and EAP-AKA. 11189251Ssam */ 12189251Ssam 13189251Ssam#include "includes.h" 14189251Ssam#include <winscard.h> 15189251Ssam 16189251Ssam#include "common.h" 17189251Ssam#include "pcsc_funcs.h" 18189251Ssam 19189251Ssam 20189251Ssam/* See ETSI GSM 11.11 and ETSI TS 102 221 for details. 21189251Ssam * SIM commands: 22189251Ssam * Command APDU: CLA INS P1 P2 P3 Data 23189251Ssam * CLA (class of instruction): A0 for GSM, 00 for USIM 24189251Ssam * INS (instruction) 25189251Ssam * P1 P2 P3 (parameters, P3 = length of Data) 26189251Ssam * Response APDU: Data SW1 SW2 27189251Ssam * SW1 SW2 (Status words) 28189251Ssam * Commands (INS P1 P2 P3): 29189251Ssam * SELECT: A4 00 00 02 <file_id, 2 bytes> 30189251Ssam * GET RESPONSE: C0 00 00 <len> 31189251Ssam * RUN GSM ALG: 88 00 00 00 <RAND len = 10> 32189251Ssam * RUN UMTS ALG: 88 00 81 <len=0x22> data: 0x10 | RAND | 0x10 | AUTN 33189251Ssam * P1 = ID of alg in card 34189251Ssam * P2 = ID of secret key 35189251Ssam * READ BINARY: B0 <offset high> <offset low> <len> 36189251Ssam * READ RECORD: B2 <record number> <mode> <len> 37189251Ssam * P2 (mode) = '02' (next record), '03' (previous record), 38189251Ssam * '04' (absolute mode) 39189251Ssam * VERIFY CHV: 20 00 <CHV number> 08 40189251Ssam * CHANGE CHV: 24 00 <CHV number> 10 41189251Ssam * DISABLE CHV: 26 00 01 08 42189251Ssam * ENABLE CHV: 28 00 01 08 43189251Ssam * UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10 44189251Ssam * SLEEP: FA 00 00 00 45189251Ssam */ 46189251Ssam 47189251Ssam/* GSM SIM commands */ 48189251Ssam#define SIM_CMD_SELECT 0xa0, 0xa4, 0x00, 0x00, 0x02 49189251Ssam#define SIM_CMD_RUN_GSM_ALG 0xa0, 0x88, 0x00, 0x00, 0x10 50189251Ssam#define SIM_CMD_GET_RESPONSE 0xa0, 0xc0, 0x00, 0x00 51189251Ssam#define SIM_CMD_READ_BIN 0xa0, 0xb0, 0x00, 0x00 52189251Ssam#define SIM_CMD_READ_RECORD 0xa0, 0xb2, 0x00, 0x00 53189251Ssam#define SIM_CMD_VERIFY_CHV1 0xa0, 0x20, 0x00, 0x01, 0x08 54189251Ssam 55189251Ssam/* USIM commands */ 56189251Ssam#define USIM_CLA 0x00 57189251Ssam#define USIM_CMD_RUN_UMTS_ALG 0x00, 0x88, 0x00, 0x81, 0x22 58189251Ssam#define USIM_CMD_GET_RESPONSE 0x00, 0xc0, 0x00, 0x00 59189251Ssam 60189251Ssam#define SIM_RECORD_MODE_ABSOLUTE 0x04 61189251Ssam 62189251Ssam#define USIM_FSP_TEMPL_TAG 0x62 63189251Ssam 64189251Ssam#define USIM_TLV_FILE_DESC 0x82 65189251Ssam#define USIM_TLV_FILE_ID 0x83 66189251Ssam#define USIM_TLV_DF_NAME 0x84 67189251Ssam#define USIM_TLV_PROPR_INFO 0xA5 68189251Ssam#define USIM_TLV_LIFE_CYCLE_STATUS 0x8A 69189251Ssam#define USIM_TLV_FILE_SIZE 0x80 70189251Ssam#define USIM_TLV_TOTAL_FILE_SIZE 0x81 71189251Ssam#define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6 72189251Ssam#define USIM_TLV_SHORT_FILE_ID 0x88 73252726Srpaulo#define USIM_TLV_SECURITY_ATTR_8B 0x8B 74252726Srpaulo#define USIM_TLV_SECURITY_ATTR_8C 0x8C 75252726Srpaulo#define USIM_TLV_SECURITY_ATTR_AB 0xAB 76189251Ssam 77189251Ssam#define USIM_PS_DO_TAG 0x90 78189251Ssam 79189251Ssam#define AKA_RAND_LEN 16 80189251Ssam#define AKA_AUTN_LEN 16 81189251Ssam#define AKA_AUTS_LEN 14 82189251Ssam#define RES_MAX_LEN 16 83189251Ssam#define IK_LEN 16 84189251Ssam#define CK_LEN 16 85189251Ssam 86189251Ssam 87252726Srpaulo/* GSM files 88252726Srpaulo * File type in first octet: 89252726Srpaulo * 3F = Master File 90252726Srpaulo * 7F = Dedicated File 91252726Srpaulo * 2F = Elementary File under the Master File 92252726Srpaulo * 6F = Elementary File under a Dedicated File 93252726Srpaulo */ 94252726Srpaulo#define SCARD_FILE_MF 0x3F00 95252726Srpaulo#define SCARD_FILE_GSM_DF 0x7F20 96252726Srpaulo#define SCARD_FILE_UMTS_DF 0x7F50 97252726Srpaulo#define SCARD_FILE_GSM_EF_IMSI 0x6F07 98252726Srpaulo#define SCARD_FILE_GSM_EF_AD 0x6FAD 99252726Srpaulo#define SCARD_FILE_EF_DIR 0x2F00 100252726Srpaulo#define SCARD_FILE_EF_ICCID 0x2FE2 101252726Srpaulo#define SCARD_FILE_EF_CK 0x6FE1 102252726Srpaulo#define SCARD_FILE_EF_IK 0x6FE2 103252726Srpaulo 104252726Srpaulo#define SCARD_CHV1_OFFSET 13 105252726Srpaulo#define SCARD_CHV1_FLAG 0x80 106252726Srpaulo 107252726Srpaulo 108189251Ssamtypedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types; 109189251Ssam 110189251Ssamstruct scard_data { 111189251Ssam SCARDCONTEXT ctx; 112189251Ssam SCARDHANDLE card; 113189251Ssam DWORD protocol; 114189251Ssam sim_types sim_type; 115189251Ssam int pin1_required; 116189251Ssam}; 117189251Ssam 118189251Ssam#ifdef __MINGW32_VERSION 119189251Ssam/* MinGW does not yet support WinScard, so load the needed functions 120189251Ssam * dynamically from winscard.dll for now. */ 121189251Ssam 122189251Ssamstatic HINSTANCE dll = NULL; /* winscard.dll */ 123189251Ssam 124189251Ssamstatic const SCARD_IO_REQUEST *dll_g_rgSCardT0Pci, *dll_g_rgSCardT1Pci; 125189251Ssam#undef SCARD_PCI_T0 126189251Ssam#define SCARD_PCI_T0 (dll_g_rgSCardT0Pci) 127189251Ssam#undef SCARD_PCI_T1 128189251Ssam#define SCARD_PCI_T1 (dll_g_rgSCardT1Pci) 129189251Ssam 130189251Ssam 131189251Ssamstatic WINSCARDAPI LONG WINAPI 132189251Ssam(*dll_SCardEstablishContext)(IN DWORD dwScope, 133189251Ssam IN LPCVOID pvReserved1, 134189251Ssam IN LPCVOID pvReserved2, 135189251Ssam OUT LPSCARDCONTEXT phContext); 136189251Ssam#define SCardEstablishContext dll_SCardEstablishContext 137189251Ssam 138189251Ssamstatic long (*dll_SCardReleaseContext)(long hContext); 139189251Ssam#define SCardReleaseContext dll_SCardReleaseContext 140189251Ssam 141189251Ssamstatic WINSCARDAPI LONG WINAPI 142189251Ssam(*dll_SCardListReadersA)(IN SCARDCONTEXT hContext, 143189251Ssam IN LPCSTR mszGroups, 144189251Ssam OUT LPSTR mszReaders, 145189251Ssam IN OUT LPDWORD pcchReaders); 146189251Ssam#undef SCardListReaders 147189251Ssam#define SCardListReaders dll_SCardListReadersA 148189251Ssam 149189251Ssamstatic WINSCARDAPI LONG WINAPI 150189251Ssam(*dll_SCardConnectA)(IN SCARDCONTEXT hContext, 151189251Ssam IN LPCSTR szReader, 152189251Ssam IN DWORD dwShareMode, 153189251Ssam IN DWORD dwPreferredProtocols, 154189251Ssam OUT LPSCARDHANDLE phCard, 155189251Ssam OUT LPDWORD pdwActiveProtocol); 156189251Ssam#undef SCardConnect 157189251Ssam#define SCardConnect dll_SCardConnectA 158189251Ssam 159189251Ssamstatic WINSCARDAPI LONG WINAPI 160189251Ssam(*dll_SCardDisconnect)(IN SCARDHANDLE hCard, 161189251Ssam IN DWORD dwDisposition); 162189251Ssam#define SCardDisconnect dll_SCardDisconnect 163189251Ssam 164189251Ssamstatic WINSCARDAPI LONG WINAPI 165189251Ssam(*dll_SCardTransmit)(IN SCARDHANDLE hCard, 166189251Ssam IN LPCSCARD_IO_REQUEST pioSendPci, 167189251Ssam IN LPCBYTE pbSendBuffer, 168189251Ssam IN DWORD cbSendLength, 169189251Ssam IN OUT LPSCARD_IO_REQUEST pioRecvPci, 170189251Ssam OUT LPBYTE pbRecvBuffer, 171189251Ssam IN OUT LPDWORD pcbRecvLength); 172189251Ssam#define SCardTransmit dll_SCardTransmit 173189251Ssam 174189251Ssamstatic WINSCARDAPI LONG WINAPI 175189251Ssam(*dll_SCardBeginTransaction)(IN SCARDHANDLE hCard); 176189251Ssam#define SCardBeginTransaction dll_SCardBeginTransaction 177189251Ssam 178189251Ssamstatic WINSCARDAPI LONG WINAPI 179189251Ssam(*dll_SCardEndTransaction)(IN SCARDHANDLE hCard, IN DWORD dwDisposition); 180189251Ssam#define SCardEndTransaction dll_SCardEndTransaction 181189251Ssam 182189251Ssam 183189251Ssamstatic int mingw_load_symbols(void) 184189251Ssam{ 185189251Ssam char *sym; 186189251Ssam 187189251Ssam if (dll) 188189251Ssam return 0; 189189251Ssam 190189251Ssam dll = LoadLibrary("winscard"); 191189251Ssam if (dll == NULL) { 192189251Ssam wpa_printf(MSG_DEBUG, "WinSCard: Could not load winscard.dll " 193189251Ssam "library"); 194189251Ssam return -1; 195189251Ssam } 196189251Ssam 197189251Ssam#define LOADSYM(s) \ 198189251Ssam sym = #s; \ 199189251Ssam dll_ ## s = (void *) GetProcAddress(dll, sym); \ 200189251Ssam if (dll_ ## s == NULL) \ 201189251Ssam goto fail; 202189251Ssam 203189251Ssam LOADSYM(SCardEstablishContext); 204189251Ssam LOADSYM(SCardReleaseContext); 205189251Ssam LOADSYM(SCardListReadersA); 206189251Ssam LOADSYM(SCardConnectA); 207189251Ssam LOADSYM(SCardDisconnect); 208189251Ssam LOADSYM(SCardTransmit); 209189251Ssam LOADSYM(SCardBeginTransaction); 210189251Ssam LOADSYM(SCardEndTransaction); 211189251Ssam LOADSYM(g_rgSCardT0Pci); 212189251Ssam LOADSYM(g_rgSCardT1Pci); 213189251Ssam 214189251Ssam#undef LOADSYM 215189251Ssam 216189251Ssam return 0; 217189251Ssam 218189251Ssamfail: 219189251Ssam wpa_printf(MSG_DEBUG, "WinSCard: Could not get address for %s from " 220189251Ssam "winscard.dll", sym); 221189251Ssam FreeLibrary(dll); 222189251Ssam dll = NULL; 223189251Ssam return -1; 224189251Ssam} 225189251Ssam 226189251Ssam 227189251Ssamstatic void mingw_unload_symbols(void) 228189251Ssam{ 229189251Ssam if (dll == NULL) 230189251Ssam return; 231189251Ssam 232189251Ssam FreeLibrary(dll); 233189251Ssam dll = NULL; 234189251Ssam} 235189251Ssam 236189251Ssam#else /* __MINGW32_VERSION */ 237189251Ssam 238189251Ssam#define mingw_load_symbols() 0 239189251Ssam#define mingw_unload_symbols() do { } while (0) 240189251Ssam 241189251Ssam#endif /* __MINGW32_VERSION */ 242189251Ssam 243189251Ssam 244189251Ssamstatic int _scard_select_file(struct scard_data *scard, unsigned short file_id, 245189251Ssam unsigned char *buf, size_t *buf_len, 246189251Ssam sim_types sim_type, unsigned char *aid, 247189251Ssam size_t aidlen); 248189251Ssamstatic int scard_select_file(struct scard_data *scard, unsigned short file_id, 249189251Ssam unsigned char *buf, size_t *buf_len); 250189251Ssamstatic int scard_verify_pin(struct scard_data *scard, const char *pin); 251189251Ssamstatic int scard_get_record_len(struct scard_data *scard, 252189251Ssam unsigned char recnum, unsigned char mode); 253189251Ssamstatic int scard_read_record(struct scard_data *scard, 254189251Ssam unsigned char *data, size_t len, 255189251Ssam unsigned char recnum, unsigned char mode); 256189251Ssam 257189251Ssam 258189251Ssamstatic int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, 259189251Ssam int *ps_do, int *file_len) 260189251Ssam{ 261252726Srpaulo unsigned char *pos, *end; 262189251Ssam 263252726Srpaulo if (ps_do) 264252726Srpaulo *ps_do = -1; 265252726Srpaulo if (file_len) 266252726Srpaulo *file_len = -1; 267189251Ssam 268252726Srpaulo pos = buf; 269252726Srpaulo end = pos + buf_len; 270252726Srpaulo if (*pos != USIM_FSP_TEMPL_TAG) { 271252726Srpaulo wpa_printf(MSG_DEBUG, "SCARD: file header did not " 272252726Srpaulo "start with FSP template tag"); 273252726Srpaulo return -1; 274252726Srpaulo } 275252726Srpaulo pos++; 276252726Srpaulo if (pos >= end) 277252726Srpaulo return -1; 278252726Srpaulo if ((pos + pos[0]) < end) 279252726Srpaulo end = pos + 1 + pos[0]; 280252726Srpaulo pos++; 281252726Srpaulo wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", 282252726Srpaulo pos, end - pos); 283189251Ssam 284252726Srpaulo while (pos + 1 < end) { 285252726Srpaulo wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d", 286252726Srpaulo pos[0], pos[1]); 287252726Srpaulo if (pos + 2 + pos[1] > end) 288252726Srpaulo break; 289189251Ssam 290252726Srpaulo switch (pos[0]) { 291252726Srpaulo case USIM_TLV_FILE_DESC: 292252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV", 293252726Srpaulo pos + 2, pos[1]); 294252726Srpaulo break; 295252726Srpaulo case USIM_TLV_FILE_ID: 296252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV", 297252726Srpaulo pos + 2, pos[1]); 298252726Srpaulo break; 299252726Srpaulo case USIM_TLV_DF_NAME: 300252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV", 301252726Srpaulo pos + 2, pos[1]); 302252726Srpaulo break; 303252726Srpaulo case USIM_TLV_PROPR_INFO: 304252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary " 305252726Srpaulo "information TLV", pos + 2, pos[1]); 306252726Srpaulo break; 307252726Srpaulo case USIM_TLV_LIFE_CYCLE_STATUS: 308252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status " 309252726Srpaulo "Integer TLV", pos + 2, pos[1]); 310252726Srpaulo break; 311252726Srpaulo case USIM_TLV_FILE_SIZE: 312252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV", 313252726Srpaulo pos + 2, pos[1]); 314252726Srpaulo if ((pos[1] == 1 || pos[1] == 2) && file_len) { 315189251Ssam if (pos[1] == 1) 316189251Ssam *file_len = (int) pos[2]; 317189251Ssam else 318189251Ssam *file_len = ((int) pos[2] << 8) | 319189251Ssam (int) pos[3]; 320189251Ssam wpa_printf(MSG_DEBUG, "SCARD: file_size=%d", 321189251Ssam *file_len); 322189251Ssam } 323252726Srpaulo break; 324252726Srpaulo case USIM_TLV_TOTAL_FILE_SIZE: 325252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV", 326252726Srpaulo pos + 2, pos[1]); 327252726Srpaulo break; 328252726Srpaulo case USIM_TLV_PIN_STATUS_TEMPLATE: 329252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template " 330252726Srpaulo "DO TLV", pos + 2, pos[1]); 331252726Srpaulo if (pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG && 332189251Ssam pos[3] >= 1 && ps_do) { 333189251Ssam wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x", 334189251Ssam pos[4]); 335189251Ssam *ps_do = (int) pos[4]; 336189251Ssam } 337252726Srpaulo break; 338252726Srpaulo case USIM_TLV_SHORT_FILE_ID: 339252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File " 340252726Srpaulo "Identifier (SFI) TLV", pos + 2, pos[1]); 341252726Srpaulo break; 342252726Srpaulo case USIM_TLV_SECURITY_ATTR_8B: 343252726Srpaulo case USIM_TLV_SECURITY_ATTR_8C: 344252726Srpaulo case USIM_TLV_SECURITY_ATTR_AB: 345252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute " 346252726Srpaulo "TLV", pos + 2, pos[1]); 347252726Srpaulo break; 348252726Srpaulo default: 349252726Srpaulo wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV", 350252726Srpaulo pos, 2 + pos[1]); 351252726Srpaulo break; 352252726Srpaulo } 353189251Ssam 354252726Srpaulo pos += 2 + pos[1]; 355189251Ssam 356252726Srpaulo if (pos == end) 357252726Srpaulo return 0; 358252726Srpaulo } 359252726Srpaulo return -1; 360189251Ssam} 361189251Ssam 362189251Ssam 363189251Ssamstatic int scard_pin_needed(struct scard_data *scard, 364189251Ssam unsigned char *hdr, size_t hlen) 365189251Ssam{ 366189251Ssam if (scard->sim_type == SCARD_GSM_SIM) { 367189251Ssam if (hlen > SCARD_CHV1_OFFSET && 368189251Ssam !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG)) 369189251Ssam return 1; 370189251Ssam return 0; 371189251Ssam } 372189251Ssam 373189251Ssam if (scard->sim_type == SCARD_USIM) { 374189251Ssam int ps_do; 375189251Ssam if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL)) 376189251Ssam return -1; 377189251Ssam /* TODO: there could be more than one PS_DO entry because of 378189251Ssam * multiple PINs in key reference.. */ 379189251Ssam if (ps_do > 0 && (ps_do & 0x80)) 380189251Ssam return 1; 381189251Ssam return 0; 382189251Ssam } 383189251Ssam 384189251Ssam return -1; 385189251Ssam} 386189251Ssam 387189251Ssam 388189251Ssamstatic int scard_get_aid(struct scard_data *scard, unsigned char *aid, 389189251Ssam size_t maxlen) 390189251Ssam{ 391189251Ssam int rlen, rec; 392189251Ssam struct efdir { 393189251Ssam unsigned char appl_template_tag; /* 0x61 */ 394189251Ssam unsigned char appl_template_len; 395189251Ssam unsigned char appl_id_tag; /* 0x4f */ 396189251Ssam unsigned char aid_len; 397189251Ssam unsigned char rid[5]; 398189251Ssam unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ 399189251Ssam } *efdir; 400252726Srpaulo unsigned char buf[127]; 401189251Ssam size_t blen; 402189251Ssam 403189251Ssam efdir = (struct efdir *) buf; 404189251Ssam blen = sizeof(buf); 405189251Ssam if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) { 406189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR"); 407189251Ssam return -1; 408189251Ssam } 409189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR select", buf, blen); 410189251Ssam 411189251Ssam for (rec = 1; rec < 10; rec++) { 412189251Ssam rlen = scard_get_record_len(scard, rec, 413189251Ssam SIM_RECORD_MODE_ABSOLUTE); 414189251Ssam if (rlen < 0) { 415189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to get EF_DIR " 416189251Ssam "record length"); 417189251Ssam return -1; 418189251Ssam } 419189251Ssam blen = sizeof(buf); 420189251Ssam if (rlen > (int) blen) { 421189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Too long EF_DIR record"); 422189251Ssam return -1; 423189251Ssam } 424189251Ssam if (scard_read_record(scard, buf, rlen, rec, 425189251Ssam SIM_RECORD_MODE_ABSOLUTE) < 0) { 426189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to read " 427189251Ssam "EF_DIR record %d", rec); 428189251Ssam return -1; 429189251Ssam } 430189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR record", buf, rlen); 431189251Ssam 432189251Ssam if (efdir->appl_template_tag != 0x61) { 433189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Unexpected application " 434189251Ssam "template tag 0x%x", 435189251Ssam efdir->appl_template_tag); 436189251Ssam continue; 437189251Ssam } 438189251Ssam 439189251Ssam if (efdir->appl_template_len > rlen - 2) { 440189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Too long application " 441189251Ssam "template (len=%d rlen=%d)", 442189251Ssam efdir->appl_template_len, rlen); 443189251Ssam continue; 444189251Ssam } 445189251Ssam 446189251Ssam if (efdir->appl_id_tag != 0x4f) { 447189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Unexpected application " 448189251Ssam "identifier tag 0x%x", efdir->appl_id_tag); 449189251Ssam continue; 450189251Ssam } 451189251Ssam 452189251Ssam if (efdir->aid_len < 1 || efdir->aid_len > 16) { 453189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %d", 454189251Ssam efdir->aid_len); 455189251Ssam continue; 456189251Ssam } 457189251Ssam 458189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record", 459189251Ssam efdir->rid, efdir->aid_len); 460189251Ssam 461189251Ssam if (efdir->appl_code[0] == 0x10 && 462189251Ssam efdir->appl_code[1] == 0x02) { 463189251Ssam wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app found from " 464189251Ssam "EF_DIR record %d", rec); 465189251Ssam break; 466189251Ssam } 467189251Ssam } 468189251Ssam 469189251Ssam if (rec >= 10) { 470189251Ssam wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app not found " 471189251Ssam "from EF_DIR records"); 472189251Ssam return -1; 473189251Ssam } 474189251Ssam 475189251Ssam if (efdir->aid_len > maxlen) { 476189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Too long AID"); 477189251Ssam return -1; 478189251Ssam } 479189251Ssam 480189251Ssam os_memcpy(aid, efdir->rid, efdir->aid_len); 481189251Ssam 482189251Ssam return efdir->aid_len; 483189251Ssam} 484189251Ssam 485189251Ssam 486189251Ssam/** 487189251Ssam * scard_init - Initialize SIM/USIM connection using PC/SC 488189251Ssam * @sim_type: Allowed SIM types (SIM, USIM, or both) 489252726Srpaulo * @reader: Reader name prefix to search for 490189251Ssam * Returns: Pointer to private data structure, or %NULL on failure 491189251Ssam * 492189251Ssam * This function is used to initialize SIM/USIM connection. PC/SC is used to 493189251Ssam * open connection to the SIM/USIM card and the card is verified to support the 494189251Ssam * selected sim_type. In addition, local flag is set if a PIN is needed to 495189251Ssam * access some of the card functions. Once the connection is not needed 496189251Ssam * anymore, scard_deinit() can be used to close it. 497189251Ssam */ 498252726Srpaulostruct scard_data * scard_init(scard_sim_type sim_type, const char *reader) 499189251Ssam{ 500189251Ssam long ret; 501252726Srpaulo unsigned long len, pos; 502189251Ssam struct scard_data *scard; 503189251Ssam#ifdef CONFIG_NATIVE_WINDOWS 504189251Ssam TCHAR *readers = NULL; 505189251Ssam#else /* CONFIG_NATIVE_WINDOWS */ 506189251Ssam char *readers = NULL; 507189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */ 508189251Ssam unsigned char buf[100]; 509189251Ssam size_t blen; 510189251Ssam int transaction = 0; 511189251Ssam int pin_needed; 512189251Ssam 513189251Ssam wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface"); 514189251Ssam if (mingw_load_symbols()) 515189251Ssam return NULL; 516189251Ssam scard = os_zalloc(sizeof(*scard)); 517189251Ssam if (scard == NULL) 518189251Ssam return NULL; 519189251Ssam 520189251Ssam ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, 521189251Ssam &scard->ctx); 522189251Ssam if (ret != SCARD_S_SUCCESS) { 523189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card " 524189251Ssam "context (err=%ld)", ret); 525189251Ssam goto failed; 526189251Ssam } 527189251Ssam 528189251Ssam ret = SCardListReaders(scard->ctx, NULL, NULL, &len); 529189251Ssam if (ret != SCARD_S_SUCCESS) { 530189251Ssam wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed " 531189251Ssam "(err=%ld)", ret); 532189251Ssam goto failed; 533189251Ssam } 534189251Ssam 535189251Ssam#ifdef UNICODE 536189251Ssam len *= 2; 537189251Ssam#endif /* UNICODE */ 538189251Ssam readers = os_malloc(len); 539189251Ssam if (readers == NULL) { 540189251Ssam wpa_printf(MSG_INFO, "SCARD: malloc failed\n"); 541189251Ssam goto failed; 542189251Ssam } 543189251Ssam 544189251Ssam ret = SCardListReaders(scard->ctx, NULL, readers, &len); 545189251Ssam if (ret != SCARD_S_SUCCESS) { 546189251Ssam wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) " 547189251Ssam "(err=%ld)", ret); 548189251Ssam goto failed; 549189251Ssam } 550189251Ssam if (len < 3) { 551189251Ssam wpa_printf(MSG_WARNING, "SCARD: No smart card readers " 552189251Ssam "available."); 553189251Ssam goto failed; 554189251Ssam } 555252726Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "SCARD: Readers", (u8 *) readers, len); 556252726Srpaulo /* 557252726Srpaulo * readers is a list of available readers. The last entry is terminated 558252726Srpaulo * with double null. 559252726Srpaulo */ 560252726Srpaulo pos = 0; 561189251Ssam#ifdef UNICODE 562252726Srpaulo /* TODO */ 563189251Ssam#else /* UNICODE */ 564252726Srpaulo while (pos < len) { 565252726Srpaulo if (reader == NULL || 566252726Srpaulo os_strncmp(&readers[pos], reader, os_strlen(reader)) == 0) 567252726Srpaulo break; 568252726Srpaulo while (pos < len && readers[pos]) 569252726Srpaulo pos++; 570252726Srpaulo pos++; /* skip separating null */ 571252726Srpaulo if (pos < len && readers[pos] == '\0') 572252726Srpaulo pos = len; /* double null terminates list */ 573252726Srpaulo } 574189251Ssam#endif /* UNICODE */ 575252726Srpaulo if (pos >= len) { 576252726Srpaulo wpa_printf(MSG_WARNING, "SCARD: No reader with prefix '%s' " 577252726Srpaulo "found", reader); 578252726Srpaulo goto failed; 579252726Srpaulo } 580189251Ssam 581252726Srpaulo#ifdef UNICODE 582252726Srpaulo wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]); 583252726Srpaulo#else /* UNICODE */ 584252726Srpaulo wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]); 585252726Srpaulo#endif /* UNICODE */ 586252726Srpaulo 587252726Srpaulo ret = SCardConnect(scard->ctx, &readers[pos], SCARD_SHARE_SHARED, 588252726Srpaulo SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, 589252726Srpaulo &scard->card, &scard->protocol); 590189251Ssam if (ret != SCARD_S_SUCCESS) { 591189251Ssam if (ret == (long) SCARD_E_NO_SMARTCARD) 592189251Ssam wpa_printf(MSG_INFO, "No smart card inserted."); 593189251Ssam else 594189251Ssam wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret); 595189251Ssam goto failed; 596189251Ssam } 597189251Ssam 598189251Ssam os_free(readers); 599189251Ssam readers = NULL; 600189251Ssam 601189251Ssam wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)", 602189251Ssam (unsigned int) scard->card, scard->protocol, 603189251Ssam scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1"); 604189251Ssam 605189251Ssam ret = SCardBeginTransaction(scard->card); 606189251Ssam if (ret != SCARD_S_SUCCESS) { 607189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Could not begin transaction: " 608189251Ssam "0x%x", (unsigned int) ret); 609189251Ssam goto failed; 610189251Ssam } 611189251Ssam transaction = 1; 612189251Ssam 613189251Ssam blen = sizeof(buf); 614189251Ssam 615189251Ssam scard->sim_type = SCARD_GSM_SIM; 616189251Ssam if (sim_type == SCARD_USIM_ONLY || sim_type == SCARD_TRY_BOTH) { 617189251Ssam wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support"); 618189251Ssam if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen, 619189251Ssam SCARD_USIM, NULL, 0)) { 620189251Ssam wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported"); 621189251Ssam if (sim_type == SCARD_USIM_ONLY) 622189251Ssam goto failed; 623189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Trying to use GSM SIM"); 624189251Ssam scard->sim_type = SCARD_GSM_SIM; 625189251Ssam } else { 626189251Ssam wpa_printf(MSG_DEBUG, "SCARD: USIM is supported"); 627189251Ssam scard->sim_type = SCARD_USIM; 628189251Ssam } 629189251Ssam } 630189251Ssam 631189251Ssam if (scard->sim_type == SCARD_GSM_SIM) { 632189251Ssam blen = sizeof(buf); 633189251Ssam if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) { 634189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF"); 635189251Ssam goto failed; 636189251Ssam } 637189251Ssam 638189251Ssam blen = sizeof(buf); 639189251Ssam if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) { 640189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF"); 641189251Ssam goto failed; 642189251Ssam } 643189251Ssam } else { 644189251Ssam unsigned char aid[32]; 645189251Ssam int aid_len; 646189251Ssam 647189251Ssam aid_len = scard_get_aid(scard, aid, sizeof(aid)); 648189251Ssam if (aid_len < 0) { 649189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to find AID for " 650189251Ssam "3G USIM app - try to use standard 3G RID"); 651189251Ssam os_memcpy(aid, "\xa0\x00\x00\x00\x87", 5); 652189251Ssam aid_len = 5; 653189251Ssam } 654189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: 3G USIM AID", aid, aid_len); 655189251Ssam 656189251Ssam /* Select based on AID = 3G RID from EF_DIR. This is usually 657189251Ssam * starting with A0 00 00 00 87. */ 658189251Ssam blen = sizeof(buf); 659189251Ssam if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type, 660189251Ssam aid, aid_len)) { 661189251Ssam wpa_printf(MSG_INFO, "SCARD: Failed to read 3G USIM " 662189251Ssam "app"); 663189251Ssam wpa_hexdump(MSG_INFO, "SCARD: 3G USIM AID", 664189251Ssam aid, aid_len); 665189251Ssam goto failed; 666189251Ssam } 667189251Ssam } 668189251Ssam 669189251Ssam /* Verify whether CHV1 (PIN1) is needed to access the card. */ 670189251Ssam pin_needed = scard_pin_needed(scard, buf, blen); 671189251Ssam if (pin_needed < 0) { 672189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to determine whether PIN " 673189251Ssam "is needed"); 674189251Ssam goto failed; 675189251Ssam } 676189251Ssam if (pin_needed) { 677189251Ssam scard->pin1_required = 1; 678252726Srpaulo wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry " 679252726Srpaulo "counter=%d)", scard_get_pin_retry_counter(scard)); 680189251Ssam } 681189251Ssam 682189251Ssam ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); 683189251Ssam if (ret != SCARD_S_SUCCESS) { 684189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Could not end transaction: " 685189251Ssam "0x%x", (unsigned int) ret); 686189251Ssam } 687189251Ssam 688189251Ssam return scard; 689189251Ssam 690189251Ssamfailed: 691189251Ssam if (transaction) 692189251Ssam SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); 693189251Ssam os_free(readers); 694189251Ssam scard_deinit(scard); 695189251Ssam return NULL; 696189251Ssam} 697189251Ssam 698189251Ssam 699189251Ssam/** 700189251Ssam * scard_set_pin - Set PIN (CHV1/PIN1) code for accessing SIM/USIM commands 701189251Ssam * @scard: Pointer to private data from scard_init() 702189251Ssam * @pin: PIN code as an ASCII string (e.g., "1234") 703189251Ssam * Returns: 0 on success, -1 on failure 704189251Ssam */ 705189251Ssamint scard_set_pin(struct scard_data *scard, const char *pin) 706189251Ssam{ 707189251Ssam if (scard == NULL) 708189251Ssam return -1; 709189251Ssam 710189251Ssam /* Verify whether CHV1 (PIN1) is needed to access the card. */ 711189251Ssam if (scard->pin1_required) { 712189251Ssam if (pin == NULL) { 713189251Ssam wpa_printf(MSG_DEBUG, "No PIN configured for SIM " 714189251Ssam "access"); 715189251Ssam return -1; 716189251Ssam } 717189251Ssam if (scard_verify_pin(scard, pin)) { 718189251Ssam wpa_printf(MSG_INFO, "PIN verification failed for " 719189251Ssam "SIM access"); 720189251Ssam return -1; 721189251Ssam } 722189251Ssam } 723189251Ssam 724189251Ssam return 0; 725189251Ssam} 726189251Ssam 727189251Ssam 728189251Ssam/** 729189251Ssam * scard_deinit - Deinitialize SIM/USIM connection 730189251Ssam * @scard: Pointer to private data from scard_init() 731189251Ssam * 732189251Ssam * This function closes the SIM/USIM connect opened with scard_init(). 733189251Ssam */ 734189251Ssamvoid scard_deinit(struct scard_data *scard) 735189251Ssam{ 736189251Ssam long ret; 737189251Ssam 738189251Ssam if (scard == NULL) 739189251Ssam return; 740189251Ssam 741189251Ssam wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface"); 742189251Ssam if (scard->card) { 743189251Ssam ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD); 744189251Ssam if (ret != SCARD_S_SUCCESS) { 745189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect " 746189251Ssam "smart card (err=%ld)", ret); 747189251Ssam } 748189251Ssam } 749189251Ssam 750189251Ssam if (scard->ctx) { 751189251Ssam ret = SCardReleaseContext(scard->ctx); 752189251Ssam if (ret != SCARD_S_SUCCESS) { 753189251Ssam wpa_printf(MSG_DEBUG, "Failed to release smart card " 754189251Ssam "context (err=%ld)", ret); 755189251Ssam } 756189251Ssam } 757189251Ssam os_free(scard); 758189251Ssam mingw_unload_symbols(); 759189251Ssam} 760189251Ssam 761189251Ssam 762189251Ssamstatic long scard_transmit(struct scard_data *scard, 763189251Ssam unsigned char *_send, size_t send_len, 764189251Ssam unsigned char *_recv, size_t *recv_len) 765189251Ssam{ 766189251Ssam long ret; 767189251Ssam unsigned long rlen; 768189251Ssam 769189251Ssam wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send", 770189251Ssam _send, send_len); 771189251Ssam rlen = *recv_len; 772189251Ssam ret = SCardTransmit(scard->card, 773189251Ssam scard->protocol == SCARD_PROTOCOL_T1 ? 774189251Ssam SCARD_PCI_T1 : SCARD_PCI_T0, 775189251Ssam _send, (unsigned long) send_len, 776189251Ssam NULL, _recv, &rlen); 777189251Ssam *recv_len = rlen; 778189251Ssam if (ret == SCARD_S_SUCCESS) { 779189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv", 780189251Ssam _recv, rlen); 781189251Ssam } else { 782189251Ssam wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " 783189251Ssam "(err=0x%lx)", ret); 784189251Ssam } 785189251Ssam return ret; 786189251Ssam} 787189251Ssam 788189251Ssam 789189251Ssamstatic int _scard_select_file(struct scard_data *scard, unsigned short file_id, 790189251Ssam unsigned char *buf, size_t *buf_len, 791189251Ssam sim_types sim_type, unsigned char *aid, 792189251Ssam size_t aidlen) 793189251Ssam{ 794189251Ssam long ret; 795189251Ssam unsigned char resp[3]; 796189251Ssam unsigned char cmd[50] = { SIM_CMD_SELECT }; 797189251Ssam int cmdlen; 798189251Ssam unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; 799189251Ssam size_t len, rlen; 800189251Ssam 801189251Ssam if (sim_type == SCARD_USIM) { 802189251Ssam cmd[0] = USIM_CLA; 803189251Ssam cmd[3] = 0x04; 804189251Ssam get_resp[0] = USIM_CLA; 805189251Ssam } 806189251Ssam 807189251Ssam wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id); 808189251Ssam if (aid) { 809189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: select file by AID", 810189251Ssam aid, aidlen); 811189251Ssam if (5 + aidlen > sizeof(cmd)) 812189251Ssam return -1; 813189251Ssam cmd[2] = 0x04; /* Select by AID */ 814189251Ssam cmd[4] = aidlen; /* len */ 815189251Ssam os_memcpy(cmd + 5, aid, aidlen); 816189251Ssam cmdlen = 5 + aidlen; 817189251Ssam } else { 818189251Ssam cmd[5] = file_id >> 8; 819189251Ssam cmd[6] = file_id & 0xff; 820189251Ssam cmdlen = 7; 821189251Ssam } 822189251Ssam len = sizeof(resp); 823189251Ssam ret = scard_transmit(scard, cmd, cmdlen, resp, &len); 824189251Ssam if (ret != SCARD_S_SUCCESS) { 825189251Ssam wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " 826189251Ssam "(err=0x%lx)", ret); 827189251Ssam return -1; 828189251Ssam } 829189251Ssam 830189251Ssam if (len != 2) { 831189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected resp len " 832189251Ssam "%d (expected 2)", (int) len); 833189251Ssam return -1; 834189251Ssam } 835189251Ssam 836189251Ssam if (resp[0] == 0x98 && resp[1] == 0x04) { 837189251Ssam /* Security status not satisfied (PIN_WLAN) */ 838189251Ssam wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied " 839189251Ssam "(PIN_WLAN)"); 840189251Ssam return -1; 841189251Ssam } 842189251Ssam 843189251Ssam if (resp[0] == 0x6e) { 844189251Ssam wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported"); 845189251Ssam return -1; 846189251Ssam } 847189251Ssam 848189251Ssam if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) { 849189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x " 850189251Ssam "(expected 0x61, 0x6c, or 0x9f)", resp[0]); 851189251Ssam return -1; 852189251Ssam } 853189251Ssam /* Normal ending of command; resp[1] bytes available */ 854189251Ssam get_resp[4] = resp[1]; 855189251Ssam wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)", 856189251Ssam resp[1]); 857189251Ssam 858189251Ssam rlen = *buf_len; 859189251Ssam ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen); 860189251Ssam if (ret == SCARD_S_SUCCESS) { 861189251Ssam *buf_len = resp[1] < rlen ? resp[1] : rlen; 862189251Ssam return 0; 863189251Ssam } 864189251Ssam 865189251Ssam wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret); 866189251Ssam return -1; 867189251Ssam} 868189251Ssam 869189251Ssam 870189251Ssamstatic int scard_select_file(struct scard_data *scard, unsigned short file_id, 871189251Ssam unsigned char *buf, size_t *buf_len) 872189251Ssam{ 873189251Ssam return _scard_select_file(scard, file_id, buf, buf_len, 874189251Ssam scard->sim_type, NULL, 0); 875189251Ssam} 876189251Ssam 877189251Ssam 878189251Ssamstatic int scard_get_record_len(struct scard_data *scard, unsigned char recnum, 879189251Ssam unsigned char mode) 880189251Ssam{ 881189251Ssam unsigned char buf[255]; 882189251Ssam unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; 883189251Ssam size_t blen; 884189251Ssam long ret; 885189251Ssam 886189251Ssam if (scard->sim_type == SCARD_USIM) 887189251Ssam cmd[0] = USIM_CLA; 888189251Ssam cmd[2] = recnum; 889189251Ssam cmd[3] = mode; 890189251Ssam cmd[4] = sizeof(buf); 891189251Ssam 892189251Ssam blen = sizeof(buf); 893189251Ssam ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); 894189251Ssam if (ret != SCARD_S_SUCCESS) { 895189251Ssam wpa_printf(MSG_DEBUG, "SCARD: failed to determine file " 896189251Ssam "length for record %d", recnum); 897189251Ssam return -1; 898189251Ssam } 899189251Ssam 900189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response", 901189251Ssam buf, blen); 902189251Ssam 903252726Srpaulo if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) { 904189251Ssam wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file " 905189251Ssam "length determination"); 906189251Ssam return -1; 907189251Ssam } 908189251Ssam 909189251Ssam return buf[1]; 910189251Ssam} 911189251Ssam 912189251Ssam 913189251Ssamstatic int scard_read_record(struct scard_data *scard, 914189251Ssam unsigned char *data, size_t len, 915189251Ssam unsigned char recnum, unsigned char mode) 916189251Ssam{ 917189251Ssam unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; 918189251Ssam size_t blen = len + 3; 919189251Ssam unsigned char *buf; 920189251Ssam long ret; 921189251Ssam 922189251Ssam if (scard->sim_type == SCARD_USIM) 923189251Ssam cmd[0] = USIM_CLA; 924189251Ssam cmd[2] = recnum; 925189251Ssam cmd[3] = mode; 926189251Ssam cmd[4] = len; 927189251Ssam 928189251Ssam buf = os_malloc(blen); 929189251Ssam if (buf == NULL) 930189251Ssam return -1; 931189251Ssam 932189251Ssam ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); 933189251Ssam if (ret != SCARD_S_SUCCESS) { 934189251Ssam os_free(buf); 935189251Ssam return -2; 936189251Ssam } 937189251Ssam if (blen != len + 2) { 938189251Ssam wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected " 939189251Ssam "length %ld (expected %ld)", 940189251Ssam (long) blen, (long) len + 2); 941189251Ssam os_free(buf); 942189251Ssam return -3; 943189251Ssam } 944189251Ssam 945189251Ssam if (buf[len] != 0x90 || buf[len + 1] != 0x00) { 946189251Ssam wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected " 947189251Ssam "status %02x %02x (expected 90 00)", 948189251Ssam buf[len], buf[len + 1]); 949189251Ssam os_free(buf); 950189251Ssam return -4; 951189251Ssam } 952189251Ssam 953189251Ssam os_memcpy(data, buf, len); 954189251Ssam os_free(buf); 955189251Ssam 956189251Ssam return 0; 957189251Ssam} 958189251Ssam 959189251Ssam 960189251Ssamstatic int scard_read_file(struct scard_data *scard, 961189251Ssam unsigned char *data, size_t len) 962189251Ssam{ 963189251Ssam unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ }; 964189251Ssam size_t blen = len + 3; 965189251Ssam unsigned char *buf; 966189251Ssam long ret; 967189251Ssam 968189251Ssam cmd[4] = len; 969189251Ssam 970189251Ssam buf = os_malloc(blen); 971189251Ssam if (buf == NULL) 972189251Ssam return -1; 973189251Ssam 974189251Ssam if (scard->sim_type == SCARD_USIM) 975189251Ssam cmd[0] = USIM_CLA; 976189251Ssam ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); 977189251Ssam if (ret != SCARD_S_SUCCESS) { 978189251Ssam os_free(buf); 979189251Ssam return -2; 980189251Ssam } 981189251Ssam if (blen != len + 2) { 982189251Ssam wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " 983189251Ssam "length %ld (expected %ld)", 984189251Ssam (long) blen, (long) len + 2); 985189251Ssam os_free(buf); 986189251Ssam return -3; 987189251Ssam } 988189251Ssam 989189251Ssam if (buf[len] != 0x90 || buf[len + 1] != 0x00) { 990189251Ssam wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " 991189251Ssam "status %02x %02x (expected 90 00)", 992189251Ssam buf[len], buf[len + 1]); 993189251Ssam os_free(buf); 994189251Ssam return -4; 995189251Ssam } 996189251Ssam 997189251Ssam os_memcpy(data, buf, len); 998189251Ssam os_free(buf); 999189251Ssam 1000189251Ssam return 0; 1001189251Ssam} 1002189251Ssam 1003189251Ssam 1004189251Ssamstatic int scard_verify_pin(struct scard_data *scard, const char *pin) 1005189251Ssam{ 1006189251Ssam long ret; 1007189251Ssam unsigned char resp[3]; 1008189251Ssam unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 }; 1009189251Ssam size_t len; 1010189251Ssam 1011189251Ssam wpa_printf(MSG_DEBUG, "SCARD: verifying PIN"); 1012189251Ssam 1013189251Ssam if (pin == NULL || os_strlen(pin) > 8) 1014189251Ssam return -1; 1015189251Ssam 1016189251Ssam if (scard->sim_type == SCARD_USIM) 1017189251Ssam cmd[0] = USIM_CLA; 1018189251Ssam os_memcpy(cmd + 5, pin, os_strlen(pin)); 1019189251Ssam os_memset(cmd + 5 + os_strlen(pin), 0xff, 8 - os_strlen(pin)); 1020189251Ssam 1021189251Ssam len = sizeof(resp); 1022189251Ssam ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); 1023189251Ssam if (ret != SCARD_S_SUCCESS) 1024189251Ssam return -2; 1025189251Ssam 1026189251Ssam if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) { 1027189251Ssam wpa_printf(MSG_WARNING, "SCARD: PIN verification failed"); 1028189251Ssam return -1; 1029189251Ssam } 1030189251Ssam 1031189251Ssam wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully"); 1032189251Ssam return 0; 1033189251Ssam} 1034189251Ssam 1035189251Ssam 1036252726Srpauloint scard_get_pin_retry_counter(struct scard_data *scard) 1037252726Srpaulo{ 1038252726Srpaulo long ret; 1039252726Srpaulo unsigned char resp[3]; 1040252726Srpaulo unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 }; 1041252726Srpaulo size_t len; 1042252726Srpaulo u16 val; 1043252726Srpaulo 1044252726Srpaulo wpa_printf(MSG_DEBUG, "SCARD: fetching PIN retry counter"); 1045252726Srpaulo 1046252726Srpaulo if (scard->sim_type == SCARD_USIM) 1047252726Srpaulo cmd[0] = USIM_CLA; 1048252726Srpaulo cmd[4] = 0; /* Empty data */ 1049252726Srpaulo 1050252726Srpaulo len = sizeof(resp); 1051252726Srpaulo ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); 1052252726Srpaulo if (ret != SCARD_S_SUCCESS) 1053252726Srpaulo return -2; 1054252726Srpaulo 1055252726Srpaulo if (len != 2) { 1056252726Srpaulo wpa_printf(MSG_WARNING, "SCARD: failed to fetch PIN retry " 1057252726Srpaulo "counter"); 1058252726Srpaulo return -1; 1059252726Srpaulo } 1060252726Srpaulo 1061252726Srpaulo val = WPA_GET_BE16(resp); 1062252726Srpaulo if (val == 0x63c0 || val == 0x6983) { 1063252726Srpaulo wpa_printf(MSG_DEBUG, "SCARD: PIN has been blocked"); 1064252726Srpaulo return 0; 1065252726Srpaulo } 1066252726Srpaulo 1067252726Srpaulo if (val >= 0x63c0 && val <= 0x63cf) 1068252726Srpaulo return val & 0x000f; 1069252726Srpaulo 1070252726Srpaulo wpa_printf(MSG_DEBUG, "SCARD: Unexpected PIN retry counter response " 1071252726Srpaulo "value 0x%x", val); 1072252726Srpaulo return 0; 1073252726Srpaulo} 1074252726Srpaulo 1075252726Srpaulo 1076189251Ssam/** 1077189251Ssam * scard_get_imsi - Read IMSI from SIM/USIM card 1078189251Ssam * @scard: Pointer to private data from scard_init() 1079189251Ssam * @imsi: Buffer for IMSI 1080189251Ssam * @len: Length of imsi buffer; set to IMSI length on success 1081189251Ssam * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file 1082189251Ssam * selection returns invalid result code, -3 if parsing FSP template file fails 1083189251Ssam * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set 1084189251Ssam * to needed length), -5 if reading IMSI file fails. 1085189251Ssam * 1086189251Ssam * This function can be used to read IMSI from the SIM/USIM card. If the IMSI 1087189251Ssam * file is PIN protected, scard_set_pin() must have been used to set the 1088189251Ssam * correct PIN code before calling scard_get_imsi(). 1089189251Ssam */ 1090189251Ssamint scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len) 1091189251Ssam{ 1092189251Ssam unsigned char buf[100]; 1093189251Ssam size_t blen, imsilen, i; 1094189251Ssam char *pos; 1095189251Ssam 1096189251Ssam wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI"); 1097189251Ssam blen = sizeof(buf); 1098189251Ssam if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen)) 1099189251Ssam return -1; 1100189251Ssam if (blen < 4) { 1101189251Ssam wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI " 1102189251Ssam "header (len=%ld)", (long) blen); 1103189251Ssam return -2; 1104189251Ssam } 1105189251Ssam 1106189251Ssam if (scard->sim_type == SCARD_GSM_SIM) { 1107189251Ssam blen = (buf[2] << 8) | buf[3]; 1108189251Ssam } else { 1109189251Ssam int file_size; 1110189251Ssam if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) 1111189251Ssam return -3; 1112189251Ssam blen = file_size; 1113189251Ssam } 1114189251Ssam if (blen < 2 || blen > sizeof(buf)) { 1115189251Ssam wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%ld", 1116189251Ssam (long) blen); 1117189251Ssam return -3; 1118189251Ssam } 1119189251Ssam 1120189251Ssam imsilen = (blen - 2) * 2 + 1; 1121189251Ssam wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%ld imsilen=%ld", 1122189251Ssam (long) blen, (long) imsilen); 1123189251Ssam if (blen < 2 || imsilen > *len) { 1124189251Ssam *len = imsilen; 1125189251Ssam return -4; 1126189251Ssam } 1127189251Ssam 1128189251Ssam if (scard_read_file(scard, buf, blen)) 1129189251Ssam return -5; 1130189251Ssam 1131189251Ssam pos = imsi; 1132189251Ssam *pos++ = '0' + (buf[1] >> 4 & 0x0f); 1133189251Ssam for (i = 2; i < blen; i++) { 1134189251Ssam unsigned char digit; 1135189251Ssam 1136189251Ssam digit = buf[i] & 0x0f; 1137189251Ssam if (digit < 10) 1138189251Ssam *pos++ = '0' + digit; 1139189251Ssam else 1140189251Ssam imsilen--; 1141189251Ssam 1142189251Ssam digit = buf[i] >> 4 & 0x0f; 1143189251Ssam if (digit < 10) 1144189251Ssam *pos++ = '0' + digit; 1145189251Ssam else 1146189251Ssam imsilen--; 1147189251Ssam } 1148189251Ssam *len = imsilen; 1149189251Ssam 1150189251Ssam return 0; 1151189251Ssam} 1152189251Ssam 1153189251Ssam 1154189251Ssam/** 1155252726Srpaulo * scard_get_mnc_len - Read length of MNC in the IMSI from SIM/USIM card 1156252726Srpaulo * @scard: Pointer to private data from scard_init() 1157252726Srpaulo * Returns: length (>0) on success, -1 if administrative data file cannot be 1158252726Srpaulo * selected, -2 if administrative data file selection returns invalid result 1159252726Srpaulo * code, -3 if parsing FSP template file fails (USIM only), -4 if length of 1160252726Srpaulo * the file is unexpected, -5 if reading file fails, -6 if MNC length is not 1161252726Srpaulo * in range (i.e. 2 or 3), -7 if MNC length is not available. 1162252726Srpaulo * 1163252726Srpaulo */ 1164252726Srpauloint scard_get_mnc_len(struct scard_data *scard) 1165252726Srpaulo{ 1166252726Srpaulo unsigned char buf[100]; 1167252726Srpaulo size_t blen; 1168252726Srpaulo int file_size; 1169252726Srpaulo 1170252726Srpaulo wpa_printf(MSG_DEBUG, "SCARD: reading MNC len from (GSM) EF-AD"); 1171252726Srpaulo blen = sizeof(buf); 1172252726Srpaulo if (scard_select_file(scard, SCARD_FILE_GSM_EF_AD, buf, &blen)) 1173252726Srpaulo return -1; 1174252726Srpaulo if (blen < 4) { 1175252726Srpaulo wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-AD " 1176252726Srpaulo "header (len=%ld)", (long) blen); 1177252726Srpaulo return -2; 1178252726Srpaulo } 1179252726Srpaulo 1180252726Srpaulo if (scard->sim_type == SCARD_GSM_SIM) { 1181252726Srpaulo file_size = (buf[2] << 8) | buf[3]; 1182252726Srpaulo } else { 1183252726Srpaulo if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) 1184252726Srpaulo return -3; 1185252726Srpaulo } 1186252726Srpaulo if (file_size == 3) { 1187252726Srpaulo wpa_printf(MSG_DEBUG, "SCARD: MNC length not available"); 1188252726Srpaulo return -7; 1189252726Srpaulo } 1190252726Srpaulo if (file_size < 4 || file_size > (int) sizeof(buf)) { 1191252726Srpaulo wpa_printf(MSG_DEBUG, "SCARD: invalid file length=%ld", 1192252726Srpaulo (long) file_size); 1193252726Srpaulo return -4; 1194252726Srpaulo } 1195252726Srpaulo 1196252726Srpaulo if (scard_read_file(scard, buf, file_size)) 1197252726Srpaulo return -5; 1198252726Srpaulo buf[3] = buf[3] & 0x0f; /* upper nibble reserved for future use */ 1199252726Srpaulo if (buf[3] < 2 || buf[3] > 3) { 1200252726Srpaulo wpa_printf(MSG_DEBUG, "SCARD: invalid MNC length=%ld", 1201252726Srpaulo (long) buf[3]); 1202252726Srpaulo return -6; 1203252726Srpaulo } 1204252726Srpaulo wpa_printf(MSG_DEBUG, "SCARD: MNC length=%ld", (long) buf[3]); 1205252726Srpaulo return buf[3]; 1206252726Srpaulo} 1207252726Srpaulo 1208252726Srpaulo 1209252726Srpaulo/** 1210189251Ssam * scard_gsm_auth - Run GSM authentication command on SIM card 1211189251Ssam * @scard: Pointer to private data from scard_init() 1212189251Ssam * @_rand: 16-byte RAND value from HLR/AuC 1213189251Ssam * @sres: 4-byte buffer for SRES 1214189251Ssam * @kc: 8-byte buffer for Kc 1215189251Ssam * Returns: 0 on success, -1 if SIM/USIM connection has not been initialized, 1216189251Ssam * -2 if authentication command execution fails, -3 if unknown response code 1217189251Ssam * for authentication command is received, -4 if reading of response fails, 1218189251Ssam * -5 if if response data is of unexpected length 1219189251Ssam * 1220189251Ssam * This function performs GSM authentication using SIM/USIM card and the 1221189251Ssam * provided RAND value from HLR/AuC. If authentication command can be completed 1222189251Ssam * successfully, SRES and Kc values will be written into sres and kc buffers. 1223189251Ssam */ 1224189251Ssamint scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand, 1225189251Ssam unsigned char *sres, unsigned char *kc) 1226189251Ssam{ 1227189251Ssam unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG }; 1228189251Ssam int cmdlen; 1229189251Ssam unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; 1230189251Ssam unsigned char resp[3], buf[12 + 3 + 2]; 1231189251Ssam size_t len; 1232189251Ssam long ret; 1233189251Ssam 1234189251Ssam if (scard == NULL) 1235189251Ssam return -1; 1236189251Ssam 1237189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", _rand, 16); 1238189251Ssam if (scard->sim_type == SCARD_GSM_SIM) { 1239189251Ssam cmdlen = 5 + 16; 1240189251Ssam os_memcpy(cmd + 5, _rand, 16); 1241189251Ssam } else { 1242189251Ssam cmdlen = 5 + 1 + 16; 1243189251Ssam cmd[0] = USIM_CLA; 1244189251Ssam cmd[3] = 0x80; 1245189251Ssam cmd[4] = 17; 1246189251Ssam cmd[5] = 16; 1247189251Ssam os_memcpy(cmd + 6, _rand, 16); 1248189251Ssam } 1249189251Ssam len = sizeof(resp); 1250189251Ssam ret = scard_transmit(scard, cmd, cmdlen, resp, &len); 1251189251Ssam if (ret != SCARD_S_SUCCESS) 1252189251Ssam return -2; 1253189251Ssam 1254189251Ssam if ((scard->sim_type == SCARD_GSM_SIM && 1255189251Ssam (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) || 1256189251Ssam (scard->sim_type == SCARD_USIM && 1257189251Ssam (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) { 1258189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM " 1259189251Ssam "auth request (len=%ld resp=%02x %02x)", 1260189251Ssam (long) len, resp[0], resp[1]); 1261189251Ssam return -3; 1262189251Ssam } 1263189251Ssam get_resp[4] = resp[1]; 1264189251Ssam 1265189251Ssam len = sizeof(buf); 1266189251Ssam ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); 1267189251Ssam if (ret != SCARD_S_SUCCESS) 1268189251Ssam return -4; 1269189251Ssam 1270189251Ssam if (scard->sim_type == SCARD_GSM_SIM) { 1271189251Ssam if (len != 4 + 8 + 2) { 1272189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected data " 1273189251Ssam "length for GSM auth (len=%ld, expected 14)", 1274189251Ssam (long) len); 1275189251Ssam return -5; 1276189251Ssam } 1277189251Ssam os_memcpy(sres, buf, 4); 1278189251Ssam os_memcpy(kc, buf + 4, 8); 1279189251Ssam } else { 1280189251Ssam if (len != 1 + 4 + 1 + 8 + 2) { 1281189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected data " 1282189251Ssam "length for USIM auth (len=%ld, " 1283189251Ssam "expected 16)", (long) len); 1284189251Ssam return -5; 1285189251Ssam } 1286189251Ssam if (buf[0] != 4 || buf[5] != 8) { 1287189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc " 1288189251Ssam "length (%d %d, expected 4 8)", 1289189251Ssam buf[0], buf[5]); 1290189251Ssam } 1291189251Ssam os_memcpy(sres, buf + 1, 4); 1292189251Ssam os_memcpy(kc, buf + 6, 8); 1293189251Ssam } 1294189251Ssam 1295189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4); 1296189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8); 1297189251Ssam 1298189251Ssam return 0; 1299189251Ssam} 1300189251Ssam 1301189251Ssam 1302189251Ssam/** 1303189251Ssam * scard_umts_auth - Run UMTS authentication command on USIM card 1304189251Ssam * @scard: Pointer to private data from scard_init() 1305189251Ssam * @_rand: 16-byte RAND value from HLR/AuC 1306189251Ssam * @autn: 16-byte AUTN value from HLR/AuC 1307189251Ssam * @res: 16-byte buffer for RES 1308189251Ssam * @res_len: Variable that will be set to RES length 1309189251Ssam * @ik: 16-byte buffer for IK 1310189251Ssam * @ck: 16-byte buffer for CK 1311189251Ssam * @auts: 14-byte buffer for AUTS 1312189251Ssam * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization 1313189251Ssam * failure 1314189251Ssam * 1315189251Ssam * This function performs AKA authentication using USIM card and the provided 1316189251Ssam * RAND and AUTN values from HLR/AuC. If authentication command can be 1317189251Ssam * completed successfully, RES, IK, and CK values will be written into provided 1318189251Ssam * buffers and res_len is set to length of received RES value. If USIM reports 1319189251Ssam * synchronization failure, the received AUTS value will be written into auts 1320189251Ssam * buffer. In this case, RES, IK, and CK are not valid. 1321189251Ssam */ 1322189251Ssamint scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, 1323189251Ssam const unsigned char *autn, 1324189251Ssam unsigned char *res, size_t *res_len, 1325189251Ssam unsigned char *ik, unsigned char *ck, unsigned char *auts) 1326189251Ssam{ 1327189251Ssam unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] = 1328189251Ssam { USIM_CMD_RUN_UMTS_ALG }; 1329189251Ssam unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE }; 1330189251Ssam unsigned char resp[3], buf[64], *pos, *end; 1331189251Ssam size_t len; 1332189251Ssam long ret; 1333189251Ssam 1334189251Ssam if (scard == NULL) 1335189251Ssam return -1; 1336189251Ssam 1337189251Ssam if (scard->sim_type == SCARD_GSM_SIM) { 1338189251Ssam wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS " 1339189251Ssam "auth"); 1340189251Ssam return -1; 1341189251Ssam } 1342189251Ssam 1343189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", _rand, AKA_RAND_LEN); 1344189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN); 1345189251Ssam cmd[5] = AKA_RAND_LEN; 1346189251Ssam os_memcpy(cmd + 6, _rand, AKA_RAND_LEN); 1347189251Ssam cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN; 1348189251Ssam os_memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN); 1349189251Ssam 1350189251Ssam len = sizeof(resp); 1351189251Ssam ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); 1352189251Ssam if (ret != SCARD_S_SUCCESS) 1353189251Ssam return -1; 1354189251Ssam 1355189251Ssam if (len <= sizeof(resp)) 1356189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len); 1357189251Ssam 1358189251Ssam if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) { 1359189251Ssam wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - " 1360189251Ssam "MAC != XMAC"); 1361189251Ssam return -1; 1362189251Ssam } else if (len != 2 || resp[0] != 0x61) { 1363189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS " 1364189251Ssam "auth request (len=%ld resp=%02x %02x)", 1365189251Ssam (long) len, resp[0], resp[1]); 1366189251Ssam return -1; 1367189251Ssam } 1368189251Ssam get_resp[4] = resp[1]; 1369189251Ssam 1370189251Ssam len = sizeof(buf); 1371189251Ssam ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); 1372189251Ssam if (ret != SCARD_S_SUCCESS || len > sizeof(buf)) 1373189251Ssam return -1; 1374189251Ssam 1375189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len); 1376189251Ssam if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc && 1377189251Ssam buf[1] == AKA_AUTS_LEN) { 1378189251Ssam wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure"); 1379189251Ssam os_memcpy(auts, buf + 2, AKA_AUTS_LEN); 1380189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN); 1381189251Ssam return -2; 1382189251Ssam } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) { 1383189251Ssam pos = buf + 1; 1384189251Ssam end = buf + len; 1385189251Ssam 1386189251Ssam /* RES */ 1387189251Ssam if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) { 1388189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Invalid RES"); 1389189251Ssam return -1; 1390189251Ssam } 1391189251Ssam *res_len = *pos++; 1392189251Ssam os_memcpy(res, pos, *res_len); 1393189251Ssam pos += *res_len; 1394189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len); 1395189251Ssam 1396189251Ssam /* CK */ 1397189251Ssam if (pos[0] != CK_LEN || pos + CK_LEN > end) { 1398189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Invalid CK"); 1399189251Ssam return -1; 1400189251Ssam } 1401189251Ssam pos++; 1402189251Ssam os_memcpy(ck, pos, CK_LEN); 1403189251Ssam pos += CK_LEN; 1404189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN); 1405189251Ssam 1406189251Ssam /* IK */ 1407189251Ssam if (pos[0] != IK_LEN || pos + IK_LEN > end) { 1408189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Invalid IK"); 1409189251Ssam return -1; 1410189251Ssam } 1411189251Ssam pos++; 1412189251Ssam os_memcpy(ik, pos, IK_LEN); 1413189251Ssam pos += IK_LEN; 1414189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN); 1415189251Ssam 1416189251Ssam return 0; 1417189251Ssam } 1418189251Ssam 1419189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response"); 1420189251Ssam return -1; 1421189251Ssam} 1422252726Srpaulo 1423252726Srpaulo 1424252726Srpauloint scard_supports_umts(struct scard_data *scard) 1425252726Srpaulo{ 1426252726Srpaulo return scard->sim_type == SCARD_USIM; 1427252726Srpaulo} 1428