]> git.saurik.com Git - apple/security.git/blob - SecurityServer/server.cpp
Security-163.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 &authority, CodeSignatures &signatures, const char *bootstrapName)
36 : MachServer(bootstrapName),
37 mBootstrapName(bootstrapName),
38 mCurrentConnection(false),
39 mCSPModule(gGuidAppleCSP, mCssm), mCSP(mCSPModule),
40 mAuthority(authority),
41 mCodeSignatures(signatures)
42 {
43 // engage the subsidiary port handler for sleep notifications
44 add(sleepWatcher);
45 }
46
47
48 //
49 // Clean up the server object
50 //
51 Server::~Server()
52 {
53 //@@@ more later
54 }
55
56
57 //
58 // Locate a connection by reply port and make it the current connection
59 // of this thread. The connection will be marked busy, and can be accessed
60 // by calling Server::connection() [no argument] until it is released by
61 // calling Connection::endWork().
62 //
63 Connection &Server::connection(mach_port_t port)
64 {
65 Server &server = active();
66 StLock<Mutex> _(server.lock);
67 ConnectionMap::iterator it = server.connections.find(port);
68 if (it == server.connections.end()) // unknown client port -- could be a hack attempt
69 CssmError::throwMe(CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);
70 Connection *conn = it->second;
71 active().mCurrentConnection = conn;
72 conn->beginWork();
73 return *conn;
74 }
75
76 Connection &Server::connection(bool tolerant)
77 {
78 Connection *conn = active().mCurrentConnection;
79 assert(conn); // have to have one
80 if (!tolerant)
81 conn->checkWork();
82 return *conn;
83 }
84
85 void Server::requestComplete()
86 {
87 // note: there may not be an active connection if connection setup failed
88 if (Connection *conn = active().mCurrentConnection) {
89 if (conn->endWork())
90 delete conn;
91 active().mCurrentConnection = NULL;
92 }
93 }
94
95
96 //
97 // Locate an ACL bearer (database or key) by handle
98 //
99 SecurityServerAcl &Server::aclBearer(AclKind kind, CSSM_HANDLE handle)
100 {
101 SecurityServerAcl &bearer = findHandle<SecurityServerAcl>(handle);
102 if (kind != bearer.kind())
103 CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE);
104 return bearer;
105 }
106
107
108 //
109 // Run the server. This will not return until the server is forced to exit.
110 //
111 void Server::run()
112 {
113 MachServer::run(0x10000,
114 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
115 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER));
116 }
117
118
119 //
120 // The primary server run-loop function.
121 // Invokes the MIG-generated main dispatch function (ucsp_server).
122 // For debug builds, look up request names in a MIG-generated table
123 // for better debug-log messages.
124 //
125 boolean_t ucsp_server(mach_msg_header_t *, mach_msg_header_t *);
126
127 #if defined(NDEBUG)
128
129 boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out)
130 {
131 return ucsp_server(in, out);
132 }
133
134 #else //NDEBUG
135
136 static const struct IPCName { const char *name; int ipc; } ipcNames[] =
137 { subsystem_to_name_map_ucsp }; // macro generated by MIG, from ucsp.h
138
139 boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out)
140 {
141 const int first = ipcNames[0].ipc;
142 const char *name = (in->msgh_id >= first && in->msgh_id < first + ucsp_MSG_COUNT) ?
143 ipcNames[in->msgh_id - first].name : "OUT OF BOUNDS";
144 secdebug("SSreq", "begin %s (%d)", name, in->msgh_id);
145 boolean_t result = ucsp_server(in, out);
146 secdebug("SSreq", "end %s (%d)", name, in->msgh_id);
147 return result;
148 }
149
150 #endif //NDEBUG
151
152
153 //
154 // Set up a new Connection. This establishes the environment (process et al) as needed
155 // and registers a properly initialized Connection object to run with.
156 // Type indicates how "deep" we need to initialize (new session, process, or connection).
157 // Everything at and below that level is constructed. This is straight-forward except
158 // in the case of session re-initialization (see below).
159 //
160 void Server::setupConnection(ConnectLevel type, Port servicePort, Port replyPort, Port taskPort,
161 const security_token_t &securityToken, const ClientSetupInfo *info, const char *identity)
162 {
163 // first, make or find the process based on task port
164 StLock<Mutex> _(lock);
165 Process * &proc = processes[taskPort];
166 if (type == connectNewSession && proc) {
167 // The client has talked to us before and now wants to create a new session.
168 // We'll unmoor the old process object and cast it adrift (it will die either now
169 // or later following the usual deferred-death mechanics).
170 // The connection object will die (it's probably already dead) because the client
171 // has destroyed its replyPort. So we don't worry about this here.
172 secdebug("server", "session setup - marooning old process %p(%d) of session %p",
173 proc, proc->pid(), &proc->session);
174 if (proc->kill(true))
175 delete proc;
176 proc = NULL;
177 }
178 if (!proc) {
179 if (type == connectNewThread) // client error (or attack)
180 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
181 assert(info && identity);
182 proc = new Process(servicePort, taskPort, info, identity,
183 securityToken.val[0], securityToken.val[1]);
184 notifyIfDead(taskPort);
185 }
186
187 // now, establish a connection and register it in the server
188 Connection *connection = new Connection(*proc, replyPort);
189 if (connections[replyPort]) // malicious re-entry attempt?
190 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ error code? (client error)
191 connections[replyPort] = connection;
192 notifyIfDead(replyPort);
193 }
194
195
196 //
197 // Synchronously end a Connection.
198 // This is due to a request from the client, so no thread races are possible.
199 //
200 void Server::endConnection(Port replyPort)
201 {
202 StLock<Mutex> _(lock);
203 Connection *connection = connections[replyPort];
204 assert(connection);
205 connections.erase(replyPort);
206 connection->terminate();
207 delete connection;
208 }
209
210
211 //
212 // Handling dead-port notifications.
213 // This receives DPNs for all kinds of ports we're interested in.
214 //
215 void Server::notifyDeadName(Port port)
216 {
217 StLock<Mutex> _(lock);
218 secdebug("SSports", "port %d is dead", port.port());
219
220 // is it a connection?
221 ConnectionMap::iterator conIt = connections.find(port);
222 if (conIt != connections.end()) {
223 Connection *connection = conIt->second;
224 if (connection->abort())
225 delete connection;
226 connections.erase(conIt);
227 return;
228 }
229
230 // is it a process?
231 ProcessMap::iterator procIt = processes.find(port);
232 if (procIt != processes.end()) {
233 Process *process = procIt->second;
234 if (process->kill())
235 delete process;
236 processes.erase(procIt);
237 return;
238 }
239
240 // is it a notification client?
241 if (Listener::remove(port))
242 return;
243
244 secdebug("server", "spurious dead port notification for port %d", port.port());
245 }
246
247
248 //
249 // Handling no-senders notifications.
250 // This is currently only used for (subsidiary) service ports
251 //
252 void Server::notifyNoSenders(Port port, mach_port_mscount_t)
253 {
254 secdebug("SSports", "port %d no senders", port.port());
255 Session::eliminate(port);
256 }
257
258
259 //
260 // Notifier for system sleep events
261 //
262 void Server::SleepWatcher::systemWillSleep()
263 {
264 secdebug("SS", "sleep notification received");
265 Session::lockAllDatabases(true);
266 }
267
268
269 //
270 // Return the primary Cryptographic Service Provider.
271 // This will be lazily loaded when it is first requested.
272 //
273 CssmClient::CSP &Server::getCsp()
274 {
275 if (!mCssm->isActive())
276 loadCssm();
277 return mCSP;
278 }
279
280
281 //
282 // Initialize the CSSM/MDS subsystem.
283 // This is thread-safe and can be done lazily.
284 //
285 static void initMds();
286
287 void Server::loadCssm()
288 {
289 if (!mCssm->isActive()) {
290 StLock<Mutex> _(lock);
291 if (!mCssm->isActive()) {
292 try {
293 initMds();
294 } catch (const CssmError &error) {
295 switch (error.cssmError()) {
296 case CSSMERR_DL_MDS_ERROR:
297 case CSSMERR_DL_OS_ACCESS_DENIED:
298 secdebug("SS", "MDS initialization failed; continuing");
299 Syslog::warning("MDS initialization failed; continuing");
300 break;
301 default:
302 throw;
303 }
304 }
305 secdebug("SS", "CSSM initializing");
306 mCssm->init();
307 mCSP->attach();
308 IFDEBUG(char guids[Guid::stringRepLength+1]);
309 secdebug("SS", "CSSM ready with CSP %s", mCSP->guid().toString(guids));
310 }
311 }
312 }
313
314 #include <Security/mds.h>
315
316 static void initMds()
317 {
318 secdebug("SS", "MDS initializing");
319 CssmAllocatorMemoryFunctions memory(CssmAllocator::standard());
320 MDS_FUNCS functions;
321 MDS_HANDLE handle;
322 CssmError::check(MDS_Initialize(NULL, &memory, &functions, &handle));
323 CssmError::check(MDS_Install(handle));
324 CssmError::check(MDS_Terminate(handle));
325 }