1/*
2 * Copyright (c) 2004-2007,2011 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// iodevices - code for finding and tracking devices via IOKit
27//
28#include "iodevices.h"
29#include <security_utilities/cfutilities.h>
30#include <IOKit/IOMessage.h>
31#include <IOKit/usb/IOUSBLib.h>
32
33using namespace MachPlusPlus;
34
35
36namespace Security {
37namespace IOKit {
38
39
40//
41// Master Ports.
42// Note that multiple MasterPort objects may refer to the same Mach port.
43//
44MasterPort::MasterPort()
45{
46	Error::check(::IOMasterPort(MACH_PORT_NULL, &port()));
47}
48
49
50//
51// IOKit Devices (a small subset of functionality)
52//
53Device::~Device()
54{
55	::IOObjectRelease(mService);
56}
57
58string Device::name() const
59{
60	io_name_t tName;
61	Error::check(::IORegistryEntryGetName(mService, tName));
62	return tName;
63}
64
65string Device::name(const char *plane) const
66{
67	io_name_t tName;
68	Error::check(::IORegistryEntryGetNameInPlane(mService, plane, tName));
69	return tName;
70}
71
72string Device::path(const char *plane) const
73{
74	io_string_t tPath;
75	Error::check(::IORegistryEntryGetPath(mService, plane, tPath));
76	return tPath;
77}
78
79CFDictionaryRef Device::properties() const
80{
81	CFMutableDictionaryRef dict;
82	Error::check(::IORegistryEntryCreateCFProperties(mService, &dict, NULL, 0));
83	return dict;
84}
85
86CFTypeRef Device::property(const char *name) const
87{
88	return ::IORegistryEntryCreateCFProperty(mService, CFTempString(name), NULL, 0);
89}
90
91
92//
93// DeviceIterators
94//
95DeviceIterator::~DeviceIterator()
96{
97	// drain the iterator to avoid port leakage
98	while (Device dev = (*this)())
99		;
100}
101
102
103io_service_t DeviceIterator::operator () ()
104{
105	io_service_t dev = ::IOIteratorNext(mIterator);
106	mAtEnd = !dev;
107	return dev;
108}
109
110
111//
112// DeviceMatches
113//
114DeviceMatch::DeviceMatch()
115	: CFRef<CFMutableDictionaryRef>(makeCFMutableDictionary())
116{
117	CFError::check(*this);
118}
119
120DeviceMatch::DeviceMatch(const char *cls)
121	: CFRef<CFMutableDictionaryRef>(::IOServiceMatching(cls))
122{
123	CFError::check(*this);
124}
125
126DeviceMatch::DeviceMatch(const char *cls, const char *name, uint32_t value, ...)
127	: CFRef<CFMutableDictionaryRef>(::IOServiceMatching(cls))
128{
129	CFError::check(*this);
130
131	va_list args;
132	va_start(args, value);
133	while (name) {
134		add(name, value);
135		name = va_arg(args, const char *);
136		if (!name)
137			break;
138		value = va_arg(args, uint32_t);
139	}
140}
141
142void DeviceMatch::add(const char *name, uint32_t value)
143{
144	CFRef<CFNumberRef> number = CFNumberCreate(NULL, kCFNumberSInt32Type, &value);
145	CFDictionarySetValue(*this, CFTempString(name), number);
146}
147
148
149//
150// NotificationPorts
151//
152NotificationPort::NotificationPort()
153{
154	CFError::check(mPortRef = ::IONotificationPortCreate(MasterPort()));
155}
156
157NotificationPort::NotificationPort(const MasterPort &master)
158{
159	CFError::check(mPortRef = ::IONotificationPortCreate(master));
160}
161
162NotificationPort::~NotificationPort()
163{
164	::IONotificationPortDestroy(mPortRef);
165}
166
167
168mach_port_t NotificationPort::port() const
169{
170	mach_port_t p = ::IONotificationPortGetMachPort(mPortRef);
171	CFError::check(p);
172	return p;
173}
174
175CFRunLoopSourceRef NotificationPort::source() const
176{
177	CFRunLoopSourceRef rls = ::IONotificationPortGetRunLoopSource(mPortRef);
178	CFError::check(rls);
179	return rls;
180}
181
182
183void NotificationPort::add(const DeviceMatch &match, Receiver &receiver, const char *type)
184{
185	io_iterator_t iterator;
186	CFRetain(match);	// compensate for IOSAMN not retaining its argument
187	Error::check(::IOServiceAddMatchingNotification(mPortRef, type,
188		match,
189		ioNotify, &receiver,
190		&iterator));
191
192	// run initial iterator to process existing devices
193	secdebug("iokit", "dispatching initial device match iterator %d", iterator);
194	DeviceIterator it(iterator);
195	receiver.ioChange(it);
196}
197
198void NotificationPort::addInterestNotification(Receiver &receiver, io_service_t service,
199	const io_name_t interestType)
200{
201	io_iterator_t iterator;
202	mach_port_t pp = NotificationPort::port();
203
204	secdebug("iokit", "NotificationPort::addInterest - type: %s [port: %p (0x%08X), service: 0x%08X]",
205		interestType, mPortRef, pp, service);
206
207	// We cannot throw if we get an error here since we will receive notifications
208	// from each plane, and not all planes have the necessary information to be
209	// able to add an interest notification
210	kern_return_t kr = ::IOServiceAddInterestNotification(mPortRef,
211		service, interestType, ioDeviceNotification, &receiver, &iterator);
212	const char *msgstr = mach_error_string(kr);
213	const char *msgtyp = mach_error_type(kr);
214	if (msgstr && msgtyp)
215		secdebug("iokit", " msg: %s, typ: %s", msgstr, msgtyp);
216}
217
218void NotificationPort::ioNotify(void *refCon, io_iterator_t iterator)
219{
220	secdebug("iokit", "dispatching new device match iterator %d", iterator);
221	DeviceIterator it(iterator);
222	try {
223		reinterpret_cast<Receiver *>(refCon)->ioChange(it);
224	} catch (...) {
225		secdebug("iokit", "ioChange callback threw an exception (ignored)");
226	}
227}
228
229void NotificationPort::ioDeviceNotification(void *refCon, io_service_t service,
230	natural_t messageType, void *messageArgument)
231{
232	secdebug("iokit", "dispatching NEW device notification iterator, service 0x%08X, msg: 0x%04X, arg: %p",
233		service, messageType, messageArgument);
234
235	const char *msgstr = mach_error_string(messageType);
236	const char *msgtyp = mach_error_type(messageType);
237	if (msgstr && msgtyp)
238		secdebug("iokit", " msg: %s, typ: %s", msgstr, msgtyp);
239
240	if (service!=io_service_t(-1))
241		reinterpret_cast<Receiver *>(refCon)->ioServiceChange(refCon, service, messageType, messageArgument);
242}
243
244//
245// Abstract NotificationPort::Receivers
246//
247NotificationPort::Receiver::~Receiver()
248{ /* virtual */ }
249
250
251//
252// MachPortNotificationPorts
253//
254MachPortNotificationPort::MachPortNotificationPort()
255{
256	NoReplyHandler::port(NotificationPort::port());
257}
258
259MachPortNotificationPort::~MachPortNotificationPort()
260{
261}
262
263boolean_t MachPortNotificationPort::handle(mach_msg_header_t *in)
264{
265	::IODispatchCalloutFromMessage(NULL, in, mPortRef);
266    return TRUE;
267}
268
269
270
271} // end namespace IOKit
272} // end namespace Security
273