/************************* READER MANAGER DISCONNECT ***********/ int readersManagerDisconnect( tReaderManager *pManager ) { LONG rv; if ( pManager == NULL ) return( SCARD_E_INVALID_PARAMETER ); if ( pManager->hContext ) { readersLogMessage( pManager, LOG_INFO, 2, "Disconnecting from pcscd server"); rv = SCardReleaseContext( (SCARDCONTEXT) (pManager->hContext) ); if ( rv != SCARD_S_SUCCESS ) PCSC_ERROR( pManager, rv, "SCardReleaseContext"); /* libreate memory that was allocated when we connected to manager */ if (pManager->mszReaders) { free(pManager->mszReaders); pManager->mszReaders = NULL; } } else rv = SCARD_E_INVALID_PARAMETER; /* now it should be NULL either way */ pManager->hContext = NULL; eventDispatch( PCSCD_DISCONNECT, NULL, 0, pManager ); return( rv ); }
static int pcsc_internal_transmit(sc_reader_t *reader, sc_slot_info_t *slot, const u8 *sendbuf, size_t sendsize, u8 *recvbuf, size_t *recvsize, unsigned long control) { struct pcsc_private_data *priv = GET_PRIV_DATA(reader); SCARD_IO_REQUEST sSendPci, sRecvPci; DWORD dwSendLength, dwRecvLength; LONG rv; SCARDHANDLE card; struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); SC_FUNC_CALLED(reader->ctx, 3); assert(pslot != NULL); card = pslot->pcsc_card; sSendPci.dwProtocol = opensc_proto_to_pcsc(slot->active_protocol); sSendPci.cbPciLength = sizeof(sSendPci); sRecvPci.dwProtocol = opensc_proto_to_pcsc(slot->active_protocol); sRecvPci.cbPciLength = sizeof(sRecvPci); dwSendLength = sendsize; dwRecvLength = *recvsize; if (!control) { rv = priv->gpriv->SCardTransmit(card, &sSendPci, sendbuf, dwSendLength, &sRecvPci, recvbuf, &dwRecvLength); } else { if (priv->gpriv->SCardControlOLD != NULL) { rv = priv->gpriv->SCardControlOLD(card, sendbuf, dwSendLength, recvbuf, &dwRecvLength); } else { rv = priv->gpriv->SCardControl(card, (DWORD) control, sendbuf, dwSendLength, recvbuf, dwRecvLength, &dwRecvLength); } } if (rv != SCARD_S_SUCCESS) { switch (rv) { case SCARD_W_REMOVED_CARD: return SC_ERROR_CARD_REMOVED; case SCARD_E_NOT_TRANSACTED: if (!(pcsc_detect_card_presence(reader, slot) & SC_SLOT_CARD_PRESENT)) return SC_ERROR_CARD_REMOVED; return SC_ERROR_TRANSMIT_FAILED; default: /* Windows' PC/SC returns 0x8010002f (??) if a card is removed */ if (pcsc_detect_card_presence(reader, slot) != 1) return SC_ERROR_CARD_REMOVED; PCSC_ERROR(reader->ctx, "SCardTransmit failed", rv); return SC_ERROR_TRANSMIT_FAILED; } } if (!control && dwRecvLength < 2) return SC_ERROR_UNKNOWN_DATA_RECEIVED; *recvsize = dwRecvLength; return SC_SUCCESS; }
/* then they will all use the same context and avoid overhead */ int readersManagerConnect( tReaderManager *pManager ) { LONG rv; if ( pManager == NULL ) return( SCARD_E_INVALID_PARAMETER ); rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, (LPSCARDCONTEXT)&(pManager->hContext) ); if (rv != SCARD_S_SUCCESS) { PCSC_ERROR( pManager, rv, "SCardEstablishContext"); pManager->hContext = NULL; eventDispatch( PCSCD_FAIL, NULL, 0, pManager ); return( rv ); } eventDispatch( PCSCD_CONNECT, NULL, 0, pManager ); /* Find all the readers and fill the readerManager data structure */ rv = readersEnumerate( pManager ); return (rv); }
/* TODO : this function hasn't really been tested */ int readerGetContactlessStatus( tReaderManager *pManager, tReader *pReader ) { DWORD dwRecvLength; BYTE pbRecvBuffer[20]; LONG rv; int i; BOOL automatic; /* Duh. If you pass me a NULL pointer then I'm out of here */ if ( ( pManager == NULL ) || ( pManager->hContext == NULL ) ) return( SCARD_E_INVALID_PARAMETER ); automatic = readersSettingBitmapBitTest( pManager, READER_BIT_AUTO ); if ( automatic ) { /* re-enumerate the readers in the system as it may have changed */ rv = readersConnect( pManager ); if ( rv != SCARD_S_SUCCESS ) return( rv ); } sprintf(messageString, "Requesting contactles status"); readersLogMessage( pManager, LOG_INFO, 2, messageString); /* then check all readers we were ABLE to connect to */ for ( i = 0; i < pManager->nbReaders; i++ ) { /* check we have connected and have a driver for this reader */ if ( ( pReader[i].hCard != NULL ) && ( pReader[i].pDriver != NULL ) && ( automatic || readersSettingBitmapNumberTest( pManager, i ) ) ) { dwRecvLength = sizeof(pbRecvBuffer); rv = ((tReaderDriver *)(pReader[i].pDriver))->getContactlessStatus(pReader, pbRecvBuffer, &dwRecvLength); if ( rv == SCARD_S_SUCCESS ) { if (pManager->libVerbosityLevel) { sprintf(messageString, "Reader %d Status: ", i); sPrintBufferHex( (messageString + strlen("Reader %d Status: ") ), dwRecvLength, pbRecvBuffer); readersLogMessage( pManager, LOG_INFO, 2, messageString); sprintf(messageString, "Number of Tags = %d", pbRecvBuffer[4]); readersLogMessage( pManager, LOG_INFO, 2, messageString); } } else PCSC_ERROR( pManager, rv, "driver->getConnectlessStatus:"); } } return (rv); }
static int pcsc_lock(sc_reader_t *reader, sc_slot_info_t *slot) { long rv; struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); struct pcsc_private_data *priv = GET_PRIV_DATA(reader); SC_FUNC_CALLED(reader->ctx, 3); assert(pslot != NULL); rv = priv->gpriv->SCardBeginTransaction(pslot->pcsc_card); switch (rv) { case SCARD_E_INVALID_HANDLE: case SCARD_E_READER_UNAVAILABLE: rv = pcsc_connect(reader, slot); if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardConnect failed", rv); return pcsc_ret_to_error(rv); } /* return failure so that upper layers will be notified and try to lock again */ return SC_ERROR_READER_REATTACHED; case SCARD_W_RESET_CARD: /* try to reconnect if the card was reset by some other application */ rv = pcsc_reconnect(reader, slot, 0); if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardReconnect failed", rv); return pcsc_ret_to_error(rv); } /* return failure so that upper layers will be notified and try to lock again */ return SC_ERROR_CARD_RESET; case SCARD_S_SUCCESS: pslot->locked = 1; return SC_SUCCESS; default: PCSC_ERROR(reader->ctx, "SCardBeginTransaction failed", rv); return pcsc_ret_to_error(rv); } }
static int pcsc_unlock(sc_reader_t *reader, sc_slot_info_t *slot) { long rv; struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); struct pcsc_private_data *priv = GET_PRIV_DATA(reader); SC_FUNC_CALLED(reader->ctx, 3); assert(pslot != NULL); rv = priv->gpriv->SCardEndTransaction(pslot->pcsc_card, priv->gpriv->transaction_reset ? SCARD_RESET_CARD : SCARD_LEAVE_CARD); pslot->locked = 0; if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardEndTransaction failed", rv); return pcsc_ret_to_error(rv); } return SC_SUCCESS; }
static int pcsc_reconnect(sc_reader_t * reader, sc_slot_info_t * slot, int reset) { DWORD active_proto, protocol; LONG rv; struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); struct pcsc_private_data *priv = GET_PRIV_DATA(reader); int r; sc_debug(reader->ctx, "Reconnecting to the card..."); r = refresh_slot_attributes(reader, slot); if (r) return r; if (!(slot->flags & SC_SLOT_CARD_PRESENT)) return SC_ERROR_CARD_NOT_PRESENT; /* reconnect always unlocks transaction */ pslot->locked = 0; rv = priv->gpriv->SCardReconnect(pslot->pcsc_card, priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED, SCARD_PROTOCOL_ANY, reset ? SCARD_UNPOWER_CARD : SCARD_LEAVE_CARD, &active_proto); /* Check for protocol difference */ if (rv == SCARD_S_SUCCESS && _sc_check_forced_protocol (reader->ctx, slot->atr, slot->atr_len, (unsigned int *)&protocol)) { protocol = opensc_proto_to_pcsc(protocol); if (pcsc_proto_to_opensc(active_proto) != protocol) { rv = priv->gpriv->SCardReconnect(pslot->pcsc_card, priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED, protocol, SCARD_UNPOWER_CARD, &active_proto); } } if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardReconnect failed", rv); return rv; } slot->active_protocol = pcsc_proto_to_opensc(active_proto); return rv; }
/************************ READER DISCONNECT ********************/ void readersDisconnect( tReaderManager *pManager ) { LONG rv; int i; /* Duh. If you pass me a NULL pointer then I'm out of here */ if ( ( pManager == NULL ) || ( pManager->hContext == NULL ) ) return; if ( readersSettingBitmapBitTest( pManager, READER_BIT_AUTO ) ) { /* re-enumerate the readers in the system as it may have changed */ rv = readersEnumerate( pManager ); if ( rv != SCARD_S_SUCCESS ) return; } /* then check all readers we were ABLE to connect to */ for ( i = 0; i < pManager->nbReaders; i++ ) { if ( pManager->readers[i].hCard != NULL ) { sprintf(messageString, "Disconnecting from reader %d", i ); readersLogMessage( pManager, LOG_INFO, 2, messageString); rv = SCardDisconnect( (SCARDHANDLE) (pManager->readers[i].hCard), SCARD_UNPOWER_CARD); RESET_READER( pManager->readers[i] ); eventDispatch( READER_DISCONNECT, NULL, i, pManager ); if ( rv != SCARD_S_SUCCESS ) PCSC_ERROR( pManager, rv, "SCardDisconnect"); } } }
static int pcsc_detect_readers(sc_context_t *ctx, void *prv_data) { struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) prv_data; LONG rv; DWORD reader_buf_size; char *reader_buf = NULL, *reader_name; const char *mszGroups = NULL; int ret = SC_ERROR_INTERNAL; SC_FUNC_CALLED(ctx, 3); if (!gpriv) { ret = SC_ERROR_NO_READERS_FOUND; goto out; } sc_debug(ctx, "Probing pcsc readers"); do { if (gpriv->pcsc_ctx == -1) { /* * Cannot call SCardListReaders with -1 * context as in Windows ERROR_INVALID_HANDLE * is returned instead of SCARD_E_INVALID_HANDLE */ rv = SCARD_E_INVALID_HANDLE; } else { rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, NULL, NULL, (LPDWORD) &reader_buf_size); } if (rv != SCARD_S_SUCCESS) { if (rv != SCARD_E_INVALID_HANDLE) { PCSC_ERROR(ctx, "SCardListReaders failed", rv); ret = pcsc_ret_to_error(rv); goto out; } sc_debug(ctx, "Establish pcsc context"); rv = gpriv->SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &gpriv->pcsc_ctx); if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(ctx, "SCardEstablishContext failed", rv); ret = pcsc_ret_to_error(rv); goto out; } rv = SCARD_E_INVALID_HANDLE; } } while (rv != SCARD_S_SUCCESS); reader_buf = (char *) malloc(sizeof(char) * reader_buf_size); if (!reader_buf) { ret = SC_ERROR_OUT_OF_MEMORY; goto out; } rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, mszGroups, reader_buf, (LPDWORD) &reader_buf_size); if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(ctx, "SCardListReaders failed", rv); ret = pcsc_ret_to_error(rv); goto out; } for (reader_name = reader_buf; *reader_name != '\x0'; reader_name += strlen (reader_name) + 1) { sc_reader_t *reader = NULL; struct pcsc_private_data *priv = NULL; struct pcsc_slot_data *pslot = NULL; sc_slot_info_t *slot = NULL; unsigned int i; int found = 0; for (i=0;i < sc_ctx_get_reader_count (ctx) && !found;i++) { sc_reader_t *reader2 = sc_ctx_get_reader (ctx, i); if (reader2 == NULL) { ret = SC_ERROR_INTERNAL; goto err1; } if (reader2->ops == &pcsc_ops && !strcmp (reader2->name, reader_name)) { found = 1; } } /* Reader already available, skip */ if (found) { continue; } sc_debug(ctx, "Found new pcsc reader '%s'", reader_name); if ((reader = (sc_reader_t *) calloc(1, sizeof(sc_reader_t))) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err1; } if ((priv = (struct pcsc_private_data *) malloc(sizeof(struct pcsc_private_data))) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err1; } if ((pslot = (struct pcsc_slot_data *) malloc(sizeof(struct pcsc_slot_data))) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err1; } reader->drv_data = priv; reader->ops = &pcsc_ops; reader->driver = &pcsc_drv; reader->slot_count = 1; if ((reader->name = strdup(reader_name)) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err1; } priv->gpriv = gpriv; if ((priv->reader_name = strdup(reader_name)) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err1; } slot = &reader->slot[0]; memset(slot, 0, sizeof(*slot)); slot->drv_data = pslot; memset(pslot, 0, sizeof(*pslot)); if (_sc_add_reader(ctx, reader)) { ret = SC_SUCCESS; /* silent ignore */ goto err1; } refresh_slot_attributes(reader, slot); continue; err1: if (priv != NULL) { if (priv->reader_name) free(priv->reader_name); free(priv); } if (reader != NULL) { if (reader->name) free(reader->name); free(reader); } if (slot != NULL) free(pslot); goto out; } ret = SC_SUCCESS; out: if (reader_buf != NULL) free (reader_buf); SC_FUNC_RETURN(ctx, 3, ret); }
static int pcsc_connect(sc_reader_t *reader, sc_slot_info_t *slot) { DWORD active_proto, protocol; SCARDHANDLE card_handle; LONG rv; struct pcsc_private_data *priv = GET_PRIV_DATA(reader); struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); int r; u8 feature_buf[256], rbuf[SC_MAX_APDU_BUFFER_SIZE]; size_t rcount; DWORD i, feature_len, display_ioctl = 0; PCSC_TLV_STRUCTURE *pcsc_tlv; r = refresh_slot_attributes(reader, slot); if (r) return r; if (!(slot->flags & SC_SLOT_CARD_PRESENT)) return SC_ERROR_CARD_NOT_PRESENT; /* Always connect with whatever protocol possible */ rv = priv->gpriv->SCardConnect(priv->gpriv->pcsc_ctx, priv->reader_name, priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED, SCARD_PROTOCOL_ANY, &card_handle, &active_proto); if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardConnect failed", rv); return pcsc_ret_to_error(rv); } slot->active_protocol = pcsc_proto_to_opensc(active_proto); pslot->pcsc_card = card_handle; /* after connect reader is not locked yet */ pslot->locked = 0; sc_debug(reader->ctx, "After connect protocol = %d", slot->active_protocol); /* If we need a specific protocol, reconnect if needed */ if (_sc_check_forced_protocol(reader->ctx, slot->atr, slot->atr_len, (unsigned int *) &protocol)) { /* If current protocol differs from the protocol we want to force */ if (slot->active_protocol != protocol) { sc_debug(reader->ctx, "Protocol difference, forcing protocol (%d)", protocol); /* Reconnect with a reset. pcsc_reconnect figures out the right forced protocol */ rv = pcsc_reconnect(reader, slot, 1); if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardReconnect (to force protocol) failed", rv); return pcsc_ret_to_error(rv); } sc_debug(reader->ctx, "Proto after reconnect = %d", slot->active_protocol); } } /* check for pinpad support */ if (priv->gpriv->SCardControl != NULL) { sc_debug(reader->ctx, "Requesting reader features ... "); rv = priv->gpriv->SCardControl(pslot->pcsc_card, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, feature_buf, sizeof(feature_buf), &feature_len); if (rv != SCARD_S_SUCCESS) { sc_debug(reader->ctx, "SCardControl failed %08x", rv); } else { if ((feature_len % sizeof(PCSC_TLV_STRUCTURE)) != 0) { sc_debug(reader->ctx, "Inconsistent TLV from reader!"); } else { char *log_disabled = "but it's disabled in configuration file"; /* get the number of elements instead of the complete size */ feature_len /= sizeof(PCSC_TLV_STRUCTURE); pcsc_tlv = (PCSC_TLV_STRUCTURE *)feature_buf; for (i = 0; i < feature_len; i++) { if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_DIRECT) { pslot->verify_ioctl = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_START) { pslot->verify_ioctl_start = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_FINISH) { pslot->verify_ioctl_finish = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_DIRECT) { pslot->modify_ioctl = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_START) { pslot->modify_ioctl_start = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_FINISH) { pslot->modify_ioctl_finish = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_IFD_PIN_PROPERTIES) { display_ioctl = ntohl(pcsc_tlv[i].value); } else { sc_debug(reader->ctx, "Reader feature %02x is not supported", pcsc_tlv[i].tag); } } /* Set slot capabilities based on detected IOCTLs */ if (pslot->verify_ioctl || (pslot->verify_ioctl_start && pslot->verify_ioctl_finish)) { char *log_text = "Reader supports pinpad PIN verification"; if (priv->gpriv->enable_pinpad) { sc_debug(reader->ctx, log_text); slot->capabilities |= SC_SLOT_CAP_PIN_PAD; } else { sc_debug(reader->ctx, "%s %s", log_text, log_disabled); } } if (pslot->modify_ioctl || (pslot->modify_ioctl_start && pslot->modify_ioctl_finish)) { char *log_text = "Reader supports pinpad PIN modification"; if (priv->gpriv->enable_pinpad) { sc_debug(reader->ctx, log_text); slot->capabilities |= SC_SLOT_CAP_PIN_PAD; } else { sc_debug(reader->ctx, "%s %s", log_text, log_disabled); } } if (display_ioctl) { rcount = sizeof(rbuf); r = pcsc_internal_transmit(reader, slot, NULL, 0, rbuf, &rcount, display_ioctl); if (r == SC_SUCCESS) { if (rcount != sizeof(PIN_PROPERTIES_STRUCTURE)) { PIN_PROPERTIES_STRUCTURE *caps = (PIN_PROPERTIES_STRUCTURE *)rbuf; if (caps->wLcdLayout > 0) { sc_debug(reader->ctx, "Reader has a display: %04X", caps->wLcdLayout); slot->capabilities |= SC_SLOT_CAP_DISPLAY; } else sc_debug(reader->ctx, "Reader does not have a display."); } else { sc_debug(reader->ctx, "Returned PIN properties structure has bad length (%d)", rcount); } } } } } } return SC_SUCCESS; }
/* Wait for an event to occur. * This function ignores the list of slots, because with * pcsc we have a 1:1 mapping of readers and slots anyway */ static int pcsc_wait_for_event(sc_reader_t **readers, sc_slot_info_t **slots, size_t nslots, unsigned int event_mask, int *reader, unsigned int *event, int timeout) { struct pcsc_private_data *priv = GET_PRIV_DATA(readers[0]); sc_context_t *ctx; SCARDCONTEXT pcsc_ctx; LONG ret; SCARD_READERSTATE_A rgReaderStates[SC_MAX_READERS]; unsigned long on_bits, off_bits; time_t end_time, now, delta; size_t i; /* Prevent buffer overflow */ if (nslots >= SC_MAX_READERS) return SC_ERROR_INVALID_ARGUMENTS; on_bits = off_bits = 0; if (event_mask & SC_EVENT_CARD_INSERTED) { event_mask &= ~SC_EVENT_CARD_INSERTED; on_bits |= SCARD_STATE_PRESENT; } if (event_mask & SC_EVENT_CARD_REMOVED) { event_mask &= ~SC_EVENT_CARD_REMOVED; off_bits |= SCARD_STATE_PRESENT; } if (event_mask != 0) return SC_ERROR_INVALID_ARGUMENTS; /* Find out the current status */ ctx = readers[0]->ctx; pcsc_ctx = priv->gpriv->pcsc_ctx; for (i = 0; i < nslots; i++) { struct pcsc_private_data *priv2 = GET_PRIV_DATA(readers[i]); rgReaderStates[i].szReader = priv2->reader_name; rgReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE; rgReaderStates[i].dwEventState = SCARD_STATE_UNAWARE; /* Can we handle readers from different PCSC contexts? */ if (priv2->gpriv->pcsc_ctx != pcsc_ctx) return SC_ERROR_INVALID_ARGUMENTS; } ret = priv->gpriv->SCardGetStatusChange(pcsc_ctx, 0, rgReaderStates, nslots); if (ret != SCARD_S_SUCCESS) { PCSC_ERROR(ctx, "SCardGetStatusChange(1) failed", ret); return pcsc_ret_to_error(ret); } time(&now); end_time = now + (timeout + 999) / 1000; /* Wait for a status change and return if it's a card insert/removal */ for( ; ; ) { SCARD_READERSTATE_A *rsp; /* Scan the current state of all readers to see if they * match any of the events we're polling for */ *event = 0; for (i = 0, rsp = rgReaderStates; i < nslots; i++, rsp++) { unsigned long state, prev_state; prev_state = rsp->dwCurrentState; state = rsp->dwEventState; if ((state & on_bits & SCARD_STATE_PRESENT) && (prev_state & SCARD_STATE_EMPTY)) *event |= SC_EVENT_CARD_INSERTED; if ((~state & off_bits & SCARD_STATE_PRESENT) && (prev_state & SCARD_STATE_PRESENT)) *event |= SC_EVENT_CARD_REMOVED; if (*event) { *reader = i; return SC_SUCCESS; } /* No match - copy the state so pcscd knows * what to watch out for */ rsp->dwCurrentState = rsp->dwEventState; } /* Set the timeout if caller wants to time out */ if (timeout == 0) return SC_ERROR_EVENT_TIMEOUT; if (timeout > 0) { time(&now); if (now >= end_time) return SC_ERROR_EVENT_TIMEOUT; delta = end_time - now; } else { delta = 3600; } ret = priv->gpriv->SCardGetStatusChange(pcsc_ctx, 1000 * delta, rgReaderStates, nslots); if (ret == (LONG) SCARD_E_TIMEOUT) { if (timeout < 0) continue; return SC_ERROR_EVENT_TIMEOUT; } if (ret != SCARD_S_SUCCESS) { PCSC_ERROR(ctx, "SCardGetStatusChange(2) failed", ret); return pcsc_ret_to_error(ret); } } }
static int refresh_slot_attributes(sc_reader_t *reader, sc_slot_info_t *slot) { struct pcsc_private_data *priv = GET_PRIV_DATA(reader); struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); LONG ret; SC_FUNC_CALLED(reader->ctx, 3); if (pslot->reader_state.szReader == NULL) { pslot->reader_state.szReader = priv->reader_name; pslot->reader_state.dwCurrentState = SCARD_STATE_UNAWARE; pslot->reader_state.dwEventState = SCARD_STATE_UNAWARE; } else { pslot->reader_state.dwCurrentState = pslot->reader_state.dwEventState; } ret = priv->gpriv->SCardGetStatusChange(priv->gpriv->pcsc_ctx, 0, &pslot->reader_state, 1); if (ret == (LONG)SCARD_E_TIMEOUT) { /* timeout: nothing changed */ slot->flags &= ~SCARD_STATE_CHANGED; return 0; } if (ret != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardGetStatusChange failed", ret); return pcsc_ret_to_error(ret); } if (pslot->reader_state.dwEventState & SCARD_STATE_PRESENT) { int old_flags = slot->flags; int maybe_changed = 0; slot->flags |= SC_SLOT_CARD_PRESENT; slot->atr_len = pslot->reader_state.cbAtr; if (slot->atr_len > SC_MAX_ATR_SIZE) slot->atr_len = SC_MAX_ATR_SIZE; memcpy(slot->atr, pslot->reader_state.rgbAtr, slot->atr_len); #ifndef _WIN32 /* On Linux, SCARD_STATE_CHANGED always means there was an * insert or removal. But we may miss events that way. */ if (pslot->reader_state.dwEventState & SCARD_STATE_CHANGED) { slot->flags |= SC_SLOT_CARD_CHANGED; } else { maybe_changed = 1; } #else /* On windows, SCARD_STATE_CHANGED is turned on by lots of * other events, so it gives us a lot of false positives. * But if it's off, there really was no change */ if (pslot->reader_state.dwEventState & SCARD_STATE_CHANGED) { maybe_changed = 1; } #endif /* If we aren't sure if the card state changed, check if * the card handle is still valid. If the card changed, * the handle will be invalid. */ slot->flags &= ~SC_SLOT_CARD_CHANGED; if (maybe_changed) { if (old_flags & SC_SLOT_CARD_PRESENT) { DWORD readers_len = 0, state, prot, atr_len = SC_MAX_ATR_SIZE; unsigned char atr[SC_MAX_ATR_SIZE]; LONG rv = priv->gpriv->SCardStatus(pslot->pcsc_card, NULL, &readers_len, &state, &prot, atr, &atr_len); if (rv == (LONG)SCARD_W_REMOVED_CARD) slot->flags |= SC_SLOT_CARD_CHANGED; } else slot->flags |= SC_SLOT_CARD_CHANGED; } } else { slot->flags &= ~(SC_SLOT_CARD_PRESENT|SC_SLOT_CARD_CHANGED); } return 0; }
/* function allocates the memory for the list you must handle it!*/ int readersGetTagList( tReaderManager *pManager ) { LONG rv = SCARD_S_SUCCESS; tTag *pTags[MAX_NUM_READERS]; /* an array of pointers to tags (that act like arrays) ! */ int i, j; int uniqueListIndex; BOOL automatic; char *namePointer; /* Duh. If you pass me a NULL pointer then I'm out of here */ if ( ( pManager == NULL ) || ( pManager->hContext == NULL ) ) return( SCARD_E_INVALID_PARAMETER ); automatic = readersSettingBitmapBitTest( pManager, READER_BIT_AUTO ); if ( automatic ) { /* re-connect the readers in the system as it may have changed */ rv = readersConnect( pManager ); if ( rv != SCARD_S_SUCCESS ) return( rv ); } /* before we start, reset the total count to 0 */ pManager->tagList.numTags = 0; /* for all readers we were connected to PREVIOUSLY or are now after AUTMATIC reconnect */ for ( i = 0; i < pManager->nbReaders; i++ ) { /* make sure it's initialized, as depending on reader settings we may skip over */ /* one of these pointers in the array and later try to free an invalid pointer */ pTags[i] = NULL; pManager->readers[i].tagList.numTags = 0; /* check we are connected, have a driver and should be reading it */ if ( ( pManager->readers[i].hCard != NULL ) && ( pManager->readers[i].pDriver != NULL ) && ( automatic || readersSettingBitmapNumberTest( pManager, i ) ) ) { /* I'd normally check if a tag is present using readerGetContactlessStatus() before */ /* querying the tag list, but all my testing to date has failed to get the contactless */ /* status APDU to work, it always returns D5 05 00 00 00 80 90 00 to indicate no tag */ /* is present. I have reported this issue to ACS by e-mail - Andrew */ /* allocate the structure for this reader to read tag list into upto max size */ pTags[i] = (tTag *)malloc( ( ((tReaderDriver *)(pManager->readers[i].pDriver))->maxTags ) * sizeof(tTag) ); /* call the reader's associated driver's function to read the tag list */ rv = ((tReaderDriver *)(pManager->readers[i].pDriver))->getTagList( &(pManager->readers[i]), pTags[i] ); if ( rv != SCARD_S_SUCCESS ) { PCSC_ERROR( pManager, rv, "driver->getTagList():"); RESET_READER( pManager->readers[i] ); if ( pTags[i] != NULL ) free( pTags[i] ); pTags[i] = NULL; } /* accumulate the total number of tags found */ pManager->tagList.numTags += pManager->readers[i].tagList.numTags; } } /* now mash them all up into one big list */ pManager->tagList.pTags = NULL; /* for the zero tag case */ uniqueListIndex = 0; if ( pManager->tagList.numTags > 0 ) { pManager->tagList.pTags = (tTag *)malloc( (pManager->tagList.numTags) * sizeof( tTag ) ); /* copy all the individual lists across into the unique list */ for( i = 0; i < pManager->nbReaders; i++) { /* make the pointer in the per-reader structure point to it's parts of the overall list */ pManager->readers[i].tagList.pTags = &(pManager->tagList.pTags[uniqueListIndex]); /* for each of the tags detected in this reader */ for ( j = 0; j < pManager->readers[i].tagList.numTags; j++ ) { /* copy the tag from the tempory list to the unique one */ pManager->tagList.pTags[uniqueListIndex] = (pTags[i])[j]; TAG_TYPE_NAME_FROM_ENUM( pManager->tagList.pTags[uniqueListIndex].tagType, namePointer ); sprintf(messageString, "Tag ID: %s\tType: %s", pManager->tagList.pTags[uniqueListIndex].uid, namePointer); readersLogMessage( pManager, LOG_INFO, 2, messageString); uniqueListIndex++; } /* free the space allocated for that list, even if it was never filled with anything */ if ( pTags[i] ) free( pTags[i] ); } } sprintf( messageString, "Total Number of tags: %d", (int)(pManager->tagList.numTags) ); readersLogMessage( pManager, LOG_INFO, 2, messageString ); return (rv); }
/************************* READER CONNECT **********************/ int readersConnect ( tReaderManager *pManager ) { LONG rv; BOOL readerSupported; DWORD dwActiveProtocol; int i, num; BOOL automatic; /* Duh. If you pass me a NULL pointer then I'm out of here */ if ( pManager == NULL ) return( SCARD_E_INVALID_PARAMETER ); automatic = ( readersSettingBitmapBitTest( pManager, READER_BIT_AUTO ) != 0 ); if ( automatic ) { /* re-enumerate the readers in the system as it may have changed */ rv = readersEnumerate( pManager ); if ( rv != SCARD_S_SUCCESS ) return( rv ); } if ( pManager->nbReaders == 0 ) return( SCARD_E_NO_READERS_AVAILABLE ); /* try and connect to all readers that are present according to readerSetting */ for ( num = 0; num < pManager->nbReaders; num++ ) { /* if we are not already connected and should be trying then do so */ if ( ( pManager->readers[num].hCard == NULL) && ( automatic || readersSettingBitmapNumberTest( pManager, num ) ) ) { dwActiveProtocol = -1; rv = SCardConnect( (SCARDCONTEXT)(pManager->hContext), pManager->readers[num].name, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 , (LPSCARDHANDLE) &(pManager->readers[num].hCard), &dwActiveProtocol); if (rv == SCARD_S_SUCCESS) { eventDispatch( READER_DETECTED, NULL, num, pManager ); /* Query the drivers in the list until one of them can handle the reader */ i = 0; readerSupported = FALSE; /* call the function to check if this driver works with this reader */ while ( (readerDriverTable[i] != NULL) && (readerSupported == FALSE) ) rv = readerDriverTable[i++]->readerCheck( &(pManager->readers[num]), &readerSupported ); if ( rv != SCARD_S_SUCCESS ) { RESET_READER( pManager->readers[num] ); PCSC_ERROR( pManager, rv, "readerCheck:" ); return( rv ); } /* we couldn't find a driver that knows how to handle this reader... */ if ( ( readerSupported == FALSE) ) { pManager->readers[num].pDriver = NULL; sprintf(messageString, "Reader (%s) not supported by any known driver", pManager->readers[num].name ); readersLogMessage( pManager, LOG_ERR, 0, messageString); return (SCARD_E_UNKNOWN_READER); } else { /* if we got this far then a driver was successfully found, remember it! */ pManager->readers[num].pDriver = readerDriverTable[i -1]; pManager->readers[num].driverDescriptor = readerDriverTable[i -1]->driverDescriptor; eventDispatch( READER_DETECTED, NULL, num, pManager ); } } else RESET_READER( pManager->readers[num] ); } } return ( SCARD_S_SUCCESS ); }
/*************************** READERS ENUMERATE *****************/ static int readersEnumerate( tReaderManager *pManager ) { LONG rv; DWORD dwReaders; char *ptr; int i, previousNumReaders; /* remember how many readers there used to be */ previousNumReaders = pManager->nbReaders; /* Call with a null buffer to get the number of bytes to allocate */ rv = SCardListReaders( (SCARDCONTEXT)(pManager->hContext), NULL, NULL, &dwReaders); if ( rv != SCARD_S_SUCCESS ) { /* if there are no readers, then zero everything out but don't report an error */ if ( rv == SCARD_E_NO_READERS_AVAILABLE ) { pManager->nbReaders = 0; if ( pManager->mszReaders ) free( pManager->mszReaders ); pManager->mszReaders = NULL; readersLogMessage( pManager, LOG_INFO, 2, "Found 0 Readers" ); } else PCSC_ERROR( pManager, rv, "SCardListReaders"); return ( rv ); } /* if array already exists, then liberate it and alloc a new one for the */ /* number of readers reported from SCardListReader */ if ( pManager->mszReaders ) free( pManager->mszReaders ); /* malloc enough memory for dwReader string */ pManager->mszReaders = malloc(sizeof(char)*dwReaders); /* now get the list into the mszReaders array */ rv = SCardListReaders( (SCARDCONTEXT)(pManager->hContext), NULL, pManager->mszReaders, &dwReaders); if (rv != SCARD_S_SUCCESS) { /* Avoid reporting an error just because no reader is connected */ if ( rv != SCARD_E_NO_READERS_AVAILABLE ) PCSC_ERROR( pManager, rv, "SCardListReaders"); return (rv); } /* Extract readers from the null separated string and get the total * number of readers */ pManager->nbReaders = 0; ptr = pManager->mszReaders; while (*ptr != '\0') { ptr += strlen(ptr)+1; (pManager->nbReaders)++; } sprintf(messageString, "Found %d Readers", pManager->nbReaders); readersLogMessage( pManager, LOG_INFO, 2, messageString); /* fill the array of readers with pointers to the appropriate point */ /* in the long mszReaders multi-string */ pManager->nbReaders = 0; ptr = pManager->mszReaders; while (*ptr != '\0') { sprintf(messageString, "Reader [%d]: %s", pManager->nbReaders, ptr); readersLogMessage( pManager, LOG_INFO, 3, messageString); pManager->readers[pManager->nbReaders].name = ptr; ptr += strlen(ptr)+1; (pManager->nbReaders)++; } /* if we have fewer readers than we used to then zero out the "lost ones" */ if ( pManager->nbReaders < previousNumReaders ) for ( i = pManager->nbReaders; i < previousNumReaders; i++ ) RESET_READER( pManager->readers[i] ); return( SCARD_S_SUCCESS ); }
/**************************** APDU SEND ************************/ static LONG apduSend ( tCardHandle hCard, const BYTE *apdu, DWORD apduLength, BYTE *pbRecvBuffer, DWORD *dwRecvLength ) { LONG rv; SCARD_IO_REQUEST pioRecvPci; BYTE pbSendBuffer[40]; DWORD dwSendLength = 0; DWORD rBufferMax; BOOL psuedoAPDU = FALSE; /* remember the size of the input buffer passed to us, so we don't exceed */ rBufferMax = *dwRecvLength; /* The special psuedo APDU's to talk to a tag, need to have their */ /* response read back in two chunks, using GET_RESPONSE for the second */ if (apdu[0] == 0xd4) { psuedoAPDU = TRUE; /* prepend the DIRECT_TRANSMIT APDU */ memcpy(pbSendBuffer, APDU_DIRECT_TRANSMIT, sizeof(APDU_DIRECT_TRANSMIT)); /* then a byte that tells it the length of the psuedo APDU to follow */ pbSendBuffer[sizeof(APDU_DIRECT_TRANSMIT)] = (BYTE)apduLength; dwSendLength += (sizeof(APDU_DIRECT_TRANSMIT) + 1); } /* Add the APDU that was requested to be sent and increase length to send */ memcpy((pbSendBuffer + dwSendLength), apdu, apduLength); dwSendLength += apduLength; #if 0 sprintf(messageString, "APDU: "); sPrintBufferHex((messageString + strlen("APDU: ")), dwSendLength, pbSendBuffer); readersLogMessage( pManager, LOG_INFO, 3, messageString); #endif rv = SCardTransmit((SCARDHANDLE) hCard, pioSendPci, pbSendBuffer, dwSendLength, &pioRecvPci, pbRecvBuffer, dwRecvLength); /* if it was a psuedo APDU then we need to get the response */ if ( (rv == SCARD_S_SUCCESS) && psuedoAPDU ) { #if 0 sprintf(messageString, "Received: "); sPrintBufferHex((messageString + strlen("Received: ")), *dwRecvLength, pbRecvBuffer); readersLogMessage(LOG_INFO, 3, messageString); #endif /* command went OK? */ if (pbRecvBuffer[SW1] != SW1_SUCCESS) { #if 0 sprintf(messageString, "APDU failed: SW1 = %02X", pbRecvBuffer[SW1]); readersLogMessage(LOG_ERR, 0, messageString); #endif return ( SCARD_F_COMM_ERROR ); } /* are their response bytes to get? */ if (pbRecvBuffer[SW2] > 0) { #if 0 sprintf(messageString, "Requesting Response Data (%d)", pbRecvBuffer[SW2]); readersLogMessage(LOG_INFO, 3, messageString); #endif /* copy the get_response APDU into the first bytes */ memcpy(pbSendBuffer, APDU_GET_RESPONSE, sizeof(APDU_GET_RESPONSE)); /* the second response byte tells us how many bytes are pending */ /* add that value at the end of the GET_RESPONSE APDU */ pbSendBuffer[sizeof(APDU_GET_RESPONSE)] = pbRecvBuffer[SW2]; dwSendLength = sizeof(APDU_GET_RESPONSE) + 1; /* specify the maximum size of the buffer that was passed in */ *dwRecvLength = rBufferMax; rv = SCardTransmit((SCARDCONTEXT)hCard, pioSendPci, pbSendBuffer, dwSendLength, &pioRecvPci, pbRecvBuffer, dwRecvLength ); #if 0 PCSC_ERROR(rv, "SCardTransmit"); sprintf(messageString, "Received: "); sPrintBufferHex(messageString, rBufferMax, pbRecvBuffer); readersLogMessage(LOG_INFO, 3, messageString); #endif } else *dwRecvLength = 0; } return(rv); }