1/* 2 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// pcsc++ - PCSC client interface layer in C++ 27// 28#include "pcsc++.h" 29#include <security_utilities/debugging.h> 30#include <PCSC/pcsclite.h> 31#include <PCSC/wintypes.h> 32#include <Security/cssmapple.h> 33 34namespace Security { 35namespace PCSC { 36 37 38// 39// Internal utilities 40// 41static void decode(vector<string> &names, const char *buffer, size_t size) 42{ 43 names.clear(); 44 for (size_t pos = 0; pos < size - 1; ) { 45 size_t len = strlen(buffer + pos); 46 names.push_back(string(buffer + pos, len)); 47 pos += len + 1; 48 } 49} 50 51inline void decode(vector<string> &names, const vector<char> &buffer, size_t size) 52{ 53 decode(names, &buffer[0], size); 54} 55 56 57// 58// PCSC domain errors 59// 60Error::Error(unsigned long err) : error(err) 61{ 62 SECURITY_EXCEPTION_THROW_PCSC(this, (unsigned int)err); 63} 64 65 66const char *Error::what() const throw () 67{ 68 return pcsc_stringify_error((int32_t)error); 69} 70 71 72void Error::throwMe(unsigned long err) 73{ 74 throw Error(err); 75} 76 77 78OSStatus Error::osStatus() const 79{ 80 switch (error) 81 { 82 // @@@ more errors should be mapped here 83 case SCARD_W_RESET_CARD: 84 return CSSMERR_CSP_DEVICE_RESET; 85 default: 86 return CSSMERR_CSP_INTERNAL_ERROR; 87 } 88} 89 90int Error::unixError() const 91{ 92 return EINVAL; //@@@ preliminary 93} 94 95 96// 97// PodWrappers 98// 99void ReaderState::set(const char *name, unsigned long known) 100{ 101 clearPod(); 102 szReader = name; 103 pvUserData = NULL; 104 dwCurrentState = (uint32_t)known; 105} 106 107 108void ReaderState::lastKnown(unsigned long s) 109{ 110 // clear out CHANGED and UNAVAILABLE 111 dwCurrentState = (uint32_t)s & ~(SCARD_STATE_CHANGED | SCARD_STATE_UNAVAILABLE); 112} 113 114 115void ReaderState::setATR(const void *atr, size_t size) 116{ 117 if (size > sizeof(rgbAtr)) 118 Error::throwMe(SCARD_E_INVALID_ATR); 119 memcpy(rgbAtr, atr, size); 120 cbAtr = (uint32_t)size; 121} 122 123 124#if defined(DEBUGDUMP) 125 126void ReaderState::dump() 127{ 128 Debug::dump("reader(%s) state=0x%x events=0x%x", 129 szReader ? szReader : "(null)", dwCurrentState, dwEventState); 130 Debug::dumpData(" ATR", rgbAtr, cbAtr); 131} 132 133#endif //DEBUGDUMP 134 135 136// 137// Session objects 138// 139Session::Session() 140 : mIsOpen(false) 141{ 142} 143 144 145Session::~Session() 146{ 147 close(); 148} 149 150 151// 152// (Re)establish PCSC context. 153// Don't fail on SCARD_F_INTERNAL_ERROR (pcscd not running - urgh). 154// 155void Session::open() 156{ 157 if (!mIsOpen) { 158 try { 159 Error::check(::SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &mContext)); 160 mIsOpen = true; 161 secdebug("pcsc", "context opened"); 162 } catch (const Error &err) { 163 if (err.error == SCARD_F_INTERNAL_ERROR) 164 { 165 secdebug("pcsc", "got internal error; assuming pcscd absent; context not ready"); 166 return; 167 } 168 } 169 } 170} 171 172void Session::close() 173{ 174 if (mIsOpen) { 175 mIsOpen = false; 176 try { 177 if (mContext) 178 Error::check(SCardReleaseContext(mContext)); 179 secdebug("pcsc", "context closed"); 180 } catch (const Error &err) { 181 if (err.error == SCARD_F_INTERNAL_ERROR) 182 { 183 secdebug("pcsc", "got internal error; assuming pcscd absent; context not ready"); 184 return; 185 } 186 } 187 } 188} 189 190bool Session::check(long rc) 191{ 192 switch (rc) { 193 case SCARD_S_SUCCESS: 194 return true; // got reader(s), call succeeded 195 case SCARD_E_READER_UNAVAILABLE: 196 return false; // no readers, but don't treat as error 197 default: 198 Error::throwMe(rc); 199 return false; // placebo 200 } 201} 202 203 204void Session::listReaders(vector<string> &readers, const char *groups) 205{ 206 uint32_t size = 0; 207 if (check(::SCardListReaders(mContext, groups, NULL, &size))) 208 { 209 mReaderBuffer.resize(size); 210 if (check(::SCardListReaders(mContext, groups, &mReaderBuffer[0], &size))) 211 { 212 decode(readers, mReaderBuffer, size); 213 return; 214 } 215 } 216 217 readers.clear(); // treat as success (returning zero readers) 218} 219 220 221// 222// Reader status check 223// 224void Session::statusChange(ReaderState *readers, unsigned int nReaders, long timeout) 225{ 226 if (nReaders == 0) 227 return; // no readers, no foul 228 check(::SCardGetStatusChange(mContext, (uint32_t)timeout, readers, nReaders)); 229} 230 231 232// 233// PCSC Card objects 234// 235Card::Card() 236 : mConnectedState(kInitial) 237{ 238} 239 240Card::~Card() 241{ 242} 243 244void Card::setIOType(unsigned long activeProtocol) 245{ 246 switch (activeProtocol) 247 { 248 case SCARD_PROTOCOL_T0: 249 mIOType = SCARD_PCI_T0; 250 break; 251 case SCARD_PROTOCOL_T1: 252 mIOType = SCARD_PCI_T1; 253 break; 254 default: 255 mIOType = SCARD_PCI_RAW; 256 break; 257 } 258} 259 260void Card::connect(Session &session, const char *reader, 261 unsigned long share, unsigned long protocols) 262{ 263 uint32_t activeProtocol; 264 Error::check(::SCardConnect(session.mContext, 265 reader, (uint32_t)share, (uint32_t)protocols, &mHandle, &activeProtocol)); 266 setIOType(activeProtocol); 267 mConnectedState = kConnected; 268} 269 270void Card::reconnect(unsigned long share, unsigned long protocols, unsigned long initialization) 271{ 272 assert(mConnectedState != kInitial); 273 274 uint32_t activeProtocol; 275 Error::check(::SCardReconnect(mHandle, (uint32_t)share, (uint32_t)protocols, 276 (uint32_t)initialization, &activeProtocol)); 277 setIOType(activeProtocol); 278 mConnectedState = kConnected; 279} 280 281void Card::disconnect(unsigned long disposition) 282{ 283 if (mConnectedState == kConnected) 284 { 285 if (mTransactionNestLevel > 0) 286 { 287 secdebug("pcsc", "%p: disconnect, dropping: %d transactions", this, mTransactionNestLevel); 288 mTransactionNestLevel = 0; 289 } 290 291 checkReset(::SCardDisconnect(mHandle, (uint32_t)disposition)); 292 didDisconnect(); 293 mConnectedState = kInitial; 294 } 295} 296 297void 298Card::checkReset(unsigned int rv) 299{ 300 if (rv == SCARD_W_RESET_CARD) 301 { 302 secdebug("pcsc", "%p: card reset during pcsc call, we're disconnected", this); 303 didDisconnect(); 304 } 305 Error::check(rv); 306} 307 308void 309Card::didDisconnect() 310{ 311 mConnectedState = kDisconnected; 312 mTransactionNestLevel = 0; 313} 314 315void 316Card::didEnd() 317{ 318} 319 320void 321Card::transmit(const unsigned char *pbSendBuffer, size_t cbSendLength, 322 unsigned char *pbRecvBuffer, size_t &pcbRecvLength) 323{ 324 if (mConnectedState == kDisconnected) 325 { 326 secdebug("pcsc", "%p: transmit after disconnect, reconnecting", this); 327 reconnect(); 328 } 329 330 IFDUMPING("pcsc", dump("->", pbSendBuffer, cbSendLength)); 331 332 uint32_t tmpRecvLength = (uint32_t)pcbRecvLength; 333 checkReset(::SCardTransmit(mHandle, mIOType, pbSendBuffer, (uint32_t)cbSendLength, 334 NULL, pbRecvBuffer, &tmpRecvLength)); 335 pcbRecvLength = tmpRecvLength; 336 337 IFDUMPING("pcsc", dump("<-", pbRecvBuffer, pcbRecvLength)); 338} 339 340void Card::begin() 341{ 342 // Only the first transaction started is sent to PCSC 343 if (mTransactionNestLevel == 0) 344 { 345 if (mConnectedState == kDisconnected) 346 { 347 secdebug("pcsc", "%p: begin transaction after disconnect, reconnecting", this); 348 reconnect(); 349 } 350 351 checkReset(::SCardBeginTransaction(mHandle)); 352 } 353 mTransactionNestLevel++; 354 secdebug("pcsc", "%p begin transaction: %d", this, mTransactionNestLevel); 355} 356 357void Card::end(unsigned long disposition) 358{ 359 // Only the last transaction ended is sent to PCSC 360 secdebug("pcsc", "%p end transaction: %d", this, mTransactionNestLevel); 361 if (disposition == SCARD_RESET_CARD) 362 { 363 if (mConnectedState == kDisconnected) 364 { 365 secdebug("pcsc", "%p: end transaction after disconnect, reconnecting to reset card", this); 366 reconnect(); 367 } 368 369 checkReset(::SCardEndTransaction(mHandle, (uint32_t)disposition)); 370 didDisconnect(); 371 } 372 else if (mTransactionNestLevel > 0) 373 { 374 --mTransactionNestLevel; 375 if (mTransactionNestLevel == 0) 376 { 377 if (mConnectedState == kDisconnected) 378 secdebug("pcsc", "%p: end transaction while disconnected ignored", this); 379 else 380 { 381 checkReset(::SCardEndTransaction(mHandle, (uint32_t)disposition)); 382 didEnd(); 383 } 384 } 385 } 386} 387 388void Card::cancel() 389{ 390 end(/*SCARD_RESET_CARD*/); 391} 392 393#if defined(DEBUGDUMP) 394 395void 396Card::dump(const char *direction, const unsigned char *buffer, size_t length) 397{ 398 Debug::dump("[%02lu]%s:", length, direction); 399 400 for (size_t ix = 0; ix < length; ++ix) 401 Debug::dump(" %02x", buffer[ix]); 402 403 Debug::dump("\n"); 404} 405 406#endif 407 408 409void Transaction::commitAction() 410{ 411 mCarrier.end(mDisposition); 412} 413 414 415} // namespace PCSC 416} // namespace Security 417