pcsc_funcs.c revision 189251
1189251Ssam/* 2189251Ssam * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM 3189251Ssam * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> 4189251Ssam * 5189251Ssam * This program is free software; you can redistribute it and/or modify 6189251Ssam * it under the terms of the GNU General Public License version 2 as 7189251Ssam * published by the Free Software Foundation. 8189251Ssam * 9189251Ssam * Alternatively, this software may be distributed under the terms of BSD 10189251Ssam * license. 11189251Ssam * 12189251Ssam * See README and COPYING for more details. 13189251Ssam * 14189251Ssam * This file implements wrapper functions for accessing GSM SIM and 3GPP USIM 15189251Ssam * cards through PC/SC smartcard library. These functions are used to implement 16189251Ssam * authentication routines for EAP-SIM and EAP-AKA. 17189251Ssam */ 18189251Ssam 19189251Ssam#include "includes.h" 20189251Ssam#include <winscard.h> 21189251Ssam 22189251Ssam#include "common.h" 23189251Ssam#include "pcsc_funcs.h" 24189251Ssam 25189251Ssam 26189251Ssam/* See ETSI GSM 11.11 and ETSI TS 102 221 for details. 27189251Ssam * SIM commands: 28189251Ssam * Command APDU: CLA INS P1 P2 P3 Data 29189251Ssam * CLA (class of instruction): A0 for GSM, 00 for USIM 30189251Ssam * INS (instruction) 31189251Ssam * P1 P2 P3 (parameters, P3 = length of Data) 32189251Ssam * Response APDU: Data SW1 SW2 33189251Ssam * SW1 SW2 (Status words) 34189251Ssam * Commands (INS P1 P2 P3): 35189251Ssam * SELECT: A4 00 00 02 <file_id, 2 bytes> 36189251Ssam * GET RESPONSE: C0 00 00 <len> 37189251Ssam * RUN GSM ALG: 88 00 00 00 <RAND len = 10> 38189251Ssam * RUN UMTS ALG: 88 00 81 <len=0x22> data: 0x10 | RAND | 0x10 | AUTN 39189251Ssam * P1 = ID of alg in card 40189251Ssam * P2 = ID of secret key 41189251Ssam * READ BINARY: B0 <offset high> <offset low> <len> 42189251Ssam * READ RECORD: B2 <record number> <mode> <len> 43189251Ssam * P2 (mode) = '02' (next record), '03' (previous record), 44189251Ssam * '04' (absolute mode) 45189251Ssam * VERIFY CHV: 20 00 <CHV number> 08 46189251Ssam * CHANGE CHV: 24 00 <CHV number> 10 47189251Ssam * DISABLE CHV: 26 00 01 08 48189251Ssam * ENABLE CHV: 28 00 01 08 49189251Ssam * UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10 50189251Ssam * SLEEP: FA 00 00 00 51189251Ssam */ 52189251Ssam 53189251Ssam/* GSM SIM commands */ 54189251Ssam#define SIM_CMD_SELECT 0xa0, 0xa4, 0x00, 0x00, 0x02 55189251Ssam#define SIM_CMD_RUN_GSM_ALG 0xa0, 0x88, 0x00, 0x00, 0x10 56189251Ssam#define SIM_CMD_GET_RESPONSE 0xa0, 0xc0, 0x00, 0x00 57189251Ssam#define SIM_CMD_READ_BIN 0xa0, 0xb0, 0x00, 0x00 58189251Ssam#define SIM_CMD_READ_RECORD 0xa0, 0xb2, 0x00, 0x00 59189251Ssam#define SIM_CMD_VERIFY_CHV1 0xa0, 0x20, 0x00, 0x01, 0x08 60189251Ssam 61189251Ssam/* USIM commands */ 62189251Ssam#define USIM_CLA 0x00 63189251Ssam#define USIM_CMD_RUN_UMTS_ALG 0x00, 0x88, 0x00, 0x81, 0x22 64189251Ssam#define USIM_CMD_GET_RESPONSE 0x00, 0xc0, 0x00, 0x00 65189251Ssam 66189251Ssam#define SIM_RECORD_MODE_ABSOLUTE 0x04 67189251Ssam 68189251Ssam#define USIM_FSP_TEMPL_TAG 0x62 69189251Ssam 70189251Ssam#define USIM_TLV_FILE_DESC 0x82 71189251Ssam#define USIM_TLV_FILE_ID 0x83 72189251Ssam#define USIM_TLV_DF_NAME 0x84 73189251Ssam#define USIM_TLV_PROPR_INFO 0xA5 74189251Ssam#define USIM_TLV_LIFE_CYCLE_STATUS 0x8A 75189251Ssam#define USIM_TLV_FILE_SIZE 0x80 76189251Ssam#define USIM_TLV_TOTAL_FILE_SIZE 0x81 77189251Ssam#define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6 78189251Ssam#define USIM_TLV_SHORT_FILE_ID 0x88 79189251Ssam 80189251Ssam#define USIM_PS_DO_TAG 0x90 81189251Ssam 82189251Ssam#define AKA_RAND_LEN 16 83189251Ssam#define AKA_AUTN_LEN 16 84189251Ssam#define AKA_AUTS_LEN 14 85189251Ssam#define RES_MAX_LEN 16 86189251Ssam#define IK_LEN 16 87189251Ssam#define CK_LEN 16 88189251Ssam 89189251Ssam 90189251Ssamtypedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types; 91189251Ssam 92189251Ssamstruct scard_data { 93189251Ssam SCARDCONTEXT ctx; 94189251Ssam SCARDHANDLE card; 95189251Ssam DWORD protocol; 96189251Ssam sim_types sim_type; 97189251Ssam int pin1_required; 98189251Ssam}; 99189251Ssam 100189251Ssam#ifdef __MINGW32_VERSION 101189251Ssam/* MinGW does not yet support WinScard, so load the needed functions 102189251Ssam * dynamically from winscard.dll for now. */ 103189251Ssam 104189251Ssamstatic HINSTANCE dll = NULL; /* winscard.dll */ 105189251Ssam 106189251Ssamstatic const SCARD_IO_REQUEST *dll_g_rgSCardT0Pci, *dll_g_rgSCardT1Pci; 107189251Ssam#undef SCARD_PCI_T0 108189251Ssam#define SCARD_PCI_T0 (dll_g_rgSCardT0Pci) 109189251Ssam#undef SCARD_PCI_T1 110189251Ssam#define SCARD_PCI_T1 (dll_g_rgSCardT1Pci) 111189251Ssam 112189251Ssam 113189251Ssamstatic WINSCARDAPI LONG WINAPI 114189251Ssam(*dll_SCardEstablishContext)(IN DWORD dwScope, 115189251Ssam IN LPCVOID pvReserved1, 116189251Ssam IN LPCVOID pvReserved2, 117189251Ssam OUT LPSCARDCONTEXT phContext); 118189251Ssam#define SCardEstablishContext dll_SCardEstablishContext 119189251Ssam 120189251Ssamstatic long (*dll_SCardReleaseContext)(long hContext); 121189251Ssam#define SCardReleaseContext dll_SCardReleaseContext 122189251Ssam 123189251Ssamstatic WINSCARDAPI LONG WINAPI 124189251Ssam(*dll_SCardListReadersA)(IN SCARDCONTEXT hContext, 125189251Ssam IN LPCSTR mszGroups, 126189251Ssam OUT LPSTR mszReaders, 127189251Ssam IN OUT LPDWORD pcchReaders); 128189251Ssam#undef SCardListReaders 129189251Ssam#define SCardListReaders dll_SCardListReadersA 130189251Ssam 131189251Ssamstatic WINSCARDAPI LONG WINAPI 132189251Ssam(*dll_SCardConnectA)(IN SCARDCONTEXT hContext, 133189251Ssam IN LPCSTR szReader, 134189251Ssam IN DWORD dwShareMode, 135189251Ssam IN DWORD dwPreferredProtocols, 136189251Ssam OUT LPSCARDHANDLE phCard, 137189251Ssam OUT LPDWORD pdwActiveProtocol); 138189251Ssam#undef SCardConnect 139189251Ssam#define SCardConnect dll_SCardConnectA 140189251Ssam 141189251Ssamstatic WINSCARDAPI LONG WINAPI 142189251Ssam(*dll_SCardDisconnect)(IN SCARDHANDLE hCard, 143189251Ssam IN DWORD dwDisposition); 144189251Ssam#define SCardDisconnect dll_SCardDisconnect 145189251Ssam 146189251Ssamstatic WINSCARDAPI LONG WINAPI 147189251Ssam(*dll_SCardTransmit)(IN SCARDHANDLE hCard, 148189251Ssam IN LPCSCARD_IO_REQUEST pioSendPci, 149189251Ssam IN LPCBYTE pbSendBuffer, 150189251Ssam IN DWORD cbSendLength, 151189251Ssam IN OUT LPSCARD_IO_REQUEST pioRecvPci, 152189251Ssam OUT LPBYTE pbRecvBuffer, 153189251Ssam IN OUT LPDWORD pcbRecvLength); 154189251Ssam#define SCardTransmit dll_SCardTransmit 155189251Ssam 156189251Ssamstatic WINSCARDAPI LONG WINAPI 157189251Ssam(*dll_SCardBeginTransaction)(IN SCARDHANDLE hCard); 158189251Ssam#define SCardBeginTransaction dll_SCardBeginTransaction 159189251Ssam 160189251Ssamstatic WINSCARDAPI LONG WINAPI 161189251Ssam(*dll_SCardEndTransaction)(IN SCARDHANDLE hCard, IN DWORD dwDisposition); 162189251Ssam#define SCardEndTransaction dll_SCardEndTransaction 163189251Ssam 164189251Ssam 165189251Ssamstatic int mingw_load_symbols(void) 166189251Ssam{ 167189251Ssam char *sym; 168189251Ssam 169189251Ssam if (dll) 170189251Ssam return 0; 171189251Ssam 172189251Ssam dll = LoadLibrary("winscard"); 173189251Ssam if (dll == NULL) { 174189251Ssam wpa_printf(MSG_DEBUG, "WinSCard: Could not load winscard.dll " 175189251Ssam "library"); 176189251Ssam return -1; 177189251Ssam } 178189251Ssam 179189251Ssam#define LOADSYM(s) \ 180189251Ssam sym = #s; \ 181189251Ssam dll_ ## s = (void *) GetProcAddress(dll, sym); \ 182189251Ssam if (dll_ ## s == NULL) \ 183189251Ssam goto fail; 184189251Ssam 185189251Ssam LOADSYM(SCardEstablishContext); 186189251Ssam LOADSYM(SCardReleaseContext); 187189251Ssam LOADSYM(SCardListReadersA); 188189251Ssam LOADSYM(SCardConnectA); 189189251Ssam LOADSYM(SCardDisconnect); 190189251Ssam LOADSYM(SCardTransmit); 191189251Ssam LOADSYM(SCardBeginTransaction); 192189251Ssam LOADSYM(SCardEndTransaction); 193189251Ssam LOADSYM(g_rgSCardT0Pci); 194189251Ssam LOADSYM(g_rgSCardT1Pci); 195189251Ssam 196189251Ssam#undef LOADSYM 197189251Ssam 198189251Ssam return 0; 199189251Ssam 200189251Ssamfail: 201189251Ssam wpa_printf(MSG_DEBUG, "WinSCard: Could not get address for %s from " 202189251Ssam "winscard.dll", sym); 203189251Ssam FreeLibrary(dll); 204189251Ssam dll = NULL; 205189251Ssam return -1; 206189251Ssam} 207189251Ssam 208189251Ssam 209189251Ssamstatic void mingw_unload_symbols(void) 210189251Ssam{ 211189251Ssam if (dll == NULL) 212189251Ssam return; 213189251Ssam 214189251Ssam FreeLibrary(dll); 215189251Ssam dll = NULL; 216189251Ssam} 217189251Ssam 218189251Ssam#else /* __MINGW32_VERSION */ 219189251Ssam 220189251Ssam#define mingw_load_symbols() 0 221189251Ssam#define mingw_unload_symbols() do { } while (0) 222189251Ssam 223189251Ssam#endif /* __MINGW32_VERSION */ 224189251Ssam 225189251Ssam 226189251Ssamstatic int _scard_select_file(struct scard_data *scard, unsigned short file_id, 227189251Ssam unsigned char *buf, size_t *buf_len, 228189251Ssam sim_types sim_type, unsigned char *aid, 229189251Ssam size_t aidlen); 230189251Ssamstatic int scard_select_file(struct scard_data *scard, unsigned short file_id, 231189251Ssam unsigned char *buf, size_t *buf_len); 232189251Ssamstatic int scard_verify_pin(struct scard_data *scard, const char *pin); 233189251Ssamstatic int scard_get_record_len(struct scard_data *scard, 234189251Ssam unsigned char recnum, unsigned char mode); 235189251Ssamstatic int scard_read_record(struct scard_data *scard, 236189251Ssam unsigned char *data, size_t len, 237189251Ssam unsigned char recnum, unsigned char mode); 238189251Ssam 239189251Ssam 240189251Ssamstatic int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, 241189251Ssam int *ps_do, int *file_len) 242189251Ssam{ 243189251Ssam unsigned char *pos, *end; 244189251Ssam 245189251Ssam if (ps_do) 246189251Ssam *ps_do = -1; 247189251Ssam if (file_len) 248189251Ssam *file_len = -1; 249189251Ssam 250189251Ssam pos = buf; 251189251Ssam end = pos + buf_len; 252189251Ssam if (*pos != USIM_FSP_TEMPL_TAG) { 253189251Ssam wpa_printf(MSG_DEBUG, "SCARD: file header did not " 254189251Ssam "start with FSP template tag"); 255189251Ssam return -1; 256189251Ssam } 257189251Ssam pos++; 258189251Ssam if (pos >= end) 259189251Ssam return -1; 260189251Ssam if ((pos + pos[0]) < end) 261189251Ssam end = pos + 1 + pos[0]; 262189251Ssam pos++; 263189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", 264189251Ssam pos, end - pos); 265189251Ssam 266189251Ssam while (pos + 1 < end) { 267189251Ssam wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV " 268189251Ssam "0x%02x len=%d", pos[0], pos[1]); 269189251Ssam if (pos + 2 + pos[1] > end) 270189251Ssam break; 271189251Ssam 272189251Ssam if (pos[0] == USIM_TLV_FILE_SIZE && 273189251Ssam (pos[1] == 1 || pos[1] == 2) && file_len) { 274189251Ssam if (pos[1] == 1) 275189251Ssam *file_len = (int) pos[2]; 276189251Ssam else 277189251Ssam *file_len = ((int) pos[2] << 8) | 278189251Ssam (int) pos[3]; 279189251Ssam wpa_printf(MSG_DEBUG, "SCARD: file_size=%d", 280189251Ssam *file_len); 281189251Ssam } 282189251Ssam 283189251Ssam if (pos[0] == USIM_TLV_PIN_STATUS_TEMPLATE && 284189251Ssam pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG && 285189251Ssam pos[3] >= 1 && ps_do) { 286189251Ssam wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x", 287189251Ssam pos[4]); 288189251Ssam *ps_do = (int) pos[4]; 289189251Ssam } 290189251Ssam 291189251Ssam pos += 2 + pos[1]; 292189251Ssam 293189251Ssam if (pos == end) 294189251Ssam return 0; 295189251Ssam } 296189251Ssam return -1; 297189251Ssam} 298189251Ssam 299189251Ssam 300189251Ssamstatic int scard_pin_needed(struct scard_data *scard, 301189251Ssam unsigned char *hdr, size_t hlen) 302189251Ssam{ 303189251Ssam if (scard->sim_type == SCARD_GSM_SIM) { 304189251Ssam if (hlen > SCARD_CHV1_OFFSET && 305189251Ssam !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG)) 306189251Ssam return 1; 307189251Ssam return 0; 308189251Ssam } 309189251Ssam 310189251Ssam if (scard->sim_type == SCARD_USIM) { 311189251Ssam int ps_do; 312189251Ssam if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL)) 313189251Ssam return -1; 314189251Ssam /* TODO: there could be more than one PS_DO entry because of 315189251Ssam * multiple PINs in key reference.. */ 316189251Ssam if (ps_do > 0 && (ps_do & 0x80)) 317189251Ssam return 1; 318189251Ssam return 0; 319189251Ssam } 320189251Ssam 321189251Ssam return -1; 322189251Ssam} 323189251Ssam 324189251Ssam 325189251Ssamstatic int scard_get_aid(struct scard_data *scard, unsigned char *aid, 326189251Ssam size_t maxlen) 327189251Ssam{ 328189251Ssam int rlen, rec; 329189251Ssam struct efdir { 330189251Ssam unsigned char appl_template_tag; /* 0x61 */ 331189251Ssam unsigned char appl_template_len; 332189251Ssam unsigned char appl_id_tag; /* 0x4f */ 333189251Ssam unsigned char aid_len; 334189251Ssam unsigned char rid[5]; 335189251Ssam unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ 336189251Ssam } *efdir; 337189251Ssam unsigned char buf[100]; 338189251Ssam size_t blen; 339189251Ssam 340189251Ssam efdir = (struct efdir *) buf; 341189251Ssam blen = sizeof(buf); 342189251Ssam if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) { 343189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR"); 344189251Ssam return -1; 345189251Ssam } 346189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR select", buf, blen); 347189251Ssam 348189251Ssam for (rec = 1; rec < 10; rec++) { 349189251Ssam rlen = scard_get_record_len(scard, rec, 350189251Ssam SIM_RECORD_MODE_ABSOLUTE); 351189251Ssam if (rlen < 0) { 352189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to get EF_DIR " 353189251Ssam "record length"); 354189251Ssam return -1; 355189251Ssam } 356189251Ssam blen = sizeof(buf); 357189251Ssam if (rlen > (int) blen) { 358189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Too long EF_DIR record"); 359189251Ssam return -1; 360189251Ssam } 361189251Ssam if (scard_read_record(scard, buf, rlen, rec, 362189251Ssam SIM_RECORD_MODE_ABSOLUTE) < 0) { 363189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to read " 364189251Ssam "EF_DIR record %d", rec); 365189251Ssam return -1; 366189251Ssam } 367189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR record", buf, rlen); 368189251Ssam 369189251Ssam if (efdir->appl_template_tag != 0x61) { 370189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Unexpected application " 371189251Ssam "template tag 0x%x", 372189251Ssam efdir->appl_template_tag); 373189251Ssam continue; 374189251Ssam } 375189251Ssam 376189251Ssam if (efdir->appl_template_len > rlen - 2) { 377189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Too long application " 378189251Ssam "template (len=%d rlen=%d)", 379189251Ssam efdir->appl_template_len, rlen); 380189251Ssam continue; 381189251Ssam } 382189251Ssam 383189251Ssam if (efdir->appl_id_tag != 0x4f) { 384189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Unexpected application " 385189251Ssam "identifier tag 0x%x", efdir->appl_id_tag); 386189251Ssam continue; 387189251Ssam } 388189251Ssam 389189251Ssam if (efdir->aid_len < 1 || efdir->aid_len > 16) { 390189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %d", 391189251Ssam efdir->aid_len); 392189251Ssam continue; 393189251Ssam } 394189251Ssam 395189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record", 396189251Ssam efdir->rid, efdir->aid_len); 397189251Ssam 398189251Ssam if (efdir->appl_code[0] == 0x10 && 399189251Ssam efdir->appl_code[1] == 0x02) { 400189251Ssam wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app found from " 401189251Ssam "EF_DIR record %d", rec); 402189251Ssam break; 403189251Ssam } 404189251Ssam } 405189251Ssam 406189251Ssam if (rec >= 10) { 407189251Ssam wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app not found " 408189251Ssam "from EF_DIR records"); 409189251Ssam return -1; 410189251Ssam } 411189251Ssam 412189251Ssam if (efdir->aid_len > maxlen) { 413189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Too long AID"); 414189251Ssam return -1; 415189251Ssam } 416189251Ssam 417189251Ssam os_memcpy(aid, efdir->rid, efdir->aid_len); 418189251Ssam 419189251Ssam return efdir->aid_len; 420189251Ssam} 421189251Ssam 422189251Ssam 423189251Ssam/** 424189251Ssam * scard_init - Initialize SIM/USIM connection using PC/SC 425189251Ssam * @sim_type: Allowed SIM types (SIM, USIM, or both) 426189251Ssam * Returns: Pointer to private data structure, or %NULL on failure 427189251Ssam * 428189251Ssam * This function is used to initialize SIM/USIM connection. PC/SC is used to 429189251Ssam * open connection to the SIM/USIM card and the card is verified to support the 430189251Ssam * selected sim_type. In addition, local flag is set if a PIN is needed to 431189251Ssam * access some of the card functions. Once the connection is not needed 432189251Ssam * anymore, scard_deinit() can be used to close it. 433189251Ssam */ 434189251Ssamstruct scard_data * scard_init(scard_sim_type sim_type) 435189251Ssam{ 436189251Ssam long ret; 437189251Ssam unsigned long len; 438189251Ssam struct scard_data *scard; 439189251Ssam#ifdef CONFIG_NATIVE_WINDOWS 440189251Ssam TCHAR *readers = NULL; 441189251Ssam#else /* CONFIG_NATIVE_WINDOWS */ 442189251Ssam char *readers = NULL; 443189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */ 444189251Ssam unsigned char buf[100]; 445189251Ssam size_t blen; 446189251Ssam int transaction = 0; 447189251Ssam int pin_needed; 448189251Ssam 449189251Ssam wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface"); 450189251Ssam if (mingw_load_symbols()) 451189251Ssam return NULL; 452189251Ssam scard = os_zalloc(sizeof(*scard)); 453189251Ssam if (scard == NULL) 454189251Ssam return NULL; 455189251Ssam 456189251Ssam ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, 457189251Ssam &scard->ctx); 458189251Ssam if (ret != SCARD_S_SUCCESS) { 459189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card " 460189251Ssam "context (err=%ld)", ret); 461189251Ssam goto failed; 462189251Ssam } 463189251Ssam 464189251Ssam ret = SCardListReaders(scard->ctx, NULL, NULL, &len); 465189251Ssam if (ret != SCARD_S_SUCCESS) { 466189251Ssam wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed " 467189251Ssam "(err=%ld)", ret); 468189251Ssam goto failed; 469189251Ssam } 470189251Ssam 471189251Ssam#ifdef UNICODE 472189251Ssam len *= 2; 473189251Ssam#endif /* UNICODE */ 474189251Ssam readers = os_malloc(len); 475189251Ssam if (readers == NULL) { 476189251Ssam wpa_printf(MSG_INFO, "SCARD: malloc failed\n"); 477189251Ssam goto failed; 478189251Ssam } 479189251Ssam 480189251Ssam ret = SCardListReaders(scard->ctx, NULL, readers, &len); 481189251Ssam if (ret != SCARD_S_SUCCESS) { 482189251Ssam wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) " 483189251Ssam "(err=%ld)", ret); 484189251Ssam goto failed; 485189251Ssam } 486189251Ssam if (len < 3) { 487189251Ssam wpa_printf(MSG_WARNING, "SCARD: No smart card readers " 488189251Ssam "available."); 489189251Ssam goto failed; 490189251Ssam } 491189251Ssam /* readers is a list of available reader. Last entry is terminated with 492189251Ssam * double NUL. 493189251Ssam * TODO: add support for selecting the reader; now just use the first 494189251Ssam * one.. */ 495189251Ssam#ifdef UNICODE 496189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", readers); 497189251Ssam#else /* UNICODE */ 498189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers); 499189251Ssam#endif /* UNICODE */ 500189251Ssam 501189251Ssam ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED, 502189251Ssam SCARD_PROTOCOL_T0, &scard->card, &scard->protocol); 503189251Ssam if (ret != SCARD_S_SUCCESS) { 504189251Ssam if (ret == (long) SCARD_E_NO_SMARTCARD) 505189251Ssam wpa_printf(MSG_INFO, "No smart card inserted."); 506189251Ssam else 507189251Ssam wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret); 508189251Ssam goto failed; 509189251Ssam } 510189251Ssam 511189251Ssam os_free(readers); 512189251Ssam readers = NULL; 513189251Ssam 514189251Ssam wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)", 515189251Ssam (unsigned int) scard->card, scard->protocol, 516189251Ssam scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1"); 517189251Ssam 518189251Ssam ret = SCardBeginTransaction(scard->card); 519189251Ssam if (ret != SCARD_S_SUCCESS) { 520189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Could not begin transaction: " 521189251Ssam "0x%x", (unsigned int) ret); 522189251Ssam goto failed; 523189251Ssam } 524189251Ssam transaction = 1; 525189251Ssam 526189251Ssam blen = sizeof(buf); 527189251Ssam 528189251Ssam scard->sim_type = SCARD_GSM_SIM; 529189251Ssam if (sim_type == SCARD_USIM_ONLY || sim_type == SCARD_TRY_BOTH) { 530189251Ssam wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support"); 531189251Ssam if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen, 532189251Ssam SCARD_USIM, NULL, 0)) { 533189251Ssam wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported"); 534189251Ssam if (sim_type == SCARD_USIM_ONLY) 535189251Ssam goto failed; 536189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Trying to use GSM SIM"); 537189251Ssam scard->sim_type = SCARD_GSM_SIM; 538189251Ssam } else { 539189251Ssam wpa_printf(MSG_DEBUG, "SCARD: USIM is supported"); 540189251Ssam scard->sim_type = SCARD_USIM; 541189251Ssam } 542189251Ssam } 543189251Ssam 544189251Ssam if (scard->sim_type == SCARD_GSM_SIM) { 545189251Ssam blen = sizeof(buf); 546189251Ssam if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) { 547189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF"); 548189251Ssam goto failed; 549189251Ssam } 550189251Ssam 551189251Ssam blen = sizeof(buf); 552189251Ssam if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) { 553189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF"); 554189251Ssam goto failed; 555189251Ssam } 556189251Ssam } else { 557189251Ssam unsigned char aid[32]; 558189251Ssam int aid_len; 559189251Ssam 560189251Ssam aid_len = scard_get_aid(scard, aid, sizeof(aid)); 561189251Ssam if (aid_len < 0) { 562189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to find AID for " 563189251Ssam "3G USIM app - try to use standard 3G RID"); 564189251Ssam os_memcpy(aid, "\xa0\x00\x00\x00\x87", 5); 565189251Ssam aid_len = 5; 566189251Ssam } 567189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: 3G USIM AID", aid, aid_len); 568189251Ssam 569189251Ssam /* Select based on AID = 3G RID from EF_DIR. This is usually 570189251Ssam * starting with A0 00 00 00 87. */ 571189251Ssam blen = sizeof(buf); 572189251Ssam if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type, 573189251Ssam aid, aid_len)) { 574189251Ssam wpa_printf(MSG_INFO, "SCARD: Failed to read 3G USIM " 575189251Ssam "app"); 576189251Ssam wpa_hexdump(MSG_INFO, "SCARD: 3G USIM AID", 577189251Ssam aid, aid_len); 578189251Ssam goto failed; 579189251Ssam } 580189251Ssam } 581189251Ssam 582189251Ssam /* Verify whether CHV1 (PIN1) is needed to access the card. */ 583189251Ssam pin_needed = scard_pin_needed(scard, buf, blen); 584189251Ssam if (pin_needed < 0) { 585189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to determine whether PIN " 586189251Ssam "is needed"); 587189251Ssam goto failed; 588189251Ssam } 589189251Ssam if (pin_needed) { 590189251Ssam scard->pin1_required = 1; 591189251Ssam wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access"); 592189251Ssam } 593189251Ssam 594189251Ssam ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); 595189251Ssam if (ret != SCARD_S_SUCCESS) { 596189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Could not end transaction: " 597189251Ssam "0x%x", (unsigned int) ret); 598189251Ssam } 599189251Ssam 600189251Ssam return scard; 601189251Ssam 602189251Ssamfailed: 603189251Ssam if (transaction) 604189251Ssam SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); 605189251Ssam os_free(readers); 606189251Ssam scard_deinit(scard); 607189251Ssam return NULL; 608189251Ssam} 609189251Ssam 610189251Ssam 611189251Ssam/** 612189251Ssam * scard_set_pin - Set PIN (CHV1/PIN1) code for accessing SIM/USIM commands 613189251Ssam * @scard: Pointer to private data from scard_init() 614189251Ssam * @pin: PIN code as an ASCII string (e.g., "1234") 615189251Ssam * Returns: 0 on success, -1 on failure 616189251Ssam */ 617189251Ssamint scard_set_pin(struct scard_data *scard, const char *pin) 618189251Ssam{ 619189251Ssam if (scard == NULL) 620189251Ssam return -1; 621189251Ssam 622189251Ssam /* Verify whether CHV1 (PIN1) is needed to access the card. */ 623189251Ssam if (scard->pin1_required) { 624189251Ssam if (pin == NULL) { 625189251Ssam wpa_printf(MSG_DEBUG, "No PIN configured for SIM " 626189251Ssam "access"); 627189251Ssam return -1; 628189251Ssam } 629189251Ssam if (scard_verify_pin(scard, pin)) { 630189251Ssam wpa_printf(MSG_INFO, "PIN verification failed for " 631189251Ssam "SIM access"); 632189251Ssam return -1; 633189251Ssam } 634189251Ssam } 635189251Ssam 636189251Ssam return 0; 637189251Ssam} 638189251Ssam 639189251Ssam 640189251Ssam/** 641189251Ssam * scard_deinit - Deinitialize SIM/USIM connection 642189251Ssam * @scard: Pointer to private data from scard_init() 643189251Ssam * 644189251Ssam * This function closes the SIM/USIM connect opened with scard_init(). 645189251Ssam */ 646189251Ssamvoid scard_deinit(struct scard_data *scard) 647189251Ssam{ 648189251Ssam long ret; 649189251Ssam 650189251Ssam if (scard == NULL) 651189251Ssam return; 652189251Ssam 653189251Ssam wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface"); 654189251Ssam if (scard->card) { 655189251Ssam ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD); 656189251Ssam if (ret != SCARD_S_SUCCESS) { 657189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect " 658189251Ssam "smart card (err=%ld)", ret); 659189251Ssam } 660189251Ssam } 661189251Ssam 662189251Ssam if (scard->ctx) { 663189251Ssam ret = SCardReleaseContext(scard->ctx); 664189251Ssam if (ret != SCARD_S_SUCCESS) { 665189251Ssam wpa_printf(MSG_DEBUG, "Failed to release smart card " 666189251Ssam "context (err=%ld)", ret); 667189251Ssam } 668189251Ssam } 669189251Ssam os_free(scard); 670189251Ssam mingw_unload_symbols(); 671189251Ssam} 672189251Ssam 673189251Ssam 674189251Ssamstatic long scard_transmit(struct scard_data *scard, 675189251Ssam unsigned char *_send, size_t send_len, 676189251Ssam unsigned char *_recv, size_t *recv_len) 677189251Ssam{ 678189251Ssam long ret; 679189251Ssam unsigned long rlen; 680189251Ssam 681189251Ssam wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send", 682189251Ssam _send, send_len); 683189251Ssam rlen = *recv_len; 684189251Ssam ret = SCardTransmit(scard->card, 685189251Ssam scard->protocol == SCARD_PROTOCOL_T1 ? 686189251Ssam SCARD_PCI_T1 : SCARD_PCI_T0, 687189251Ssam _send, (unsigned long) send_len, 688189251Ssam NULL, _recv, &rlen); 689189251Ssam *recv_len = rlen; 690189251Ssam if (ret == SCARD_S_SUCCESS) { 691189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv", 692189251Ssam _recv, rlen); 693189251Ssam } else { 694189251Ssam wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " 695189251Ssam "(err=0x%lx)", ret); 696189251Ssam } 697189251Ssam return ret; 698189251Ssam} 699189251Ssam 700189251Ssam 701189251Ssamstatic int _scard_select_file(struct scard_data *scard, unsigned short file_id, 702189251Ssam unsigned char *buf, size_t *buf_len, 703189251Ssam sim_types sim_type, unsigned char *aid, 704189251Ssam size_t aidlen) 705189251Ssam{ 706189251Ssam long ret; 707189251Ssam unsigned char resp[3]; 708189251Ssam unsigned char cmd[50] = { SIM_CMD_SELECT }; 709189251Ssam int cmdlen; 710189251Ssam unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; 711189251Ssam size_t len, rlen; 712189251Ssam 713189251Ssam if (sim_type == SCARD_USIM) { 714189251Ssam cmd[0] = USIM_CLA; 715189251Ssam cmd[3] = 0x04; 716189251Ssam get_resp[0] = USIM_CLA; 717189251Ssam } 718189251Ssam 719189251Ssam wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id); 720189251Ssam if (aid) { 721189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: select file by AID", 722189251Ssam aid, aidlen); 723189251Ssam if (5 + aidlen > sizeof(cmd)) 724189251Ssam return -1; 725189251Ssam cmd[2] = 0x04; /* Select by AID */ 726189251Ssam cmd[4] = aidlen; /* len */ 727189251Ssam os_memcpy(cmd + 5, aid, aidlen); 728189251Ssam cmdlen = 5 + aidlen; 729189251Ssam } else { 730189251Ssam cmd[5] = file_id >> 8; 731189251Ssam cmd[6] = file_id & 0xff; 732189251Ssam cmdlen = 7; 733189251Ssam } 734189251Ssam len = sizeof(resp); 735189251Ssam ret = scard_transmit(scard, cmd, cmdlen, resp, &len); 736189251Ssam if (ret != SCARD_S_SUCCESS) { 737189251Ssam wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " 738189251Ssam "(err=0x%lx)", ret); 739189251Ssam return -1; 740189251Ssam } 741189251Ssam 742189251Ssam if (len != 2) { 743189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected resp len " 744189251Ssam "%d (expected 2)", (int) len); 745189251Ssam return -1; 746189251Ssam } 747189251Ssam 748189251Ssam if (resp[0] == 0x98 && resp[1] == 0x04) { 749189251Ssam /* Security status not satisfied (PIN_WLAN) */ 750189251Ssam wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied " 751189251Ssam "(PIN_WLAN)"); 752189251Ssam return -1; 753189251Ssam } 754189251Ssam 755189251Ssam if (resp[0] == 0x6e) { 756189251Ssam wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported"); 757189251Ssam return -1; 758189251Ssam } 759189251Ssam 760189251Ssam if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) { 761189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x " 762189251Ssam "(expected 0x61, 0x6c, or 0x9f)", resp[0]); 763189251Ssam return -1; 764189251Ssam } 765189251Ssam /* Normal ending of command; resp[1] bytes available */ 766189251Ssam get_resp[4] = resp[1]; 767189251Ssam wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)", 768189251Ssam resp[1]); 769189251Ssam 770189251Ssam rlen = *buf_len; 771189251Ssam ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen); 772189251Ssam if (ret == SCARD_S_SUCCESS) { 773189251Ssam *buf_len = resp[1] < rlen ? resp[1] : rlen; 774189251Ssam return 0; 775189251Ssam } 776189251Ssam 777189251Ssam wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret); 778189251Ssam return -1; 779189251Ssam} 780189251Ssam 781189251Ssam 782189251Ssamstatic int scard_select_file(struct scard_data *scard, unsigned short file_id, 783189251Ssam unsigned char *buf, size_t *buf_len) 784189251Ssam{ 785189251Ssam return _scard_select_file(scard, file_id, buf, buf_len, 786189251Ssam scard->sim_type, NULL, 0); 787189251Ssam} 788189251Ssam 789189251Ssam 790189251Ssamstatic int scard_get_record_len(struct scard_data *scard, unsigned char recnum, 791189251Ssam unsigned char mode) 792189251Ssam{ 793189251Ssam unsigned char buf[255]; 794189251Ssam unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; 795189251Ssam size_t blen; 796189251Ssam long ret; 797189251Ssam 798189251Ssam if (scard->sim_type == SCARD_USIM) 799189251Ssam cmd[0] = USIM_CLA; 800189251Ssam cmd[2] = recnum; 801189251Ssam cmd[3] = mode; 802189251Ssam cmd[4] = sizeof(buf); 803189251Ssam 804189251Ssam blen = sizeof(buf); 805189251Ssam ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); 806189251Ssam if (ret != SCARD_S_SUCCESS) { 807189251Ssam wpa_printf(MSG_DEBUG, "SCARD: failed to determine file " 808189251Ssam "length for record %d", recnum); 809189251Ssam return -1; 810189251Ssam } 811189251Ssam 812189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response", 813189251Ssam buf, blen); 814189251Ssam 815189251Ssam if (blen < 2 || buf[0] != 0x6c) { 816189251Ssam wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file " 817189251Ssam "length determination"); 818189251Ssam return -1; 819189251Ssam } 820189251Ssam 821189251Ssam return buf[1]; 822189251Ssam} 823189251Ssam 824189251Ssam 825189251Ssamstatic int scard_read_record(struct scard_data *scard, 826189251Ssam unsigned char *data, size_t len, 827189251Ssam unsigned char recnum, unsigned char mode) 828189251Ssam{ 829189251Ssam unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; 830189251Ssam size_t blen = len + 3; 831189251Ssam unsigned char *buf; 832189251Ssam long ret; 833189251Ssam 834189251Ssam if (scard->sim_type == SCARD_USIM) 835189251Ssam cmd[0] = USIM_CLA; 836189251Ssam cmd[2] = recnum; 837189251Ssam cmd[3] = mode; 838189251Ssam cmd[4] = len; 839189251Ssam 840189251Ssam buf = os_malloc(blen); 841189251Ssam if (buf == NULL) 842189251Ssam return -1; 843189251Ssam 844189251Ssam ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); 845189251Ssam if (ret != SCARD_S_SUCCESS) { 846189251Ssam os_free(buf); 847189251Ssam return -2; 848189251Ssam } 849189251Ssam if (blen != len + 2) { 850189251Ssam wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected " 851189251Ssam "length %ld (expected %ld)", 852189251Ssam (long) blen, (long) len + 2); 853189251Ssam os_free(buf); 854189251Ssam return -3; 855189251Ssam } 856189251Ssam 857189251Ssam if (buf[len] != 0x90 || buf[len + 1] != 0x00) { 858189251Ssam wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected " 859189251Ssam "status %02x %02x (expected 90 00)", 860189251Ssam buf[len], buf[len + 1]); 861189251Ssam os_free(buf); 862189251Ssam return -4; 863189251Ssam } 864189251Ssam 865189251Ssam os_memcpy(data, buf, len); 866189251Ssam os_free(buf); 867189251Ssam 868189251Ssam return 0; 869189251Ssam} 870189251Ssam 871189251Ssam 872189251Ssamstatic int scard_read_file(struct scard_data *scard, 873189251Ssam unsigned char *data, size_t len) 874189251Ssam{ 875189251Ssam unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ }; 876189251Ssam size_t blen = len + 3; 877189251Ssam unsigned char *buf; 878189251Ssam long ret; 879189251Ssam 880189251Ssam cmd[4] = len; 881189251Ssam 882189251Ssam buf = os_malloc(blen); 883189251Ssam if (buf == NULL) 884189251Ssam return -1; 885189251Ssam 886189251Ssam if (scard->sim_type == SCARD_USIM) 887189251Ssam cmd[0] = USIM_CLA; 888189251Ssam ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); 889189251Ssam if (ret != SCARD_S_SUCCESS) { 890189251Ssam os_free(buf); 891189251Ssam return -2; 892189251Ssam } 893189251Ssam if (blen != len + 2) { 894189251Ssam wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " 895189251Ssam "length %ld (expected %ld)", 896189251Ssam (long) blen, (long) len + 2); 897189251Ssam os_free(buf); 898189251Ssam return -3; 899189251Ssam } 900189251Ssam 901189251Ssam if (buf[len] != 0x90 || buf[len + 1] != 0x00) { 902189251Ssam wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " 903189251Ssam "status %02x %02x (expected 90 00)", 904189251Ssam buf[len], buf[len + 1]); 905189251Ssam os_free(buf); 906189251Ssam return -4; 907189251Ssam } 908189251Ssam 909189251Ssam os_memcpy(data, buf, len); 910189251Ssam os_free(buf); 911189251Ssam 912189251Ssam return 0; 913189251Ssam} 914189251Ssam 915189251Ssam 916189251Ssamstatic int scard_verify_pin(struct scard_data *scard, const char *pin) 917189251Ssam{ 918189251Ssam long ret; 919189251Ssam unsigned char resp[3]; 920189251Ssam unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 }; 921189251Ssam size_t len; 922189251Ssam 923189251Ssam wpa_printf(MSG_DEBUG, "SCARD: verifying PIN"); 924189251Ssam 925189251Ssam if (pin == NULL || os_strlen(pin) > 8) 926189251Ssam return -1; 927189251Ssam 928189251Ssam if (scard->sim_type == SCARD_USIM) 929189251Ssam cmd[0] = USIM_CLA; 930189251Ssam os_memcpy(cmd + 5, pin, os_strlen(pin)); 931189251Ssam os_memset(cmd + 5 + os_strlen(pin), 0xff, 8 - os_strlen(pin)); 932189251Ssam 933189251Ssam len = sizeof(resp); 934189251Ssam ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); 935189251Ssam if (ret != SCARD_S_SUCCESS) 936189251Ssam return -2; 937189251Ssam 938189251Ssam if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) { 939189251Ssam wpa_printf(MSG_WARNING, "SCARD: PIN verification failed"); 940189251Ssam return -1; 941189251Ssam } 942189251Ssam 943189251Ssam wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully"); 944189251Ssam return 0; 945189251Ssam} 946189251Ssam 947189251Ssam 948189251Ssam/** 949189251Ssam * scard_get_imsi - Read IMSI from SIM/USIM card 950189251Ssam * @scard: Pointer to private data from scard_init() 951189251Ssam * @imsi: Buffer for IMSI 952189251Ssam * @len: Length of imsi buffer; set to IMSI length on success 953189251Ssam * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file 954189251Ssam * selection returns invalid result code, -3 if parsing FSP template file fails 955189251Ssam * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set 956189251Ssam * to needed length), -5 if reading IMSI file fails. 957189251Ssam * 958189251Ssam * This function can be used to read IMSI from the SIM/USIM card. If the IMSI 959189251Ssam * file is PIN protected, scard_set_pin() must have been used to set the 960189251Ssam * correct PIN code before calling scard_get_imsi(). 961189251Ssam */ 962189251Ssamint scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len) 963189251Ssam{ 964189251Ssam unsigned char buf[100]; 965189251Ssam size_t blen, imsilen, i; 966189251Ssam char *pos; 967189251Ssam 968189251Ssam wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI"); 969189251Ssam blen = sizeof(buf); 970189251Ssam if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen)) 971189251Ssam return -1; 972189251Ssam if (blen < 4) { 973189251Ssam wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI " 974189251Ssam "header (len=%ld)", (long) blen); 975189251Ssam return -2; 976189251Ssam } 977189251Ssam 978189251Ssam if (scard->sim_type == SCARD_GSM_SIM) { 979189251Ssam blen = (buf[2] << 8) | buf[3]; 980189251Ssam } else { 981189251Ssam int file_size; 982189251Ssam if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) 983189251Ssam return -3; 984189251Ssam blen = file_size; 985189251Ssam } 986189251Ssam if (blen < 2 || blen > sizeof(buf)) { 987189251Ssam wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%ld", 988189251Ssam (long) blen); 989189251Ssam return -3; 990189251Ssam } 991189251Ssam 992189251Ssam imsilen = (blen - 2) * 2 + 1; 993189251Ssam wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%ld imsilen=%ld", 994189251Ssam (long) blen, (long) imsilen); 995189251Ssam if (blen < 2 || imsilen > *len) { 996189251Ssam *len = imsilen; 997189251Ssam return -4; 998189251Ssam } 999189251Ssam 1000189251Ssam if (scard_read_file(scard, buf, blen)) 1001189251Ssam return -5; 1002189251Ssam 1003189251Ssam pos = imsi; 1004189251Ssam *pos++ = '0' + (buf[1] >> 4 & 0x0f); 1005189251Ssam for (i = 2; i < blen; i++) { 1006189251Ssam unsigned char digit; 1007189251Ssam 1008189251Ssam digit = buf[i] & 0x0f; 1009189251Ssam if (digit < 10) 1010189251Ssam *pos++ = '0' + digit; 1011189251Ssam else 1012189251Ssam imsilen--; 1013189251Ssam 1014189251Ssam digit = buf[i] >> 4 & 0x0f; 1015189251Ssam if (digit < 10) 1016189251Ssam *pos++ = '0' + digit; 1017189251Ssam else 1018189251Ssam imsilen--; 1019189251Ssam } 1020189251Ssam *len = imsilen; 1021189251Ssam 1022189251Ssam return 0; 1023189251Ssam} 1024189251Ssam 1025189251Ssam 1026189251Ssam/** 1027189251Ssam * scard_gsm_auth - Run GSM authentication command on SIM card 1028189251Ssam * @scard: Pointer to private data from scard_init() 1029189251Ssam * @_rand: 16-byte RAND value from HLR/AuC 1030189251Ssam * @sres: 4-byte buffer for SRES 1031189251Ssam * @kc: 8-byte buffer for Kc 1032189251Ssam * Returns: 0 on success, -1 if SIM/USIM connection has not been initialized, 1033189251Ssam * -2 if authentication command execution fails, -3 if unknown response code 1034189251Ssam * for authentication command is received, -4 if reading of response fails, 1035189251Ssam * -5 if if response data is of unexpected length 1036189251Ssam * 1037189251Ssam * This function performs GSM authentication using SIM/USIM card and the 1038189251Ssam * provided RAND value from HLR/AuC. If authentication command can be completed 1039189251Ssam * successfully, SRES and Kc values will be written into sres and kc buffers. 1040189251Ssam */ 1041189251Ssamint scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand, 1042189251Ssam unsigned char *sres, unsigned char *kc) 1043189251Ssam{ 1044189251Ssam unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG }; 1045189251Ssam int cmdlen; 1046189251Ssam unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; 1047189251Ssam unsigned char resp[3], buf[12 + 3 + 2]; 1048189251Ssam size_t len; 1049189251Ssam long ret; 1050189251Ssam 1051189251Ssam if (scard == NULL) 1052189251Ssam return -1; 1053189251Ssam 1054189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", _rand, 16); 1055189251Ssam if (scard->sim_type == SCARD_GSM_SIM) { 1056189251Ssam cmdlen = 5 + 16; 1057189251Ssam os_memcpy(cmd + 5, _rand, 16); 1058189251Ssam } else { 1059189251Ssam cmdlen = 5 + 1 + 16; 1060189251Ssam cmd[0] = USIM_CLA; 1061189251Ssam cmd[3] = 0x80; 1062189251Ssam cmd[4] = 17; 1063189251Ssam cmd[5] = 16; 1064189251Ssam os_memcpy(cmd + 6, _rand, 16); 1065189251Ssam } 1066189251Ssam len = sizeof(resp); 1067189251Ssam ret = scard_transmit(scard, cmd, cmdlen, resp, &len); 1068189251Ssam if (ret != SCARD_S_SUCCESS) 1069189251Ssam return -2; 1070189251Ssam 1071189251Ssam if ((scard->sim_type == SCARD_GSM_SIM && 1072189251Ssam (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) || 1073189251Ssam (scard->sim_type == SCARD_USIM && 1074189251Ssam (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) { 1075189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM " 1076189251Ssam "auth request (len=%ld resp=%02x %02x)", 1077189251Ssam (long) len, resp[0], resp[1]); 1078189251Ssam return -3; 1079189251Ssam } 1080189251Ssam get_resp[4] = resp[1]; 1081189251Ssam 1082189251Ssam len = sizeof(buf); 1083189251Ssam ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); 1084189251Ssam if (ret != SCARD_S_SUCCESS) 1085189251Ssam return -4; 1086189251Ssam 1087189251Ssam if (scard->sim_type == SCARD_GSM_SIM) { 1088189251Ssam if (len != 4 + 8 + 2) { 1089189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected data " 1090189251Ssam "length for GSM auth (len=%ld, expected 14)", 1091189251Ssam (long) len); 1092189251Ssam return -5; 1093189251Ssam } 1094189251Ssam os_memcpy(sres, buf, 4); 1095189251Ssam os_memcpy(kc, buf + 4, 8); 1096189251Ssam } else { 1097189251Ssam if (len != 1 + 4 + 1 + 8 + 2) { 1098189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected data " 1099189251Ssam "length for USIM auth (len=%ld, " 1100189251Ssam "expected 16)", (long) len); 1101189251Ssam return -5; 1102189251Ssam } 1103189251Ssam if (buf[0] != 4 || buf[5] != 8) { 1104189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc " 1105189251Ssam "length (%d %d, expected 4 8)", 1106189251Ssam buf[0], buf[5]); 1107189251Ssam } 1108189251Ssam os_memcpy(sres, buf + 1, 4); 1109189251Ssam os_memcpy(kc, buf + 6, 8); 1110189251Ssam } 1111189251Ssam 1112189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4); 1113189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8); 1114189251Ssam 1115189251Ssam return 0; 1116189251Ssam} 1117189251Ssam 1118189251Ssam 1119189251Ssam/** 1120189251Ssam * scard_umts_auth - Run UMTS authentication command on USIM card 1121189251Ssam * @scard: Pointer to private data from scard_init() 1122189251Ssam * @_rand: 16-byte RAND value from HLR/AuC 1123189251Ssam * @autn: 16-byte AUTN value from HLR/AuC 1124189251Ssam * @res: 16-byte buffer for RES 1125189251Ssam * @res_len: Variable that will be set to RES length 1126189251Ssam * @ik: 16-byte buffer for IK 1127189251Ssam * @ck: 16-byte buffer for CK 1128189251Ssam * @auts: 14-byte buffer for AUTS 1129189251Ssam * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization 1130189251Ssam * failure 1131189251Ssam * 1132189251Ssam * This function performs AKA authentication using USIM card and the provided 1133189251Ssam * RAND and AUTN values from HLR/AuC. If authentication command can be 1134189251Ssam * completed successfully, RES, IK, and CK values will be written into provided 1135189251Ssam * buffers and res_len is set to length of received RES value. If USIM reports 1136189251Ssam * synchronization failure, the received AUTS value will be written into auts 1137189251Ssam * buffer. In this case, RES, IK, and CK are not valid. 1138189251Ssam */ 1139189251Ssamint scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, 1140189251Ssam const unsigned char *autn, 1141189251Ssam unsigned char *res, size_t *res_len, 1142189251Ssam unsigned char *ik, unsigned char *ck, unsigned char *auts) 1143189251Ssam{ 1144189251Ssam unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] = 1145189251Ssam { USIM_CMD_RUN_UMTS_ALG }; 1146189251Ssam unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE }; 1147189251Ssam unsigned char resp[3], buf[64], *pos, *end; 1148189251Ssam size_t len; 1149189251Ssam long ret; 1150189251Ssam 1151189251Ssam if (scard == NULL) 1152189251Ssam return -1; 1153189251Ssam 1154189251Ssam if (scard->sim_type == SCARD_GSM_SIM) { 1155189251Ssam wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS " 1156189251Ssam "auth"); 1157189251Ssam return -1; 1158189251Ssam } 1159189251Ssam 1160189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", _rand, AKA_RAND_LEN); 1161189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN); 1162189251Ssam cmd[5] = AKA_RAND_LEN; 1163189251Ssam os_memcpy(cmd + 6, _rand, AKA_RAND_LEN); 1164189251Ssam cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN; 1165189251Ssam os_memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN); 1166189251Ssam 1167189251Ssam len = sizeof(resp); 1168189251Ssam ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); 1169189251Ssam if (ret != SCARD_S_SUCCESS) 1170189251Ssam return -1; 1171189251Ssam 1172189251Ssam if (len <= sizeof(resp)) 1173189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len); 1174189251Ssam 1175189251Ssam if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) { 1176189251Ssam wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - " 1177189251Ssam "MAC != XMAC"); 1178189251Ssam return -1; 1179189251Ssam } else if (len != 2 || resp[0] != 0x61) { 1180189251Ssam wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS " 1181189251Ssam "auth request (len=%ld resp=%02x %02x)", 1182189251Ssam (long) len, resp[0], resp[1]); 1183189251Ssam return -1; 1184189251Ssam } 1185189251Ssam get_resp[4] = resp[1]; 1186189251Ssam 1187189251Ssam len = sizeof(buf); 1188189251Ssam ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); 1189189251Ssam if (ret != SCARD_S_SUCCESS || len > sizeof(buf)) 1190189251Ssam return -1; 1191189251Ssam 1192189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len); 1193189251Ssam if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc && 1194189251Ssam buf[1] == AKA_AUTS_LEN) { 1195189251Ssam wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure"); 1196189251Ssam os_memcpy(auts, buf + 2, AKA_AUTS_LEN); 1197189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN); 1198189251Ssam return -2; 1199189251Ssam } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) { 1200189251Ssam pos = buf + 1; 1201189251Ssam end = buf + len; 1202189251Ssam 1203189251Ssam /* RES */ 1204189251Ssam if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) { 1205189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Invalid RES"); 1206189251Ssam return -1; 1207189251Ssam } 1208189251Ssam *res_len = *pos++; 1209189251Ssam os_memcpy(res, pos, *res_len); 1210189251Ssam pos += *res_len; 1211189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len); 1212189251Ssam 1213189251Ssam /* CK */ 1214189251Ssam if (pos[0] != CK_LEN || pos + CK_LEN > end) { 1215189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Invalid CK"); 1216189251Ssam return -1; 1217189251Ssam } 1218189251Ssam pos++; 1219189251Ssam os_memcpy(ck, pos, CK_LEN); 1220189251Ssam pos += CK_LEN; 1221189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN); 1222189251Ssam 1223189251Ssam /* IK */ 1224189251Ssam if (pos[0] != IK_LEN || pos + IK_LEN > end) { 1225189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Invalid IK"); 1226189251Ssam return -1; 1227189251Ssam } 1228189251Ssam pos++; 1229189251Ssam os_memcpy(ik, pos, IK_LEN); 1230189251Ssam pos += IK_LEN; 1231189251Ssam wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN); 1232189251Ssam 1233189251Ssam return 0; 1234189251Ssam } 1235189251Ssam 1236189251Ssam wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response"); 1237189251Ssam return -1; 1238189251Ssam} 1239