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