pcsc_funcs.c
Go to the documentation of this file.
00001 /*
00002  * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
00003  * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 2 as
00007  * published by the Free Software Foundation.
00008  *
00009  * Alternatively, this software may be distributed under the terms of BSD
00010  * license.
00011  *
00012  * See README and COPYING for more details.
00013  *
00014  * This file implements wrapper functions for accessing GSM SIM and 3GPP USIM
00015  * cards through PC/SC smartcard library. These functions are used to implement
00016  * authentication routines for EAP-SIM and EAP-AKA.
00017  */
00018 
00019 #include "includes.h"
00020 #include <winscard.h>
00021 
00022 #include "common.h"
00023 #include "pcsc_funcs.h"
00024 
00025 
00026 /* See ETSI GSM 11.11 and ETSI TS 102 221 for details.
00027  * SIM commands:
00028  * Command APDU: CLA INS P1 P2 P3 Data
00029  *   CLA (class of instruction): A0 for GSM, 00 for USIM
00030  *   INS (instruction)
00031  *   P1 P2 P3 (parameters, P3 = length of Data)
00032  * Response APDU: Data SW1 SW2
00033  *   SW1 SW2 (Status words)
00034  * Commands (INS P1 P2 P3):
00035  *   SELECT: A4 00 00 02 <file_id, 2 bytes>
00036  *   GET RESPONSE: C0 00 00 <len>
00037  *   RUN GSM ALG: 88 00 00 00 <RAND len = 10>
00038  *   RUN UMTS ALG: 88 00 81 <len=0x22> data: 0x10 | RAND | 0x10 | AUTN
00039  *      P1 = ID of alg in card
00040  *      P2 = ID of secret key
00041  *   READ BINARY: B0 <offset high> <offset low> <len>
00042  *   READ RECORD: B2 <record number> <mode> <len>
00043  *      P2 (mode) = '02' (next record), '03' (previous record),
00044  *                  '04' (absolute mode)
00045  *   VERIFY CHV: 20 00 <CHV number> 08
00046  *   CHANGE CHV: 24 00 <CHV number> 10
00047  *   DISABLE CHV: 26 00 01 08
00048  *   ENABLE CHV: 28 00 01 08
00049  *   UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10
00050  *   SLEEP: FA 00 00 00
00051  */
00052 
00053 /* GSM SIM commands */
00054 #define SIM_CMD_SELECT                  0xa0, 0xa4, 0x00, 0x00, 0x02
00055 #define SIM_CMD_RUN_GSM_ALG             0xa0, 0x88, 0x00, 0x00, 0x10
00056 #define SIM_CMD_GET_RESPONSE            0xa0, 0xc0, 0x00, 0x00
00057 #define SIM_CMD_READ_BIN                0xa0, 0xb0, 0x00, 0x00
00058 #define SIM_CMD_READ_RECORD             0xa0, 0xb2, 0x00, 0x00
00059 #define SIM_CMD_VERIFY_CHV1             0xa0, 0x20, 0x00, 0x01, 0x08
00060 
00061 /* USIM commands */
00062 #define USIM_CLA                        0x00
00063 #define USIM_CMD_RUN_UMTS_ALG           0x00, 0x88, 0x00, 0x81, 0x22
00064 #define USIM_CMD_GET_RESPONSE           0x00, 0xc0, 0x00, 0x00
00065 
00066 #define SIM_RECORD_MODE_ABSOLUTE 0x04
00067 
00068 #define USIM_FSP_TEMPL_TAG              0x62
00069 
00070 #define USIM_TLV_FILE_DESC              0x82
00071 #define USIM_TLV_FILE_ID                0x83
00072 #define USIM_TLV_DF_NAME                0x84
00073 #define USIM_TLV_PROPR_INFO             0xA5
00074 #define USIM_TLV_LIFE_CYCLE_STATUS      0x8A
00075 #define USIM_TLV_FILE_SIZE              0x80
00076 #define USIM_TLV_TOTAL_FILE_SIZE        0x81
00077 #define USIM_TLV_PIN_STATUS_TEMPLATE    0xC6
00078 #define USIM_TLV_SHORT_FILE_ID          0x88
00079 
00080 #define USIM_PS_DO_TAG                  0x90
00081 
00082 #define AKA_RAND_LEN 16
00083 #define AKA_AUTN_LEN 16
00084 #define AKA_AUTS_LEN 14
00085 #define RES_MAX_LEN 16
00086 #define IK_LEN 16
00087 #define CK_LEN 16
00088 
00089 
00090 typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types;
00091 
00092 struct scard_data {
00093         SCARDCONTEXT ctx;
00094         SCARDHANDLE card;
00095         DWORD protocol;
00096         sim_types sim_type;
00097         int pin1_required;
00098 };
00099 
00100 #ifdef __MINGW32_VERSION
00101 /* MinGW does not yet support WinScard, so load the needed functions
00102  * dynamically from winscard.dll for now. */
00103 
00104 static HINSTANCE dll = NULL; /* winscard.dll */
00105 
00106 static const SCARD_IO_REQUEST *dll_g_rgSCardT0Pci, *dll_g_rgSCardT1Pci;
00107 #undef SCARD_PCI_T0
00108 #define SCARD_PCI_T0 (dll_g_rgSCardT0Pci)
00109 #undef SCARD_PCI_T1
00110 #define SCARD_PCI_T1 (dll_g_rgSCardT1Pci)
00111 
00112 
00113 static WINSCARDAPI LONG WINAPI
00114 (*dll_SCardEstablishContext)(IN DWORD dwScope,
00115                              IN LPCVOID pvReserved1,
00116                              IN LPCVOID pvReserved2,
00117                              OUT LPSCARDCONTEXT phContext);
00118 #define SCardEstablishContext dll_SCardEstablishContext
00119 
00120 static long (*dll_SCardReleaseContext)(long hContext);
00121 #define SCardReleaseContext dll_SCardReleaseContext
00122 
00123 static WINSCARDAPI LONG WINAPI
00124 (*dll_SCardListReadersA)(IN SCARDCONTEXT hContext,
00125                          IN LPCSTR mszGroups,
00126                          OUT LPSTR mszReaders,
00127                          IN OUT LPDWORD pcchReaders);
00128 #undef SCardListReaders
00129 #define SCardListReaders dll_SCardListReadersA
00130 
00131 static WINSCARDAPI LONG WINAPI
00132 (*dll_SCardConnectA)(IN SCARDCONTEXT hContext,
00133                      IN LPCSTR szReader,
00134                      IN DWORD dwShareMode,
00135                      IN DWORD dwPreferredProtocols,
00136                      OUT LPSCARDHANDLE phCard,
00137                      OUT LPDWORD pdwActiveProtocol);
00138 #undef SCardConnect
00139 #define SCardConnect dll_SCardConnectA
00140 
00141 static WINSCARDAPI LONG WINAPI
00142 (*dll_SCardDisconnect)(IN SCARDHANDLE hCard,
00143                        IN DWORD dwDisposition);
00144 #define SCardDisconnect dll_SCardDisconnect
00145 
00146 static WINSCARDAPI LONG WINAPI
00147 (*dll_SCardTransmit)(IN SCARDHANDLE hCard,
00148                      IN LPCSCARD_IO_REQUEST pioSendPci,
00149                      IN LPCBYTE pbSendBuffer,
00150                      IN DWORD cbSendLength,
00151                      IN OUT LPSCARD_IO_REQUEST pioRecvPci,
00152                      OUT LPBYTE pbRecvBuffer,
00153                      IN OUT LPDWORD pcbRecvLength);
00154 #define SCardTransmit dll_SCardTransmit
00155 
00156 static WINSCARDAPI LONG WINAPI
00157 (*dll_SCardBeginTransaction)(IN SCARDHANDLE hCard);
00158 #define SCardBeginTransaction dll_SCardBeginTransaction
00159 
00160 static WINSCARDAPI LONG WINAPI
00161 (*dll_SCardEndTransaction)(IN SCARDHANDLE hCard, IN DWORD dwDisposition);
00162 #define SCardEndTransaction dll_SCardEndTransaction
00163 
00164 
00165 static int mingw_load_symbols(void)
00166 {
00167         char *sym;
00168 
00169         if (dll)
00170                 return 0;
00171 
00172         dll = LoadLibrary("winscard");
00173         if (dll == NULL) {
00174                 wpa_printf(MSG_DEBUG, "WinSCard: Could not load winscard.dll "
00175                            "library");
00176                 return -1;
00177         }
00178 
00179 #define LOADSYM(s) \
00180         sym = #s; \
00181         dll_ ## s = (void *) GetProcAddress(dll, sym); \
00182         if (dll_ ## s == NULL) \
00183                 goto fail;
00184 
00185         LOADSYM(SCardEstablishContext);
00186         LOADSYM(SCardReleaseContext);
00187         LOADSYM(SCardListReadersA);
00188         LOADSYM(SCardConnectA);
00189         LOADSYM(SCardDisconnect);
00190         LOADSYM(SCardTransmit);
00191         LOADSYM(SCardBeginTransaction);
00192         LOADSYM(SCardEndTransaction);
00193         LOADSYM(g_rgSCardT0Pci);
00194         LOADSYM(g_rgSCardT1Pci);
00195 
00196 #undef LOADSYM
00197 
00198         return 0;
00199 
00200 fail:
00201         wpa_printf(MSG_DEBUG, "WinSCard: Could not get address for %s from "
00202                    "winscard.dll", sym);
00203         FreeLibrary(dll);
00204         dll = NULL;
00205         return -1;
00206 }
00207 
00208 
00209 static void mingw_unload_symbols(void)
00210 {
00211         if (dll == NULL)
00212                 return;
00213 
00214         FreeLibrary(dll);
00215         dll = NULL;
00216 }
00217 
00218 #else /* __MINGW32_VERSION */
00219 
00220 #define mingw_load_symbols() 0
00221 #define mingw_unload_symbols() do { } while (0)
00222 
00223 #endif /* __MINGW32_VERSION */
00224 
00225 
00226 static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
00227                               unsigned char *buf, size_t *buf_len,
00228                               sim_types sim_type, unsigned char *aid,
00229                               size_t aidlen);
00230 static int scard_select_file(struct scard_data *scard, unsigned short file_id,
00231                              unsigned char *buf, size_t *buf_len);
00232 static int scard_verify_pin(struct scard_data *scard, const char *pin);
00233 static int scard_get_record_len(struct scard_data *scard,
00234                                 unsigned char recnum, unsigned char mode);
00235 static int scard_read_record(struct scard_data *scard,
00236                              unsigned char *data, size_t len,
00237                              unsigned char recnum, unsigned char mode);
00238 
00239 
00240 static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len,
00241                                  int *ps_do, int *file_len)
00242 {
00243                 unsigned char *pos, *end;
00244 
00245                 if (ps_do)
00246                         *ps_do = -1;
00247                 if (file_len)
00248                         *file_len = -1;
00249 
00250                 pos = buf;
00251                 end = pos + buf_len;
00252                 if (*pos != USIM_FSP_TEMPL_TAG) {
00253                         wpa_printf(MSG_DEBUG, "SCARD: file header did not "
00254                                    "start with FSP template tag");
00255                         return -1;
00256                 }
00257                 pos++;
00258                 if (pos >= end)
00259                         return -1;
00260                 if ((pos + pos[0]) < end)
00261                         end = pos + 1 + pos[0];
00262                 pos++;
00263                 wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
00264                             pos, end - pos);
00265 
00266                 while (pos + 1 < end) {
00267                         wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV "
00268                                    "0x%02x len=%d", pos[0], pos[1]);
00269                         if (pos + 2 + pos[1] > end)
00270                                 break;
00271 
00272                         if (pos[0] == USIM_TLV_FILE_SIZE &&
00273                             (pos[1] == 1 || pos[1] == 2) && file_len) {
00274                                 if (pos[1] == 1)
00275                                         *file_len = (int) pos[2];
00276                                 else
00277                                         *file_len = ((int) pos[2] << 8) |
00278                                                 (int) pos[3];
00279                                 wpa_printf(MSG_DEBUG, "SCARD: file_size=%d",
00280                                            *file_len);
00281                         }
00282 
00283                         if (pos[0] == USIM_TLV_PIN_STATUS_TEMPLATE &&
00284                             pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG &&
00285                             pos[3] >= 1 && ps_do) {
00286                                 wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x",
00287                                            pos[4]);
00288                                 *ps_do = (int) pos[4];
00289                         }
00290 
00291                         pos += 2 + pos[1];
00292 
00293                         if (pos == end)
00294                                 return 0;
00295                 }
00296                 return -1;
00297 }
00298 
00299 
00300 static int scard_pin_needed(struct scard_data *scard,
00301                             unsigned char *hdr, size_t hlen)
00302 {
00303         if (scard->sim_type == SCARD_GSM_SIM) {
00304                 if (hlen > SCARD_CHV1_OFFSET &&
00305                     !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG))
00306                         return 1;
00307                 return 0;
00308         }
00309 
00310         if (scard->sim_type == SCARD_USIM) {
00311                 int ps_do;
00312                 if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL))
00313                         return -1;
00314                 /* TODO: there could be more than one PS_DO entry because of
00315                  * multiple PINs in key reference.. */
00316                 if (ps_do > 0 && (ps_do & 0x80))
00317                         return 1;
00318                 return 0;
00319         }
00320 
00321         return -1;
00322 }
00323 
00324 
00325 static int scard_get_aid(struct scard_data *scard, unsigned char *aid,
00326                          size_t maxlen)
00327 {
00328         int rlen, rec;
00329         struct efdir {
00330                 unsigned char appl_template_tag; /* 0x61 */
00331                 unsigned char appl_template_len;
00332                 unsigned char appl_id_tag; /* 0x4f */
00333                 unsigned char aid_len;
00334                 unsigned char rid[5];
00335                 unsigned char appl_code[2]; /* 0x1002 for 3G USIM */
00336         } *efdir;
00337         unsigned char buf[100];
00338         size_t blen;
00339 
00340         efdir = (struct efdir *) buf;
00341         blen = sizeof(buf);
00342         if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) {
00343                 wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR");
00344                 return -1;
00345         }
00346         wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR select", buf, blen);
00347 
00348         for (rec = 1; rec < 10; rec++) {
00349                 rlen = scard_get_record_len(scard, rec,
00350                                             SIM_RECORD_MODE_ABSOLUTE);
00351                 if (rlen < 0) {
00352                         wpa_printf(MSG_DEBUG, "SCARD: Failed to get EF_DIR "
00353                                    "record length");
00354                         return -1;
00355                 }
00356                 blen = sizeof(buf);
00357                 if (rlen > (int) blen) {
00358                         wpa_printf(MSG_DEBUG, "SCARD: Too long EF_DIR record");
00359                         return -1;
00360                 }
00361                 if (scard_read_record(scard, buf, rlen, rec,
00362                                       SIM_RECORD_MODE_ABSOLUTE) < 0) {
00363                         wpa_printf(MSG_DEBUG, "SCARD: Failed to read "
00364                                    "EF_DIR record %d", rec);
00365                         return -1;
00366                 }
00367                 wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR record", buf, rlen);
00368 
00369                 if (efdir->appl_template_tag != 0x61) {
00370                         wpa_printf(MSG_DEBUG, "SCARD: Unexpected application "
00371                                    "template tag 0x%x",
00372                                    efdir->appl_template_tag);
00373                         continue;
00374                 }
00375 
00376                 if (efdir->appl_template_len > rlen - 2) {
00377                         wpa_printf(MSG_DEBUG, "SCARD: Too long application "
00378                                    "template (len=%d rlen=%d)",
00379                                    efdir->appl_template_len, rlen);
00380                         continue;
00381                 }
00382 
00383                 if (efdir->appl_id_tag != 0x4f) {
00384                         wpa_printf(MSG_DEBUG, "SCARD: Unexpected application "
00385                                    "identifier tag 0x%x", efdir->appl_id_tag);
00386                         continue;
00387                 }
00388 
00389                 if (efdir->aid_len < 1 || efdir->aid_len > 16) {
00390                         wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %d",
00391                                    efdir->aid_len);
00392                         continue;
00393                 }
00394 
00395                 wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record",
00396                             efdir->rid, efdir->aid_len);
00397 
00398                 if (efdir->appl_code[0] == 0x10 &&
00399                     efdir->appl_code[1] == 0x02) {
00400                         wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app found from "
00401                                    "EF_DIR record %d", rec);
00402                         break;
00403                 }
00404         }
00405 
00406         if (rec >= 10) {
00407                 wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app not found "
00408                            "from EF_DIR records");
00409                 return -1;
00410         }
00411 
00412         if (efdir->aid_len > maxlen) {
00413                 wpa_printf(MSG_DEBUG, "SCARD: Too long AID");
00414                 return -1;
00415         }
00416 
00417         os_memcpy(aid, efdir->rid, efdir->aid_len);
00418 
00419         return efdir->aid_len;
00420 }
00421 
00422 
00434 struct scard_data * scard_init(scard_sim_type sim_type)
00435 {
00436         long ret;
00437         unsigned long len;
00438         struct scard_data *scard;
00439 #ifdef CONFIG_NATIVE_WINDOWS
00440         TCHAR *readers = NULL;
00441 #else /* CONFIG_NATIVE_WINDOWS */
00442         char *readers = NULL;
00443 #endif /* CONFIG_NATIVE_WINDOWS */
00444         unsigned char buf[100];
00445         size_t blen;
00446         int transaction = 0;
00447         int pin_needed;
00448 
00449         wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface");
00450         if (mingw_load_symbols())
00451                 return NULL;
00452         scard = os_zalloc(sizeof(*scard));
00453         if (scard == NULL)
00454                 return NULL;
00455 
00456         ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
00457                                     &scard->ctx);
00458         if (ret != SCARD_S_SUCCESS) {
00459                 wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card "
00460                            "context (err=%ld)", ret);
00461                 goto failed;
00462         }
00463 
00464         ret = SCardListReaders(scard->ctx, NULL, NULL, &len);
00465         if (ret != SCARD_S_SUCCESS) {
00466                 wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed "
00467                            "(err=%ld)", ret);
00468                 goto failed;
00469         }
00470 
00471 #ifdef UNICODE
00472         len *= 2;
00473 #endif /* UNICODE */
00474         readers = os_malloc(len);
00475         if (readers == NULL) {
00476                 wpa_printf(MSG_INFO, "SCARD: malloc failed\n");
00477                 goto failed;
00478         }
00479 
00480         ret = SCardListReaders(scard->ctx, NULL, readers, &len);
00481         if (ret != SCARD_S_SUCCESS) {
00482                 wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) "
00483                            "(err=%ld)", ret);
00484                 goto failed;
00485         }
00486         if (len < 3) {
00487                 wpa_printf(MSG_WARNING, "SCARD: No smart card readers "
00488                            "available.");
00489                 goto failed;
00490         }
00491         /* readers is a list of available reader. Last entry is terminated with
00492          * double NUL.
00493          * TODO: add support for selecting the reader; now just use the first
00494          * one.. */
00495 #ifdef UNICODE
00496         wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", readers);
00497 #else /* UNICODE */
00498         wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers);
00499 #endif /* UNICODE */
00500 
00501         ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED,
00502                            SCARD_PROTOCOL_T0, &scard->card, &scard->protocol);
00503         if (ret != SCARD_S_SUCCESS) {
00504                 if (ret == (long) SCARD_E_NO_SMARTCARD)
00505                         wpa_printf(MSG_INFO, "No smart card inserted.");
00506                 else
00507                         wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret);
00508                 goto failed;
00509         }
00510 
00511         os_free(readers);
00512         readers = NULL;
00513 
00514         wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)",
00515                    (unsigned int) scard->card, scard->protocol,
00516                    scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1");
00517 
00518         ret = SCardBeginTransaction(scard->card);
00519         if (ret != SCARD_S_SUCCESS) {
00520                 wpa_printf(MSG_DEBUG, "SCARD: Could not begin transaction: "
00521                            "0x%x", (unsigned int) ret);
00522                 goto failed;
00523         }
00524         transaction = 1;
00525 
00526         blen = sizeof(buf);
00527 
00528         scard->sim_type = SCARD_GSM_SIM;
00529         if (sim_type == SCARD_USIM_ONLY || sim_type == SCARD_TRY_BOTH) {
00530                 wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support");
00531                 if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen,
00532                                        SCARD_USIM, NULL, 0)) {
00533                         wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported");
00534                         if (sim_type == SCARD_USIM_ONLY)
00535                                 goto failed;
00536                         wpa_printf(MSG_DEBUG, "SCARD: Trying to use GSM SIM");
00537                         scard->sim_type = SCARD_GSM_SIM;
00538                 } else {
00539                         wpa_printf(MSG_DEBUG, "SCARD: USIM is supported");
00540                         scard->sim_type = SCARD_USIM;
00541                 }
00542         }
00543 
00544         if (scard->sim_type == SCARD_GSM_SIM) {
00545                 blen = sizeof(buf);
00546                 if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) {
00547                         wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF");
00548                         goto failed;
00549                 }
00550 
00551                 blen = sizeof(buf);
00552                 if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) {
00553                         wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF");
00554                         goto failed;
00555                 }
00556         } else {
00557                 unsigned char aid[32];
00558                 int aid_len;
00559 
00560                 aid_len = scard_get_aid(scard, aid, sizeof(aid));
00561                 if (aid_len < 0) {
00562                         wpa_printf(MSG_DEBUG, "SCARD: Failed to find AID for "
00563                                    "3G USIM app - try to use standard 3G RID");
00564                         os_memcpy(aid, "\xa0\x00\x00\x00\x87", 5);
00565                         aid_len = 5;
00566                 }
00567                 wpa_hexdump(MSG_DEBUG, "SCARD: 3G USIM AID", aid, aid_len);
00568 
00569                 /* Select based on AID = 3G RID from EF_DIR. This is usually
00570                  * starting with A0 00 00 00 87. */
00571                 blen = sizeof(buf);
00572                 if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type,
00573                                        aid, aid_len)) {
00574                         wpa_printf(MSG_INFO, "SCARD: Failed to read 3G USIM "
00575                                    "app");
00576                         wpa_hexdump(MSG_INFO, "SCARD: 3G USIM AID",
00577                                     aid, aid_len);
00578                         goto failed;
00579                 }
00580         }
00581 
00582         /* Verify whether CHV1 (PIN1) is needed to access the card. */
00583         pin_needed = scard_pin_needed(scard, buf, blen);
00584         if (pin_needed < 0) {
00585                 wpa_printf(MSG_DEBUG, "SCARD: Failed to determine whether PIN "
00586                            "is needed");
00587                 goto failed;
00588         }
00589         if (pin_needed) {
00590                 scard->pin1_required = 1;
00591                 wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access");
00592         }
00593 
00594         ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
00595         if (ret != SCARD_S_SUCCESS) {
00596                 wpa_printf(MSG_DEBUG, "SCARD: Could not end transaction: "
00597                            "0x%x", (unsigned int) ret);
00598         }
00599 
00600         return scard;
00601 
00602 failed:
00603         if (transaction)
00604                 SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
00605         os_free(readers);
00606         scard_deinit(scard);
00607         return NULL;
00608 }
00609 
00610 
00617 int scard_set_pin(struct scard_data *scard, const char *pin)
00618 {
00619         if (scard == NULL)
00620                 return -1;
00621 
00622         /* Verify whether CHV1 (PIN1) is needed to access the card. */
00623         if (scard->pin1_required) {
00624                 if (pin == NULL) {
00625                         wpa_printf(MSG_DEBUG, "No PIN configured for SIM "
00626                                    "access");
00627                         return -1;
00628                 }
00629                 if (scard_verify_pin(scard, pin)) {
00630                         wpa_printf(MSG_INFO, "PIN verification failed for "
00631                                 "SIM access");
00632                         return -1;
00633                 }
00634         }
00635 
00636         return 0;
00637 }
00638 
00639 
00646 void scard_deinit(struct scard_data *scard)
00647 {
00648         long ret;
00649 
00650         if (scard == NULL)
00651                 return;
00652 
00653         wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface");
00654         if (scard->card) {
00655                 ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD);
00656                 if (ret != SCARD_S_SUCCESS) {
00657                         wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect "
00658                                    "smart card (err=%ld)", ret);
00659                 }
00660         }
00661 
00662         if (scard->ctx) {
00663                 ret = SCardReleaseContext(scard->ctx);
00664                 if (ret != SCARD_S_SUCCESS) {
00665                         wpa_printf(MSG_DEBUG, "Failed to release smart card "
00666                                    "context (err=%ld)", ret);
00667                 }
00668         }
00669         os_free(scard);
00670         mingw_unload_symbols();
00671 }
00672 
00673 
00674 static long scard_transmit(struct scard_data *scard,
00675                            unsigned char *_send, size_t send_len,
00676                            unsigned char *_recv, size_t *recv_len)
00677 {
00678         long ret;
00679         unsigned long rlen;
00680 
00681         wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send",
00682                         _send, send_len);
00683         rlen = *recv_len;
00684         ret = SCardTransmit(scard->card,
00685                             scard->protocol == SCARD_PROTOCOL_T1 ?
00686                             SCARD_PCI_T1 : SCARD_PCI_T0,
00687                             _send, (unsigned long) send_len,
00688                             NULL, _recv, &rlen);
00689         *recv_len = rlen;
00690         if (ret == SCARD_S_SUCCESS) {
00691                 wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv",
00692                             _recv, rlen);
00693         } else {
00694                 wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
00695                            "(err=0x%lx)", ret);
00696         }
00697         return ret;
00698 }
00699 
00700 
00701 static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
00702                               unsigned char *buf, size_t *buf_len,
00703                               sim_types sim_type, unsigned char *aid,
00704                               size_t aidlen)
00705 {
00706         long ret;
00707         unsigned char resp[3];
00708         unsigned char cmd[50] = { SIM_CMD_SELECT };
00709         int cmdlen;
00710         unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
00711         size_t len, rlen;
00712 
00713         if (sim_type == SCARD_USIM) {
00714                 cmd[0] = USIM_CLA;
00715                 cmd[3] = 0x04;
00716                 get_resp[0] = USIM_CLA;
00717         }
00718 
00719         wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id);
00720         if (aid) {
00721                 wpa_hexdump(MSG_DEBUG, "SCARD: select file by AID",
00722                             aid, aidlen);
00723                 if (5 + aidlen > sizeof(cmd))
00724                         return -1;
00725                 cmd[2] = 0x04; /* Select by AID */
00726                 cmd[4] = aidlen; /* len */
00727                 os_memcpy(cmd + 5, aid, aidlen);
00728                 cmdlen = 5 + aidlen;
00729         } else {
00730                 cmd[5] = file_id >> 8;
00731                 cmd[6] = file_id & 0xff;
00732                 cmdlen = 7;
00733         }
00734         len = sizeof(resp);
00735         ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
00736         if (ret != SCARD_S_SUCCESS) {
00737                 wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
00738                            "(err=0x%lx)", ret);
00739                 return -1;
00740         }
00741 
00742         if (len != 2) {
00743                 wpa_printf(MSG_WARNING, "SCARD: unexpected resp len "
00744                            "%d (expected 2)", (int) len);
00745                 return -1;
00746         }
00747 
00748         if (resp[0] == 0x98 && resp[1] == 0x04) {
00749                 /* Security status not satisfied (PIN_WLAN) */
00750                 wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied "
00751                            "(PIN_WLAN)");
00752                 return -1;
00753         }
00754 
00755         if (resp[0] == 0x6e) {
00756                 wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported");
00757                 return -1;
00758         }
00759 
00760         if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) {
00761                 wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x "
00762                            "(expected 0x61, 0x6c, or 0x9f)", resp[0]);
00763                 return -1;
00764         }
00765         /* Normal ending of command; resp[1] bytes available */
00766         get_resp[4] = resp[1];
00767         wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)",
00768                    resp[1]);
00769 
00770         rlen = *buf_len;
00771         ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen);
00772         if (ret == SCARD_S_SUCCESS) {
00773                 *buf_len = resp[1] < rlen ? resp[1] : rlen;
00774                 return 0;
00775         }
00776 
00777         wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret);
00778         return -1;
00779 }
00780 
00781 
00782 static int scard_select_file(struct scard_data *scard, unsigned short file_id,
00783                              unsigned char *buf, size_t *buf_len)
00784 {
00785         return _scard_select_file(scard, file_id, buf, buf_len,
00786                                   scard->sim_type, NULL, 0);
00787 }
00788 
00789 
00790 static int scard_get_record_len(struct scard_data *scard, unsigned char recnum,
00791                                 unsigned char mode)
00792 {
00793         unsigned char buf[255];
00794         unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ };
00795         size_t blen;
00796         long ret;
00797 
00798         if (scard->sim_type == SCARD_USIM)
00799                 cmd[0] = USIM_CLA;
00800         cmd[2] = recnum;
00801         cmd[3] = mode;
00802         cmd[4] = sizeof(buf);
00803 
00804         blen = sizeof(buf);
00805         ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
00806         if (ret != SCARD_S_SUCCESS) {
00807                 wpa_printf(MSG_DEBUG, "SCARD: failed to determine file "
00808                            "length for record %d", recnum);
00809                 return -1;
00810         }
00811 
00812         wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response",
00813                     buf, blen);
00814 
00815         if (blen < 2 || buf[0] != 0x6c) {
00816                 wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file "
00817                            "length determination");
00818                 return -1;
00819         }
00820 
00821         return buf[1];
00822 }
00823 
00824 
00825 static int scard_read_record(struct scard_data *scard,
00826                              unsigned char *data, size_t len,
00827                              unsigned char recnum, unsigned char mode)
00828 {
00829         unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ };
00830         size_t blen = len + 3;
00831         unsigned char *buf;
00832         long ret;
00833 
00834         if (scard->sim_type == SCARD_USIM)
00835                 cmd[0] = USIM_CLA;
00836         cmd[2] = recnum;
00837         cmd[3] = mode;
00838         cmd[4] = len;
00839 
00840         buf = os_malloc(blen);
00841         if (buf == NULL)
00842                 return -1;
00843 
00844         ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
00845         if (ret != SCARD_S_SUCCESS) {
00846                 os_free(buf);
00847                 return -2;
00848         }
00849         if (blen != len + 2) {
00850                 wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected "
00851                            "length %ld (expected %ld)",
00852                            (long) blen, (long) len + 2);
00853                 os_free(buf);
00854                 return -3;
00855         }
00856 
00857         if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
00858                 wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected "
00859                            "status %02x %02x (expected 90 00)",
00860                            buf[len], buf[len + 1]);
00861                 os_free(buf);
00862                 return -4;
00863         }
00864 
00865         os_memcpy(data, buf, len);
00866         os_free(buf);
00867 
00868         return 0;
00869 }
00870 
00871 
00872 static int scard_read_file(struct scard_data *scard,
00873                            unsigned char *data, size_t len)
00874 {
00875         unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ };
00876         size_t blen = len + 3;
00877         unsigned char *buf;
00878         long ret;
00879 
00880         cmd[4] = len;
00881 
00882         buf = os_malloc(blen);
00883         if (buf == NULL)
00884                 return -1;
00885 
00886         if (scard->sim_type == SCARD_USIM)
00887                 cmd[0] = USIM_CLA;
00888         ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
00889         if (ret != SCARD_S_SUCCESS) {
00890                 os_free(buf);
00891                 return -2;
00892         }
00893         if (blen != len + 2) {
00894                 wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
00895                            "length %ld (expected %ld)",
00896                            (long) blen, (long) len + 2);
00897                 os_free(buf);
00898                 return -3;
00899         }
00900 
00901         if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
00902                 wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
00903                            "status %02x %02x (expected 90 00)",
00904                            buf[len], buf[len + 1]);
00905                 os_free(buf);
00906                 return -4;
00907         }
00908 
00909         os_memcpy(data, buf, len);
00910         os_free(buf);
00911 
00912         return 0;
00913 }
00914 
00915 
00916 static int scard_verify_pin(struct scard_data *scard, const char *pin)
00917 {
00918         long ret;
00919         unsigned char resp[3];
00920         unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 };
00921         size_t len;
00922 
00923         wpa_printf(MSG_DEBUG, "SCARD: verifying PIN");
00924 
00925         if (pin == NULL || os_strlen(pin) > 8)
00926                 return -1;
00927 
00928         if (scard->sim_type == SCARD_USIM)
00929                 cmd[0] = USIM_CLA;
00930         os_memcpy(cmd + 5, pin, os_strlen(pin));
00931         os_memset(cmd + 5 + os_strlen(pin), 0xff, 8 - os_strlen(pin));
00932 
00933         len = sizeof(resp);
00934         ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
00935         if (ret != SCARD_S_SUCCESS)
00936                 return -2;
00937 
00938         if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) {
00939                 wpa_printf(MSG_WARNING, "SCARD: PIN verification failed");
00940                 return -1;
00941         }
00942 
00943         wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully");
00944         return 0;
00945 }
00946 
00947 
00962 int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len)
00963 {
00964         unsigned char buf[100];
00965         size_t blen, imsilen, i;
00966         char *pos;
00967 
00968         wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI");
00969         blen = sizeof(buf);
00970         if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen))
00971                 return -1;
00972         if (blen < 4) {
00973                 wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI "
00974                            "header (len=%ld)", (long) blen);
00975                 return -2;
00976         }
00977 
00978         if (scard->sim_type == SCARD_GSM_SIM) {
00979                 blen = (buf[2] << 8) | buf[3];
00980         } else {
00981                 int file_size;
00982                 if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
00983                         return -3;
00984                 blen = file_size;
00985         }
00986         if (blen < 2 || blen > sizeof(buf)) {
00987                 wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%ld",
00988                            (long) blen);
00989                 return -3;
00990         }
00991 
00992         imsilen = (blen - 2) * 2 + 1;
00993         wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%ld imsilen=%ld",
00994                    (long) blen, (long) imsilen);
00995         if (blen < 2 || imsilen > *len) {
00996                 *len = imsilen;
00997                 return -4;
00998         }
00999 
01000         if (scard_read_file(scard, buf, blen))
01001                 return -5;
01002 
01003         pos = imsi;
01004         *pos++ = '0' + (buf[1] >> 4 & 0x0f);
01005         for (i = 2; i < blen; i++) {
01006                 unsigned char digit;
01007 
01008                 digit = buf[i] & 0x0f;
01009                 if (digit < 10)
01010                         *pos++ = '0' + digit;
01011                 else
01012                         imsilen--;
01013 
01014                 digit = buf[i] >> 4 & 0x0f;
01015                 if (digit < 10)
01016                         *pos++ = '0' + digit;
01017                 else
01018                         imsilen--;
01019         }
01020         *len = imsilen;
01021 
01022         return 0;
01023 }
01024 
01025 
01041 int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
01042                    unsigned char *sres, unsigned char *kc)
01043 {
01044         unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG };
01045         int cmdlen;
01046         unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
01047         unsigned char resp[3], buf[12 + 3 + 2];
01048         size_t len;
01049         long ret;
01050 
01051         if (scard == NULL)
01052                 return -1;
01053 
01054         wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", _rand, 16);
01055         if (scard->sim_type == SCARD_GSM_SIM) {
01056                 cmdlen = 5 + 16;
01057                 os_memcpy(cmd + 5, _rand, 16);
01058         } else {
01059                 cmdlen = 5 + 1 + 16;
01060                 cmd[0] = USIM_CLA;
01061                 cmd[3] = 0x80;
01062                 cmd[4] = 17;
01063                 cmd[5] = 16;
01064                 os_memcpy(cmd + 6, _rand, 16);
01065         }
01066         len = sizeof(resp);
01067         ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
01068         if (ret != SCARD_S_SUCCESS)
01069                 return -2;
01070 
01071         if ((scard->sim_type == SCARD_GSM_SIM &&
01072              (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) ||
01073             (scard->sim_type == SCARD_USIM &&
01074              (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) {
01075                 wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM "
01076                            "auth request (len=%ld resp=%02x %02x)",
01077                            (long) len, resp[0], resp[1]);
01078                 return -3;
01079         }
01080         get_resp[4] = resp[1];
01081 
01082         len = sizeof(buf);
01083         ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
01084         if (ret != SCARD_S_SUCCESS)
01085                 return -4;
01086 
01087         if (scard->sim_type == SCARD_GSM_SIM) {
01088                 if (len != 4 + 8 + 2) {
01089                         wpa_printf(MSG_WARNING, "SCARD: unexpected data "
01090                                    "length for GSM auth (len=%ld, expected 14)",
01091                                    (long) len);
01092                         return -5;
01093                 }
01094                 os_memcpy(sres, buf, 4);
01095                 os_memcpy(kc, buf + 4, 8);
01096         } else {
01097                 if (len != 1 + 4 + 1 + 8 + 2) {
01098                         wpa_printf(MSG_WARNING, "SCARD: unexpected data "
01099                                    "length for USIM auth (len=%ld, "
01100                                    "expected 16)", (long) len);
01101                         return -5;
01102                 }
01103                 if (buf[0] != 4 || buf[5] != 8) {
01104                         wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc "
01105                                    "length (%d %d, expected 4 8)",
01106                                    buf[0], buf[5]);
01107                 }
01108                 os_memcpy(sres, buf + 1, 4);
01109                 os_memcpy(kc, buf + 6, 8);
01110         }
01111 
01112         wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4);
01113         wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8);
01114 
01115         return 0;
01116 }
01117 
01118 
01139 int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
01140                     const unsigned char *autn,
01141                     unsigned char *res, size_t *res_len,
01142                     unsigned char *ik, unsigned char *ck, unsigned char *auts)
01143 {
01144         unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] =
01145                 { USIM_CMD_RUN_UMTS_ALG };
01146         unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE };
01147         unsigned char resp[3], buf[64], *pos, *end;
01148         size_t len;
01149         long ret;
01150 
01151         if (scard == NULL)
01152                 return -1;
01153 
01154         if (scard->sim_type == SCARD_GSM_SIM) {
01155                 wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS "
01156                            "auth");
01157                 return -1;
01158         }
01159 
01160         wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", _rand, AKA_RAND_LEN);
01161         wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN);
01162         cmd[5] = AKA_RAND_LEN;
01163         os_memcpy(cmd + 6, _rand, AKA_RAND_LEN);
01164         cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN;
01165         os_memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN);
01166 
01167         len = sizeof(resp);
01168         ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
01169         if (ret != SCARD_S_SUCCESS)
01170                 return -1;
01171 
01172         if (len <= sizeof(resp))
01173                 wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len);
01174 
01175         if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) {
01176                 wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - "
01177                            "MAC != XMAC");
01178                 return -1;
01179         } else if (len != 2 || resp[0] != 0x61) {
01180                 wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS "
01181                            "auth request (len=%ld resp=%02x %02x)",
01182                            (long) len, resp[0], resp[1]);
01183                 return -1;
01184         }
01185         get_resp[4] = resp[1];
01186 
01187         len = sizeof(buf);
01188         ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
01189         if (ret != SCARD_S_SUCCESS || len > sizeof(buf))
01190                 return -1;
01191 
01192         wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len);
01193         if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc &&
01194             buf[1] == AKA_AUTS_LEN) {
01195                 wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure");
01196                 os_memcpy(auts, buf + 2, AKA_AUTS_LEN);
01197                 wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN);
01198                 return -2;
01199         } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) {
01200                 pos = buf + 1;
01201                 end = buf + len;
01202 
01203                 /* RES */
01204                 if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) {
01205                         wpa_printf(MSG_DEBUG, "SCARD: Invalid RES");
01206                         return -1;
01207                 }
01208                 *res_len = *pos++;
01209                 os_memcpy(res, pos, *res_len);
01210                 pos += *res_len;
01211                 wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len);
01212 
01213                 /* CK */
01214                 if (pos[0] != CK_LEN || pos + CK_LEN > end) {
01215                         wpa_printf(MSG_DEBUG, "SCARD: Invalid CK");
01216                         return -1;
01217                 }
01218                 pos++;
01219                 os_memcpy(ck, pos, CK_LEN);
01220                 pos += CK_LEN;
01221                 wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN);
01222 
01223                 /* IK */
01224                 if (pos[0] != IK_LEN || pos + IK_LEN > end) {
01225                         wpa_printf(MSG_DEBUG, "SCARD: Invalid IK");
01226                         return -1;
01227                 }
01228                 pos++;
01229                 os_memcpy(ik, pos, IK_LEN);
01230                 pos += IK_LEN;
01231                 wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN);
01232 
01233                 return 0;
01234         }
01235 
01236         wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response");
01237         return -1;
01238 }


wpa_supplicant_node
Author(s): Package maintained by Blaise Gassend
autogenerated on Thu Apr 24 2014 15:33:21