]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurityd/lib/ssclient.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurityd / lib / ssclient.cpp
diff --git a/Security/libsecurityd/lib/ssclient.cpp b/Security/libsecurityd/lib/ssclient.cpp
new file mode 100644 (file)
index 0000000..3795100
--- /dev/null
@@ -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 <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