--- /dev/null
+/*
+ * 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 <servers/netname.h>
+#include <security_utilities/debugging.h>
+
+using MachPlusPlus::check;
+using MachPlusPlus::Bootstrap;
+
+
+namespace Security {
+namespace SecurityServer {
+
+
+//
+// The process-global object
+//
+UnixPlusPlus::StaticForkMonitor ClientSession::mHasForked;
+ModuleNexus<ClientSession::Global> 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