1/* 2 * Copyright (c) 2000-2008,2011,2013 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// ssclient - SecurityServer client interface library 27// 28#include "sstransit.h" 29#include <servers/netname.h> 30#include <security_utilities/debugging.h> 31 32using MachPlusPlus::check; 33using MachPlusPlus::Bootstrap; 34 35 36namespace Security { 37namespace SecurityServer { 38 39 40// 41// The process-global object 42// 43UnixPlusPlus::StaticForkMonitor ClientSession::mHasForked; 44ModuleNexus<ClientSession::Global> ClientSession::mGlobal; 45const char *ClientSession::mContactName; 46SecGuestRef ClientSession::mDedicatedGuest = kSecNoGuest; 47 48 49// 50// Construct a client session 51// 52ClientSession::ClientSession(Allocator &std, Allocator &rtn) 53: ClientCommon(std, rtn), mCallback(NULL), mCallbackContext(NULL) 54{ } 55 56 57// 58// Destroy a session 59// 60ClientSession::~ClientSession() 61{ } 62 63 64void 65ClientSession::registerForAclEdits(DidChangeKeyAclCallback *callback, void *context) 66{ 67 mCallback = callback; 68 mCallbackContext = context; 69} 70 71// 72// Perform any preambles required to be a securityd client in good standing. 73// This includes initial setup calls, thread registration, fork management, 74// and (Code Signing) guest status. 75// 76void ClientSession::activate() 77{ 78 // Guard against fork-without-exec. If we are the child of a fork 79 // (that has not exec'ed), our apparent connection to SecurityServer 80 // is just a mirage, and we better reset it. 81 if (mHasForked()) { 82 secdebug("SSclnt", "process has forked (now pid=%d) - resetting connection object", getpid()); 83 mGlobal.reset(); 84 } 85 86 // now pick up the (new or existing) connection state 87 Global &global = mGlobal(); 88 Thread &thread = global.thread(); 89 if (!thread) { 90 // first time for this thread - use abbreviated registration 91 IPCN(ucsp_client_setupThread(UCSP_ARGS, mach_task_self())); 92 thread.registered = true; 93 secdebug("SSclnt", "Thread registered with %s", mContactName); 94 } 95 96 // if the thread's guest state has changed, tell securityd 97 if (thread.currentGuest != thread.lastGuest) { 98 IPCN(ucsp_client_setGuest(UCSP_ARGS, thread.currentGuest, kSecCSDefaultFlags)); 99 thread.lastGuest = thread.currentGuest; 100 secdebug("SSclnt", "switched guest state to 0x%x", thread.currentGuest); 101 } 102} 103 104 105// 106// The contactName method allows the caller to explicitly override the bootstrap 107// name under which SecurityServer is located. Use this only with great caution, 108// and probably only for debugging. 109// Note that no explicit locking is done here. It is the caller's responsibility 110// to make sure this is called from thread-safe context before the real dance begins. 111// 112void ClientSession::contactName(const char *name) 113{ 114 mContactName = name; 115} 116 117const char *ClientSession::contactName() const 118{ 119 return mContactName; 120} 121 122 123// 124// Construct the process-global state object. 125// The ModuleNexus construction magic will ensure that this happens uniquely 126// even if the face of multithreaded attack. 127// 128ClientSession::Global::Global() 129{ 130 // find server port 131 serverPort = findSecurityd(); 132 133 mach_port_t originPort = MACH_PORT_NULL; 134 IPCN(ucsp_client_verifyPrivileged2(serverPort.port(), mig_get_reply_port(), &securitydCreds, &rcode, &originPort)); 135 if (originPort != serverPort.port()) 136 CssmError::throwMe(CSSM_ERRCODE_VERIFICATION_FAILURE); 137 mach_port_mod_refs(mach_task_self(), originPort, MACH_PORT_RIGHT_SEND, -1); 138 139 // send identification/setup message 140 static const char extForm[] = "?:obsolete"; 141 ClientSetupInfo info = { 0x1234, SSPROTOVERSION }; 142 143 // cannot use UCSP_ARGS here because it uses mGlobal() -> deadlock 144 Thread &thread = this->thread(); 145 146 IPCN(ucsp_client_setup(serverPort, thread.replyPort, &securitydCreds, &rcode, 147 mach_task_self(), info, extForm)); 148 thread.registered = true; // as a side-effect of setup call above 149 IFDEBUG(serverPort.requestNotify(thread.replyPort)); 150 secdebug("SSclnt", "contact with %s established", mContactName); 151} 152 153 154// 155// Reset the connection. 156// This discards all client state accumulated for the securityd link. 157// Existing connections will go stale and fail; new connections will 158// re-establish the link. This is an expert tool ONLY. If you don't know 159// exactly how this gig is danced, you don't want to call this. Really. 160// 161void ClientSession::reset() 162{ 163 secdebug("SSclnt", "resetting client state (OUCH)"); 164 mGlobal.reset(); 165} 166 167 168// 169// Common utility for finding the registered securityd port for the current 170// session. This does not cache the port anywhere, though it does effectively 171// cache the name. 172// 173Port ClientSession::findSecurityd() 174{ 175 if (!mContactName) 176 { 177 mContactName = getenv(SECURITYSERVER_BOOTSTRAP_ENV); 178 if (!mContactName) 179 mContactName = SECURITYSERVER_BOOTSTRAP_NAME; 180 } 181 182 secdebug("SSclnt", "Locating %s", mContactName); 183 Port serverPort = Bootstrap().lookup2(mContactName); 184 secdebug("SSclnt", "contacting %s at port %d (version %d)", 185 mContactName, serverPort.port(), SSPROTOVERSION); 186 return serverPort; 187} 188 189 190// 191// Subsidiary process management. 192// This does not go through the generic securityd-client setup. 193// 194void ClientSession::childCheckIn(Port serverPort, Port taskPort) 195{ 196 Port securitydPort = findSecurityd(); 197 mach_port_t originPort = MACH_PORT_NULL; 198 IPCN(ucsp_client_verifyPrivileged2(securitydPort.port(), mig_get_reply_port(), &securitydCreds, &rcode, &originPort)); 199 if (originPort != securitydPort.port()) 200 CssmError::throwMe(CSSM_ERRCODE_VERIFICATION_FAILURE); 201 mach_port_mod_refs(mach_task_self(), originPort, MACH_PORT_RIGHT_SEND, -1); 202 check(ucsp_client_childCheckIn(securitydPort, serverPort, taskPort)); 203} 204 205 206// 207// Notify an (interested) caller that a securityd-mediated ACL change 208// MAY have happened on a key object involved in an operation. This allows 209// such callers to re-encode key blobs for storage. 210// 211void ClientSession::notifyAclChange(KeyHandle key, CSSM_ACL_AUTHORIZATION_TAG tag) 212{ 213 if (mCallback) { 214 secdebug("keyacl", "ACL change key %u operation %u", key, tag); 215 mCallback(mCallbackContext, *this, key, tag); 216 } else 217 secdebug("keyacl", "dropped ACL change notice for key %u operation %u", 218 key, tag); 219} 220 221 222} // end namespace SecurityServer 223} // end namespace Security 224