]> git.saurik.com Git - apple/security.git/blob - SecurityServer/server.cpp
Security-54.tar.gz
[apple/security.git] / SecurityServer / server.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 //
20 // server - the actual SecurityServer server object
21 //
22 #include "server.h"
23 #include "session.h"
24 #include "acls.h"
25 #include "notifications.h"
26 #include "ucsp.h"
27 #include <mach/mach_error.h>
28
29 using namespace MachPlusPlus;
30
31
32 //
33 // Construct the server object
34 //
35 Server::Server(Authority &myAuthority, const char *bootstrapName)
36 : MachServer(bootstrapName),
37 mBootstrapName(bootstrapName),
38 mCurrentConnection(false),
39 mCSPModule(gGuidAppleCSP, mCssm), mCSP(mCSPModule),
40 mAuthority(myAuthority)
41 {
42 // engage the subsidiary port handler for sleep notifications
43 add(sleepWatcher);
44 }
45
46
47 //
48 // Clean up the server object
49 //
50 Server::~Server()
51 {
52 //@@@ more later
53 }
54
55
56 //
57 // Locate a connection by reply port and make it the current connection
58 // of this thread. The connection will be marked busy, and can be accessed
59 // by calling Server::connection() [no argument] until it is released by
60 // calling Connection::endWork().
61 //
62 Connection &Server::connection(mach_port_t port)
63 {
64 Server &server = active();
65 StLock<Mutex> _(server.lock);
66 if (Connection *conn = server.connections[port]) {
67 active().mCurrentConnection = conn;
68 conn->beginWork();
69 return *conn;
70 }
71 // unknown client port -- could be a hack attempt
72 CssmError::throwMe(CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);
73 }
74
75 Connection &Server::connection(bool tolerant)
76 {
77 Connection *conn = active().mCurrentConnection;
78 assert(conn); // have to have one
79 if (!tolerant)
80 conn->checkWork();
81 return *conn;
82 }
83
84 void Server::requestComplete()
85 {
86 // note: there may not be an active connection if connection setup failed
87 if (Connection *conn = active().mCurrentConnection) {
88 if (conn->endWork())
89 delete conn;
90 active().mCurrentConnection = NULL;
91 }
92 }
93
94
95 //
96 // Locate an ACL bearer (database or key) by handle
97 //
98 SecurityServerAcl &Server::aclBearer(AclKind kind, CSSM_HANDLE handle)
99 {
100 SecurityServerAcl &bearer = findHandle<SecurityServerAcl>(handle);
101 if (kind != bearer.kind())
102 CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE);
103 return bearer;
104 }
105
106
107 //
108 // Run the server. This will not return until the server is forced to exit.
109 //
110 void Server::run()
111 {
112 MachServer::run(0x10000,
113 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
114 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER));
115 }
116
117
118 //
119 // The primary server run-loop function.
120 // Invokes the MIG-generated main dispatch function (ucsp_server).
121 // For debug builds, look up request names in a MIG-generated table
122 // for better debug-log messages.
123 //
124 boolean_t ucsp_server(mach_msg_header_t *, mach_msg_header_t *);
125
126 #if defined(NDEBUG)
127
128 boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out)
129 {
130 return ucsp_server(in, out);
131 }
132
133 #else //NDEBUG
134
135 static const struct IPCName { const char *name; int ipc; } ipcNames[] =
136 { subsystem_to_name_map_ucsp }; // macro generated by MIG, from ucsp.h
137
138 boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out)
139 {
140 const int first = ipcNames[0].ipc;
141 assert(in->msgh_id >= first && in->msgh_id < first + ucsp_MSG_COUNT);
142 const char *name = ipcNames[in->msgh_id - first].name;
143 debug("SSreq", "begin %s (%d)", name, in->msgh_id);
144 boolean_t result = ucsp_server(in, out);
145 debug("SSreq", "end %s (%d)", name, in->msgh_id);
146 return result;
147 }
148
149 #endif //NDEBUG
150
151
152 //
153 // Set up a new Connection. This establishes the environment (process et al) as needed
154 // and registers a properly initialized Connection object to run with.
155 //
156 void Server::setupConnection(Port servicePort, Port replyPort, Port taskPort,
157 const security_token_t &securityToken, const char *identity)
158 {
159 // first, make or find the process based on task port
160 StLock<Mutex> _(lock);
161 Process * &proc = processes[taskPort];
162 if (proc == NULL) {
163 proc = new Process(servicePort, taskPort, identity,
164 securityToken.val[0], securityToken.val[1]);
165 notifyIfDead(taskPort);
166 }
167
168 // now, establish a connection and register it in the server
169 Connection *connection = new Connection(*proc, replyPort);
170 if (connections[replyPort]) // malicious re-entry attempt?
171 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ error code? (client error)
172 connections[replyPort] = connection;
173 notifyIfDead(replyPort);
174 }
175
176
177 //
178 // Synchronously end a Connection.
179 // This is due to a request from the client, so no thread races are possible.
180 //
181 void Server::endConnection(Port replyPort)
182 {
183 StLock<Mutex> _(lock);
184 Connection *connection = connections[replyPort];
185 assert(connection);
186 connections.erase(replyPort);
187 connection->terminate();
188 delete connection;
189 }
190
191
192 //
193 // Take an existing Connection/Process combo. Tear them down even though
194 // the client-side thread/process is still alive and construct new ones in their place.
195 // This is a high-wire act with a frayed net. We use it ONLY to deal with clients
196 // who change their Session (by changing their bootstrap subset port) in mid-stream.
197 // In other words, this is a hack that the client would be well advised to avoid.
198 // (Avoid it by calling SessionCreate before calling any other Security interfaces in
199 // the process's life.)
200 //
201 #if 0
202 Process *Server::resetConnection()
203 {
204 Connection *oldConnection = mCurrentConnection;
205 Process *oldProcess = &oldConnection->process;
206 debug("SS", "reset process %p connection %p for session switch",
207 oldProcess, oldConnection);
208
209 Port replyPort = oldConnection->clientPort();
210
211 oldConnection->endWork();
212 oldConnection->abort(true);
213 delete oldConnection;
214
215 oldProcess->kill();
216
217 Process * &proc = processes[oldProcess->taskPort()];
218 proc = new Process(*oldProcess);
219 delete oldProcess;
220
221 Connection *connection = new Connection(*proc, replyPort);
222 connections[replyPort] = connection;
223 mCurrentConnection = connection;
224 connection->beginWork();
225
226 return proc;
227 }
228 #endif
229
230
231 //
232 // Handling dead-port notifications.
233 // This receives DPNs for all kinds of ports we're interested in.
234 //
235 void Server::notifyDeadName(Port port)
236 {
237 StLock<Mutex> _(lock);
238 debug("SSports", "port %d is dead", port.port());
239
240 // is it a connection?
241 ConnectionMap::iterator conIt = connections.find(port);
242 if (conIt != connections.end()) {
243 Connection *connection = conIt->second;
244 if (connection->abort())
245 delete connection;
246 connections.erase(conIt);
247 return;
248 }
249
250 // is it a process?
251 ProcessMap::iterator procIt = processes.find(port);
252 if (procIt != processes.end()) {
253 Process *process = procIt->second;
254 if (process->kill())
255 delete process;
256 processes.erase(procIt);
257 return;
258 }
259
260 // is it a notification client?
261 if (Listener::remove(port))
262 return;
263
264 debug("server", "spurious dead port notification for port %d", port.port());
265 }
266
267
268 //
269 // Handling no-senders notifications.
270 // This is currently only used for (subsidiary) service ports
271 //
272 void Server::notifyNoSenders(Port port, mach_port_mscount_t)
273 {
274 debug("SSports", "port %d no senders", port.port());
275 Session::eliminate(port);
276 }
277
278
279 //
280 // Notifier for system sleep events
281 //
282 void Server::SleepWatcher::systemWillSleep()
283 {
284 debug("SS", "sleep notification received");
285 Database::lockAllDatabases(true);
286 }
287
288
289 //
290 // Return the primary Cryptographic Service Provider.
291 // This will be lazily loaded when it is first requested.
292 //
293 CssmClient::CSP &Server::getCsp()
294 {
295 if (!mCssm->isActive())
296 loadCssm();
297 return mCSP;
298 }
299
300
301 //
302 // Initialize the CSSM/MDS subsystem.
303 // This is thread-safe and can be done lazily.
304 //
305 static void initMds();
306
307 void Server::loadCssm()
308 {
309 if (!mCssm->isActive()) {
310 StLock<Mutex> _(lock);
311 if (!mCssm->isActive()) {
312 initMds();
313 debug("SS", "CSSM initializing");
314 mCssm->init();
315 mCSP->attach();
316 char guids[Guid::stringRepLength+1];
317 IFDEBUG(debug("SS", "CSSM ready with CSP %s", mCSP->guid().toString(guids)));
318 }
319 }
320 }
321
322 #include <Security/mds.h>
323
324 static void initMds()
325 {
326 debug("SS", "MDS initializing");
327 CssmAllocatorMemoryFunctions memory(CssmAllocator::standard());
328 MDS_FUNCS functions;
329 MDS_HANDLE handle;
330 CssmError::check(MDS_Initialize(NULL, &memory, &functions, &handle));
331 CssmError::check(MDS_Install(handle));
332 CssmError::check(MDS_Terminate(handle));
333 }