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