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
34namespace Security {
35namespace PCSC {
36
37
38//
39// Internal utilities
40//
41static 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
51inline 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//
60Error::Error(unsigned long err) : error(err)
61{
62	SECURITY_EXCEPTION_THROW_PCSC(this, (unsigned int)err);
63}
64
65
66const char *Error::what() const throw ()
67{
68	return pcsc_stringify_error((int32_t)error);
69}
70
71
72void Error::throwMe(unsigned long err)
73{
74	throw Error(err);
75}
76
77
78OSStatus 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
90int Error::unixError() const
91{
92	return EINVAL;  //@@@ preliminary
93}
94
95
96//
97// PodWrappers
98//
99void 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
108void 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
115void 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
126void 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//
139Session::Session()
140	: mIsOpen(false)
141{
142}
143
144
145Session::~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//
155void 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
172void 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
190bool 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
204void 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//
224void Session::statusChange(ReaderState *readers, unsigned int nReaders, long timeout)
225{
226	if (nReaders == 0)
227		return; // no readers, no foul
228	check(::SCardGetStatusChange(mContext, (uint32_t)timeout, readers, nReaders));
229}
230
231
232//
233// PCSC Card objects
234//
235Card::Card()
236	: mConnectedState(kInitial)
237{
238}
239
240Card::~Card()
241{
242}
243
244void 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
260void 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, (uint32_t)share, (uint32_t)protocols, &mHandle, &activeProtocol));
266	setIOType(activeProtocol);
267	mConnectedState = kConnected;
268}
269
270void Card::reconnect(unsigned long share, unsigned long protocols, unsigned long initialization)
271{
272	assert(mConnectedState != kInitial);
273
274	uint32_t activeProtocol;
275	Error::check(::SCardReconnect(mHandle, (uint32_t)share, (uint32_t)protocols,
276		(uint32_t)initialization, &activeProtocol));
277	setIOType(activeProtocol);
278	mConnectedState = kConnected;
279}
280
281void 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, (uint32_t)disposition));
292		didDisconnect();
293		mConnectedState = kInitial;
294	}
295}
296
297void
298Card::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
308void
309Card::didDisconnect()
310{
311	mConnectedState = kDisconnected;
312	mTransactionNestLevel = 0;
313}
314
315void
316Card::didEnd()
317{
318}
319
320void
321Card::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 = (uint32_t)pcbRecvLength;
333	checkReset(::SCardTransmit(mHandle, mIOType, pbSendBuffer, (uint32_t)cbSendLength,
334		NULL, pbRecvBuffer, &tmpRecvLength));
335	pcbRecvLength = tmpRecvLength;
336
337	IFDUMPING("pcsc", dump("<-", pbRecvBuffer, pcbRecvLength));
338}
339
340void 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
357void 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, (uint32_t)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, (uint32_t)disposition));
382				didEnd();
383			}
384		}
385	}
386}
387
388void Card::cancel()
389{
390	end(/*SCARD_RESET_CARD*/);
391}
392
393#if defined(DEBUGDUMP)
394
395void
396Card::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
409void Transaction::commitAction()
410{
411	mCarrier.end(mDisposition);
412}
413
414
415}   // namespace PCSC
416}   // namespace Security
417