]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | { | |
427c49bc | 62 | SECURITY_EXCEPTION_THROW_PCSC(this, (unsigned int)err); |
b1ab9ed8 A |
63 | } |
64 | ||
65 | ||
66 | const char *Error::what() const throw () | |
67 | { | |
427c49bc | 68 | return pcsc_stringify_error((int32_t)error); |
b1ab9ed8 A |
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; | |
427c49bc | 104 | dwCurrentState = (uint32_t)known; |
b1ab9ed8 A |
105 | } |
106 | ||
107 | ||
108 | void 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 | ||
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); | |
427c49bc | 120 | cbAtr = (uint32_t)size; |
b1ab9ed8 A |
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 | { | |
427c49bc | 226 | check(::SCardGetStatusChange(mContext, (uint32_t)timeout, readers, nReaders)); |
b1ab9ed8 A |
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, | |
427c49bc | 263 | reader, (uint32_t)share, (uint32_t)protocols, &mHandle, &activeProtocol)); |
b1ab9ed8 A |
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 | ||
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 | ||
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 | ||
427c49bc | 289 | checkReset(::SCardDisconnect(mHandle, (uint32_t)disposition)); |
b1ab9ed8 A |
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 | ||
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 | ||
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 | ||
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 | ||
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 |