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