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