]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurityd/lib/ssclient.cpp
76853a35f620d8936d9dc56455d1b871d332baa8
[apple/security.git] / OSX / libsecurityd / lib / ssclient.cpp
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
32 using MachPlusPlus::check;
33 using MachPlusPlus::Bootstrap;
34
35
36 namespace Security {
37 namespace SecurityServer {
38
39
40 //
41 // The process-global object
42 //
43 UnixPlusPlus::StaticForkMonitor ClientSession::mHasForked;
44 ModuleNexus<ClientSession::Global> ClientSession::mGlobal;
45 const char *ClientSession::mContactName;
46 SecGuestRef ClientSession::mDedicatedGuest = kSecNoGuest;
47
48
49 //
50 // Construct a client session
51 //
52 ClientSession::ClientSession(Allocator &std, Allocator &rtn)
53 : ClientCommon(std, rtn), mCallback(NULL), mCallbackContext(NULL)
54 { }
55
56
57 //
58 // Destroy a session
59 //
60 ClientSession::~ClientSession()
61 { }
62
63
64 void
65 ClientSession::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 //
76 void 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 secinfo("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 secinfo("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 secinfo("SSclnt", "switched guest state to 0x%x", thread.currentGuest);
101 }
102 }
103
104 //
105 // The contactName method allows the caller to explicitly override the bootstrap
106 // name under which SecurityServer is located. Use this only with great caution,
107 // and probably only for debugging.
108 // Note that no explicit locking is done here. It is the caller's responsibility
109 // to make sure this is called from thread-safe context before the real dance begins.
110 //
111 void ClientSession::contactName(const char *name)
112 {
113 mContactName = name;
114 }
115
116 const char *ClientSession::contactName() const
117 {
118 return mContactName;
119 }
120
121
122 //
123 // Construct the process-global state object.
124 // The ModuleNexus construction magic will ensure that this happens uniquely
125 // even if the face of multithreaded attack.
126 //
127 ClientSession::Global::Global()
128 {
129 // find server port
130 serverPort = findSecurityd();
131
132 mach_port_t originPort = MACH_PORT_NULL;
133 IPCBASIC(ucsp_client_verifyPrivileged2(serverPort.port(), mig_get_reply_port(), &securitydCreds, &rcode, &originPort));
134 if (originPort != serverPort.port())
135 CssmError::throwMe(CSSM_ERRCODE_VERIFICATION_FAILURE);
136 mach_port_mod_refs(mach_task_self(), originPort, MACH_PORT_RIGHT_SEND, -1);
137
138 // send identification/setup message
139 static const char extForm[] = "?:obsolete";
140 ClientSetupInfo info = { 0x1234, SSPROTOVERSION };
141
142 // cannot use UCSP_ARGS here because it uses mGlobal() -> deadlock
143 Thread &thread = this->thread();
144
145 IPCBASIC(ucsp_client_setup(serverPort, thread.replyPort, &securitydCreds, &rcode,
146 mach_task_self(), info, extForm));
147 thread.registered = true; // as a side-effect of setup call above
148 IFDEBUG(serverPort.requestNotify(thread.replyPort));
149 secinfo("SSclnt", "contact with %s established", mContactName);
150 }
151
152
153 //
154 // Reset the connection.
155 // This discards all client state accumulated for the securityd link.
156 // Existing connections will go stale and fail; new connections will
157 // re-establish the link. This is an expert tool ONLY. If you don't know
158 // exactly how this gig is danced, you don't want to call this. Really.
159 //
160 void ClientSession::reset()
161 {
162 secinfo("SSclnt", "resetting client state (OUCH)");
163 mGlobal.reset();
164 }
165
166
167 //
168 // Common utility for finding the registered securityd port for the current
169 // session. This does not cache the port anywhere, though it does effectively
170 // cache the name.
171 //
172 Port ClientSession::findSecurityd()
173 {
174 if (!mContactName)
175 {
176 mContactName = getenv(SECURITYSERVER_BOOTSTRAP_ENV);
177 if (!mContactName)
178 mContactName = SECURITYSERVER_BOOTSTRAP_NAME;
179 }
180
181 secinfo("SSclnt", "Locating %s", mContactName);
182 Port serverPort = Bootstrap().lookup2(mContactName);
183 secinfo("SSclnt", "contacting %s at port %d (version %d)",
184 mContactName, serverPort.port(), SSPROTOVERSION);
185 return serverPort;
186 }
187
188
189 //
190 // Subsidiary process management.
191 // This does not go through the generic securityd-client setup.
192 //
193 void ClientSession::childCheckIn(Port serverPort, Port taskPort)
194 {
195 Port securitydPort = findSecurityd();
196 mach_port_t originPort = MACH_PORT_NULL;
197 IPCN(ucsp_client_verifyPrivileged2(securitydPort.port(), mig_get_reply_port(), &securitydCreds, &rcode, &originPort));
198 if (originPort != securitydPort.port())
199 CssmError::throwMe(CSSM_ERRCODE_VERIFICATION_FAILURE);
200 mach_port_mod_refs(mach_task_self(), originPort, MACH_PORT_RIGHT_SEND, -1);
201 check(ucsp_client_childCheckIn(securitydPort, serverPort, taskPort));
202 }
203
204
205 //
206 // Notify an (interested) caller that a securityd-mediated ACL change
207 // MAY have happened on a key object involved in an operation. This allows
208 // such callers to re-encode key blobs for storage.
209 //
210 void ClientSession::notifyAclChange(KeyHandle key, CSSM_ACL_AUTHORIZATION_TAG tag)
211 {
212 if (mCallback) {
213 secinfo("keyacl", "ACL change key %u operation %u", key, tag);
214 mCallback(mCallbackContext, *this, key, tag);
215 } else
216 secinfo("keyacl", "dropped ACL change notice for key %u operation %u",
217 key, tag);
218 }
219
220
221 } // end namespace SecurityServer
222 } // end namespace Security