]>
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 | |
fa7225c8 | 78 | secnotice("SS", "%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 | { | |
fa7225c8 | 88 | secnotice("SS", "%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 | ||
98 | ||
99 | // | |
100 | // Locate a session object by session identifier | |
101 | // | |
102 | Session &Session::find(pid_t id, bool create) | |
103 | { | |
6b200bc3 | 104 | if (id == (pid_t)callerSecuritySession) |
d8f41ccd A |
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); | |
fa7225c8 | 117 | RefPointer<Session> session = new Session(info, Server::active()); |
d8f41ccd A |
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 | |
d8f41ccd A |
133 | RefPointer<Session> session = NULL; |
134 | { | |
135 | StLock<Mutex> _(mSessionLock); | |
136 | SessionMap::iterator it = mSessions.find(id); | |
137 | if (it != mSessions.end()) { | |
138 | session = it->second; | |
139 | assert(session->sessionId() == id); | |
140 | mSessions.erase(it); | |
d8f41ccd A |
141 | } |
142 | } | |
143 | ||
144 | if (session.get()) { | |
d8f41ccd A |
145 | session->kill(); |
146 | } | |
147 | } | |
148 | ||
149 | ||
150 | void Session::kill() | |
151 | { | |
152 | StLock<Mutex> _(*this); // do we need to take this so early? | |
fa7225c8 | 153 | secnotice("SS", "%p killing session %d", this, this->sessionId()); |
d8f41ccd A |
154 | invalidateSessionAuthHosts(); |
155 | ||
d8f41ccd A |
156 | // base kill processing |
157 | PerSession::kill(); | |
158 | } | |
159 | ||
160 | ||
161 | // | |
162 | // Refetch audit session data for the current audit session (to catch outside updates | |
163 | // to the audit record). This is the price we're paying for not requiring an IPC to | |
164 | // securityd when audit session data changes (this is desirable for delayering the | |
165 | // software layer cake). | |
166 | // If we ever disallow changes to (parts of the) audit session record in the kernel, | |
167 | // we can loosen up on this continual re-fetching. | |
168 | // | |
169 | void Session::updateAudit() const | |
170 | { | |
171 | CommonCriteria::AuditInfo info; | |
172 | try { | |
173 | info.get(mAudit.sessionId()); | |
174 | } catch (...) { | |
175 | return; | |
176 | } | |
177 | mAudit = info; | |
178 | } | |
179 | ||
dd5fb164 A |
180 | // Second and third arguments defaults to false |
181 | void Session::verifyKeyStorePassphrase(int32_t retries, bool useForACLFallback, const char *itemname) | |
d8f41ccd A |
182 | { |
183 | QueryKeybagPassphrase keybagQuery(*this, retries); | |
184 | keybagQuery.inferHints(Server::process()); | |
dd5fb164 A |
185 | |
186 | // Parasitic takeover to enable user confirmation when ACL validation ends up without a database | |
187 | if (useForACLFallback) { | |
188 | keybagQuery.addHint("acl-fallback", &useForACLFallback, sizeof(useForACLFallback)); | |
189 | keybagQuery.addHint("keychain-item-name", itemname, itemname ? (uint32_t)strlen(itemname) : 0, 0); | |
190 | } | |
191 | ||
d8f41ccd A |
192 | if (keybagQuery.query() != SecurityAgent::noReason) { |
193 | CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED); | |
194 | } | |
195 | } | |
196 | ||
197 | void Session::changeKeyStorePassphrase() | |
198 | { | |
199 | service_context_t context = get_current_service_context(); | |
200 | QueryKeybagNewPassphrase keybagQuery(*this); | |
201 | keybagQuery.inferHints(Server::process()); | |
202 | CssmAutoData pass(Allocator::standard(Allocator::sensitive)); | |
203 | CssmAutoData oldPass(Allocator::standard(Allocator::sensitive)); | |
204 | SecurityAgent::Reason queryReason = keybagQuery.query(oldPass, pass); | |
205 | if (queryReason == SecurityAgent::noReason) { | |
206 | service_client_kb_change_secret(&context, oldPass.data(), (int)oldPass.length(), pass.data(), (int)pass.length()); | |
207 | } else { | |
208 | CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED); | |
209 | } | |
210 | } | |
211 | ||
212 | void Session::resetKeyStorePassphrase(const CssmData &passphrase) | |
213 | { | |
214 | service_context_t context = get_current_service_context(); | |
215 | service_client_kb_reset(&context, passphrase.data(), (int)passphrase.length()); | |
216 | } | |
217 | ||
218 | service_context_t Session::get_current_service_context() | |
219 | { | |
5c19dc3a | 220 | service_context_t context = { sessionId(), originatorUid(), *Server::connection().auditToken() }; |
d8f41ccd A |
221 | return context; |
222 | } | |
223 | ||
224 | void Session::keybagClearState(int state) | |
225 | { | |
226 | mKeybagState &= ~state; | |
227 | } | |
228 | ||
229 | void Session::keybagSetState(int state) | |
230 | { | |
231 | mKeybagState |= state; | |
232 | } | |
233 | ||
234 | bool Session::keybagGetState(int state) | |
235 | { | |
236 | return mKeybagState & state; | |
237 | } | |
238 | ||
239 | ||
240 | // | |
241 | // Manage authorization client processes | |
242 | // | |
243 | void Session::invalidateSessionAuthHosts() | |
244 | { | |
245 | StLock<Mutex> _(mAuthHostLock); | |
246 | ||
247 | // if you got here, we don't care about pending operations: the auth hosts die | |
248 | Syslog::warning("Killing auth hosts"); | |
249 | if (mSecurityAgent) mSecurityAgent->UnixPlusPlus::Child::kill(SIGTERM); | |
d8f41ccd | 250 | mSecurityAgent = NULL; |
d8f41ccd A |
251 | } |
252 | ||
253 | void Session::invalidateAuthHosts() | |
254 | { | |
255 | StLock<Mutex> _(mSessionLock); | |
256 | for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++) | |
257 | it->second->invalidateSessionAuthHosts(); | |
258 | } | |
259 | ||
260 | // | |
261 | // On system sleep, call sleepProcessing on all DbCommons of all Sessions | |
262 | // | |
263 | void Session::processSystemSleep() | |
264 | { | |
265 | SecurityAgentXPCQuery::killAllXPCClients(); | |
266 | ||
267 | StLock<Mutex> _(mSessionLock); | |
268 | for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++) | |
269 | it->second->allReferences(&DbCommon::sleepProcessing); | |
270 | } | |
271 | ||
272 | ||
273 | // | |
274 | // On "lockAll", call sleepProcessing on all DbCommons of this session (only) | |
275 | // | |
276 | void Session::processLockAll() | |
277 | { | |
278 | allReferences(&DbCommon::lockProcessing); | |
279 | } | |
280 | ||
281 | ||
282 | // | |
283 | // The root session corresponds to the audit session that security is running in. | |
284 | // This is usually the initial system session; but in debug scenarios it may be | |
285 | // an "ordinary" graphic login session. In such a debug case, we may add attribute | |
286 | // flags to the session to make our (debugging) life easier. | |
287 | // | |
288 | RootSession::RootSession(uint64_t attributes, Server &server) | |
289 | : Session(AuditInfo::current(), server) | |
290 | { | |
291 | ref(); // eternalize | |
292 | mAudit.ai_flags |= attributes; // merge imposed attributes | |
293 | } | |
294 | ||
295 | ||
d8f41ccd A |
296 | // |
297 | // Accessor method for setting audit session flags. | |
298 | // | |
299 | void Session::setAttributes(SessionAttributeBits bits) | |
300 | { | |
301 | StLock<Mutex> _(*this); | |
302 | updateAudit(); | |
303 | // assert((bits & ~settableAttributes) == 0); | |
304 | mAudit.ai_flags = bits; | |
305 | mAudit.set(); | |
306 | } | |
307 | ||
308 | // | |
309 | // The default session setup operation always fails. | |
310 | // Subclasses can override this to support session setup calls. | |
311 | // | |
312 | void Session::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs) | |
313 | { | |
314 | MacOSError::throwMe(errSessionAuthorizationDenied); | |
315 | } | |
316 | ||
317 | uid_t Session::originatorUid() | |
318 | { | |
319 | if (mAudit.uid() == AU_DEFAUDITID) { | |
320 | StLock<Mutex> _(*this); | |
321 | updateAudit(); | |
322 | } | |
323 | return mAudit.uid(); | |
324 | } | |
325 | ||
d8f41ccd A |
326 | |
327 | RefPointer<AuthHostInstance> | |
fa7225c8 | 328 | Session::authhost(const bool restart) |
d8f41ccd A |
329 | { |
330 | StLock<Mutex> _(mAuthHostLock); | |
331 | ||
fa7225c8 | 332 | if (restart || !mSecurityAgent || (mSecurityAgent->state() != Security::UnixPlusPlus::Child::alive)) |
d8f41ccd | 333 | { |
fa7225c8 A |
334 | if (mSecurityAgent) |
335 | PerSession::kill(*mSecurityAgent); | |
336 | mSecurityAgent = new AuthHostInstance(*this); | |
d8f41ccd | 337 | } |
fa7225c8 | 338 | return mSecurityAgent; |
d8f41ccd A |
339 | } |
340 | ||
341 | ||
342 | // | |
343 | // Debug dumping | |
344 | // | |
345 | #if defined(DEBUGDUMP) | |
346 | ||
347 | void Session::dumpNode() | |
348 | { | |
349 | PerSession::dumpNode(); | |
fa7225c8 A |
350 | Debug::dump(" auid=%d attrs=%#x securityagent=%p", |
351 | this->sessionId(), uint32_t(this->attributes()), mSecurityAgent); | |
d8f41ccd A |
352 | } |
353 | ||
354 | #endif //DEBUGDUMP |