X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurityd/lib/ssclient.cpp diff --git a/Security/libsecurityd/lib/ssclient.cpp b/Security/libsecurityd/lib/ssclient.cpp new file mode 100644 index 00000000..37951004 --- /dev/null +++ b/Security/libsecurityd/lib/ssclient.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2000-2008,2011,2013 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@ + */ + + +// +// ssclient - SecurityServer client interface library +// +#include "sstransit.h" +#include +#include + +using MachPlusPlus::check; +using MachPlusPlus::Bootstrap; + + +namespace Security { +namespace SecurityServer { + + +// +// The process-global object +// +UnixPlusPlus::StaticForkMonitor ClientSession::mHasForked; +ModuleNexus ClientSession::mGlobal; +const char *ClientSession::mContactName; +SecGuestRef ClientSession::mDedicatedGuest = kSecNoGuest; + + +// +// Construct a client session +// +ClientSession::ClientSession(Allocator &std, Allocator &rtn) +: ClientCommon(std, rtn), mCallback(NULL), mCallbackContext(NULL) +{ } + + +// +// Destroy a session +// +ClientSession::~ClientSession() +{ } + + +void +ClientSession::registerForAclEdits(DidChangeKeyAclCallback *callback, void *context) +{ + mCallback = callback; + mCallbackContext = context; +} + +// +// Perform any preambles required to be a securityd client in good standing. +// This includes initial setup calls, thread registration, fork management, +// and (Code Signing) guest status. +// +void ClientSession::activate() +{ + // Guard against fork-without-exec. If we are the child of a fork + // (that has not exec'ed), our apparent connection to SecurityServer + // is just a mirage, and we better reset it. + if (mHasForked()) { + secdebug("SSclnt", "process has forked (now pid=%d) - resetting connection object", getpid()); + mGlobal.reset(); + } + + // now pick up the (new or existing) connection state + Global &global = mGlobal(); + Thread &thread = global.thread(); + if (!thread) { + // first time for this thread - use abbreviated registration + IPCN(ucsp_client_setupThread(UCSP_ARGS, mach_task_self())); + thread.registered = true; + secdebug("SSclnt", "Thread registered with %s", mContactName); + } + + // if the thread's guest state has changed, tell securityd + if (thread.currentGuest != thread.lastGuest) { + IPCN(ucsp_client_setGuest(UCSP_ARGS, thread.currentGuest, kSecCSDefaultFlags)); + thread.lastGuest = thread.currentGuest; + secdebug("SSclnt", "switched guest state to 0x%x", thread.currentGuest); + } +} + + +// +// The contactName method allows the caller to explicitly override the bootstrap +// name under which SecurityServer is located. Use this only with great caution, +// and probably only for debugging. +// Note that no explicit locking is done here. It is the caller's responsibility +// to make sure this is called from thread-safe context before the real dance begins. +// +void ClientSession::contactName(const char *name) +{ + mContactName = name; +} + +const char *ClientSession::contactName() const +{ + return mContactName; +} + + +// +// Construct the process-global state object. +// The ModuleNexus construction magic will ensure that this happens uniquely +// even if the face of multithreaded attack. +// +ClientSession::Global::Global() +{ + // find server port + serverPort = findSecurityd(); + + mach_port_t originPort = MACH_PORT_NULL; + IPCN(ucsp_client_verifyPrivileged2(serverPort.port(), mig_get_reply_port(), &securitydCreds, &rcode, &originPort)); + if (originPort != serverPort.port()) + CssmError::throwMe(CSSM_ERRCODE_VERIFICATION_FAILURE); + mach_port_mod_refs(mach_task_self(), originPort, MACH_PORT_RIGHT_SEND, -1); + + // send identification/setup message + static const char extForm[] = "?:obsolete"; + ClientSetupInfo info = { 0x1234, SSPROTOVERSION }; + + // cannot use UCSP_ARGS here because it uses mGlobal() -> deadlock + Thread &thread = this->thread(); + + IPCN(ucsp_client_setup(serverPort, thread.replyPort, &securitydCreds, &rcode, + mach_task_self(), info, extForm)); + thread.registered = true; // as a side-effect of setup call above + IFDEBUG(serverPort.requestNotify(thread.replyPort)); + secdebug("SSclnt", "contact with %s established", mContactName); +} + + +// +// Reset the connection. +// This discards all client state accumulated for the securityd link. +// Existing connections will go stale and fail; new connections will +// re-establish the link. This is an expert tool ONLY. If you don't know +// exactly how this gig is danced, you don't want to call this. Really. +// +void ClientSession::reset() +{ + secdebug("SSclnt", "resetting client state (OUCH)"); + mGlobal.reset(); +} + + +// +// Common utility for finding the registered securityd port for the current +// session. This does not cache the port anywhere, though it does effectively +// cache the name. +// +Port ClientSession::findSecurityd() +{ + if (!mContactName) + { + mContactName = getenv(SECURITYSERVER_BOOTSTRAP_ENV); + if (!mContactName) + mContactName = SECURITYSERVER_BOOTSTRAP_NAME; + } + + secdebug("SSclnt", "Locating %s", mContactName); + Port serverPort = Bootstrap().lookup2(mContactName); + secdebug("SSclnt", "contacting %s at port %d (version %d)", + mContactName, serverPort.port(), SSPROTOVERSION); + return serverPort; +} + + +// +// Subsidiary process management. +// This does not go through the generic securityd-client setup. +// +void ClientSession::childCheckIn(Port serverPort, Port taskPort) +{ + Port securitydPort = findSecurityd(); + mach_port_t originPort = MACH_PORT_NULL; + IPCN(ucsp_client_verifyPrivileged2(securitydPort.port(), mig_get_reply_port(), &securitydCreds, &rcode, &originPort)); + if (originPort != securitydPort.port()) + CssmError::throwMe(CSSM_ERRCODE_VERIFICATION_FAILURE); + mach_port_mod_refs(mach_task_self(), originPort, MACH_PORT_RIGHT_SEND, -1); + check(ucsp_client_childCheckIn(securitydPort, serverPort, taskPort)); +} + + +// +// Notify an (interested) caller that a securityd-mediated ACL change +// MAY have happened on a key object involved in an operation. This allows +// such callers to re-encode key blobs for storage. +// +void ClientSession::notifyAclChange(KeyHandle key, CSSM_ACL_AUTHORIZATION_TAG tag) +{ + if (mCallback) { + secdebug("keyacl", "ACL change key %u operation %u", key, tag); + mCallback(mCallbackContext, *this, key, tag); + } else + secdebug("keyacl", "dropped ACL change notice for key %u operation %u", + key, tag); +} + + +} // end namespace SecurityServer +} // end namespace Security