]> git.saurik.com Git - apple/security.git/blame - OSX/include/security_utilities/pcsc++.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / include / security_utilities / pcsc++.cpp
CommitLineData
b1ab9ed8 1/*
d8f41ccd 2 * Copyright (c) 2004,2011-2012,2014 Apple Inc. All Rights Reserved.
b1ab9ed8
A
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{
427c49bc 62 SECURITY_EXCEPTION_THROW_PCSC(this, (unsigned int)err);
b1ab9ed8
A
63}
64
65
66const char *Error::what() const throw ()
67{
427c49bc 68 return pcsc_stringify_error((int32_t)error);
b1ab9ed8
A
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;
427c49bc 104 dwCurrentState = (uint32_t)known;
b1ab9ed8
A
105}
106
107
108void ReaderState::lastKnown(unsigned long s)
109{
110 // clear out CHANGED and UNAVAILABLE
427c49bc 111 dwCurrentState = (uint32_t)s & ~(SCARD_STATE_CHANGED | SCARD_STATE_UNAVAILABLE);
b1ab9ed8
A
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);
427c49bc 120 cbAtr = (uint32_t)size;
b1ab9ed8
A
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{
427c49bc 226 check(::SCardGetStatusChange(mContext, (uint32_t)timeout, readers, nReaders));
b1ab9ed8
A
227}
228
229
230//
231// PCSC Card objects
232//
233Card::Card()
234 : mConnectedState(kInitial)
235{
236}
237
238Card::~Card()
239{
240}
241
242void Card::setIOType(unsigned long activeProtocol)
243{
244 switch (activeProtocol)
245 {
246 case SCARD_PROTOCOL_T0:
247 mIOType = SCARD_PCI_T0;
248 break;
249 case SCARD_PROTOCOL_T1:
250 mIOType = SCARD_PCI_T1;
251 break;
252 default:
253 mIOType = SCARD_PCI_RAW;
254 break;
255 }
256}
257
258void Card::connect(Session &session, const char *reader,
259 unsigned long share, unsigned long protocols)
260{
261 uint32_t activeProtocol;
262 Error::check(::SCardConnect(session.mContext,
427c49bc 263 reader, (uint32_t)share, (uint32_t)protocols, &mHandle, &activeProtocol));
b1ab9ed8
A
264 setIOType(activeProtocol);
265 mConnectedState = kConnected;
266}
267
268void Card::reconnect(unsigned long share, unsigned long protocols, unsigned long initialization)
269{
270 assert(mConnectedState != kInitial);
271
427c49bc
A
272 uint32_t activeProtocol;
273 Error::check(::SCardReconnect(mHandle, (uint32_t)share, (uint32_t)protocols,
274 (uint32_t)initialization, &activeProtocol));
b1ab9ed8
A
275 setIOType(activeProtocol);
276 mConnectedState = kConnected;
277}
278
279void Card::disconnect(unsigned long disposition)
280{
281 if (mConnectedState == kConnected)
282 {
283 if (mTransactionNestLevel > 0)
284 {
285 secdebug("pcsc", "%p: disconnect, dropping: %d transactions", this, mTransactionNestLevel);
286 mTransactionNestLevel = 0;
287 }
288
427c49bc 289 checkReset(::SCardDisconnect(mHandle, (uint32_t)disposition));
b1ab9ed8
A
290 didDisconnect();
291 mConnectedState = kInitial;
292 }
293}
294
295void
296Card::checkReset(unsigned int rv)
297{
298 if (rv == SCARD_W_RESET_CARD)
299 {
300 secdebug("pcsc", "%p: card reset during pcsc call, we're disconnected", this);
301 didDisconnect();
302 }
303 Error::check(rv);
304}
305
306void
307Card::didDisconnect()
308{
309 mConnectedState = kDisconnected;
310 mTransactionNestLevel = 0;
311}
312
313void
314Card::didEnd()
315{
316}
317
318void
319Card::transmit(const unsigned char *pbSendBuffer, size_t cbSendLength,
320 unsigned char *pbRecvBuffer, size_t &pcbRecvLength)
321{
322 if (mConnectedState == kDisconnected)
323 {
324 secdebug("pcsc", "%p: transmit after disconnect, reconnecting", this);
325 reconnect();
326 }
327
328 IFDUMPING("pcsc", dump("->", pbSendBuffer, cbSendLength));
329
427c49bc
A
330 uint32_t tmpRecvLength = (uint32_t)pcbRecvLength;
331 checkReset(::SCardTransmit(mHandle, mIOType, pbSendBuffer, (uint32_t)cbSendLength,
b1ab9ed8
A
332 NULL, pbRecvBuffer, &tmpRecvLength));
333 pcbRecvLength = tmpRecvLength;
334
335 IFDUMPING("pcsc", dump("<-", pbRecvBuffer, pcbRecvLength));
336}
337
338void Card::begin()
339{
340 // Only the first transaction started is sent to PCSC
341 if (mTransactionNestLevel == 0)
342 {
343 if (mConnectedState == kDisconnected)
344 {
345 secdebug("pcsc", "%p: begin transaction after disconnect, reconnecting", this);
346 reconnect();
347 }
348
349 checkReset(::SCardBeginTransaction(mHandle));
350 }
351 mTransactionNestLevel++;
352 secdebug("pcsc", "%p begin transaction: %d", this, mTransactionNestLevel);
353}
354
355void Card::end(unsigned long disposition)
356{
357 // Only the last transaction ended is sent to PCSC
358 secdebug("pcsc", "%p end transaction: %d", this, mTransactionNestLevel);
359 if (disposition == SCARD_RESET_CARD)
360 {
361 if (mConnectedState == kDisconnected)
362 {
363 secdebug("pcsc", "%p: end transaction after disconnect, reconnecting to reset card", this);
364 reconnect();
365 }
366
427c49bc 367 checkReset(::SCardEndTransaction(mHandle, (uint32_t)disposition));
b1ab9ed8
A
368 didDisconnect();
369 }
370 else if (mTransactionNestLevel > 0)
371 {
372 --mTransactionNestLevel;
373 if (mTransactionNestLevel == 0)
374 {
375 if (mConnectedState == kDisconnected)
376 secdebug("pcsc", "%p: end transaction while disconnected ignored", this);
377 else
378 {
427c49bc 379 checkReset(::SCardEndTransaction(mHandle, (uint32_t)disposition));
b1ab9ed8
A
380 didEnd();
381 }
382 }
383 }
384}
385
386void Card::cancel()
387{
388 end(/*SCARD_RESET_CARD*/);
389}
390
391#if defined(DEBUGDUMP)
392
393void
394Card::dump(const char *direction, const unsigned char *buffer, size_t length)
395{
396 Debug::dump("[%02lu]%s:", length, direction);
397
398 for (size_t ix = 0; ix < length; ++ix)
399 Debug::dump(" %02x", buffer[ix]);
400
401 Debug::dump("\n");
402}
403
404#endif
405
406
407void Transaction::commitAction()
408{
409 mCarrier.end(mDisposition);
410}
411
412
413} // namespace PCSC
414} // namespace Security