]> git.saurik.com Git - apple/securityd.git/blob - src/session.cpp
securityd-40120.tar.gz
[apple/securityd.git] / src / session.cpp
1 /*
2 * Copyright (c) 2000-2004,2008-2009 Apple 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 <pwd.h>
35 #include <signal.h> // SIGTERM
36 #include <Security/AuthorizationPriv.h> // kAuthorizationFlagLeastPrivileged
37 #include "session.h"
38 #include "connection.h"
39 #include "database.h"
40 #include "server.h"
41 #include <security_utilities/logging.h>
42
43 //
44 // The static session map
45 //
46 PortMap<Session> Session::mSessions;
47
48 std::string Session::kUsername = "username";
49 std::string Session::kRealname = "realname";
50
51 //
52 // Create a Session object from initial parameters (create)
53 //
54 Session::Session(Bootstrap bootstrap, Port servicePort, SessionAttributeBits attrs)
55 : mBootstrap(bootstrap), mServicePort(servicePort),
56 mAttributes(attrs), mSecurityAgent(NULL), mAuthHost(NULL)
57 {
58 secdebug("SSsession", "%p CREATED: handle=%#x bootstrap=%d service=%d attrs=%#x",
59 this, handle(), mBootstrap.port(), mServicePort.port(), uint32_t(mAttributes));
60 SECURITYD_SESSION_CREATE(this, attrs, servicePort);
61 Syslog::notice("Session 0x%lx created", this->handle());
62 }
63
64
65 //
66 // Destroy a Session
67 //
68 Session::~Session()
69 {
70 secdebug("SSsession", "%p DESTROYED: handle=%#x bootstrap=%d",
71 this, handle(), mBootstrap.port());
72 Syslog::notice("Session 0x%lx destroyed", this->handle());
73 }
74
75
76 //
77 // Locate a session object by service port or (Session API) identifier
78 //
79 Session &Session::find(Port servicePort)
80 {
81 StLock<Mutex> _(mSessions);
82 PortMap<Session>::const_iterator it = mSessions.find(servicePort);
83 assert(it != mSessions.end());
84 return *it->second;
85 }
86
87 Session &Session::find(SecuritySessionId id)
88 {
89 switch (id) {
90 case callerSecuritySession:
91 return Server::session();
92 default:
93 try {
94 return U32HandleObject::find<Session>(id, CSSMERR_CSSM_INVALID_ADDIN_HANDLE);
95 } catch (const CommonError &err) {
96 Syslog::warning("Session::find(%#x) failed rcode=%d", id, err.osStatus());
97 for (PortMap<Session>::const_iterator it = mSessions.begin(); it != mSessions.end(); ++it)
98 Syslog::notice(" Valid sessions include %#x attrs=%#x",
99 it->second->handle(), it->second->attributes());
100 throw;
101 }
102 }
103 }
104
105
106 //
107 // Act on a death notification for a session's (sub)bootstrap port.
108 // We may not destroy the Session outright here (due to processes that use it),
109 // but we do clear out its accumulated wealth.
110 //
111 void Session::destroy(Port servPort)
112 {
113 // remove session from session map
114 StLock<Mutex> _(mSessions);
115 PortMap<Session>::iterator it = mSessions.find(servPort);
116 assert(it != mSessions.end());
117 RefPointer<Session> session = it->second;
118 SECURITYD_SESSION_DESTROY(session);
119 Syslog::notice("Session 0x%lx dead", session->handle());
120 mSessions.erase(it);
121 session->kill();
122 }
123
124 void Session::kill()
125 {
126 StLock<Mutex> _(*this); // do we need to take this so early?
127
128 invalidateSessionAuthHosts();
129
130 // invalidate shared credentials
131 {
132 StLock<Mutex> _(mCredsLock);
133
134 IFDEBUG(if (!mSessionCreds.empty())
135 secdebug("SSauth", "session %p clearing %d shared credentials",
136 this, int(mSessionCreds.size())));
137 for (CredentialSet::iterator it = mSessionCreds.begin(); it != mSessionCreds.end(); it++)
138 (*it)->invalidate();
139 }
140
141 // base kill processing
142 PerSession::kill();
143 }
144
145 void Session::invalidateSessionAuthHosts()
146 {
147 StLock<Mutex> _(mAuthHostLock);
148
149 // if you got here, we don't care about pending operations: the auth hosts die
150 Syslog::warning("Killing auth hosts");
151 if (mSecurityAgent) mSecurityAgent->UnixPlusPlus::Child::kill(SIGTERM);
152 if (mAuthHost) mAuthHost->UnixPlusPlus::Child::kill(SIGTERM);
153 mSecurityAgent = NULL;
154 mAuthHost = NULL;
155 }
156
157 void Session::invalidateAuthHosts()
158 {
159 StLock<Mutex> _(mSessions);
160 for (PortMap<Session>::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
161 it->second->invalidateSessionAuthHosts();
162 }
163
164 //
165 // On system sleep, call sleepProcessing on all DbCommons of all Sessions
166 //
167 void Session::processSystemSleep()
168 {
169 StLock<Mutex> _(mSessions);
170 for (PortMap<Session>::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
171 it->second->allReferences(&DbCommon::sleepProcessing);
172 }
173
174
175 //
176 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
177 //
178 void Session::processLockAll()
179 {
180 allReferences(&DbCommon::lockProcessing);
181 }
182
183 //
184 // The root session inherits the startup bootstrap and service port
185 //
186 RootSession::RootSession(Server &server, SessionAttributeBits attrs)
187 : Session(Bootstrap(), server.primaryServicePort(),
188 sessionIsRoot | sessionWasInitialized | attrs)
189 {
190 parent(server); // the Server is our parent
191 ref(); // eternalize
192
193 // self-install (no thread safety issues here)
194 mSessions[mServicePort] = this;
195 }
196
197 //
198 // Dynamic sessions use the given bootstrap and re-register in it
199 //
200 DynamicSession::DynamicSession(TaskPort taskPort)
201 : ReceivePort(Server::active().bootstrapName(), taskPort.bootstrap(), false),
202 Session(taskPort.bootstrap(), *this),
203 mOriginatorTask(taskPort), mHaveOriginatorUid(false)
204 {
205 // link to Server as the global nexus in the object mesh
206 parent(Server::active());
207
208 // tell the server to listen to our port
209 Server::active().add(*this);
210
211 // register for port notifications
212 Server::active().notifyIfDead(bootstrapPort()); //@@@??? still needed?
213 Server::active().notifyIfUnused(*this);
214
215 // self-register
216 StLock<Mutex> _(mSessions);
217 assert(!mSessions[*this]); // can't be registered already (we just made it)
218 mSessions[*this] = this;
219
220 secdebug("SSsession", "%p dynamic session originator=%d (pid=%d)",
221 this, mOriginatorTask.port(), taskPort.pid());
222 }
223
224 DynamicSession::~DynamicSession()
225 {
226 // remove our service port from the server
227 Server::active().remove(*this);
228 }
229
230
231 void DynamicSession::kill()
232 {
233 StLock<Mutex> _(*this);
234 mBootstrap.destroy(); // release our bootstrap port
235 Session::kill(); // continue with parent kill
236 }
237
238
239 //
240 // Set up a DynamicSession.
241 // This call must be made from a process within the session, and it must be the first
242 // such process to make the call.
243 //
244 void DynamicSession::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
245 {
246 StLock<Mutex> _(*this);
247 SECURITYD_SESSION_SETATTR(this, attrs);
248 Syslog::notice("Session 0x%lx attributes 0x%x", this->handle(), attrs);
249 secdebug("SSsession", "%p setup flags=%#x attrs=%#x", this, uint32_t(flags), uint32_t(attrs));
250 if (attrs & ~settableAttributes)
251 MacOSError::throwMe(errSessionInvalidAttributes);
252 checkOriginator();
253 if (attribute(sessionWasInitialized))
254 MacOSError::throwMe(errSessionAuthorizationDenied);
255 setAttributes(attrs | sessionWasInitialized);
256 }
257
258
259 //
260 // Check whether the calling process is the session originator.
261 // If it's not, throw.
262 //
263 void DynamicSession::checkOriginator()
264 {
265 if (mOriginatorTask != Server::process().taskPort())
266 MacOSError::throwMe(errSessionAuthorizationDenied);
267 }
268
269
270 //
271 // The "originator uid" is a uid value that can be provided by the session originator
272 // and retrieved by anyone. Securityd places no semantic meaning on this value.
273 //
274 uid_t DynamicSession::originatorUid() const
275 {
276 if (mHaveOriginatorUid)
277 return mOriginatorUid;
278 else
279 MacOSError::throwMe(errSessionValueNotSet);
280 }
281
282
283 void DynamicSession::originatorUid(uid_t uid)
284 {
285 checkOriginator();
286 if (mHaveOriginatorUid) // must not re-set this
287 MacOSError::throwMe(errSessionAuthorizationDenied);
288 mHaveOriginatorUid = true;
289 mOriginatorUid = uid;
290
291 Server::active().longTermActivity();
292 struct passwd *pw = getpwuid(uid);
293
294 if (pw != NULL) {
295
296 mOriginatorCredential = Credential(uid, pw->pw_name ? pw->pw_name : "", pw->pw_gecos ? pw->pw_gecos : "", "", true/*shared*/);
297 endpwent();
298 }
299
300 secdebug("SSsession", "%p session uid set to %d", this, uid);
301 }
302
303 //
304 // Authorization operations
305 //
306 OSStatus Session::authCreate(const AuthItemSet &rights,
307 const AuthItemSet &environment,
308 AuthorizationFlags flags,
309 AuthorizationBlob &newHandle,
310 const audit_token_t &auditToken)
311 {
312 // invoke the authorization computation engine
313 CredentialSet resultCreds;
314
315 // this will acquire the object lock, so we delay acquiring it (@@@ no longer needed)
316 auto_ptr<AuthorizationToken> auth(new AuthorizationToken(*this, resultCreds, auditToken, (flags&kAuthorizationFlagLeastPrivileged)));
317
318 SECURITYD_AUTH_CREATE(this, auth.get());
319
320 // Make a copy of the mSessionCreds
321 CredentialSet sessionCreds;
322 {
323 StLock<Mutex> _(mCredsLock);
324 sessionCreds = mSessionCreds;
325 }
326
327 AuthItemSet outRights;
328 OSStatus result = Server::authority().authorize(rights, environment, flags,
329 &sessionCreds, &resultCreds, outRights, *auth);
330 newHandle = auth->handle();
331
332 // merge resulting creds into shared pool
333 if ((flags & kAuthorizationFlagExtendRights) &&
334 !(flags & kAuthorizationFlagDestroyRights))
335 {
336 StLock<Mutex> _(mCredsLock);
337 mergeCredentials(resultCreds);
338 auth->mergeCredentials(resultCreds);
339 }
340
341 // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to
342 // not be destroyed anymore since it's destructor asserts it has no processes
343 Server::process().addAuthorization(auth.get());
344 auth.release();
345 return result;
346 }
347
348 void Session::authFree(const AuthorizationBlob &authBlob, AuthorizationFlags flags)
349 {
350 AuthorizationToken::Deleter deleter(authBlob);
351 AuthorizationToken &auth = deleter;
352 Process &process = Server::process();
353 process.checkAuthorization(&auth);
354
355 if (flags & kAuthorizationFlagDestroyRights) {
356 // explicitly invalidate all shared credentials and remove them from the session
357 for (CredentialSet::const_iterator it = auth.begin(); it != auth.end(); it++)
358 if ((*it)->isShared())
359 (*it)->invalidate();
360 }
361
362 // now get rid of the authorization itself
363 if (process.removeAuthorization(&auth))
364 deleter.remove();
365 }
366
367 OSStatus Session::authGetRights(const AuthorizationBlob &authBlob,
368 const AuthItemSet &rights, const AuthItemSet &environment,
369 AuthorizationFlags flags,
370 AuthItemSet &grantedRights)
371 {
372 AuthorizationToken &auth = authorization(authBlob);
373 return auth.session().authGetRights(auth, rights, environment, flags, grantedRights);
374 }
375
376 OSStatus Session::authGetRights(AuthorizationToken &auth,
377 const AuthItemSet &rights, const AuthItemSet &environment,
378 AuthorizationFlags flags,
379 AuthItemSet &grantedRights)
380 {
381 CredentialSet resultCreds;
382 CredentialSet effective;
383 {
384 StLock<Mutex> _(mCredsLock);
385 effective = auth.effectiveCreds();
386 }
387 OSStatus result = Server::authority().authorize(rights, environment, flags,
388 &effective, &resultCreds, grantedRights, auth);
389
390 // merge resulting creds into shared pool
391 if ((flags & kAuthorizationFlagExtendRights) && !(flags & kAuthorizationFlagDestroyRights))
392 {
393 StLock<Mutex> _(mCredsLock);
394 mergeCredentials(resultCreds);
395 auth.mergeCredentials(resultCreds);
396 }
397
398 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
399 &auth, int(rights.size()), int(grantedRights.size()));
400 return result;
401 }
402
403 OSStatus Session::authGetInfo(const AuthorizationBlob &authBlob,
404 const char *tag,
405 AuthItemSet &contextInfo)
406 {
407 AuthorizationToken &auth = authorization(authBlob);
408 secdebug("SSauth", "Authorization %p get-info", &auth);
409 contextInfo = auth.infoSet(tag);
410 return noErr;
411 }
412
413 OSStatus Session::authExternalize(const AuthorizationBlob &authBlob,
414 AuthorizationExternalForm &extForm)
415 {
416 const AuthorizationToken &auth = authorization(authBlob);
417 StLock<Mutex> _(*this);
418 if (auth.mayExternalize(Server::process())) {
419 memset(&extForm, 0, sizeof(extForm));
420 AuthorizationExternalBlob &extBlob =
421 reinterpret_cast<AuthorizationExternalBlob &>(extForm);
422 extBlob.blob = auth.handle();
423 extBlob.session = bootstrapPort();
424 secdebug("SSauth", "Authorization %p externalized", &auth);
425 return noErr;
426 } else
427 return errAuthorizationExternalizeNotAllowed;
428 }
429
430 OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm,
431 AuthorizationBlob &authBlob)
432 {
433 // interpret the external form
434 const AuthorizationExternalBlob &extBlob =
435 reinterpret_cast<const AuthorizationExternalBlob &>(extForm);
436
437 // locate source authorization
438 AuthorizationToken &sourceAuth = AuthorizationToken::find(extBlob.blob);
439
440 // check for permission and do it
441 if (sourceAuth.mayInternalize(Server::process(), true)) {
442 StLock<Mutex> _(*this);
443 authBlob = extBlob.blob;
444 Server::process().addAuthorization(&sourceAuth);
445 secdebug("SSauth", "Authorization %p internalized", &sourceAuth);
446 return noErr;
447 } else
448 return errAuthorizationInternalizeNotAllowed;
449 }
450
451
452 //
453 // The default session setup operation always fails.
454 // Subclasses can override this to support session setup calls.
455 //
456 void Session::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
457 {
458 MacOSError::throwMe(errSessionAuthorizationDenied);
459 }
460
461
462 //
463 // Authorization database I/O
464 //
465 OSStatus Session::authorizationdbGet(AuthorizationString inRightName, CFDictionaryRef *rightDict)
466 {
467 string rightName(inRightName);
468 return Server::authority().getRule(rightName, rightDict);
469 }
470
471
472 OSStatus Session::authorizationdbSet(const AuthorizationBlob &authBlob, AuthorizationString inRightName, CFDictionaryRef rightDict)
473 {
474 CredentialSet resultCreds;
475 AuthorizationToken &auth = authorization(authBlob);
476 CredentialSet effective;
477
478 {
479 StLock<Mutex> _(mCredsLock);
480 effective = auth.effectiveCreds();
481 }
482
483 OSStatus result = Server::authority().setRule(inRightName, rightDict, &effective, &resultCreds, auth);
484
485 {
486 StLock<Mutex> _(mCredsLock);
487 mergeCredentials(resultCreds);
488 auth.mergeCredentials(resultCreds);
489 }
490
491 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%d)",
492 &authorization(authBlob), inRightName, int32_t(result));
493 return result;
494 }
495
496
497 OSStatus Session::authorizationdbRemove(const AuthorizationBlob &authBlob, AuthorizationString inRightName)
498 {
499 CredentialSet resultCreds;
500 AuthorizationToken &auth = authorization(authBlob);
501 CredentialSet effective;
502
503 {
504 StLock<Mutex> _(mCredsLock);
505 effective = auth.effectiveCreds();
506 }
507
508 OSStatus result = Server::authority().removeRule(inRightName, &effective, &resultCreds, auth);
509
510 {
511 StLock<Mutex> _(mCredsLock);
512 mergeCredentials(resultCreds);
513 auth.mergeCredentials(resultCreds);
514 }
515
516 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%d)",
517 &authorization(authBlob), inRightName, int32_t(result));
518 return result;
519 }
520
521
522 //
523 // Merge a set of credentials into the shared-session credential pool
524 //
525 // must hold mCredsLock
526 void Session::mergeCredentials(CredentialSet &creds)
527 {
528 secdebug("SSsession", "%p merge creds @%p", this, &creds);
529 CredentialSet updatedCredentials = creds;
530 for (CredentialSet::const_iterator it = creds.begin(); it != creds.end(); it++)
531 if ((*it)->isShared() && (*it)->isValid()) {
532 CredentialSet::iterator old = mSessionCreds.find(*it);
533 if (old == mSessionCreds.end()) {
534 mSessionCreds.insert(*it);
535 } else {
536 // replace "new" with "old" in input set to retain synchronization
537 (*old)->merge(**it);
538 updatedCredentials.erase(*it);
539 updatedCredentials.insert(*old);
540 }
541 }
542 creds.swap(updatedCredentials);
543 }
544
545
546 //
547 // Locate an AuthorizationToken given a blob
548 //
549 AuthorizationToken &Session::authorization(const AuthorizationBlob &blob)
550 {
551 AuthorizationToken &auth = AuthorizationToken::find(blob);
552 Server::process().checkAuthorization(&auth);
553 return auth;
554 }
555
556 //
557 // Run the Authorization engine to check if a given right has been authorized,
558 // independent of an external client request.
559 //
560 OSStatus Session::authCheckRight(string &rightName, Connection &connection, bool allowUI)
561 {
562 // dummy up the arguments for authCreate()
563 AuthorizationItem rightItem = { rightName.c_str(), 0, NULL, 0 };
564 AuthorizationItemSet rightItemSet = { 1, &rightItem };
565 AuthItemSet rightAuthItemSet(&rightItemSet);
566 AuthItemSet envAuthItemSet(kAuthorizationEmptyEnvironment);
567 AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagExtendRights;
568 if (true == allowUI)
569 flags |= kAuthorizationFlagInteractionAllowed;
570 AuthorizationBlob dummyHandle;
571 const audit_token_t *at = connection.auditToken();
572
573 return authCreate(rightAuthItemSet, envAuthItemSet, flags, dummyHandle, *at);
574 }
575
576 // for places within securityd that don't want to #include
577 // <libsecurity_authorization/Authorization.h> or to fuss about exceptions
578 bool Session::isRightAuthorized(string &rightName, Connection &connection, bool allowUI)
579 {
580 bool isAuthorized = false;
581
582 try {
583 OSStatus status = authCheckRight(rightName, connection, allowUI);
584 if (errAuthorizationSuccess == status)
585 isAuthorized = true;
586 }
587 catch (...) {
588 }
589 return isAuthorized;
590 }
591
592 RefPointer<AuthHostInstance>
593 Session::authhost(const AuthHostType hostType, const bool restart)
594 {
595 StLock<Mutex> _(mAuthHostLock);
596
597 if (hostType == privilegedAuthHost)
598 {
599 if (restart || !mAuthHost || (mAuthHost->state() != Security::UnixPlusPlus::Child::alive))
600 {
601 if (mAuthHost)
602 PerSession::kill(*mAuthHost);
603 mAuthHost = new AuthHostInstance(*this, hostType);
604 }
605 return mAuthHost;
606 }
607 else /* if (hostType == securityAgent) */
608 {
609 if (restart || !mSecurityAgent || (mSecurityAgent->state() != Security::UnixPlusPlus::Child::alive))
610 {
611 if (mSecurityAgent)
612 PerSession::kill(*mSecurityAgent);
613 mSecurityAgent = new AuthHostInstance(*this, hostType);
614 }
615 return mSecurityAgent;
616 }
617 }
618
619 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict)
620 {
621 checkOriginator();
622 if (Server::process().uid() != 0)
623 MacOSError::throwMe(errSessionAuthorizationDenied);
624 StLock<Mutex> _(*this);
625 mSessionAgentPrefs = userPrefsDict;
626 }
627
628 CFDataRef DynamicSession::copyUserPrefs()
629 {
630 StLock<Mutex> _(*this);
631 if (mSessionAgentPrefs)
632 CFRetain(mSessionAgentPrefs);
633 return mSessionAgentPrefs;
634 }
635
636
637 //
638 // Debug dumping
639 //
640 #if defined(DEBUGDUMP)
641
642 void Session::dumpNode()
643 {
644 PerSession::dumpNode();
645 Debug::dump(" boot=%d service=%d attrs=%#x authhost=%p securityagent=%p",
646 mBootstrap.port(), mServicePort.port(), uint32_t(mAttributes), mAuthHost, mSecurityAgent);
647 }
648
649 #endif //DEBUGDUMP