]> git.saurik.com Git - apple/securityd.git/blob - src/session.cpp
securityd-26674.tar.gz
[apple/securityd.git] / src / session.cpp
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 //
26 // session - authentication session domains
27 //
28 // A Session is defined by a mach_init bootstrap dictionary. These dictionaries are
29 // hierarchical and inherited, so they work well for characterization of processes
30 // that "belong" together. (Of course, if your mach_init is broken, you're in bad shape.)
31 //
32 // Sessions are multi-threaded objects.
33 //
34 #include "session.h"
35 #include "connection.h"
36 #include "database.h"
37 #include "server.h"
38
39
40 //
41 // The static session map
42 //
43 PortMap<Session> Session::mSessions;
44
45
46 //
47 // Create a Session object from initial parameters (create)
48 //
49 Session::Session(Bootstrap bootstrap, Port servicePort, SessionAttributeBits attrs)
50 : mBootstrap(bootstrap), mServicePort(servicePort),
51 mAttributes(attrs), mSecurityAgent(NULL), mAuthHost(NULL)
52 {
53 secdebug("SSsession", "%p CREATED: handle=0x%lx bootstrap=%d service=%d attrs=0x%lx",
54 this, handle(), mBootstrap.port(), mServicePort.port(), mAttributes);
55 }
56
57
58 //
59 // Destroy a Session
60 //
61 Session::~Session()
62 {
63 secdebug("SSsession", "%p DESTROYED: handle=0x%lx bootstrap=%d",
64 this, handle(), mBootstrap.port());
65 }
66
67
68 //
69 // Locate a session object by service port or (Session API) identifier
70 //
71 Session &Session::find(Port servicePort)
72 {
73 StLock<Mutex> _(mSessions);
74 PortMap<Session>::const_iterator it = mSessions.find(servicePort);
75 assert(it != mSessions.end());
76 return *it->second;
77 }
78
79 Session &Session::find(SecuritySessionId id)
80 {
81 switch (id) {
82 case callerSecuritySession:
83 return Server::session();
84 default:
85 return HandleObject::find<Session>(id, CSSMERR_CSSM_INVALID_ADDIN_HANDLE);
86 }
87 }
88
89
90 //
91 // Act on a death notification for a session's (sub)bootstrap port.
92 // We may not destroy the Session outright here (due to processes that use it),
93 // but we do clear out its accumulated wealth.
94 //
95 void Session::destroy(Port servPort)
96 {
97 // remove session from session map
98 StLock<Mutex> _(mSessions);
99 PortMap<Session>::iterator it = mSessions.find(servPort);
100 assert(it != mSessions.end());
101 RefPointer<Session> session = it->second;
102 mSessions.erase(it);
103 session->kill();
104 }
105
106 void Session::kill()
107 {
108 StLock<Mutex> _(*this);
109
110 // release authorization host objects
111 {
112 StLock<Mutex> _(mAuthHostLock);
113 mSecurityAgent = NULL;
114 mAuthHost = NULL;
115 }
116
117 // invalidate shared credentials
118 {
119 StLock<Mutex> _(mCredsLock);
120
121 IFDEBUG(if (!mSessionCreds.empty())
122 secdebug("SSauth", "session %p clearing %d shared credentials",
123 this, int(mSessionCreds.size())));
124 for (CredentialSet::iterator it = mSessionCreds.begin(); it != mSessionCreds.end(); it++)
125 (*it)->invalidate();
126 }
127
128 // base kill processing
129 PerSession::kill();
130 }
131
132
133 //
134 // On system sleep, call sleepProcessing on all DbCommons of all Sessions
135 //
136 void Session::processSystemSleep()
137 {
138 StLock<Mutex> _(mSessions);
139 for (PortMap<Session>::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
140 it->second->allReferences(&DbCommon::sleepProcessing);
141 }
142
143
144 //
145 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
146 //
147 void Session::processLockAll()
148 {
149 allReferences(&DbCommon::lockProcessing);
150 }
151
152
153 //
154 // The root session inherits the startup bootstrap and service port
155 //
156 RootSession::RootSession(Server &server, SessionAttributeBits attrs)
157 : Session(Bootstrap(), server.primaryServicePort(),
158 sessionIsRoot | sessionWasInitialized | attrs)
159 {
160 parent(server); // the Server is our parent
161 ref(); // eternalize
162
163 // self-install (no thread safety issues here)
164 mSessions[mServicePort] = this;
165 }
166
167 uid_t RootSession::originatorUid() const
168 {
169 return 0; // it's root, obviously
170 }
171
172 CFDataRef RootSession::copyUserPrefs()
173 {
174 return NULL;
175 }
176
177 //
178 // Dynamic sessions use the given bootstrap and re-register in it
179 //
180 DynamicSession::DynamicSession(TaskPort taskPort)
181 : ReceivePort(Server::active().bootstrapName(), taskPort.bootstrap()),
182 Session(taskPort.bootstrap(), *this),
183 mOriginatorTask(taskPort), mHaveOriginatorUid(false)
184 {
185 // link to Server as the global nexus in the object mesh
186 parent(Server::active());
187
188 // tell the server to listen to our port
189 Server::active().add(*this);
190
191 // register for port notifications
192 Server::active().notifyIfDead(bootstrapPort()); //@@@??? still needed?
193 Server::active().notifyIfUnused(*this);
194
195 // self-register
196 StLock<Mutex> _(mSessions);
197 assert(!mSessions[*this]); // can't be registered already (we just made it)
198 mSessions[*this] = this;
199
200 secdebug("SSsession", "%p dynamic session originator=%d (pid=%d)",
201 this, mOriginatorTask.port(), taskPort.pid());
202 }
203
204 DynamicSession::~DynamicSession()
205 {
206 // remove our service port from the server
207 Server::active().remove(*this);
208 }
209
210
211 void DynamicSession::kill()
212 {
213 StLock<Mutex> _(*this);
214 mBootstrap.destroy(); // release our bootstrap port
215 Session::kill(); // continue with parent kill
216 }
217
218
219 //
220 // Set up a DynamicSession.
221 // This call must be made from a process within the session, and it must be the first
222 // such process to make the call.
223 //
224 void DynamicSession::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
225 {
226 StLock<Mutex> _(*this);
227 secdebug("SSsession", "%p setup flags=0x%lx attrs=0x%lx", this, flags, attrs);
228 if (attrs & ~settableAttributes)
229 MacOSError::throwMe(errSessionInvalidAttributes);
230 checkOriginator();
231 if (attribute(sessionWasInitialized))
232 MacOSError::throwMe(errSessionAuthorizationDenied);
233 setAttributes(attrs | sessionWasInitialized);
234 }
235
236
237 //
238 // Check whether the calling process is the session originator.
239 // If it's not, throw.
240 //
241 void DynamicSession::checkOriginator()
242 {
243 if (mOriginatorTask != Server::process().taskPort())
244 MacOSError::throwMe(errSessionAuthorizationDenied);
245 }
246
247
248 //
249 // The "originator uid" is a uid value that can be provided by the session originator
250 // and retrieved by anyone. Securityd places no semantic meaning on this value.
251 //
252 uid_t DynamicSession::originatorUid() const
253 {
254 if (mHaveOriginatorUid)
255 return mOriginatorUid;
256 else
257 MacOSError::throwMe(errSessionValueNotSet);
258 }
259
260
261 void DynamicSession::originatorUid(uid_t uid)
262 {
263 checkOriginator();
264 if (mHaveOriginatorUid) // must not re-set this
265 MacOSError::throwMe(errSessionAuthorizationDenied);
266 mHaveOriginatorUid = true;
267 mOriginatorUid = uid;
268 secdebug("SSsession", "%p session uid set to %d", this, uid);
269 }
270
271
272 //
273 // Authorization operations
274 //
275 OSStatus Session::authCreate(const AuthItemSet &rights,
276 const AuthItemSet &environment,
277 AuthorizationFlags flags,
278 AuthorizationBlob &newHandle,
279 const audit_token_t &auditToken)
280 {
281 // invoke the authorization computation engine
282 CredentialSet resultCreds;
283
284 // this will acquire the object lock, so we delay acquiring it (@@@ no longer needed)
285 auto_ptr<AuthorizationToken> auth(new AuthorizationToken(*this, resultCreds, auditToken));
286
287 // Make a copy of the mSessionCreds
288 CredentialSet sessionCreds;
289 {
290 StLock<Mutex> _(mCredsLock);
291 sessionCreds = mSessionCreds;
292 }
293
294 AuthItemSet outRights;
295 OSStatus result = Server::authority().authorize(rights, environment, flags,
296 &sessionCreds, &resultCreds, outRights, *auth);
297 newHandle = auth->handle();
298
299 // merge resulting creds into shared pool
300 if ((flags & kAuthorizationFlagExtendRights) &&
301 !(flags & kAuthorizationFlagDestroyRights))
302 {
303 StLock<Mutex> _(mCredsLock);
304 mergeCredentials(resultCreds);
305 auth->mergeCredentials(resultCreds);
306 }
307
308 // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to
309 // not be destroyed anymore since it's destructor asserts it has no processes
310 Server::process().addAuthorization(auth.get());
311 auth.release();
312 return result;
313 }
314
315 void Session::authFree(const AuthorizationBlob &authBlob, AuthorizationFlags flags)
316 {
317 AuthorizationToken::Deleter deleter(authBlob);
318 AuthorizationToken &auth = deleter;
319 Process &process = Server::process();
320 process.checkAuthorization(&auth);
321
322 if (flags & kAuthorizationFlagDestroyRights) {
323 // explicitly invalidate all shared credentials and remove them from the session
324 for (CredentialSet::const_iterator it = auth.begin(); it != auth.end(); it++)
325 if ((*it)->isShared())
326 (*it)->invalidate();
327 }
328
329 // now get rid of the authorization itself
330 if (process.removeAuthorization(&auth))
331 deleter.remove();
332 }
333
334 OSStatus Session::authGetRights(const AuthorizationBlob &authBlob,
335 const AuthItemSet &rights, const AuthItemSet &environment,
336 AuthorizationFlags flags,
337 AuthItemSet &grantedRights)
338 {
339 CredentialSet resultCreds;
340 AuthorizationToken &auth = authorization(authBlob);
341 CredentialSet effective;
342 {
343 StLock<Mutex> _(mCredsLock);
344 effective = auth.effectiveCreds();
345 }
346 OSStatus result = Server::authority().authorize(rights, environment, flags,
347 &effective, &resultCreds, grantedRights, auth);
348
349 // merge resulting creds into shared pool
350 if ((flags & kAuthorizationFlagExtendRights) && !(flags & kAuthorizationFlagDestroyRights))
351 {
352 StLock<Mutex> _(mCredsLock);
353 mergeCredentials(resultCreds);
354 auth.mergeCredentials(resultCreds);
355 }
356
357 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
358 &authorization(authBlob), int(rights.size()), int(grantedRights.size()));
359 return result;
360 }
361
362 OSStatus Session::authGetInfo(const AuthorizationBlob &authBlob,
363 const char *tag,
364 AuthItemSet &contextInfo)
365 {
366 AuthorizationToken &auth = authorization(authBlob);
367 secdebug("SSauth", "Authorization %p get-info", &auth);
368 contextInfo = auth.infoSet(tag);
369 return noErr;
370 }
371
372 OSStatus Session::authExternalize(const AuthorizationBlob &authBlob,
373 AuthorizationExternalForm &extForm)
374 {
375 const AuthorizationToken &auth = authorization(authBlob);
376 StLock<Mutex> _(*this);
377 if (auth.mayExternalize(Server::process())) {
378 memset(&extForm, 0, sizeof(extForm));
379 AuthorizationExternalBlob &extBlob =
380 reinterpret_cast<AuthorizationExternalBlob &>(extForm);
381 extBlob.blob = auth.handle();
382 extBlob.session = bootstrapPort();
383 secdebug("SSauth", "Authorization %p externalized", &auth);
384 return noErr;
385 } else
386 return errAuthorizationExternalizeNotAllowed;
387 }
388
389 OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm,
390 AuthorizationBlob &authBlob)
391 {
392 // interpret the external form
393 const AuthorizationExternalBlob &extBlob =
394 reinterpret_cast<const AuthorizationExternalBlob &>(extForm);
395
396 // locate source authorization
397 AuthorizationToken &sourceAuth = AuthorizationToken::find(extBlob.blob);
398
399 // check for permission and do it
400 if (sourceAuth.mayInternalize(Server::process(), true)) {
401 StLock<Mutex> _(*this);
402 authBlob = extBlob.blob;
403 Server::process().addAuthorization(&sourceAuth);
404 secdebug("SSauth", "Authorization %p internalized", &sourceAuth);
405 return noErr;
406 } else
407 return errAuthorizationInternalizeNotAllowed;
408 }
409
410
411 //
412 // The default session setup operation always fails.
413 // Subclasses can override this to support session setup calls.
414 //
415 void Session::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
416 {
417 MacOSError::throwMe(errSessionAuthorizationDenied);
418 }
419
420
421 //
422 // Authorization database I/O
423 //
424 OSStatus Session::authorizationdbGet(AuthorizationString inRightName, CFDictionaryRef *rightDict)
425 {
426 string rightName(inRightName);
427 return Server::authority().getRule(rightName, rightDict);
428 }
429
430
431 OSStatus Session::authorizationdbSet(const AuthorizationBlob &authBlob, AuthorizationString inRightName, CFDictionaryRef rightDict)
432 {
433 CredentialSet resultCreds;
434 AuthorizationToken &auth = authorization(authBlob);
435 CredentialSet effective;
436
437 {
438 StLock<Mutex> _(mCredsLock);
439 effective = auth.effectiveCreds();
440 }
441
442 OSStatus result = Server::authority().setRule(inRightName, rightDict, &effective, &resultCreds, auth);
443
444 {
445 StLock<Mutex> _(mCredsLock);
446 mergeCredentials(resultCreds);
447 auth.mergeCredentials(resultCreds);
448 }
449
450 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%ld)",
451 &authorization(authBlob), inRightName, result);
452 return result;
453 }
454
455
456 OSStatus Session::authorizationdbRemove(const AuthorizationBlob &authBlob, AuthorizationString inRightName)
457 {
458 CredentialSet resultCreds;
459 AuthorizationToken &auth = authorization(authBlob);
460 CredentialSet effective;
461
462 {
463 StLock<Mutex> _(mCredsLock);
464 effective = auth.effectiveCreds();
465 }
466
467 OSStatus result = Server::authority().removeRule(inRightName, &effective, &resultCreds, auth);
468
469 {
470 StLock<Mutex> _(mCredsLock);
471 mergeCredentials(resultCreds);
472 auth.mergeCredentials(resultCreds);
473 }
474
475 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%ld)",
476 &authorization(authBlob), inRightName, result);
477 return result;
478 }
479
480
481 //
482 // Merge a set of credentials into the shared-session credential pool
483 //
484 // must hold mCredsLock
485 void Session::mergeCredentials(CredentialSet &creds)
486 {
487 secdebug("SSsession", "%p merge creds @%p", this, &creds);
488 CredentialSet updatedCredentials = creds;
489 for (CredentialSet::const_iterator it = creds.begin(); it != creds.end(); it++)
490 if (((*it)->isShared() && (*it)->isValid())) {
491 CredentialSet::iterator old = mSessionCreds.find(*it);
492 if (old == mSessionCreds.end()) {
493 mSessionCreds.insert(*it);
494 } else {
495 // replace "new" with "old" in input set to retain synchronization
496 (*old)->merge(**it);
497 updatedCredentials.erase(*it);
498 updatedCredentials.insert(*old);
499 }
500 }
501 creds.swap(updatedCredentials);
502 }
503
504
505 //
506 // Locate an AuthorizationToken given a blob
507 //
508 AuthorizationToken &Session::authorization(const AuthorizationBlob &blob)
509 {
510 AuthorizationToken &auth = AuthorizationToken::find(blob);
511 Server::process().checkAuthorization(&auth);
512 return auth;
513 }
514
515 RefPointer<AuthHostInstance>
516 Session::authhost(const AuthHostType hostType, const bool restart)
517 {
518 StLock<Mutex> _(mAuthHostLock);
519
520 if (hostType == privilegedAuthHost)
521 {
522 if (restart || !mAuthHost || (mAuthHost->state() != Security::UnixPlusPlus::Child::alive))
523 {
524 if (mAuthHost)
525 PerSession::kill(*mAuthHost);
526 mAuthHost = new AuthHostInstance(*this, hostType);
527 }
528 return mAuthHost;
529 }
530 else /* if (hostType == securityAgent) */
531 {
532 if (restart || !mSecurityAgent || (mSecurityAgent->state() != Security::UnixPlusPlus::Child::alive))
533 {
534 if (mSecurityAgent)
535 PerSession::kill(*mSecurityAgent);
536 mSecurityAgent = new AuthHostInstance(*this, hostType);
537 }
538 return mSecurityAgent;
539 }
540 }
541
542 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict)
543 {
544 checkOriginator();
545 StLock<Mutex> _(*this);
546 mSessionAgentPrefs = userPrefsDict;
547 }
548
549 CFDataRef DynamicSession::copyUserPrefs()
550 {
551 StLock<Mutex> _(*this);
552 if (mSessionAgentPrefs)
553 CFRetain(mSessionAgentPrefs);
554 return mSessionAgentPrefs;
555 }
556
557
558 //
559 // Debug dumping
560 //
561 #if defined(DEBUGDUMP)
562
563 void Session::dumpNode()
564 {
565 PerSession::dumpNode();
566 Debug::dump(" boot=%d service=%d attrs=0x%lx authhost=%p securityagent=%p",
567 mBootstrap.port(), mServicePort.port(), mAttributes, mAuthHost, mSecurityAgent);
568 }
569
570 #endif //DEBUGDUMP