]> git.saurik.com Git - apple/security.git/blob - securityd/src/session.cpp
Security-59306.101.1.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), 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 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());
80 }
81
82
83 //
84 // Destroy a Session
85 //
86 Session::~Session()
87 {
88 secnotice("SecServer", "%p Session %d destroyed", 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 // Locate a session object by session identifier
100 //
101 Session &Session::find(pid_t id, bool create)
102 {
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())
108 return *it->second;
109
110 // new session
111 if (!create)
112 CssmError::throwMe(errSessionInvalidId);
113 AuditInfo info;
114 info.get(id);
115 assert(info.sessionId() == id);
116 RefPointer<Session> session = new Session(info, Server::active());
117 mSessions.insert(make_pair(id, session));
118 return *session;
119 }
120
121
122 //
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.
128 //
129 void Session::destroy(SessionId id)
130 {
131 // remove session from session map
132 RefPointer<Session> session = NULL;
133 {
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);
139 mSessions.erase(it);
140 }
141 }
142
143 if (session.get()) {
144 session->kill();
145 }
146 }
147
148
149 void Session::kill()
150 {
151 StLock<Mutex> _(*this); // do we need to take this so early?
152 secnotice("SecServer", "%p killing session %d", this, this->sessionId());
153 invalidateSessionAuthHosts();
154
155 // base kill processing
156 PerSession::kill();
157 }
158
159
160 //
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.
167 //
168 void Session::updateAudit() const
169 {
170 CommonCriteria::AuditInfo info;
171 try {
172 info.get(mAudit.sessionId());
173 } catch (...) {
174 return;
175 }
176 mAudit = info;
177 }
178
179 // Second and third arguments defaults to false
180 void Session::verifyKeyStorePassphrase(int32_t retries, bool useForACLFallback, const char *itemname)
181 {
182 QueryKeybagPassphrase keybagQuery(*this, retries);
183 keybagQuery.inferHints(Server::process());
184
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);
189 }
190
191 if (keybagQuery.query() != SecurityAgent::noReason) {
192 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
193 }
194 }
195
196 void Session::changeKeyStorePassphrase()
197 {
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());
206 } else {
207 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
208 }
209 }
210
211 void Session::resetKeyStorePassphrase(const CssmData &passphrase)
212 {
213 service_context_t context = get_current_service_context();
214 service_client_kb_reset(&context, passphrase.data(), (int)passphrase.length());
215 }
216
217 service_context_t Session::get_current_service_context()
218 {
219 service_context_t context = { sessionId(), originatorUid(), *Server::connection().auditToken(), 0 };
220 return context;
221 }
222
223 void Session::keybagClearState(int state)
224 {
225 mKeybagState &= ~state;
226 }
227
228 void Session::keybagSetState(int state)
229 {
230 mKeybagState |= state;
231 }
232
233 bool Session::keybagGetState(int state)
234 {
235 return mKeybagState & state;
236 }
237
238
239 //
240 // Manage authorization client processes
241 //
242 void Session::invalidateSessionAuthHosts()
243 {
244 StLock<Mutex> _(mAuthHostLock);
245
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);
251 } else {
252 secnotice("shutdown", "No securityagent for session %d", this->sessionId());
253 }
254 mSecurityAgent = NULL;
255 }
256
257 void Session::invalidateAuthHosts()
258 {
259 StLock<Mutex> _(mSessionLock);
260 for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++) {
261 it->second->invalidateSessionAuthHosts();
262 }
263 }
264
265 //
266 // On system sleep, call sleepProcessing on all DbCommons of all Sessions
267 //
268 void Session::processSystemSleep()
269 {
270 SecurityAgentXPCQuery::killAllXPCClients();
271
272 StLock<Mutex> _(mSessionLock);
273 for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
274 it->second->allReferences(&DbCommon::sleepProcessing);
275 }
276
277
278 //
279 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
280 //
281 void Session::processLockAll()
282 {
283 allReferences(&DbCommon::lockProcessing);
284 }
285
286
287 //
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.
292 //
293 RootSession::RootSession(uint64_t attributes, Server &server)
294 : Session(AuditInfo::current(), server)
295 {
296 ref(); // eternalize
297 mAudit.ai_flags |= attributes; // merge imposed attributes
298 }
299
300
301 //
302 // Accessor method for setting audit session flags.
303 //
304 void Session::setAttributes(SessionAttributeBits bits)
305 {
306 StLock<Mutex> _(*this);
307 updateAudit();
308 // assert((bits & ~settableAttributes) == 0);
309 mAudit.ai_flags = bits;
310 mAudit.set();
311 }
312
313 //
314 // The default session setup operation always fails.
315 // Subclasses can override this to support session setup calls.
316 //
317 void Session::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
318 {
319 MacOSError::throwMe(errSessionAuthorizationDenied);
320 }
321
322 uid_t Session::originatorUid()
323 {
324 if (mAudit.uid() == AU_DEFAUDITID) {
325 StLock<Mutex> _(*this);
326 updateAudit();
327 }
328 return mAudit.uid();
329 }
330
331
332 RefPointer<AuthHostInstance>
333 Session::authhost(const bool restart)
334 {
335 StLock<Mutex> _(mAuthHostLock);
336
337 if (restart || !mSecurityAgent || (mSecurityAgent->state() != Security::UnixPlusPlus::Child::alive))
338 {
339 if (mSecurityAgent)
340 PerSession::kill(*mSecurityAgent);
341 mSecurityAgent = new AuthHostInstance(*this);
342 }
343 return mSecurityAgent;
344 }
345
346
347 //
348 // Debug dumping
349 //
350 #if defined(DEBUGDUMP)
351
352 void Session::dumpNode()
353 {
354 PerSession::dumpNode();
355 Debug::dump(" auid=%d attrs=%#x securityagent=%p",
356 this->sessionId(), uint32_t(this->attributes()), mSecurityAgent);
357 }
358
359 #endif //DEBUGDUMP