X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/84aacf34eae6543be9f0280b2015385f91e5c2c6..b54c578e17e9bcbd74aa30ea75e25e955b9a6205:/securityd/src/csproxy.cpp?ds=inline diff --git a/securityd/src/csproxy.cpp b/securityd/src/csproxy.cpp deleted file mode 100644 index 5ca68c60..00000000 --- a/securityd/src/csproxy.cpp +++ /dev/null @@ -1,586 +0,0 @@ -/* - * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -// -// csproxy - Code Signing Hosting Proxy -// -#include - -#include "csproxy.h" -#include "server.h" -#include -#include -#include -#include -#include - -// -// Construct a CodeSigningHost -// -CodeSigningHost::CodeSigningHost() - : mLock(Mutex::recursive), mHostingState(noHosting) -{ -} - - -// -// Cleanup code. -// -CodeSigningHost::~CodeSigningHost() -{ - reset(); -} - - -// -// Reset Code Signing Hosting state. -// This turns hosting off and clears all children. -// -void CodeSigningHost::reset() -{ - StLock _(mLock); - switch (mHostingState) { - case noHosting: - break; // nothing to do - case dynamicHosting: - mHostingPort.deallocate(); - secnotice("SecServer", "%d host unregister", mHostingPort.port()); - break; - case proxyHosting: - Server::active().remove(*this); // unhook service handler - mHostingPort.modRefs(MACH_PORT_RIGHT_RECEIVE, -1); - mHostingState = noHosting; - mHostingPort = MACH_PORT_NULL; - mGuests.erase(mGuests.begin(), mGuests.end()); - secnotice("SecServer", "%d host unregister", mHostingPort.port()); - break; - } -} - - -// -// Given a host reference (possibly NULL for the process itself), locate -// its most dedicated guest. This descends a contiguous chain of dedicated -// guests until it find a host that either has no guests, or whose guests -// are not dedicated. -// -CodeSigningHost::Guest *CodeSigningHost::findHost(SecGuestRef hostRef) -{ - Guest *host = findGuest(hostRef, true); - for (;;) { - if (Guest *guest = findGuest(host)) - if (guest->dedicated) { - host = guest; - continue; - } - return host; - } -} - - -// -// Look up guest by guestRef. -// Throws if we don't have a guest by that ref. -// -CodeSigningHost::Guest *CodeSigningHost::findGuest(SecGuestRef guestRef, bool hostOk /* = false */) -{ - GuestMap::iterator it = mGuests.find(guestRef); - if (it == mGuests.end()) { - if (hostOk) { - return NULL; - } else { - MacOSError::throwMe(errSecCSNoSuchCode); - } - } - assert(it->first == it->second->guestRef()); - return it->second; -} - - -// -// Look up guest by attribute set. -// Returns the host if the attributes can't be found (*loose* interpretation). -// Throws if multiple guests are found (ambiguity). -// Implicitly matches dedicated guests no matter what attributes are requested. -// -CodeSigningHost::Guest *CodeSigningHost::findGuest(Guest *host, const CssmData &attrData) -{ - CFRef attrDict = attrData - ? makeCFDictionaryFrom(attrData.data(), attrData.length()) - : makeCFDictionary(0); - CFDictionary attrs(attrDict, errSecCSInvalidAttributeValues); - - // if a guest handle was provided, start with that - it must be valid or we fail - if (CFNumberRef canonical = attrs.get(kSecGuestAttributeCanonical)) { - // direct lookup by SecGuestRef (canonical guest handle) - SecGuestRef guestRef = cfNumber(canonical); - if (Guest *guest = findGuest(guestRef, true)) // found guest handle - if (guest->isGuestOf(host, loose)) - host = guest; // new starting point - else - MacOSError::throwMe(errSecCSNoSuchCode); // not a guest of given host - else - MacOSError::throwMe(errSecCSNoSuchCode); // not there at all - } - - // now take the rest of the attrs - CFIndex count = CFDictionaryGetCount(attrs); - - CFTypeRef *keys = (CFTypeRef*)malloc(count*sizeof(CFTypeRef)); - CFTypeRef *values = (CFTypeRef*)malloc(count*sizeof(CFTypeRef)); - - if (keys == NULL || values == NULL) { - free(keys); - free(values); - MacOSError::throwMe(errSecMemoryError); - } - - CFDictionaryGetKeysAndValues(attrs, keys, values); - for (;;) { - Guest *match = NULL; // previous match found - for (GuestMap::const_iterator it = mGuests.begin(); it != mGuests.end(); ++it) { - if (it->second->isGuestOf(host, strict)) { - if (it->second->matches(count, keys, values)) { - if (match) { - free(keys); - free(values); - MacOSError::throwMe(errSecCSMultipleGuests); // ambiguous - } else { - match = it->second; - } - } - } - } - if (!match) { // nothing found - free(keys); - free(values); - return host; - } - else { - host = match; // and repeat - } - } -} - - -// -// Find any guest of a given host. -// This will return a randomly chosen guest of this host if it has any, -// or NULL if it has none (i.e. it is not a host). -// -CodeSigningHost::Guest *CodeSigningHost::findGuest(Guest *host) -{ - for (GuestMap::const_iterator it = mGuests.begin(); it != mGuests.end(); ++it) - if (it->second->isGuestOf(host, strict)) - return it->second; - return NULL; -} - - -// -// Register a hosting API service port where the host will dynamically -// answer hosting queries from interested parties. This switches the process -// to dynamic hosting mode, and is incompatible with proxy hosting. -// -void CodeSigningHost::registerCodeSigning(mach_port_t hostingPort, SecCSFlags flags) -{ - StLock _(mLock); - switch (mHostingState) { - case noHosting: - mHostingPort = hostingPort; - mHostingState = dynamicHosting; - secnotice("SecServer", "%d host register: %d", mHostingPort.port(), mHostingPort.port()); - break; - default: - MacOSError::throwMe(errSecCSHostProtocolContradiction); - } -} - - -// -// Create a guest entry for the given host and prepare to answer for it -// when dynamic hosting queries are received for it. -// This engages proxy hosting mode, and is incompatible with dynamic hosting mode. -// -SecGuestRef CodeSigningHost::createGuest(SecGuestRef hostRef, - uint32_t status, const char *path, - const CssmData &cdhash, const CssmData &attributes, SecCSFlags flags) -{ - StLock _(mLock); - if (path[0] != '/') // relative path (relative to what? :-) - MacOSError::throwMe(errSecCSHostProtocolRelativePath); - if (cdhash.length() > maxUcspHashLength) - MacOSError::throwMe(errSecCSHostProtocolInvalidHash); - - // set up for hosting proxy services if nothing's there yet - switch (mHostingState) { - case noHosting: // first hosting call, this host - // set up proxy hosting - mHostingPort.allocate(); // allocate service port - MachServer::Handler::port(mHostingPort); // put into Handler - MachServer::active().add(*this); // start listening - mHostingState = proxyHosting; // now proxying for this host - secnotice("SecServer", "%d host proxy: %d", mHostingPort.port(), mHostingPort.port()); - break; - case proxyHosting: // already proxying - break; - case dynamicHosting: // in dynamic mode, can't switch - MacOSError::throwMe(errSecCSHostProtocolContradiction); - } - - RefPointer host = findHost(hostRef); - if (RefPointer knownGuest = findGuest(host)) { // got a guest already - if (flags & kSecCSDedicatedHost) { - MacOSError::throwMe(errSecCSHostProtocolDedicationError); // can't dedicate with other guests - } else if (knownGuest->dedicated) { - MacOSError::throwMe(errSecCSHostProtocolDedicationError); // other guest is already dedicated - } - } - - // create the new guest - RefPointer guest = new Guest; - if (host) - guest->guestPath = host->guestPath; - guest->guestPath.push_back(int_cast(guest->handle())); - guest->status = status; - guest->path = path; - guest->setAttributes(attributes); - guest->setHash(cdhash, flags & kSecCSGenerateGuestHash); - guest->dedicated = (flags & kSecCSDedicatedHost); - mGuests[guest->guestRef()] = guest; - secnotice("SecServer", "%d guest create %d %d status:%d %d %s", mHostingPort.port(), hostRef, guest->guestRef(), guest->status, flags, guest->path.c_str()); - return guest->guestRef(); -} - - -void CodeSigningHost::setGuestStatus(SecGuestRef guestRef, uint32_t status, const CssmData &attributes) -{ - StLock _(mLock); - if (mHostingState != proxyHosting) - MacOSError::throwMe(errSecCSHostProtocolNotProxy); - Guest *guest = findGuest(guestRef); - - // state modification machine - if ((status & ~guest->status) & kSecCodeStatusValid) - MacOSError::throwMe(errSecCSHostProtocolStateError); // can't set - if ((~status & guest->status) & (kSecCodeStatusHard | kSecCodeStatusKill)) - MacOSError::throwMe(errSecCSHostProtocolStateError); // can't clear - guest->status = status; - secnotice("SecServer", "%d guest change %d %d", mHostingPort.port(), guestRef, status); - - // replace attributes if requested - if (attributes) - guest->setAttributes(attributes); -} - - -// -// Remove a guest previously introduced via createGuest(). -// -void CodeSigningHost::removeGuest(SecGuestRef hostRef, SecGuestRef guestRef) -{ - StLock _(mLock); - if (mHostingState != proxyHosting) - MacOSError::throwMe(errSecCSHostProtocolNotProxy); - RefPointer host = findHost(hostRef); - RefPointer guest = findGuest(guestRef); - if (guest->dedicated) // can't remove a dedicated guest - MacOSError::throwMe(errSecCSHostProtocolDedicationError); - if (!guest->isGuestOf(host, strict)) - MacOSError::throwMe(errSecCSHostProtocolUnrelated); - - set matchingGuests; - - for (auto &it : mGuests) { - if (it.second->isGuestOf(guest, loose)) { - matchingGuests.insert(it.first); - } - } - - for (auto &it : matchingGuests) { - secnotice("SecServer", "%d guest destroy %d", mHostingPort.port(), it); - mGuests.erase(it); - } -} - - -// -// The internal Guest object -// -CodeSigningHost::Guest::Guest() : mAttrData(NULL), mAttrDataLength(0) -{ -} - -CodeSigningHost::Guest::~Guest() -{ - if (mAttrData != NULL) { - vm_size_t rounded_size = mach_vm_round_page(mAttrDataLength); - vm_deallocate(mach_task_self(), reinterpret_cast(mAttrData), rounded_size); - } -} - -void CodeSigningHost::Guest::setAttributes(const CssmData &attrData) -{ - CFRef guest = makeCFNumber(guestRef()); - if (attrData) { - CFDictionaryRef attrs = makeCFDictionaryFrom(attrData.data(), attrData.length()); - attributes.take(cfmake("{+%O,%O=%O}", - attrs, kSecGuestAttributeCanonical, guest.get())); - CFReleaseNull(attrs); - } else { - attributes.take(makeCFDictionary(1, kSecGuestAttributeCanonical, guest.get())); - } -} - -void CodeSigningHost::Guest::createAttrData() const { - if (!mAttrData) { - CFRef data = makeCFData(this->attributes.get()); - - /* cshosting_server_identifyGuest() will point to the attrData in a MIG - * OOL buffer. To prevent leaking surrounding memory, the attr data gets its - * own (zeroed) pages. */ - vm_address_t addr = 0; - vm_size_t rounded_size = mach_vm_round_page(CFDataGetLength(data.get())); - kern_return_t ret = vm_allocate(mach_task_self(), &addr, rounded_size, VM_FLAGS_ANYWHERE); - - if (ret == KERN_SUCCESS) { - mAttrData = reinterpret_cast(addr); - mAttrDataLength = CFDataGetLength(data.get()); - memcpy(mAttrData, CFDataGetBytePtr(data.get()), mAttrDataLength); - // pages returned by vm_allocate are zeroed, no need to fill out padding. - } else { - secerror("csproxy attrData vm_allocate failed: %d", ret); - } - } -} - -void CodeSigningHost::Guest::setHash(const CssmData &given, bool generate) -{ - if (given.length()) // explicitly given - this->cdhash.take(makeCFData(given)); - else if (CFTypeRef hash = CFDictionaryGetValue(this->attributes, kSecGuestAttributeHash)) - if (CFGetTypeID(hash) == CFDataGetTypeID()) - this->cdhash = CFDataRef(hash); - else - MacOSError::throwMe(errSecCSHostProtocolInvalidHash); - else if (generate) { // generate from path (well, try) - CFRef code; - MacOSError::check(SecStaticCodeCreateWithPath(CFTempURL(this->path), kSecCSDefaultFlags, &code.aref())); - CFRef info; - MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref())); - this->cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique)); - } -} - - -bool CodeSigningHost::Guest::isGuestOf(Guest *host, GuestCheck check) const -{ - vector hostPath; - if (host) - hostPath = host->guestPath; - if (hostPath.size() <= guestPath.size() - && !memcmp(&hostPath[0], &guestPath[0], sizeof(SecGuestRef) * hostPath.size())) - // hostPath is a prefix of guestPath - switch (check) { - case loose: - return true; - case strict: - return guestPath.size() == hostPath.size() + 1; // immediate guest - } - return false; -} - - -// -// Check to see if a given guest matches the (possibly empty) attribute set provided -// (in broken-open form, for efficiency). A dedicated guest will match all attribute -// specifications, even empty ones. A non-dedicated guest matches if at least one -// attribute value requested matches exactly (in the sense of CFEqual) that given -// by the host for this guest. -// -bool CodeSigningHost::Guest::matches(CFIndex count, CFTypeRef keys[], CFTypeRef values[]) const -{ - if (dedicated) - return true; - for (CFIndex n = 0; n < count; n++) { - CFStringRef key = CFStringRef(keys[n]); - if (CFEqual(key, kSecGuestAttributeCanonical)) // ignore canonical attribute (handled earlier) - continue; - if (CFTypeRef value = CFDictionaryGetValue(attributes, key)) - if (CFEqual(value, values[n])) - return true; - } - return false; -} - - -// -// The MachServer dispatch handler for proxy hosting. -// - -// give MIG handlers access to the object lock -class CodeSigningHost::Lock : private StLock { -public: - Lock(CodeSigningHost *host) : StLock(host->mLock) { } -}; - - -boolean_t cshosting_server(mach_msg_header_t *, mach_msg_header_t *); - -static ThreadNexus context; - -boolean_t CodeSigningHost::handle(mach_msg_header_t *in, mach_msg_header_t *out) -{ - CodeSigningHost::Lock _(this); - context() = this; - return cshosting_server(in, out); -} - - -// -// Proxy implementation of Code Signing Hosting protocol -// -#define CSH_ARGS mach_port_t servicePort, mach_port_t replyPort, OSStatus *rcode - -#define BEGIN_IPC try { -#define END_IPC *rcode = noErr; } \ - catch (const CommonError &err) { *rcode = err.osStatus(); } \ - catch (...) { *rcode = errSecCSInternalError; } \ - return KERN_SUCCESS; - -#define DATA_IN(base) void *base, mach_msg_type_number_t base##Length -#define DATA_OUT(base) void **base, mach_msg_type_number_t *base##Length -#define DATA(base) CssmData(base, base##Length) - - -// -// Find a guest by arbitrary attribute set. -// -// This returns an array of canonical guest references describing the path -// from the host given to the guest found. If the host itself is returned -// as a guest, this will be an empty array (zero length). -// -// The subhost return argument may in the future return the hosting port for -// a guest who dynamically manages its hosting (thus breaking out of proxy mode), -// but this is not yet implemented. -// -kern_return_t cshosting_server_findGuest(CSH_ARGS, SecGuestRef hostRef, - DATA_IN(attributes), - GuestChain *foundGuest, mach_msg_type_number_t *depth, mach_port_t *subhost) -{ - BEGIN_IPC - - *subhost = MACH_PORT_NULL; // preset no sub-hosting port returned - - Process::Guest *host = context()->findGuest(hostRef, true); - if (Process::Guest *guest = context()->findGuest(host, DATA(attributes))) { - *foundGuest = &guest->guestPath[0]; - *depth = int_cast(guest->guestPath.size()); - } else { - *foundGuest = NULL; - *depth = 0; - } - END_IPC -} - - -// -// Retrieve the path to a guest specified by canonical reference. -// -kern_return_t cshosting_server_identifyGuest(CSH_ARGS, SecGuestRef guestRef, - char *path, char *hash, uint32_t *hashLength, DATA_OUT(attributes)) -{ - BEGIN_IPC - CodeSigningHost::Guest *guest = context()->findGuest(guestRef); - strncpy(path, guest->path.c_str(), MAXPATHLEN); - - // canonical cdhash - if (guest->cdhash) { - *hashLength = int_cast(CFDataGetLength(guest->cdhash)); - assert(*hashLength <= maxUcspHashLength); - memcpy(hash, CFDataGetBytePtr(guest->cdhash), *hashLength); - } else - *hashLength = 0; // unavailable - - // visible attributes. This proxy returns all attributes set by the host - *attributes = (void*)guest->attrData(); // MIG botch (it doesn't need a writable pointer) - *attributesLength = int_cast(guest->attrDataLength()); - - END_IPC -} - - -// -// Retrieve the status word for a guest specified by canonical reference. -// -kern_return_t cshosting_server_guestStatus(CSH_ARGS, SecGuestRef guestRef, uint32_t *status) -{ - BEGIN_IPC - *status = context()->findGuest(guestRef)->status; - END_IPC -} - - -// -// Debug support -// -#if defined(DEBUGDUMP) - -void CodeSigningHost::dump() const -{ - StLock _(mLock); - switch (mHostingState) { - case noHosting: - break; - case dynamicHosting: - Debug::dump(" dynamic host port=%d", mHostingPort.port()); - break; - case proxyHosting: - Debug::dump(" proxy-host port=%d", mHostingPort.port()); - if (!mGuests.empty()) { - Debug::dump(" %d guests={", int(mGuests.size())); - for (GuestMap::const_iterator it = mGuests.begin(); it != mGuests.end(); ++it) { - if (it != mGuests.begin()) - Debug::dump(", "); - it->second->dump(); - } - Debug::dump("}"); - } - break; - } -} - -void CodeSigningHost::Guest::dump() const -{ - Debug::dump("%s[", path.c_str()); - for (vector::const_iterator it = guestPath.begin(); it != guestPath.end(); ++it) { - if (it != guestPath.begin()) - Debug::dump("/"); - Debug::dump("0x%x", *it); - } - Debug::dump("; status=0x%x attrs=%s]", - status, cfStringRelease(CFCopyDescription(attributes)).c_str()); -} - -#endif //DEBUGDUMP