2 * Copyright (c) 2000-2009,2011-2013 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // session - authentication session domains
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
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.
40 #include <signal.h> // SIGTERM
41 #include <Security/AuthorizationPriv.h> // kAuthorizationFlagLeastPrivileged
43 #include "connection.h"
46 #include <security_utilities/logging.h>
47 #include <agentquery.h>
49 using namespace CommonCriteria
;
53 // The static session map
55 Session::SessionMap
Session::mSessions
;
56 Mutex
Session::mSessionLock(Mutex::recursive
);
59 const char Session::kUsername
[] = "username";
60 const char Session::kRealname
[] = "realname";
64 // Create a Session object from initial parameters (create)
66 Session::Session(const AuditInfo
&audit
, Server
&server
)
67 : mAudit(audit
), mSecurityAgent(NULL
), mKeybagState(0)
69 // link to Server as the global nexus in the object mesh
73 StLock
<Mutex
> _(mSessionLock
);
74 assert(!mSessions
[audit
.sessionId()]);
75 mSessions
[audit
.sessionId()] = this;
78 secnotice("SecServer", "%p Session %d created, uid:%d sessionId:%d", this, this->sessionId(), mAudit
.uid(), mAudit
.sessionId());
79 Syslog::notice("Session %d created", this->sessionId());
88 secnotice("SecServer", "%p Session %d destroyed", this, this->sessionId());
89 Syslog::notice("Session %d destroyed", this->sessionId());
93 Server
&Session::server() const
95 return parent
<Server
>();
99 // Locate a session object by session identifier
101 Session
&Session::find(pid_t id
, bool create
)
103 if (id
== (pid_t
)callerSecuritySession
)
104 return Server::session();
105 StLock
<Mutex
> _(mSessionLock
);
106 SessionMap::iterator it
= mSessions
.find(id
);
107 if (it
!= mSessions
.end())
112 CssmError::throwMe(errSessionInvalidId
);
115 assert(info
.sessionId() == id
);
116 RefPointer
<Session
> session
= new Session(info
, Server::active());
117 mSessions
.insert(make_pair(id
, session
));
123 // Act on a death notification for a session's underlying audit session object.
124 // We may not destroy the Session outright here (due to processes that use it),
125 // but we do clear out its accumulated wealth.
126 // Note that we may get spurious death notifications for audit sessions that we
127 // never learned about. Ignore those.
129 void Session::destroy(SessionId id
)
131 // remove session from session map
132 RefPointer
<Session
> session
= NULL
;
134 StLock
<Mutex
> _(mSessionLock
);
135 SessionMap::iterator it
= mSessions
.find(id
);
136 if (it
!= mSessions
.end()) {
137 session
= it
->second
;
138 assert(session
->sessionId() == id
);
151 StLock
<Mutex
> _(*this); // do we need to take this so early?
152 secnotice("SecServer", "%p killing session %d", this, this->sessionId());
153 invalidateSessionAuthHosts();
155 // base kill processing
161 // Refetch audit session data for the current audit session (to catch outside updates
162 // to the audit record). This is the price we're paying for not requiring an IPC to
163 // securityd when audit session data changes (this is desirable for delayering the
164 // software layer cake).
165 // If we ever disallow changes to (parts of the) audit session record in the kernel,
166 // we can loosen up on this continual re-fetching.
168 void Session::updateAudit() const
170 CommonCriteria::AuditInfo info
;
172 info
.get(mAudit
.sessionId());
179 // Second and third arguments defaults to false
180 void Session::verifyKeyStorePassphrase(int32_t retries
, bool useForACLFallback
, const char *itemname
)
182 QueryKeybagPassphrase
keybagQuery(*this, retries
);
183 keybagQuery
.inferHints(Server::process());
185 // Parasitic takeover to enable user confirmation when ACL validation ends up without a database
186 if (useForACLFallback
) {
187 keybagQuery
.addHint("acl-fallback", &useForACLFallback
, sizeof(useForACLFallback
));
188 keybagQuery
.addHint("keychain-item-name", itemname
, itemname
? (uint32_t)strlen(itemname
) : 0, 0);
191 if (keybagQuery
.query() != SecurityAgent::noReason
) {
192 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
196 void Session::changeKeyStorePassphrase()
198 service_context_t context
= get_current_service_context();
199 QueryKeybagNewPassphrase
keybagQuery(*this);
200 keybagQuery
.inferHints(Server::process());
201 CssmAutoData
pass(Allocator::standard(Allocator::sensitive
));
202 CssmAutoData
oldPass(Allocator::standard(Allocator::sensitive
));
203 SecurityAgent::Reason queryReason
= keybagQuery
.query(oldPass
, pass
);
204 if (queryReason
== SecurityAgent::noReason
) {
205 service_client_kb_change_secret(&context
, oldPass
.data(), (int)oldPass
.length(), pass
.data(), (int)pass
.length());
207 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
211 void Session::resetKeyStorePassphrase(const CssmData
&passphrase
)
213 service_context_t context
= get_current_service_context();
214 service_client_kb_reset(&context
, passphrase
.data(), (int)passphrase
.length());
217 service_context_t
Session::get_current_service_context()
219 service_context_t context
= { sessionId(), originatorUid(), *Server::connection().auditToken(), 0 };
223 void Session::keybagClearState(int state
)
225 mKeybagState
&= ~state
;
228 void Session::keybagSetState(int state
)
230 mKeybagState
|= state
;
233 bool Session::keybagGetState(int state
)
235 return mKeybagState
& state
;
240 // Manage authorization client processes
242 void Session::invalidateSessionAuthHosts()
244 StLock
<Mutex
> _(mAuthHostLock
);
246 // if you got here, we don't care about pending operations: the auth hosts die
247 Syslog::warning("Killing auth hosts for session %d", this->sessionId());
248 if (mSecurityAgent
) {
249 secnotice("shutdown", "SIGTERMing child in state %d, pid %d", mSecurityAgent
->UnixPlusPlus::Child::state(), mSecurityAgent
->UnixPlusPlus::Child::pid());
250 mSecurityAgent
->UnixPlusPlus::Child::kill(SIGTERM
);
252 secnotice("shutdown", "No securityagent for session %d", this->sessionId());
254 mSecurityAgent
= NULL
;
257 void Session::invalidateAuthHosts()
259 StLock
<Mutex
> _(mSessionLock
);
260 for (SessionMap::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++) {
261 it
->second
->invalidateSessionAuthHosts();
266 // On system sleep, call sleepProcessing on all DbCommons of all Sessions
268 void Session::processSystemSleep()
270 SecurityAgentXPCQuery::killAllXPCClients();
272 StLock
<Mutex
> _(mSessionLock
);
273 for (SessionMap::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++)
274 it
->second
->allReferences(&DbCommon::sleepProcessing
);
279 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
281 void Session::processLockAll()
283 allReferences(&DbCommon::lockProcessing
);
288 // The root session corresponds to the audit session that security is running in.
289 // This is usually the initial system session; but in debug scenarios it may be
290 // an "ordinary" graphic login session. In such a debug case, we may add attribute
291 // flags to the session to make our (debugging) life easier.
293 RootSession::RootSession(uint64_t attributes
, Server
&server
)
294 : Session(AuditInfo::current(), server
)
297 mAudit
.ai_flags
|= attributes
; // merge imposed attributes
302 // Accessor method for setting audit session flags.
304 void Session::setAttributes(SessionAttributeBits bits
)
306 StLock
<Mutex
> _(*this);
308 // assert((bits & ~settableAttributes) == 0);
309 mAudit
.ai_flags
= bits
;
314 // The default session setup operation always fails.
315 // Subclasses can override this to support session setup calls.
317 void Session::setupAttributes(SessionCreationFlags flags
, SessionAttributeBits attrs
)
319 MacOSError::throwMe(errSessionAuthorizationDenied
);
322 uid_t
Session::originatorUid()
324 if (mAudit
.uid() == AU_DEFAUDITID
) {
325 StLock
<Mutex
> _(*this);
332 RefPointer
<AuthHostInstance
>
333 Session::authhost(const bool restart
)
335 StLock
<Mutex
> _(mAuthHostLock
);
337 if (restart
|| !mSecurityAgent
|| (mSecurityAgent
->state() != Security::UnixPlusPlus::Child::alive
))
340 PerSession::kill(*mSecurityAgent
);
341 mSecurityAgent
= new AuthHostInstance(*this);
343 return mSecurityAgent
;
350 #if defined(DEBUGDUMP)
352 void Session::dumpNode()
354 PerSession::dumpNode();
355 Debug::dump(" auid=%d attrs=%#x securityagent=%p",
356 this->sessionId(), uint32_t(this->attributes()), mSecurityAgent
);