]> git.saurik.com Git - apple/security.git/blob - securityd/src/session.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / securityd / src / session.cpp
1 /*
2 * Copyright (c) 2000-2009,2011-2013 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 // Security sessions are now by definition congruent to audit subsystem sessions.
29 // We represent these sessions within securityd as subclasses of class Session,
30 // but we reach for the kernel's data whenever we're not sure if our data is
31 // up to date.
32 //
33 // Modifications to session state are made from client space using system calls.
34 // We discover them when we see changes in audit records as they come in with
35 // new requests. We cannot use system notifications for such changes because
36 // securityd is fully symmetrically multi-threaded, and thus may process new
37 // requests from clients before it gets those notifications.
38 //
39 #include <pwd.h>
40 #include <signal.h> // SIGTERM
41 #include <Security/AuthorizationPriv.h> // kAuthorizationFlagLeastPrivileged
42 #include "session.h"
43 #include "connection.h"
44 #include "database.h"
45 #include "server.h"
46 #include <security_utilities/logging.h>
47 #include <agentquery.h>
48
49 using namespace CommonCriteria;
50
51
52 //
53 // The static session map
54 //
55 Session::SessionMap Session::mSessions;
56 Mutex Session::mSessionLock(Mutex::recursive);
57
58
59 const char Session::kUsername[] = "username";
60 const char Session::kRealname[] = "realname";
61
62
63 //
64 // Create a Session object from initial parameters (create)
65 //
66 Session::Session(const AuditInfo &audit, Server &server)
67 : mAudit(audit), mSecurityAgent(NULL), mAuthHost(NULL), mKeybagState(0)
68 {
69 // link to Server as the global nexus in the object mesh
70 parent(server);
71
72 // self-register
73 StLock<Mutex> _(mSessionLock);
74 assert(!mSessions[audit.sessionId()]);
75 mSessions[audit.sessionId()] = this;
76
77 // log it
78 SECURITYD_SESSION_CREATE(this, this->sessionId(), &mAudit, sizeof(mAudit));
79 Syslog::notice("Session %d created", this->sessionId());
80 }
81
82
83 //
84 // Destroy a Session
85 //
86 Session::~Session()
87 {
88 SECURITYD_SESSION_DESTROY(this, this->sessionId());
89 Syslog::notice("Session %d destroyed", this->sessionId());
90 }
91
92
93 Server &Session::server() const
94 {
95 return parent<Server>();
96 }
97
98
99 //
100 // Locate a session object by session identifier
101 //
102 Session &Session::find(pid_t id, bool create)
103 {
104 if (id == callerSecuritySession)
105 return Server::session();
106 StLock<Mutex> _(mSessionLock);
107 SessionMap::iterator it = mSessions.find(id);
108 if (it != mSessions.end())
109 return *it->second;
110
111 // new session
112 if (!create)
113 CssmError::throwMe(errSessionInvalidId);
114 AuditInfo info;
115 info.get(id);
116 assert(info.sessionId() == id);
117 RefPointer<Session> session = new DynamicSession(info);
118 mSessions.insert(make_pair(id, session));
119 return *session;
120 }
121
122
123 //
124 // Act on a death notification for a session's underlying audit session object.
125 // We may not destroy the Session outright here (due to processes that use it),
126 // but we do clear out its accumulated wealth.
127 // Note that we may get spurious death notifications for audit sessions that we
128 // never learned about. Ignore those.
129 //
130 void Session::destroy(SessionId id)
131 {
132 // remove session from session map
133 bool unlocked = false;
134 RefPointer<Session> session = NULL;
135 {
136 StLock<Mutex> _(mSessionLock);
137 SessionMap::iterator it = mSessions.find(id);
138 if (it != mSessions.end()) {
139 session = it->second;
140 assert(session->sessionId() == id);
141 mSessions.erase(it);
142
143 for (SessionMap::iterator kb_it = mSessions.begin(); kb_it != mSessions.end(); kb_it++) {
144 RefPointer<Session> kb_session = kb_it->second;
145 if (kb_session->originatorUid() == session->originatorUid()) {
146 if (kb_session->keybagGetState(session_keybag_unlocked)) unlocked = true;
147 }
148 }
149 }
150 }
151
152 if (session.get()) {
153 if (!unlocked) {
154 // sessions are destroy outside of a process request session->get_current_service_context()
155 service_context_t context = { session->sessionId(), session->originatorUid(), {} };
156 service_client_kb_lock(&context);
157 }
158 session->kill();
159 }
160 }
161
162
163 void Session::kill()
164 {
165 StLock<Mutex> _(*this); // do we need to take this so early?
166 SECURITYD_SESSION_KILL(this, this->sessionId());
167 invalidateSessionAuthHosts();
168
169 // invalidate shared credentials
170 {
171 StLock<Mutex> _(mCredsLock);
172
173 IFDEBUG(if (!mSessionCreds.empty())
174 secdebug("SSauth", "session %p clearing %d shared credentials",
175 this, int(mSessionCreds.size())));
176 for (CredentialSet::iterator it = mSessionCreds.begin(); it != mSessionCreds.end(); it++)
177 (*it)->invalidate();
178 }
179
180 // base kill processing
181 PerSession::kill();
182 }
183
184
185 //
186 // Refetch audit session data for the current audit session (to catch outside updates
187 // to the audit record). This is the price we're paying for not requiring an IPC to
188 // securityd when audit session data changes (this is desirable for delayering the
189 // software layer cake).
190 // If we ever disallow changes to (parts of the) audit session record in the kernel,
191 // we can loosen up on this continual re-fetching.
192 //
193 void Session::updateAudit() const
194 {
195 CommonCriteria::AuditInfo info;
196 try {
197 info.get(mAudit.sessionId());
198 } catch (...) {
199 return;
200 }
201 mAudit = info;
202 }
203
204 void Session::verifyKeyStorePassphrase(int32_t retries)
205 {
206 QueryKeybagPassphrase keybagQuery(*this, retries);
207 keybagQuery.inferHints(Server::process());
208 if (keybagQuery.query() != SecurityAgent::noReason) {
209 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
210 }
211 }
212
213 void Session::changeKeyStorePassphrase()
214 {
215 service_context_t context = get_current_service_context();
216 QueryKeybagNewPassphrase keybagQuery(*this);
217 keybagQuery.inferHints(Server::process());
218 CssmAutoData pass(Allocator::standard(Allocator::sensitive));
219 CssmAutoData oldPass(Allocator::standard(Allocator::sensitive));
220 SecurityAgent::Reason queryReason = keybagQuery.query(oldPass, pass);
221 if (queryReason == SecurityAgent::noReason) {
222 service_client_kb_change_secret(&context, oldPass.data(), (int)oldPass.length(), pass.data(), (int)pass.length());
223 } else {
224 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
225 }
226 }
227
228 void Session::resetKeyStorePassphrase(const CssmData &passphrase)
229 {
230 service_context_t context = get_current_service_context();
231 service_client_kb_reset(&context, passphrase.data(), (int)passphrase.length());
232 }
233
234 service_context_t Session::get_current_service_context()
235 {
236 service_context_t context = { sessionId(), originatorUid(), *Server::connection().auditToken() };
237 return context;
238 }
239
240 void Session::keybagClearState(int state)
241 {
242 mKeybagState &= ~state;
243 }
244
245 void Session::keybagSetState(int state)
246 {
247 mKeybagState |= state;
248 }
249
250 bool Session::keybagGetState(int state)
251 {
252 return mKeybagState & state;
253 }
254
255
256 //
257 // Manage authorization client processes
258 //
259 void Session::invalidateSessionAuthHosts()
260 {
261 StLock<Mutex> _(mAuthHostLock);
262
263 // if you got here, we don't care about pending operations: the auth hosts die
264 Syslog::warning("Killing auth hosts");
265 if (mSecurityAgent) mSecurityAgent->UnixPlusPlus::Child::kill(SIGTERM);
266 if (mAuthHost) mAuthHost->UnixPlusPlus::Child::kill(SIGTERM);
267 mSecurityAgent = NULL;
268 mAuthHost = NULL;
269 }
270
271 void Session::invalidateAuthHosts()
272 {
273 StLock<Mutex> _(mSessionLock);
274 for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
275 it->second->invalidateSessionAuthHosts();
276 }
277
278 //
279 // On system sleep, call sleepProcessing on all DbCommons of all Sessions
280 //
281 void Session::processSystemSleep()
282 {
283 SecurityAgentXPCQuery::killAllXPCClients();
284
285 StLock<Mutex> _(mSessionLock);
286 for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
287 it->second->allReferences(&DbCommon::sleepProcessing);
288 }
289
290
291 //
292 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
293 //
294 void Session::processLockAll()
295 {
296 allReferences(&DbCommon::lockProcessing);
297 }
298
299
300 //
301 // The root session corresponds to the audit session that security is running in.
302 // This is usually the initial system session; but in debug scenarios it may be
303 // an "ordinary" graphic login session. In such a debug case, we may add attribute
304 // flags to the session to make our (debugging) life easier.
305 //
306 RootSession::RootSession(uint64_t attributes, Server &server)
307 : Session(AuditInfo::current(), server)
308 {
309 ref(); // eternalize
310 mAudit.ai_flags |= attributes; // merge imposed attributes
311 }
312
313
314 //
315 // Dynamic sessions use the audit session context of the first-contact client caller.
316 //
317 DynamicSession::DynamicSession(const AuditInfo &audit)
318 : Session(audit, Server::active())
319 {
320 }
321
322
323 //
324 // Authorization operations
325 //
326 OSStatus Session::authCreate(const AuthItemSet &rights,
327 const AuthItemSet &environment,
328 AuthorizationFlags flags,
329 AuthorizationBlob &newHandle,
330 const audit_token_t &auditToken)
331 {
332 // invoke the authorization computation engine
333 CredentialSet resultCreds;
334
335 // this will acquire the object lock, so we delay acquiring it (@@@ no longer needed)
336 auto_ptr<AuthorizationToken> auth(new AuthorizationToken(*this, resultCreds, auditToken, (flags&kAuthorizationFlagLeastPrivileged)));
337
338 SECURITYD_AUTH_CREATE(this, auth.get());
339
340 // Make a copy of the mSessionCreds
341 CredentialSet sessionCreds;
342 {
343 StLock<Mutex> _(mCredsLock);
344 sessionCreds = mSessionCreds;
345 }
346
347 AuthItemSet outRights;
348 OSStatus result = Server::authority().authorize(rights, environment, flags,
349 &sessionCreds, &resultCreds, outRights, *auth);
350 newHandle = auth->handle();
351
352 // merge resulting creds into shared pool
353 if ((flags & kAuthorizationFlagExtendRights) &&
354 !(flags & kAuthorizationFlagDestroyRights))
355 {
356 StLock<Mutex> _(mCredsLock);
357 mergeCredentials(resultCreds);
358 auth->mergeCredentials(resultCreds);
359 }
360
361 // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to
362 // not be destroyed anymore since it's destructor asserts it has no processes
363 Server::process().addAuthorization(auth.get());
364 auth.release();
365 return result;
366 }
367
368 void Session::authFree(const AuthorizationBlob &authBlob, AuthorizationFlags flags)
369 {
370 AuthorizationToken::Deleter deleter(authBlob);
371 AuthorizationToken &auth = deleter;
372 Process &process = Server::process();
373 process.checkAuthorization(&auth);
374
375 if (flags & kAuthorizationFlagDestroyRights) {
376 // explicitly invalidate all shared credentials and remove them from the session
377 for (CredentialSet::const_iterator it = auth.begin(); it != auth.end(); it++)
378 if ((*it)->isShared())
379 (*it)->invalidate();
380 }
381
382 // now get rid of the authorization itself
383 if (process.removeAuthorization(&auth))
384 deleter.remove();
385 }
386
387 OSStatus Session::authGetRights(const AuthorizationBlob &authBlob,
388 const AuthItemSet &rights, const AuthItemSet &environment,
389 AuthorizationFlags flags,
390 AuthItemSet &grantedRights)
391 {
392 AuthorizationToken &auth = authorization(authBlob);
393 return auth.session().authGetRights(auth, rights, environment, flags, grantedRights);
394 }
395
396 OSStatus Session::authGetRights(AuthorizationToken &auth,
397 const AuthItemSet &rights, const AuthItemSet &environment,
398 AuthorizationFlags flags,
399 AuthItemSet &grantedRights)
400 {
401 CredentialSet resultCreds;
402 CredentialSet effective;
403 {
404 StLock<Mutex> _(mCredsLock);
405 effective = auth.effectiveCreds();
406 }
407 OSStatus result = Server::authority().authorize(rights, environment, flags,
408 &effective, &resultCreds, grantedRights, auth);
409
410 // merge resulting creds into shared pool
411 if ((flags & kAuthorizationFlagExtendRights) && !(flags & kAuthorizationFlagDestroyRights))
412 {
413 StLock<Mutex> _(mCredsLock);
414 mergeCredentials(resultCreds);
415 auth.mergeCredentials(resultCreds);
416 }
417
418 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
419 &auth, int(rights.size()), int(grantedRights.size()));
420 return result;
421 }
422
423 OSStatus Session::authGetInfo(const AuthorizationBlob &authBlob,
424 const char *tag,
425 AuthItemSet &contextInfo)
426 {
427 AuthorizationToken &auth = authorization(authBlob);
428 secdebug("SSauth", "Authorization %p get-info", &auth);
429 contextInfo = auth.infoSet(tag);
430 return noErr;
431 }
432
433 OSStatus Session::authExternalize(const AuthorizationBlob &authBlob,
434 AuthorizationExternalForm &extForm)
435 {
436 const AuthorizationToken &auth = authorization(authBlob);
437 StLock<Mutex> _(*this);
438 if (auth.mayExternalize(Server::process())) {
439 memset(&extForm, 0, sizeof(extForm));
440 AuthorizationExternalBlob &extBlob =
441 reinterpret_cast<AuthorizationExternalBlob &>(extForm);
442 extBlob.blob = auth.handle();
443 extBlob.session = this->sessionId();
444 secdebug("SSauth", "Authorization %p externalized", &auth);
445 return noErr;
446 } else
447 return errAuthorizationExternalizeNotAllowed;
448 }
449
450 OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm,
451 AuthorizationBlob &authBlob)
452 {
453 // interpret the external form
454 const AuthorizationExternalBlob &extBlob =
455 reinterpret_cast<const AuthorizationExternalBlob &>(extForm);
456
457 // locate source authorization
458 AuthorizationToken &sourceAuth = AuthorizationToken::find(extBlob.blob);
459
460 // check for permission and do it
461 if (sourceAuth.mayInternalize(Server::process(), true)) {
462 StLock<Mutex> _(*this);
463 authBlob = extBlob.blob;
464 Server::process().addAuthorization(&sourceAuth);
465 secdebug("SSauth", "Authorization %p internalized", &sourceAuth);
466 return noErr;
467 } else
468 return errAuthorizationInternalizeNotAllowed;
469 }
470
471
472 //
473 // Accessor method for setting audit session flags.
474 //
475 void Session::setAttributes(SessionAttributeBits bits)
476 {
477 StLock<Mutex> _(*this);
478 updateAudit();
479 // assert((bits & ~settableAttributes) == 0);
480 mAudit.ai_flags = bits;
481 mAudit.set();
482 }
483
484 //
485 // The default session setup operation always fails.
486 // Subclasses can override this to support session setup calls.
487 //
488 void Session::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
489 {
490 MacOSError::throwMe(errSessionAuthorizationDenied);
491 }
492
493 uid_t Session::originatorUid()
494 {
495 if (mAudit.uid() == AU_DEFAUDITID) {
496 StLock<Mutex> _(*this);
497 updateAudit();
498 }
499 return mAudit.uid();
500 }
501
502 //
503 // Authorization database I/O
504 //
505 OSStatus Session::authorizationdbGet(AuthorizationString inRightName, CFDictionaryRef *rightDict)
506 {
507 string rightName(inRightName);
508 return Server::authority().getRule(rightName, rightDict);
509 }
510
511
512 OSStatus Session::authorizationdbSet(const AuthorizationBlob &authBlob, AuthorizationString inRightName, CFDictionaryRef rightDict)
513 {
514 CredentialSet resultCreds;
515 AuthorizationToken &auth = authorization(authBlob);
516 CredentialSet effective;
517
518 {
519 StLock<Mutex> _(mCredsLock);
520 effective = auth.effectiveCreds();
521 }
522
523 OSStatus result = Server::authority().setRule(inRightName, rightDict, &effective, &resultCreds, auth);
524
525 {
526 StLock<Mutex> _(mCredsLock);
527 mergeCredentials(resultCreds);
528 auth.mergeCredentials(resultCreds);
529 }
530
531 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%d)",
532 &authorization(authBlob), inRightName, int32_t(result));
533 return result;
534 }
535
536
537 OSStatus Session::authorizationdbRemove(const AuthorizationBlob &authBlob, AuthorizationString inRightName)
538 {
539 CredentialSet resultCreds;
540 AuthorizationToken &auth = authorization(authBlob);
541 CredentialSet effective;
542
543 {
544 StLock<Mutex> _(mCredsLock);
545 effective = auth.effectiveCreds();
546 }
547
548 OSStatus result = Server::authority().removeRule(inRightName, &effective, &resultCreds, auth);
549
550 {
551 StLock<Mutex> _(mCredsLock);
552 mergeCredentials(resultCreds);
553 auth.mergeCredentials(resultCreds);
554 }
555
556 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%d)",
557 &authorization(authBlob), inRightName, int32_t(result));
558 return result;
559 }
560
561
562 //
563 // Merge a set of credentials into the shared-session credential pool
564 //
565 // must hold mCredsLock
566 void Session::mergeCredentials(CredentialSet &creds)
567 {
568 secdebug("SSsession", "%p merge creds @%p", this, &creds);
569 CredentialSet updatedCredentials = creds;
570 for (CredentialSet::const_iterator it = creds.begin(); it != creds.end(); it++)
571 if ((*it)->isShared() && (*it)->isValid()) {
572 CredentialSet::iterator old = mSessionCreds.find(*it);
573 if (old == mSessionCreds.end()) {
574 mSessionCreds.insert(*it);
575 } else {
576 // replace "new" with "old" in input set to retain synchronization
577 (*old)->merge(**it);
578 updatedCredentials.erase(*it);
579 updatedCredentials.insert(*old);
580 }
581 }
582 creds.swap(updatedCredentials);
583 }
584
585
586 //
587 // Locate an AuthorizationToken given a blob
588 //
589 AuthorizationToken &Session::authorization(const AuthorizationBlob &blob)
590 {
591 AuthorizationToken &auth = AuthorizationToken::find(blob);
592 Server::process().checkAuthorization(&auth);
593 return auth;
594 }
595
596 //
597 // Run the Authorization engine to check if a given right has been authorized,
598 // independent of an external client request.
599 //
600 OSStatus Session::authCheckRight(string &rightName, Connection &connection, bool allowUI)
601 {
602 // dummy up the arguments for authCreate()
603 AuthorizationItem rightItem = { rightName.c_str(), 0, NULL, 0 };
604 AuthorizationItemSet rightItemSet = { 1, &rightItem };
605 AuthItemSet rightAuthItemSet(&rightItemSet);
606 AuthItemSet envAuthItemSet(kAuthorizationEmptyEnvironment);
607 AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagExtendRights;
608 if (true == allowUI)
609 flags |= kAuthorizationFlagInteractionAllowed;
610 AuthorizationBlob dummyHandle;
611 const audit_token_t *at = connection.auditToken();
612
613 return authCreate(rightAuthItemSet, envAuthItemSet, flags, dummyHandle, *at);
614 }
615
616 // for places within securityd that don't want to #include
617 // <libsecurity_authorization/Authorization.h> or to fuss about exceptions
618 bool Session::isRightAuthorized(string &rightName, Connection &connection, bool allowUI)
619 {
620 bool isAuthorized = false;
621
622 try {
623 OSStatus status = authCheckRight(rightName, connection, allowUI);
624 if (errAuthorizationSuccess == status)
625 isAuthorized = true;
626 }
627 catch (...) {
628 }
629 return isAuthorized;
630 }
631
632 RefPointer<AuthHostInstance>
633 Session::authhost(const AuthHostType hostType, const bool restart)
634 {
635 StLock<Mutex> _(mAuthHostLock);
636
637 if (hostType == privilegedAuthHost)
638 {
639 if (restart || !mAuthHost || (mAuthHost->state() != Security::UnixPlusPlus::Child::alive))
640 {
641 if (mAuthHost)
642 PerSession::kill(*mAuthHost);
643 mAuthHost = new AuthHostInstance(*this, hostType);
644 }
645 return mAuthHost;
646 }
647 else /* if (hostType == securityAgent) */
648 {
649 if (restart || !mSecurityAgent || (mSecurityAgent->state() != Security::UnixPlusPlus::Child::alive))
650 {
651 if (mSecurityAgent)
652 PerSession::kill(*mSecurityAgent);
653 mSecurityAgent = new AuthHostInstance(*this, hostType);
654 }
655 return mSecurityAgent;
656 }
657 }
658
659 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict)
660 {
661 if (Server::process().uid() != 0)
662 MacOSError::throwMe(errSessionAuthorizationDenied);
663 StLock<Mutex> _(*this);
664 mSessionAgentPrefs = userPrefsDict;
665 }
666
667 CFDataRef DynamicSession::copyUserPrefs()
668 {
669 StLock<Mutex> _(*this);
670 if (mSessionAgentPrefs)
671 CFRetain(mSessionAgentPrefs);
672 return mSessionAgentPrefs;
673 }
674
675
676 //
677 // Debug dumping
678 //
679 #if defined(DEBUGDUMP)
680
681 void Session::dumpNode()
682 {
683 PerSession::dumpNode();
684 Debug::dump(" auid=%d attrs=%#x authhost=%p securityagent=%p",
685 this->sessionId(), uint32_t(this->attributes()), mAuthHost, mSecurityAgent);
686 }
687
688 #endif //DEBUGDUMP