2 * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // csproxy - Code Signing Hosting Proxy
30 #include <securityd_client/cshosting.h>
34 // Construct a CodeSigningHost
36 CodeSigningHost::CodeSigningHost()
37 : mHostingState(noHosting
)
45 CodeSigningHost::~CodeSigningHost()
52 // Reset Code Signing Hosting state.
53 // This turns hosting off and clears all children.
55 void CodeSigningHost::reset()
57 switch (mHostingState
) {
59 break; // nothing to do
61 mHostingPort
.destroy();
62 mHostingPort
= MACH_PORT_NULL
;
65 Server::active().remove(*this); // unhook service handler
66 mHostingPort
.destroy(); // destroy receive right
67 mHostingState
= noHosting
;
68 mHostingPort
= MACH_PORT_NULL
;
69 mGuests
.erase(mGuests
.begin(), mGuests
.end());
76 // Given a host reference (possibly NULL for the process itself), locate
77 // its most dedicated guest. This descends a contiguous chain of dedicated
78 // guests until it find a host that either has no guests, or whose guests
81 CodeSigningHost::Guest
*CodeSigningHost::findHost(SecGuestRef hostRef
)
83 Guest
*host
= findGuest(hostRef
, true);
85 if (Guest
*guest
= findGuest(host
))
86 if (guest
->dedicated
) {
87 secdebug("hosting", "%p selecting dedicated guest %p of %p", this, guest
, host
);
97 // Look up guest by guestRef.
98 // Throws if they we don't have a guest by that ref.
100 CodeSigningHost::Guest
*CodeSigningHost::findGuest(SecGuestRef guestRef
, bool hostOk
/* = false */)
102 GuestMap::iterator it
= mGuests
.find(guestRef
);
103 if (it
== mGuests
.end())
107 MacOSError::throwMe(errSecCSNoSuchCode
);
108 assert(it
->first
== it
->second
->guestRef());
114 // Look up guest by attribute set.
115 // Returns the host if the attributes can't be found (*loose* interpretation).
116 // Throws if multiple guests are found (ambiguity).
117 // Implicitly matches dedicated guests no matter what attributes are requested.
119 CodeSigningHost::Guest
*CodeSigningHost::findGuest(Guest
*host
, const CssmData
&attrData
)
121 CFRef
<CFDictionaryRef
> attrDict
= attrData
122 ? makeCFDictionaryFrom(attrData
.data(), attrData
.length())
123 : makeCFDictionary(0);
124 CFDictionary
attrs(attrDict
, errSecCSInvalidAttributeValues
);
126 // if a guest handle was provided, start with that - it must be valid or we fail
127 if (CFNumberRef canonical
= attrs
.get
<CFNumberRef
>(kSecGuestAttributeCanonical
)) {
128 // direct lookup by SecGuestRef (canonical guest handle)
129 SecGuestRef guestRef
= cfNumber
<SecGuestRef
>(canonical
);
130 secdebug("hosting", "host %p looking for guest handle 0x%x", host
, guestRef
);
131 if (Guest
*guest
= findGuest(guestRef
, true)) // found guest handle
132 if (guest
->isGuestOf(host
, loose
)) {
133 secdebug("hosting", "found guest %p, continuing search", guest
);
134 host
= guest
; // new starting point
136 MacOSError::throwMe(errSecCSNoSuchCode
); // not a guest of given host
138 MacOSError::throwMe(errSecCSNoSuchCode
); // not there at all
141 // now take the rest of the attrs
142 CFIndex count
= CFDictionaryGetCount(attrs
);
143 CFTypeRef keys
[count
], values
[count
];
144 CFDictionaryGetKeysAndValues(attrs
, keys
, values
);
146 secdebug("hosting", "searching host %p by attributes", host
);
147 Guest
*match
= NULL
; // previous match found
148 for (GuestMap::const_iterator it
= mGuests
.begin(); it
!= mGuests
.end(); ++it
)
149 if (it
->second
->isGuestOf(host
, strict
))
150 if (it
->second
->matches(count
, keys
, values
))
152 MacOSError::throwMe(errSecCSMultipleGuests
); // ambiguous
155 if (!match
) { // nothing found
156 secdebug("hosting", "nothing found, returning %p", host
);
159 secdebug("hosting", "found guest %p, continuing", match
);
160 host
= match
; // and repeat
167 // Find any guest of a given host.
168 // This will return a randomly chosen guest of this host if it has any,
169 // or NULL if it has none (i.e. it is not a host).
171 CodeSigningHost::Guest
*CodeSigningHost::findGuest(Guest
*host
)
173 for (GuestMap::const_iterator it
= mGuests
.begin(); it
!= mGuests
.end(); ++it
)
174 if (it
->second
->isGuestOf(host
, strict
))
181 // Register a hosting API service port where the host will dynamically
182 // answer hosting queries from interested parties. This switches the process
183 // to dynamic hosting mode, and is incompatible with proxy hosting.
185 void CodeSigningHost::registerCodeSigning(mach_port_t hostingPort
, SecCSFlags flags
)
187 switch (mHostingState
) {
189 secdebug("hosting", "%p registering for dynamic hosting on port %d",
191 mHostingPort
= hostingPort
;
192 mHostingState
= dynamicHosting
;
195 MacOSError::throwMe(errSecCSHostProtocolContradiction
);
201 // Create a guest entry for the given host and prepare to answer for it
202 // when dynamic hosting queries are received for it.
203 // This engages proxy hosting mode, and is incompatible with dynamic hosting mode.
205 SecGuestRef
CodeSigningHost::createGuest(SecGuestRef hostRef
,
206 uint32_t status
, const char *path
, const CssmData
&attributes
, SecCSFlags flags
)
208 secdebug("hosting", "%p create guest from host %d", this, hostRef
);
210 if (path
[0] != '/') // relative path (relative to what? :-)
211 MacOSError::throwMe(errSecCSHostProtocolRelativePath
);
213 // set up for hosting proxy services if nothing's there yet
214 switch (mHostingState
) {
216 // set up proxy hosting
217 mHostingPort
.allocate();
218 MachServer::Handler::port(mHostingPort
);
219 MachServer::active().add(*this);
220 mHostingState
= proxyHosting
;
221 secdebug("hosting", "%p created hosting port %d for proxy hosting", this, mHostingPort
.port());
226 MacOSError::throwMe(errSecCSHostProtocolContradiction
);
229 RefPointer
<Guest
> host
= findHost(hostRef
);
230 RefPointer
<Guest
> knownGuest
= findGuest(host
);
231 if ((flags
& kSecCSDedicatedHost
) && knownGuest
)
232 MacOSError::throwMe(errSecCSHostProtocolDedicationError
); // can't dedicate with other guests
233 else if (knownGuest
&& knownGuest
->dedicated
)
234 MacOSError::throwMe(errSecCSHostProtocolDedicationError
); // other guest is already dedicated
236 // create the new guest
237 RefPointer
<Guest
> guest
= new Guest
;
239 guest
->guestPath
= host
->guestPath
;
240 guest
->guestPath
.push_back(guest
->handle());
241 guest
->status
= status
;
243 guest
->setAttributes(attributes
);
244 guest
->dedicated
= (flags
& kSecCSDedicatedHost
);
245 mGuests
[guest
->guestRef()] = guest
;
246 secdebug("hosting", "guest 0x%x created %sstatus=0x%x path=%s",
247 guest
->guestRef(), guest
->dedicated
? "dedicated " : "", guest
->status
, guest
->path
.c_str());
248 return guest
->guestRef();
252 void CodeSigningHost::setGuestStatus(SecGuestRef guestRef
, uint32_t status
, const CssmData
&attributes
)
254 secdebug("hosting", "%p set guest 0x%x", this, guestRef
);
255 if (mHostingState
!= proxyHosting
)
256 MacOSError::throwMe(errSecCSHostProtocolNotProxy
);
257 Guest
*guest
= findGuest(guestRef
);
259 // state modification machine
260 if ((status
& ~guest
->status
) & CS_VALID
)
261 MacOSError::throwMe(errSecCSHostProtocolStateError
); // can't set
262 if ((~status
& guest
->status
) & (CS_HARD
| CS_KILL
))
263 MacOSError::throwMe(errSecCSHostProtocolStateError
); // can't clear
264 guest
->status
= status
;
266 // replace attributes if requested
268 guest
->setAttributes(attributes
);
273 // Remove a guest previously introduced via createGuest().
275 void CodeSigningHost::removeGuest(SecGuestRef hostRef
, SecGuestRef guestRef
)
277 secdebug("hosting", "%p removes guest %d from host %d", this, guestRef
, hostRef
);
278 if (mHostingState
!= proxyHosting
)
279 MacOSError::throwMe(errSecCSHostProtocolNotProxy
);
280 RefPointer
<Guest
> host
= findHost(hostRef
);
281 RefPointer
<Guest
> guest
= findGuest(guestRef
);
282 if (guest
->dedicated
) // can't remove a dedicated guest
283 MacOSError::throwMe(errSecCSHostProtocolDedicationError
);
284 if (!guest
->isGuestOf(host
, strict
))
285 MacOSError::throwMe(errSecCSHostProtocolUnrelated
);
286 for (GuestMap::iterator it
= mGuests
.begin(); it
!= mGuests
.end(); ++it
)
287 if (it
->second
->isGuestOf(guest
, loose
))
293 // The internal Guest object
295 CodeSigningHost::Guest::~Guest()
297 secdebug("hosting", "guest %ld destroyed", handle());
300 void CodeSigningHost::Guest::setAttributes(const CssmData
&attrData
)
302 CFRef
<CFNumberRef
> guest
= makeCFNumber(guestRef());
304 CFRef
<CFDictionaryRef
> inputDict
= makeCFDictionaryFrom(attrData
.data(), attrData
.length());
305 CFRef
<CFMutableDictionaryRef
> dict
= CFDictionaryCreateMutableCopy(NULL
, 0, inputDict
);
306 CFDictionaryAddValue(dict
, kSecGuestAttributeCanonical
, guest
);
307 attributes
.take(dict
);
309 attributes
.take(makeCFDictionary(1, kSecGuestAttributeCanonical
, guest
.get()));
314 bool CodeSigningHost::Guest::isGuestOf(Guest
*host
, GuestCheck check
) const
316 vector
<SecGuestRef
> hostPath
;
318 hostPath
= host
->guestPath
;
319 if (hostPath
.size() <= guestPath
.size()
320 && !memcmp(&hostPath
[0], &guestPath
[0], sizeof(SecGuestRef
) * hostPath
.size()))
321 // hostPath is a prefix of guestPath
326 return guestPath
.size() == hostPath
.size() + 1; // immediate guest
333 // Check to see if a given guest matches the (possibly empty) attribute set provided
334 // (in broken-open form, for efficiency). A dedicated guest will match all attribute
335 // specifications, even empty ones. A non-dedicated guest matches if at least one
336 // attribute value requested matches exactly (in the sense of CFEqual) that given
337 // by the host for this guest.
339 bool CodeSigningHost::Guest::matches(CFIndex count
, CFTypeRef keys
[], CFTypeRef values
[]) const
343 for (CFIndex n
= 0; n
< count
; n
++) {
344 CFStringRef key
= CFStringRef(keys
[n
]);
345 if (CFEqual(key
, kSecGuestAttributeCanonical
)) // ignore canonical attribute (handled earlier)
347 if (CFTypeRef value
= CFDictionaryGetValue(attributes
, key
))
348 if (CFEqual(value
, values
[n
]))
356 // The MachServer dispatch handler for proxy hosting.
358 boolean_t
cshosting_server(mach_msg_header_t
*, mach_msg_header_t
*);
360 static ThreadNexus
<CodeSigningHost
*> context
;
362 boolean_t
CodeSigningHost::handle(mach_msg_header_t
*in
, mach_msg_header_t
*out
)
365 return cshosting_server(in
, out
);
370 // Proxy implementation of Code Signing Hosting protocol
372 #define CSH_ARGS mach_port_t servicePort, mach_port_t replyPort, OSStatus *rcode
374 #define BEGIN_IPC try {
375 #define END_IPC *rcode = noErr; } \
376 catch (const CommonError &err) { *rcode = err.osStatus(); } \
377 catch (...) { *rcode = errSecCSInternalError; } \
380 #define DATA_IN(base) void *base, mach_msg_type_number_t base##Length
381 #define DATA_OUT(base) void **base, mach_msg_type_number_t *base##Length
382 #define DATA(base) CssmData(base, base##Length)
386 // Find a guest by arbitrary attribute set.
388 // This returns an array of canonical guest references describing the path
389 // from the host given to the guest found. If the host itself is returned
390 // as a guest, this will be an empty array (zero length).
392 // The subhost return argument may in the future return the hosting port for
393 // a guest who dynamically manages its hosting (thus breaking out of proxy mode),
394 // but this is not yet implemented.
396 kern_return_t
cshosting_server_findGuest(CSH_ARGS
, SecGuestRef hostRef
,
398 GuestChain
*foundGuest
, mach_msg_type_number_t
*depth
, mach_port_t
*subhost
)
402 *subhost
= MACH_PORT_NULL
; // preset no sub-hosting port returned
404 Process::Guest
*host
= context()->findGuest(hostRef
, true);
405 if (Process::Guest
*guest
= context()->findGuest(host
, DATA(attributes
))) {
406 *foundGuest
= &guest
->guestPath
[0];
407 *depth
= guest
->guestPath
.size();
417 // Retrieve the path to a guest specified by canonical reference.
419 kern_return_t
cshosting_server_guestPath(CSH_ARGS
, SecGuestRef guestRef
, char *path
)
422 strncpy(path
, context()->findGuest(guestRef
)->path
.c_str(), MAXPATHLEN
);
428 // Retrieve the status word for a guest specified by canonical reference.
430 kern_return_t
cshosting_server_guestStatus(CSH_ARGS
, SecGuestRef guestRef
, uint32_t *status
)
433 *status
= context()->findGuest(guestRef
)->status
;
441 #if defined(DEBUGDUMP)
443 void CodeSigningHost::dump() const
445 switch (mHostingState
) {
449 Debug::dump(" dynamic host port=%d", mHostingPort
.port());
452 Debug::dump(" proxy-host port=%d", mHostingPort
.port());
453 if (!mGuests
.empty()) {
454 Debug::dump(" %d guests={", int(mGuests
.size()));
455 for (GuestMap::const_iterator it
= mGuests
.begin(); it
!= mGuests
.end(); ++it
) {
456 if (it
!= mGuests
.begin())
466 void CodeSigningHost::Guest::dump() const
468 Debug::dump("%s[", path
.c_str());
469 for (vector
<SecGuestRef
>::const_iterator it
= guestPath
.begin(); it
!= guestPath
.end(); ++it
) {
470 if (it
!= guestPath
.begin())
472 Debug::dump("0x%x", *it
);
474 Debug::dump("; status=0x%x attrs=%s]",
475 status
, cfString(CFCopyDescription(attributes
), true).c_str());