]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/pcsc++.cpp
Security-57337.20.44.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 }
64
65
66 const char *Error::what() const throw ()
67 {
68 return pcsc_stringify_error((int32_t)error);
69 }
70
71
72 void Error::throwMe(unsigned long err)
73 {
74 throw Error(err);
75 }
76
77
78 OSStatus 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
90 int Error::unixError() const
91 {
92 return EINVAL; //@@@ preliminary
93 }
94
95
96 //
97 // PodWrappers
98 //
99 void 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
108 void 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
115 void 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
126 void 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 //
139 Session::Session()
140 : mIsOpen(false)
141 {
142 }
143
144
145 Session::~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 //
155 void 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
172 void 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
190 bool 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
204 void 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 //
224 void Session::statusChange(ReaderState *readers, unsigned int nReaders, long timeout)
225 {
226 check(::SCardGetStatusChange(mContext, (uint32_t)timeout, readers, nReaders));
227 }
228
229
230 //
231 // PCSC Card objects
232 //
233 Card::Card()
234 : mConnectedState(kInitial)
235 {
236 }
237
238 Card::~Card()
239 {
240 }
241
242 void 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
258 void 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,
263 reader, (uint32_t)share, (uint32_t)protocols, &mHandle, &activeProtocol));
264 setIOType(activeProtocol);
265 mConnectedState = kConnected;
266 }
267
268 void Card::reconnect(unsigned long share, unsigned long protocols, unsigned long initialization)
269 {
270 assert(mConnectedState != kInitial);
271
272 uint32_t activeProtocol;
273 Error::check(::SCardReconnect(mHandle, (uint32_t)share, (uint32_t)protocols,
274 (uint32_t)initialization, &activeProtocol));
275 setIOType(activeProtocol);
276 mConnectedState = kConnected;
277 }
278
279 void 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
289 checkReset(::SCardDisconnect(mHandle, (uint32_t)disposition));
290 didDisconnect();
291 mConnectedState = kInitial;
292 }
293 }
294
295 void
296 Card::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
306 void
307 Card::didDisconnect()
308 {
309 mConnectedState = kDisconnected;
310 mTransactionNestLevel = 0;
311 }
312
313 void
314 Card::didEnd()
315 {
316 }
317
318 void
319 Card::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
330 uint32_t tmpRecvLength = (uint32_t)pcbRecvLength;
331 checkReset(::SCardTransmit(mHandle, mIOType, pbSendBuffer, (uint32_t)cbSendLength,
332 NULL, pbRecvBuffer, &tmpRecvLength));
333 pcbRecvLength = tmpRecvLength;
334
335 IFDUMPING("pcsc", dump("<-", pbRecvBuffer, pcbRecvLength));
336 }
337
338 void 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
355 void 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
367 checkReset(::SCardEndTransaction(mHandle, (uint32_t)disposition));
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 {
379 checkReset(::SCardEndTransaction(mHandle, (uint32_t)disposition));
380 didEnd();
381 }
382 }
383 }
384 }
385
386 void Card::cancel()
387 {
388 end(/*SCARD_RESET_CARD*/);
389 }
390
391 #if defined(DEBUGDUMP)
392
393 void
394 Card::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
407 void Transaction::commitAction()
408 {
409 mCarrier.end(mDisposition);
410 }
411
412
413 } // namespace PCSC
414 } // namespace Security