]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
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) | |
fa7225c8 | 67 | : mAudit(audit), mSecurityAgent(NULL), mKeybagState(0) |
d8f41ccd A |
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 | |
79b9da22 | 78 | secnotice("SecServer", "%p Session %d created, uid:%d sessionId:%d", this, this->sessionId(), mAudit.uid(), mAudit.sessionId()); |
d8f41ccd A |
79 | Syslog::notice("Session %d created", this->sessionId()); |
80 | } | |
81 | ||
82 | ||
83 | // | |
84 | // Destroy a Session | |
85 | // | |
86 | Session::~Session() | |
87 | { | |
79b9da22 | 88 | secnotice("SecServer", "%p Session %d destroyed", this, this->sessionId()); |
d8f41ccd A |
89 | Syslog::notice("Session %d destroyed", this->sessionId()); |
90 | } | |
91 | ||
92 | ||
93 | Server &Session::server() const | |
94 | { | |
95 | return parent<Server>(); | |
96 | } | |
97 | ||
d8f41ccd A |
98 | // |
99 | // Locate a session object by session identifier | |
100 | // | |
101 | Session &Session::find(pid_t id, bool create) | |
102 | { | |
6b200bc3 | 103 | if (id == (pid_t)callerSecuritySession) |
d8f41ccd A |
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); | |
fa7225c8 | 116 | RefPointer<Session> session = new Session(info, Server::active()); |
d8f41ccd A |
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 | |
d8f41ccd A |
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); | |
d8f41ccd A |
140 | } |
141 | } | |
142 | ||
143 | if (session.get()) { | |
d8f41ccd A |
144 | session->kill(); |
145 | } | |
146 | } | |
147 | ||
148 | ||
149 | void Session::kill() | |
150 | { | |
151 | StLock<Mutex> _(*this); // do we need to take this so early? | |
79b9da22 | 152 | secnotice("SecServer", "%p killing session %d", this, this->sessionId()); |
d8f41ccd A |
153 | invalidateSessionAuthHosts(); |
154 | ||
d8f41ccd A |
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 | ||
dd5fb164 A |
179 | // Second and third arguments defaults to false |
180 | void Session::verifyKeyStorePassphrase(int32_t retries, bool useForACLFallback, const char *itemname) | |
d8f41ccd A |
181 | { |
182 | QueryKeybagPassphrase keybagQuery(*this, retries); | |
183 | keybagQuery.inferHints(Server::process()); | |
dd5fb164 A |
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 | ||
d8f41ccd A |
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 | { | |
79b9da22 | 219 | service_context_t context = { sessionId(), originatorUid(), *Server::connection().auditToken(), 0 }; |
d8f41ccd A |
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 | |
b54c578e A |
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 | } | |
d8f41ccd | 254 | mSecurityAgent = NULL; |
d8f41ccd A |
255 | } |
256 | ||
257 | void Session::invalidateAuthHosts() | |
258 | { | |
259 | StLock<Mutex> _(mSessionLock); | |
b54c578e | 260 | for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++) { |
d8f41ccd | 261 | it->second->invalidateSessionAuthHosts(); |
b54c578e | 262 | } |
d8f41ccd A |
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 | ||
d8f41ccd A |
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 | ||
d8f41ccd A |
331 | |
332 | RefPointer<AuthHostInstance> | |
fa7225c8 | 333 | Session::authhost(const bool restart) |
d8f41ccd A |
334 | { |
335 | StLock<Mutex> _(mAuthHostLock); | |
336 | ||
fa7225c8 | 337 | if (restart || !mSecurityAgent || (mSecurityAgent->state() != Security::UnixPlusPlus::Child::alive)) |
d8f41ccd | 338 | { |
fa7225c8 A |
339 | if (mSecurityAgent) |
340 | PerSession::kill(*mSecurityAgent); | |
341 | mSecurityAgent = new AuthHostInstance(*this); | |
d8f41ccd | 342 | } |
fa7225c8 | 343 | return mSecurityAgent; |
d8f41ccd A |
344 | } |
345 | ||
346 | ||
347 | // | |
348 | // Debug dumping | |
349 | // | |
350 | #if defined(DEBUGDUMP) | |
351 | ||
352 | void Session::dumpNode() | |
353 | { | |
354 | PerSession::dumpNode(); | |
fa7225c8 A |
355 | Debug::dump(" auid=%d attrs=%#x securityagent=%p", |
356 | this->sessionId(), uint32_t(this->attributes()), mSecurityAgent); | |
d8f41ccd A |
357 | } |
358 | ||
359 | #endif //DEBUGDUMP |