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