2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
20 // server - the actual SecurityServer server object
25 #include "notifications.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>
35 using namespace MachPlusPlus
;
38 // Construct the server object
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
)
51 // engage the subsidiary port handler for sleep notifications
57 // Clean up the server object
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().
71 Connection
&Server::connection(mach_port_t port
)
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
;
84 Connection
&Server::connection(bool tolerant
)
86 Connection
*conn
= active().mCurrentConnection
;
87 assert(conn
); // have to have one
93 void Server::requestComplete()
95 // note: there may not be an active connection if connection setup failed
96 if (Connection
*conn
= active().mCurrentConnection
) {
99 active().mCurrentConnection
= NULL
;
105 // Locate an ACL bearer (database or key) by handle
107 SecurityServerAcl
&Server::aclBearer(AclKind kind
, CSSM_HANDLE handle
)
109 SecurityServerAcl
&bearer
= findHandle
<SecurityServerAcl
>(handle
);
110 if (kind
!= bearer
.kind())
111 CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE
);
117 // Run the server. This will not return until the server is forced to exit.
121 MachServer::run(0x10000,
122 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0
) |
123 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT
));
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.
133 boolean_t
ucsp_server(mach_msg_header_t
*, mach_msg_header_t
*);
137 boolean_t
Server::handle(mach_msg_header_t
*in
, mach_msg_header_t
*out
)
139 return ucsp_server(in
, out
);
144 static const struct IPCName
{ const char *name
; int ipc
; } ipcNames
[] =
145 { subsystem_to_name_map_ucsp
}; // macro generated by MIG, from ucsp.h
147 boolean_t
Server::handle(mach_msg_header_t
*in
, mach_msg_header_t
*out
)
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
);
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).
168 // audit_token_t.val[1] is the EUID, audit_token_t.val[2] is the EGID.
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
)
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))
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
);
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
);
208 // Synchronously end a Connection.
209 // This is due to a request from the client, so no thread races are possible.
211 void Server::endConnection(Port replyPort
)
213 StLock
<Mutex
> _(lock
);
214 Connection
*connection
= connections
[replyPort
];
216 connections
.erase(replyPort
);
217 connection
->terminate();
223 // Handling dead-port notifications.
224 // This receives DPNs for all kinds of ports we're interested in.
226 void Server::notifyDeadName(Port port
)
228 StLock
<Mutex
> _(lock
);
229 secdebug("SSports", "port %d is dead", port
.port());
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())
237 connections
.erase(conIt
);
242 ProcessMap::iterator procIt
= processes
.find(port
);
243 if (procIt
!= processes
.end()) {
244 Process
*process
= procIt
->second
;
247 processes
.erase(procIt
);
251 // is it a notification client?
252 if (Listener::remove(port
))
255 secdebug("server", "spurious dead port notification for port %d", port
.port());
260 // Handling no-senders notifications.
261 // This is currently only used for (subsidiary) service ports
263 void Server::notifyNoSenders(Port port
, mach_port_mscount_t
)
265 secdebug("SSports", "port %d no senders", port
.port());
266 Session::eliminate(port
);
271 // Notifier for system sleep events
273 void Server::SleepWatcher::systemWillSleep()
275 secdebug("SS", "sleep notification received");
276 Session::lockAllDatabases(true);
279 void Server::initAudit(void)
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();
292 // Return the primary Cryptographic Service Provider.
293 // This will be lazily loaded when it is first requested.
295 CssmClient::CSP
&Server::getCsp()
297 if (!mCssm
->isActive())
304 // Initialize the CSSM/MDS subsystem.
305 // This is thread-safe and can be done lazily.
307 static void initMds();
309 void Server::loadCssm()
311 if (!mCssm
->isActive()) {
312 StLock
<Mutex
> _(lock
);
313 if (!mCssm
->isActive()) {
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");
327 secdebug("SS", "CSSM initializing");
330 IFDEBUG(char guids
[Guid::stringRepLength
+1]);
331 secdebug("SS", "CSSM ready with CSP %s", mCSP
->guid().toString(guids
));
336 #include <Security/mds.h>
338 static void initMds()
340 secdebug("SS", "MDS initializing");
341 CssmAllocatorMemoryFunctions
memory(CssmAllocator::standard());
344 CssmError::check(MDS_Initialize(NULL
, &memory
, &functions
, &handle
));
345 CssmError::check(MDS_Install(handle
));
346 CssmError::check(MDS_Terminate(handle
));