]> git.saurik.com Git - apple/security.git/blob - SecurityServer/session.cpp
Security-163.tar.gz
[apple/security.git] / SecurityServer / session.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 // session - authentication session domains
21 //
22 // A Session is defined by a mach_init bootstrap dictionary. These dictionaries are
23 // hierarchical and inherited, so they work well for characterization of processes
24 // that "belong" together. (Of course, if your mach_init is broken, you're in bad shape.)
25 //
26 // Sessions are multi-threaded objects.
27 //
28 #include "session.h"
29 #include "connection.h"
30 #include "server.h"
31
32
33 //
34 // The static session map
35 //
36 Session::SessionMap Session::sessionMap;
37 Mutex Session::sessionMapLock;
38
39
40 //
41 // Create a Session object from initial parameters (create)
42 //
43 Session::Session(Bootstrap bootstrap, Port servicePort, SessionAttributeBits attrs)
44 : mBootstrap(bootstrap), mServicePort(servicePort),
45 mAttributes(attrs), mProcessCount(0), mAuthCount(0), mDying(false)
46 {
47 secdebug("SSsession", "%p CREATED: handle=0x%lx bootstrap=%d service=%d attrs=0x%lx",
48 this, handle(), mBootstrap.port(), mServicePort.port(), mAttributes);
49 }
50
51
52 void Session::release()
53 {
54 // nothing by default
55 }
56
57
58 //
59 // The root session inherits the startup bootstrap and service port
60 //
61 RootSession::RootSession(Port servicePort, SessionAttributeBits attrs)
62 : Session(Bootstrap(), servicePort, sessionIsRoot | sessionWasInitialized | attrs)
63 {
64 // self-install (no thread safety issues here)
65 sessionMap[mServicePort] = this;
66 }
67
68
69 //
70 // Dynamic sessions use the given bootstrap and re-register in it
71 //
72 DynamicSession::DynamicSession(const Bootstrap &bootstrap)
73 : ReceivePort(Server::active().bootstrapName(), bootstrap),
74 Session(bootstrap, *this)
75 {
76 // tell the server to listen to our port
77 Server::active().add(*this);
78
79 // register for port notifications
80 Server::active().notifyIfDead(bootstrapPort()); //@@@??? still needed?
81 Server::active().notifyIfUnused(*this);
82
83 // self-register
84 StLock<Mutex> _(sessionMapLock);
85 sessionMap[*this] = this;
86 }
87
88 DynamicSession::~DynamicSession()
89 {
90 // remove our service port from the server
91 Server::active().remove(*this);
92
93 // if this is a (the) graphic login session, lock all databases
94 secdebug("session", "%p Locking all %ld databases",
95 this, databases().size());
96 Database::lockAllDatabases(databases());
97 }
98
99
100 void DynamicSession::release()
101 {
102 mBootstrap.destroy();
103 }
104
105
106 //
107 // Destroy a Session
108 //
109 Session::~Session()
110 {
111 assert(mProcessCount == 0); // can't die with processes still alive
112 secdebug("SSsession", "%p DESTROYED: handle=0x%lx bootstrap=%d",
113 this, handle(), mBootstrap.port());
114 }
115
116
117 //
118 // Locate a session object by service port or (Session API) identifier
119 //
120 Session &Session::find(Port servicePort)
121 {
122 StLock<Mutex> _(sessionMapLock);
123 SessionMap::const_iterator it = sessionMap.find(servicePort);
124 assert(it != sessionMap.end());
125 return *it->second;
126 }
127
128 Session &Session::find(SecuritySessionId id)
129 {
130 switch (id) {
131 case callerSecuritySession:
132 return Server::connection().process.session;
133 default:
134 return findHandle<Session>(id);
135 }
136 }
137
138
139 //
140 // Act on a death notification for a session's (sub)bootstrap port.
141 // We may not destroy the Session outright here (due to processes that use it),
142 // but we do clear out its accumulated wealth.
143 //
144 void Session::eliminate(Port servPort)
145 {
146 // remove session from session map
147 StLock<Mutex> _(sessionMapLock);
148 SessionMap::iterator it = sessionMap.find(servPort);
149 assert(it != sessionMap.end());
150 Session *session = it->second;
151 sessionMap.erase(it);
152
153 // destroy the session service port (this releases mach_init to proceed)
154 session->release();
155
156 // clear resources
157 if (session->clearResources())
158 delete session;
159 else
160 secdebug("SSsession", "session %p zombified for %d processes and %d auths",
161 session, int(session->mProcessCount), int(session->mAuthCount));
162 }
163
164 bool Session::clearResources()
165 {
166 StLock<Mutex> _(mLock);
167
168 // this session is now officially dying
169 mDying = true;
170
171 // invalidate shared credentials
172 {
173 StLock<Mutex> _(mCredsLock);
174
175 IFDEBUG(if (!mSessionCreds.empty())
176 secdebug("SSauth", "session %p clearing %d shared credentials",
177 this, int(mSessionCreds.size())));
178 for (CredentialSet::iterator it = mSessionCreds.begin(); it != mSessionCreds.end(); it++)
179 (*it)->invalidate();
180 }
181 // let the caller know if we are ready to die NOW
182 return mProcessCount == 0 && mAuthCount == 0;
183 }
184
185
186 //
187 // Relay lockAllDatabases to all known sessions
188 //
189 void Session::lockAllDatabases(bool forSleep)
190 {
191 StLock<Mutex> _(sessionMapLock);
192 for (SessionMap::const_iterator it = begin(); it != end(); it++) {
193 secdebug("SSdb", "locking all %d known databases %s in session %p",
194 int(it->second->databases().size()), forSleep ? " for sleep" : "", it->second);
195 Database::lockAllDatabases(it->second->databases(), forSleep);
196 }
197 }
198
199
200 //
201 // Process management
202 //
203 void Session::addProcess(Process *)
204 {
205 StLock<Mutex> _(mLock);
206 mProcessCount++;
207 }
208
209 bool Session::removeProcess(Process *)
210 {
211 StLock<Mutex> _(mLock);
212 assert(mProcessCount > 0);
213 return --mProcessCount == 0 && mDying && mAuthCount == 0;
214 }
215
216
217 //
218 // Authorization retention management.
219 //
220 void Session::addAuthorization(AuthorizationToken *)
221 {
222 StLock<Mutex> _(mLock);
223 mAuthCount++;
224 }
225
226 bool Session::removeAuthorization(AuthorizationToken *)
227 {
228 StLock<Mutex> _(mLock);
229 assert(mAuthCount > 0);
230 return --mAuthCount == 0 && mDying && mProcessCount == 0;
231 }
232
233
234 //
235 // Authorization operations
236 //
237 OSStatus Session::authCreate(const AuthItemSet &rights,
238 const AuthItemSet &environment,
239 AuthorizationFlags flags,
240 AuthorizationBlob &newHandle,
241 const security_token_t &securityToken)
242 {
243 // invoke the authorization computation engine
244 CredentialSet resultCreds;
245
246 // this will acquire mLock, so we delay acquiring it
247 auto_ptr<AuthorizationToken> auth(new AuthorizationToken(*this, resultCreds, securityToken));
248
249 // Make a copy of the mSessionCreds
250 CredentialSet sessionCreds;
251 {
252 StLock<Mutex> _(mCredsLock);
253 sessionCreds = mSessionCreds;
254 }
255
256 AuthItemSet outRights;
257 OSStatus result = Server::authority().authorize(rights, environment, flags,
258 &sessionCreds, &resultCreds, outRights, *auth);
259 newHandle = auth->handle();
260
261 // merge resulting creds into shared pool
262 if ((flags & kAuthorizationFlagExtendRights) &&
263 !(flags & kAuthorizationFlagDestroyRights))
264 {
265 StLock<Mutex> _(mCredsLock);
266 mergeCredentials(resultCreds);
267 auth->mergeCredentials(resultCreds);
268 }
269
270 // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to
271 // not be destroyed anymore since it's destructor asserts it has no processes
272 Server::connection().process.addAuthorization(auth.get());
273 auth.release();
274 return result;
275 }
276
277 void Session::authFree(const AuthorizationBlob &authBlob, AuthorizationFlags flags)
278 {
279 AuthorizationToken::Deleter deleter(authBlob);
280 AuthorizationToken &auth = deleter;
281 Process &process = Server::connection().process;
282 process.checkAuthorization(&auth);
283
284 if (flags & kAuthorizationFlagDestroyRights) {
285 // explicitly invalidate all shared credentials and remove them from the session
286 for (CredentialSet::const_iterator it = auth.begin(); it != auth.end(); it++)
287 if ((*it)->isShared())
288 (*it)->invalidate();
289 }
290
291 // now get rid of the authorization itself
292 if (process.removeAuthorization(&auth))
293 deleter.remove();
294 }
295
296 OSStatus Session::authGetRights(const AuthorizationBlob &authBlob,
297 const AuthItemSet &rights, const AuthItemSet &environment,
298 AuthorizationFlags flags,
299 AuthItemSet &grantedRights)
300 {
301 CredentialSet resultCreds;
302 AuthorizationToken &auth = authorization(authBlob);
303 CredentialSet effective;
304 {
305 StLock<Mutex> _(mCredsLock);
306 effective = auth.effectiveCreds();
307 }
308 OSStatus result = Server::authority().authorize(rights, environment, flags,
309 &effective, &resultCreds, grantedRights, auth);
310
311 // merge resulting creds into shared pool
312 if ((flags & kAuthorizationFlagExtendRights) && !(flags & kAuthorizationFlagDestroyRights))
313 {
314 StLock<Mutex> _(mCredsLock);
315 mergeCredentials(resultCreds);
316 auth.mergeCredentials(resultCreds);
317 }
318
319 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
320 &authorization(authBlob), int(rights.size()), int(grantedRights.size()));
321 return result;
322 }
323
324 OSStatus Session::authGetInfo(const AuthorizationBlob &authBlob,
325 const char *tag,
326 AuthItemSet &contextInfo)
327 {
328 AuthorizationToken &auth = authorization(authBlob);
329 secdebug("SSauth", "Authorization %p get-info", &auth);
330 contextInfo = auth.infoSet(tag);
331 return noErr;
332 }
333
334 OSStatus Session::authExternalize(const AuthorizationBlob &authBlob,
335 AuthorizationExternalForm &extForm)
336 {
337 const AuthorizationToken &auth = authorization(authBlob);
338 StLock<Mutex> _(mLock);
339 if (auth.mayExternalize(Server::connection().process)) {
340 memset(&extForm, 0, sizeof(extForm));
341 AuthorizationExternalBlob &extBlob =
342 reinterpret_cast<AuthorizationExternalBlob &>(extForm);
343 extBlob.blob = auth.handle();
344 extBlob.session = bootstrapPort();
345 secdebug("SSauth", "Authorization %p externalized", &auth);
346 return noErr;
347 } else
348 return errAuthorizationExternalizeNotAllowed;
349 }
350
351 OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm,
352 AuthorizationBlob &authBlob)
353 {
354 // interpret the external form
355 const AuthorizationExternalBlob &extBlob =
356 reinterpret_cast<const AuthorizationExternalBlob &>(extForm);
357
358 // locate source authorization
359 AuthorizationToken &sourceAuth = AuthorizationToken::find(extBlob.blob);
360
361 // check for permission and do it
362 if (sourceAuth.mayInternalize(Server::connection().process, true)) {
363 StLock<Mutex> _(mLock);
364 authBlob = extBlob.blob;
365 Server::connection().process.addAuthorization(&sourceAuth);
366 mAuthCount++;
367 secdebug("SSauth", "Authorization %p internalized", &sourceAuth);
368 return noErr;
369 } else
370 return errAuthorizationInternalizeNotAllowed;
371 }
372
373
374 //
375 // Set up a (new-ish) Session.
376 // This call must be made from a process within the session, and it must be the first
377 // such process to make the call.
378 //
379 void Session::setup(SessionCreationFlags flags, SessionAttributeBits attrs)
380 {
381 // check current process object - it may have been cached before the client's bootstrap switch
382 Process *process = &Server::connection().process;
383 process->session.setupAttributes(attrs);
384 }
385
386
387 void Session::setupAttributes(SessionAttributeBits attrs)
388 {
389 secdebug("SSsession", "%p setup attrs=0x%lx", this, attrs);
390 if (attrs & ~settableAttributes)
391 MacOSError::throwMe(errSessionInvalidAttributes);
392 if (attribute(sessionWasInitialized))
393 MacOSError::throwMe(errSessionAuthorizationDenied);
394 setAttributes(attrs | sessionWasInitialized);
395 }
396
397
398 OSStatus Session::authorizationdbGet(AuthorizationString inRightName, CFDictionaryRef *rightDict)
399 {
400 string rightName(inRightName);
401 return Server::authority().getRule(rightName, rightDict);
402 }
403
404
405 OSStatus Session::authorizationdbSet(const AuthorizationBlob &authBlob, AuthorizationString inRightName, CFDictionaryRef rightDict)
406 {
407 CredentialSet resultCreds;
408 AuthorizationToken &auth = authorization(authBlob);
409 CredentialSet effective;
410
411 {
412 StLock<Mutex> _(mCredsLock);
413 effective = auth.effectiveCreds();
414 }
415
416 OSStatus result = Server::authority().setRule(inRightName, rightDict, &effective, &resultCreds, auth);
417
418 {
419 StLock<Mutex> _(mCredsLock);
420 mergeCredentials(resultCreds);
421 auth.mergeCredentials(resultCreds);
422 }
423
424 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%ld)",
425 &authorization(authBlob), inRightName, result);
426 return result;
427 }
428
429
430 OSStatus Session::authorizationdbRemove(const AuthorizationBlob &authBlob, AuthorizationString inRightName)
431 {
432 CredentialSet resultCreds;
433 AuthorizationToken &auth = authorization(authBlob);
434 CredentialSet effective;
435
436 {
437 StLock<Mutex> _(mCredsLock);
438 effective = auth.effectiveCreds();
439 }
440
441 OSStatus result = Server::authority().removeRule(inRightName, &effective, &resultCreds, auth);
442
443 {
444 StLock<Mutex> _(mCredsLock);
445 mergeCredentials(resultCreds);
446 auth.mergeCredentials(resultCreds);
447 }
448
449 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%ld)",
450 &authorization(authBlob), inRightName, result);
451 return result;
452 }
453
454
455 //
456 // Merge a set of credentials into the shared-session credential pool
457 //
458 // must hold mCredsLock
459 void Session::mergeCredentials(CredentialSet &creds)
460 {
461 secdebug("SSsession", "%p merge creds @%p", this, &creds);
462 for (CredentialSet::const_iterator it = creds.begin(); it != creds.end(); it++)
463 if (((*it)->isShared() && (*it)->isValid())) {
464 CredentialSet::iterator old = mSessionCreds.find(*it);
465 if (old == mSessionCreds.end()) {
466 mSessionCreds.insert(*it);
467 } else {
468 // replace "new" with "old" in input set to retain synchronization
469 (*old)->merge(**it);
470 creds.erase(it);
471 creds.insert(*old);
472 }
473 }
474 }
475
476
477 //
478 // Locate an AuthorizationToken given a blob
479 //
480 AuthorizationToken &Session::authorization(const AuthorizationBlob &blob)
481 {
482 AuthorizationToken &auth = AuthorizationToken::find(blob);
483 Server::connection().process.checkAuthorization(&auth);
484 return auth;
485 }