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