Annotation of embedaddon/strongswan/src/libcharon/plugins/eap_sim_pcsc/eap_sim_pcsc_card.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2011 Duncan Salerno
! 3: *
! 4: * This program is free software; you can redistribute it and/or modify it
! 5: * under the terms of the GNU General Public License as published by the
! 6: * Free Software Foundation; either version 2 of the License, or (at your
! 7: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 8: *
! 9: * This program is distributed in the hope that it will be useful, but
! 10: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 11: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 12: * for more details.
! 13: */
! 14:
! 15: #include "eap_sim_pcsc_card.h"
! 16:
! 17: #include <PCSC/wintypes.h>
! 18: #include <PCSC/winscard.h>
! 19: #include <daemon.h>
! 20:
! 21: typedef struct private_eap_sim_pcsc_card_t private_eap_sim_pcsc_card_t;
! 22:
! 23: /**
! 24: * Private data of an eap_sim_pcsc_card_t object.
! 25: */
! 26: struct private_eap_sim_pcsc_card_t {
! 27:
! 28: /**
! 29: * Public eap_sim_pcsc_card_t interface.
! 30: */
! 31: eap_sim_pcsc_card_t public;
! 32: };
! 33:
! 34: /**
! 35: * Maximum length for an IMSI.
! 36: */
! 37: #define SIM_IMSI_MAX_LEN 15
! 38:
! 39: /**
! 40: * Length of the status at the end of response APDUs.
! 41: */
! 42: #define APDU_STATUS_LEN 2
! 43:
! 44: /**
! 45: * First byte of status word indicating success.
! 46: */
! 47: #define APDU_SW1_SUCCESS 0x90
! 48:
! 49: /**
! 50: * First byte of status word indicating there is response data to be read.
! 51: */
! 52: #define APDU_SW1_RESPONSE_DATA 0x9f
! 53:
! 54: /**
! 55: * Decode IMSI EF (Elementary File) into an ASCII string
! 56: */
! 57: static bool decode_imsi_ef(unsigned char *input, int input_len, char *output)
! 58: {
! 59: /* Only digits 0-9 valid in IMSIs */
! 60: static const char bcd_num_digits[] = {
! 61: '0', '1', '2', '3', '4', '5', '6', '7',
! 62: '8', '9', '\0', '\0', '\0', '\0', '\0', '\0'
! 63: };
! 64: int i;
! 65:
! 66: /* Check length byte matches how many bytes we have, and that input
! 67: * is correct length for an IMSI */
! 68: if (input[0] != input_len-1 || input_len < 2 || input_len > 9)
! 69: {
! 70: return FALSE;
! 71: }
! 72:
! 73: /* Check type byte is IMSI (bottom 3 bits == 001) */
! 74: if ((input[1] & 0x07) != 0x01)
! 75: {
! 76: return FALSE;
! 77: }
! 78: *output++ = bcd_num_digits[input[1] >> 4];
! 79:
! 80: for (i = 2; i < input_len; i++)
! 81: {
! 82: *output++ = bcd_num_digits[input[i] & 0xf];
! 83: *output++ = bcd_num_digits[input[i] >> 4];
! 84: }
! 85:
! 86: *output++ = '\0';
! 87: return TRUE;
! 88: }
! 89:
! 90: METHOD(simaka_card_t, get_triplet, bool,
! 91: private_eap_sim_pcsc_card_t *this, identification_t *id,
! 92: char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN])
! 93: {
! 94: status_t found = FALSE;
! 95: LONG rv;
! 96: SCARDCONTEXT hContext;
! 97: DWORD dwReaders;
! 98: LPSTR mszReaders;
! 99: char *cur_reader;
! 100: char full_nai[128];
! 101: SCARDHANDLE hCard;
! 102: enum { DISCONNECTED, CONNECTED, TRANSACTION } hCard_status = DISCONNECTED;
! 103:
! 104: snprintf(full_nai, sizeof(full_nai), "%Y", id);
! 105:
! 106: DBG2(DBG_IKE, "looking for triplet: %Y rand %b", id, rand, SIM_RAND_LEN);
! 107:
! 108: rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
! 109: if (rv != SCARD_S_SUCCESS)
! 110: {
! 111: DBG1(DBG_IKE, "SCardEstablishContext: %s", pcsc_stringify_error(rv));
! 112: return FALSE;
! 113: }
! 114:
! 115: rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
! 116: if (rv != SCARD_S_SUCCESS)
! 117: {
! 118: DBG1(DBG_IKE, "SCardListReaders: %s", pcsc_stringify_error(rv));
! 119: return FALSE;
! 120: }
! 121: mszReaders = malloc(sizeof(char)*dwReaders);
! 122:
! 123: rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
! 124: if (rv != SCARD_S_SUCCESS)
! 125: {
! 126: DBG1(DBG_IKE, "SCardListReaders: %s", pcsc_stringify_error(rv));
! 127: free(mszReaders);
! 128: return FALSE;
! 129: }
! 130:
! 131: /* mszReaders is a multi-string of readers, separated by '\0' and
! 132: * terminated by an additional '\0' */
! 133: for (cur_reader = mszReaders; *cur_reader != '\0' && found == FALSE;
! 134: cur_reader += strlen(cur_reader) + 1)
! 135: {
! 136: DWORD dwActiveProtocol = -1;
! 137: const SCARD_IO_REQUEST *pioSendPci;
! 138: SCARD_IO_REQUEST pioRecvPci;
! 139: BYTE pbRecvBuffer[64];
! 140: DWORD dwRecvLength;
! 141: char imsi[SIM_IMSI_MAX_LEN + 1];
! 142:
! 143: /* See GSM 11.11 for SIM APDUs */
! 144: static const BYTE pbSelectMF[] = { 0xa0, 0xa4, 0x00, 0x00, 0x02, 0x3f, 0x00 };
! 145: static const BYTE pbSelectDFGSM[] = { 0xa0, 0xa4, 0x00, 0x00, 0x02, 0x7f, 0x20 };
! 146: static const BYTE pbSelectIMSI[] = { 0xa0, 0xa4, 0x00, 0x00, 0x02, 0x6f, 0x07 };
! 147: static const BYTE pbReadBinary[] = { 0xa0, 0xb0, 0x00, 0x00, 0x09 };
! 148: BYTE pbRunGSMAlgorithm[5 + SIM_RAND_LEN] = { 0xa0, 0x88, 0x00, 0x00, 0x10 };
! 149: static const BYTE pbGetResponse[] = { 0xa0, 0xc0, 0x00, 0x00, 0x0c };
! 150:
! 151: /* If on 2nd or later reader, make sure we end the transaction
! 152: * and disconnect card in the previous reader */
! 153: switch (hCard_status)
! 154: {
! 155: case TRANSACTION:
! 156: SCardEndTransaction(hCard, SCARD_LEAVE_CARD);
! 157: /* FALLTHRU */
! 158: case CONNECTED:
! 159: SCardDisconnect(hCard, SCARD_LEAVE_CARD);
! 160: /* FALLTHRU */
! 161: case DISCONNECTED:
! 162: hCard_status = DISCONNECTED;
! 163: }
! 164:
! 165: /* Copy RAND into APDU */
! 166: memcpy(pbRunGSMAlgorithm + 5, rand, SIM_RAND_LEN);
! 167:
! 168: rv = SCardConnect(hContext, cur_reader, SCARD_SHARE_SHARED,
! 169: SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
! 170: if (rv != SCARD_S_SUCCESS)
! 171: {
! 172: DBG1(DBG_IKE, "SCardConnect: %s", pcsc_stringify_error(rv));
! 173: continue;
! 174: }
! 175: hCard_status = CONNECTED;
! 176:
! 177: switch(dwActiveProtocol)
! 178: {
! 179: case SCARD_PROTOCOL_T0:
! 180: pioSendPci = SCARD_PCI_T0;
! 181: break;
! 182: case SCARD_PROTOCOL_T1:
! 183: pioSendPci = SCARD_PCI_T1;
! 184: break;
! 185: default:
! 186: DBG1(DBG_IKE, "Unknown SCARD_PROTOCOL");
! 187: continue;
! 188: }
! 189:
! 190: /* Start transaction */
! 191: rv = SCardBeginTransaction(hCard);
! 192: if (rv != SCARD_S_SUCCESS)
! 193: {
! 194: DBG1(DBG_IKE, "SCardBeginTransaction: %s", pcsc_stringify_error(rv));
! 195: continue;
! 196: }
! 197: hCard_status = TRANSACTION;
! 198:
! 199: /* APDU: Select MF */
! 200: dwRecvLength = sizeof(pbRecvBuffer);
! 201: rv = SCardTransmit(hCard, pioSendPci, pbSelectMF, sizeof(pbSelectMF),
! 202: &pioRecvPci, pbRecvBuffer, &dwRecvLength);
! 203: if (rv != SCARD_S_SUCCESS)
! 204: {
! 205: DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
! 206: continue;
! 207: }
! 208: if (dwRecvLength < APDU_STATUS_LEN ||
! 209: pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
! 210: {
! 211: DBG1(DBG_IKE, "Select MF failed: %b", pbRecvBuffer,
! 212: (u_int)dwRecvLength);
! 213: continue;
! 214: }
! 215:
! 216: /* APDU: Select DF GSM */
! 217: dwRecvLength = sizeof(pbRecvBuffer);
! 218: rv = SCardTransmit(hCard, pioSendPci, pbSelectDFGSM, sizeof(pbSelectDFGSM),
! 219: &pioRecvPci, pbRecvBuffer, &dwRecvLength);
! 220: if (rv != SCARD_S_SUCCESS)
! 221: {
! 222: DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
! 223: continue;
! 224: }
! 225: if (dwRecvLength < APDU_STATUS_LEN ||
! 226: pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
! 227: {
! 228: DBG1(DBG_IKE, "Select DF GSM failed: %b", pbRecvBuffer,
! 229: (u_int)dwRecvLength);
! 230: continue;
! 231: }
! 232:
! 233: /* APDU: Select IMSI */
! 234: dwRecvLength = sizeof(pbRecvBuffer);
! 235: rv = SCardTransmit(hCard, pioSendPci, pbSelectIMSI, sizeof(pbSelectIMSI),
! 236: &pioRecvPci, pbRecvBuffer, &dwRecvLength);
! 237: if (rv != SCARD_S_SUCCESS)
! 238: {
! 239: DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
! 240: continue;
! 241: }
! 242: if (dwRecvLength < APDU_STATUS_LEN ||
! 243: pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
! 244: {
! 245: DBG1(DBG_IKE, "Select IMSI failed: %b", pbRecvBuffer,
! 246: (u_int)dwRecvLength);
! 247: continue;
! 248: }
! 249:
! 250: /* APDU: Read Binary (of IMSI) */
! 251: dwRecvLength = sizeof(pbRecvBuffer);
! 252: rv = SCardTransmit(hCard, pioSendPci, pbReadBinary, sizeof(pbReadBinary),
! 253: &pioRecvPci, pbRecvBuffer, &dwRecvLength);
! 254: if (rv != SCARD_S_SUCCESS)
! 255: {
! 256: DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
! 257: continue;
! 258: }
! 259: if (dwRecvLength < APDU_STATUS_LEN ||
! 260: pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_SUCCESS)
! 261: {
! 262: DBG1(DBG_IKE, "Select IMSI failed: %b", pbRecvBuffer,
! 263: (u_int)dwRecvLength);
! 264: continue;
! 265: }
! 266:
! 267: if (!decode_imsi_ef(pbRecvBuffer, dwRecvLength-APDU_STATUS_LEN, imsi))
! 268: {
! 269: DBG1(DBG_IKE, "Couldn't decode IMSI EF: %b",
! 270: pbRecvBuffer, (u_int)dwRecvLength);
! 271: continue;
! 272: }
! 273:
! 274: /* The IMSI could be post/prefixed in the full NAI, so just make sure
! 275: * it's in there */
! 276: if (!(strlen(full_nai) && strstr(full_nai, imsi)))
! 277: {
! 278: DBG1(DBG_IKE, "Not the SIM we're looking for, IMSI: %s", imsi);
! 279: continue;
! 280: }
! 281:
! 282: /* APDU: Run GSM Algorithm */
! 283: dwRecvLength = sizeof(pbRecvBuffer);
! 284: rv = SCardTransmit(hCard, pioSendPci,
! 285: pbRunGSMAlgorithm, sizeof(pbRunGSMAlgorithm),
! 286: &pioRecvPci, pbRecvBuffer, &dwRecvLength);
! 287: if (rv != SCARD_S_SUCCESS)
! 288: {
! 289: DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
! 290: continue;
! 291: }
! 292: if (dwRecvLength < APDU_STATUS_LEN ||
! 293: pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
! 294: {
! 295: DBG1(DBG_IKE, "Run GSM Algorithm failed: %b",
! 296: pbRecvBuffer, (u_int)dwRecvLength);
! 297: continue;
! 298: }
! 299:
! 300: /* APDU: Get Response (of Run GSM Algorithm) */
! 301: dwRecvLength = sizeof(pbRecvBuffer);
! 302: rv = SCardTransmit(hCard, pioSendPci, pbGetResponse, sizeof(pbGetResponse),
! 303: &pioRecvPci, pbRecvBuffer, &dwRecvLength);
! 304: if (rv != SCARD_S_SUCCESS)
! 305: {
! 306: DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
! 307: continue;
! 308: }
! 309:
! 310: if (dwRecvLength < APDU_STATUS_LEN ||
! 311: pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_SUCCESS)
! 312: {
! 313: DBG1(DBG_IKE, "Get Response failed: %b", pbRecvBuffer,
! 314: (u_int)dwRecvLength);
! 315: continue;
! 316: }
! 317:
! 318: /* Extract out Kc and SRES from response */
! 319: if (dwRecvLength == SIM_SRES_LEN + SIM_KC_LEN + APDU_STATUS_LEN)
! 320: {
! 321: memcpy(sres, pbRecvBuffer, SIM_SRES_LEN);
! 322: memcpy(kc, pbRecvBuffer+4, SIM_KC_LEN);
! 323: /* This will also cause the loop to exit */
! 324: found = TRUE;
! 325: }
! 326: else
! 327: {
! 328: DBG1(DBG_IKE, "Get Response incorrect length: %b",
! 329: pbRecvBuffer, (u_int)dwRecvLength);
! 330: continue;
! 331: }
! 332:
! 333: /* Transaction will be ended and card disconnected at the
! 334: * beginning of this loop or after this loop */
! 335: }
! 336:
! 337: /* Make sure we end any previous transaction and disconnect card */
! 338: switch (hCard_status)
! 339: {
! 340: case TRANSACTION:
! 341: SCardEndTransaction(hCard, SCARD_LEAVE_CARD);
! 342: /* FALLTHRU */
! 343: case CONNECTED:
! 344: SCardDisconnect(hCard, SCARD_LEAVE_CARD);
! 345: /* FALLTHRU */
! 346: case DISCONNECTED:
! 347: hCard_status = DISCONNECTED;
! 348: }
! 349:
! 350: rv = SCardReleaseContext(hContext);
! 351: if (rv != SCARD_S_SUCCESS)
! 352: {
! 353: DBG1(DBG_IKE, "SCardReleaseContext: %s", pcsc_stringify_error(rv));
! 354: }
! 355:
! 356: free(mszReaders);
! 357: return found;
! 358: }
! 359:
! 360: METHOD(simaka_card_t, get_quintuplet, status_t,
! 361: private_eap_sim_pcsc_card_t *this, identification_t *id,
! 362: char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN], char ck[AKA_CK_LEN],
! 363: char ik[AKA_IK_LEN], char res[AKA_RES_MAX], int *res_len)
! 364: {
! 365: return NOT_SUPPORTED;
! 366: }
! 367:
! 368: METHOD(eap_sim_pcsc_card_t, destroy, void,
! 369: private_eap_sim_pcsc_card_t *this)
! 370: {
! 371: free(this);
! 372: }
! 373:
! 374: /**
! 375: * See header
! 376: */
! 377: eap_sim_pcsc_card_t *eap_sim_pcsc_card_create()
! 378: {
! 379: private_eap_sim_pcsc_card_t *this;
! 380:
! 381: INIT(this,
! 382: .public = {
! 383: .card = {
! 384: .get_triplet = _get_triplet,
! 385: .get_quintuplet = _get_quintuplet,
! 386: .resync = (void*)return_false,
! 387: .get_pseudonym = (void*)return_null,
! 388: .set_pseudonym = (void*)nop,
! 389: .get_reauth = (void*)return_null,
! 390: .set_reauth = (void*)nop,
! 391: },
! 392: .destroy = _destroy,
! 393: },
! 394: );
! 395:
! 396: return &this->public;
! 397: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>