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//
27// C++ gate to "Muscle" smartcard interface layer
28//
29#include "muscle++.h"
30#include <security_utilities/debugging.h>
31
32
33namespace Security {
34namespace Muscle {
35
36
37//
38// PCSC domain errors
39//
40Error::Error(MSC_RV err) : error(err)
41{
42	SECURITY_EXCEPTION_THROW_OTHER(this, err, (char *)"muscle");
43}
44
45
46const char *Error::what() const throw ()
47{
48	return msc_error(error);
49}
50
51
52void Error::throwMe(MSC_RV err)
53{
54	throw Error(err);
55}
56
57
58OSStatus Error::osStatus() const
59{
60	return -1;	//@@@ preliminary
61}
62
63int Error::unixError() const
64{
65	return EINVAL;  //@@@ preliminary
66}
67
68
69//
70// Open a connection with PCSC layer information.
71// The ReaderState fields required are the slot name and the ATR.
72//
73Connection::Connection()
74	: mIsOpen(false), mCurrentTransaction(NULL)
75{
76}
77
78Connection::~Connection()
79{
80	assert(!mCurrentTransaction);
81	close();
82}
83
84void Connection::open(const PCSC::ReaderState &reader, unsigned share)
85{
86	// fill in the minimum needed to identify the card
87	MSCTokenInfo info;
88
89	// set slot name in info
90	strncpy(info.slotName, reader.name(), MAX_READERNAME);
91
92	// set ATR in info
93	assert(reader.length() <= MAX_ATR_SIZE);
94	memcpy(info.tokenId, reader.data(), reader.length());
95	info.tokenIdLength = (MSCULong32)reader.length();
96
97	// establish Muscle-level connection to card
98	Error::check(::MSCEstablishConnection(&info, share, NULL, 0, this));
99	mIsOpen = true;
100	secdebug("muscle", "%p opened %s", this, info.slotName);
101
102	// pull initial status
103	updateStatus();
104}
105
106void Connection::close()
107{
108	if (mIsOpen) {
109		secdebug("muscle", "%p closing", this);
110		Error::check(::MSCReleaseConnection(this, SCARD_LEAVE_CARD));
111		mIsOpen = false;
112	}
113}
114
115
116void Connection::begin(Transaction *trans)
117{
118	assert(!mCurrentTransaction);
119	Error::check(::MSCBeginTransaction(this));
120	secdebug("muscle", "%p start transaction %p", this, trans);
121	mCurrentTransaction = trans;
122}
123
124void Connection::end(Transaction *trans)
125{
126	assert(trans == mCurrentTransaction);
127	secdebug("muscle", "%p end transaction %p", this, trans);
128	Error::check(::MSCEndTransaction(this, SCARD_LEAVE_CARD));
129	mCurrentTransaction = NULL;
130}
131
132
133//
134// Update card status (cached in the Connection object)
135//
136void Connection::updateStatus()
137{
138	Error::check(::MSCGetStatus(this, this));
139}
140
141
142//
143// Get all items off the card
144//
145template <class Info, class Item, MSC_RV (*list)(MSCTokenConnection *, MSCUChar8, Info *)>
146static void get(Connection *conn, Connection::ItemSet &items)
147{
148	Info info;
149	MSCUChar8 seq = MSC_SEQUENCE_RESET;
150	for (;;) {
151		switch (MSC_RV rc = list(conn, seq, &info)) {
152		case MSC_SEQUENCE_END:
153			return;
154		case MSC_SUCCESS:
155			items.insert(new Item(info));
156			seq = MSC_SEQUENCE_NEXT;
157			break;
158		default:
159			Error::throwMe(rc);
160		}
161	}
162}
163
164void Connection::getItems(ItemSet &result, bool getKeys, bool getOthers)
165{
166	ItemSet items;
167	if (getKeys)
168		get<MSCKeyInfo, Key, MSCListKeys>(this, items);
169	if (getOthers)
170		get<MSCObjectInfo, Object, MSCListObjects>(this, items);
171	items.swap(result);
172}
173
174
175//
176// Transaction monitors
177//
178Transaction::Transaction(Connection &con)
179    : connection(con)
180{
181    connection.begin(this);
182}
183
184Transaction::~Transaction()
185{
186    connection.end(this);
187}
188
189
190//
191// ACLs (Muscle style)
192//
193static void aclForm(string &s, MSCUShort16 acl, int offset, char c)
194{
195	for (int n = 0; n < 5; n++) {
196		char p = '-';
197		switch (acl) {
198		case MSC_AUT_ALL:		p = c; break;
199		case MSC_AUT_NONE:		break;
200		default:				if (acl & (MSC_AUT_PIN_0 << n)) p = c; break;
201		}
202		s[3 * n + offset] = p;
203	}
204}
205
206string ACL::form(char ue) const
207{
208	string r = "---------------";
209	aclForm(r, mRead, 0, 'r');
210	aclForm(r, mWrite, 1, 'w');
211	aclForm(r, mErase, 2, ue);
212	return r;
213}
214
215
216//
217// Keys and objects
218//
219CardItem::~CardItem()
220{ /* virtual */ }
221
222
223Key::Key(const MSCKeyInfo &info)
224	: MSCKeyInfo(info)
225{
226	snprintf(mKeyName, sizeof(mKeyName), "K%d", id());
227}
228
229
230const ACL &Key::acl() const		{ return reinterpret_cast<const ACL &>(keyACL); }
231ACL &Key::acl()					{ return reinterpret_cast<ACL &>(keyACL); }
232
233const char *Key::name() const	{ return mKeyName; }
234unsigned Key::size() const		{ return keySize; }
235
236void Key::debugDump()
237{
238	printf("Key %d type %d size %d mode=0x%x dir=0x%x ACL %s\n",
239		keyNum, keyType, keySize, mode(), operations(), acl().form('u').c_str());
240}
241
242const char *Object::name() const { return objectID; }
243unsigned Object::size() const	{ return objectSize; }
244
245const ACL &Object::acl() const	{ return reinterpret_cast<const ACL &>(objectACL); }
246ACL &Object::acl()				{ return reinterpret_cast<ACL &>(objectACL); }
247
248void Object::debugDump()
249{
250	printf("Object %s size %d ACL %s\n",
251		objectID, objectSize, acl().form('e').c_str());
252}
253
254
255}	// namespace Muscle
256}	// namespace Security
257