]> git.saurik.com Git - apple/securityd.git/blob - src/session.cpp
securityd-55137.5.tar.gz
[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 SecurityAgent::Clients::killAllClients();
214
215 StLock<Mutex> _(mSessionLock);
216 for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
217 it->second->allReferences(&DbCommon::sleepProcessing);
218 }
219
220
221 //
222 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
223 //
224 void Session::processLockAll()
225 {
226 allReferences(&DbCommon::lockProcessing);
227 }
228
229
230 //
231 // The root session corresponds to the audit session that security is running in.
232 // This is usually the initial system session; but in debug scenarios it may be
233 // an "ordinary" graphic login session. In such a debug case, we may add attribute
234 // flags to the session to make our (debugging) life easier.
235 //
236 RootSession::RootSession(uint64_t attributes, Server &server)
237 : Session(AuditInfo::current(), server)
238 {
239 ref(); // eternalize
240 mAudit.ai_flags |= attributes; // merge imposed attributes
241 }
242
243
244 //
245 // Dynamic sessions use the audit session context of the first-contact client caller.
246 //
247 DynamicSession::DynamicSession(const AuditInfo &audit)
248 : Session(audit, Server::active())
249 {
250 }
251
252
253 //
254 // Authorization operations
255 //
256 OSStatus Session::authCreate(const AuthItemSet &rights,
257 const AuthItemSet &environment,
258 AuthorizationFlags flags,
259 AuthorizationBlob &newHandle,
260 const audit_token_t &auditToken)
261 {
262 // invoke the authorization computation engine
263 CredentialSet resultCreds;
264
265 // this will acquire the object lock, so we delay acquiring it (@@@ no longer needed)
266 auto_ptr<AuthorizationToken> auth(new AuthorizationToken(*this, resultCreds, auditToken, (flags&kAuthorizationFlagLeastPrivileged)));
267
268 SECURITYD_AUTH_CREATE(this, auth.get());
269
270 // Make a copy of the mSessionCreds
271 CredentialSet sessionCreds;
272 {
273 StLock<Mutex> _(mCredsLock);
274 sessionCreds = mSessionCreds;
275 }
276
277 AuthItemSet outRights;
278 OSStatus result = Server::authority().authorize(rights, environment, flags,
279 &sessionCreds, &resultCreds, outRights, *auth);
280 newHandle = auth->handle();
281
282 // merge resulting creds into shared pool
283 if ((flags & kAuthorizationFlagExtendRights) &&
284 !(flags & kAuthorizationFlagDestroyRights))
285 {
286 StLock<Mutex> _(mCredsLock);
287 mergeCredentials(resultCreds);
288 auth->mergeCredentials(resultCreds);
289 }
290
291 // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to
292 // not be destroyed anymore since it's destructor asserts it has no processes
293 Server::process().addAuthorization(auth.get());
294 auth.release();
295 return result;
296 }
297
298 void Session::authFree(const AuthorizationBlob &authBlob, AuthorizationFlags flags)
299 {
300 AuthorizationToken::Deleter deleter(authBlob);
301 AuthorizationToken &auth = deleter;
302 Process &process = Server::process();
303 process.checkAuthorization(&auth);
304
305 if (flags & kAuthorizationFlagDestroyRights) {
306 // explicitly invalidate all shared credentials and remove them from the session
307 for (CredentialSet::const_iterator it = auth.begin(); it != auth.end(); it++)
308 if ((*it)->isShared())
309 (*it)->invalidate();
310 }
311
312 // now get rid of the authorization itself
313 if (process.removeAuthorization(&auth))
314 deleter.remove();
315 }
316
317 OSStatus Session::authGetRights(const AuthorizationBlob &authBlob,
318 const AuthItemSet &rights, const AuthItemSet &environment,
319 AuthorizationFlags flags,
320 AuthItemSet &grantedRights)
321 {
322 AuthorizationToken &auth = authorization(authBlob);
323 return auth.session().authGetRights(auth, rights, environment, flags, grantedRights);
324 }
325
326 OSStatus Session::authGetRights(AuthorizationToken &auth,
327 const AuthItemSet &rights, const AuthItemSet &environment,
328 AuthorizationFlags flags,
329 AuthItemSet &grantedRights)
330 {
331 CredentialSet resultCreds;
332 CredentialSet effective;
333 {
334 StLock<Mutex> _(mCredsLock);
335 effective = auth.effectiveCreds();
336 }
337 OSStatus result = Server::authority().authorize(rights, environment, flags,
338 &effective, &resultCreds, grantedRights, auth);
339
340 // merge resulting creds into shared pool
341 if ((flags & kAuthorizationFlagExtendRights) && !(flags & kAuthorizationFlagDestroyRights))
342 {
343 StLock<Mutex> _(mCredsLock);
344 mergeCredentials(resultCreds);
345 auth.mergeCredentials(resultCreds);
346 }
347
348 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
349 &auth, int(rights.size()), int(grantedRights.size()));
350 return result;
351 }
352
353 OSStatus Session::authGetInfo(const AuthorizationBlob &authBlob,
354 const char *tag,
355 AuthItemSet &contextInfo)
356 {
357 AuthorizationToken &auth = authorization(authBlob);
358 secdebug("SSauth", "Authorization %p get-info", &auth);
359 contextInfo = auth.infoSet(tag);
360 return noErr;
361 }
362
363 OSStatus Session::authExternalize(const AuthorizationBlob &authBlob,
364 AuthorizationExternalForm &extForm)
365 {
366 const AuthorizationToken &auth = authorization(authBlob);
367 StLock<Mutex> _(*this);
368 if (auth.mayExternalize(Server::process())) {
369 memset(&extForm, 0, sizeof(extForm));
370 AuthorizationExternalBlob &extBlob =
371 reinterpret_cast<AuthorizationExternalBlob &>(extForm);
372 extBlob.blob = auth.handle();
373 extBlob.session = this->sessionId();
374 secdebug("SSauth", "Authorization %p externalized", &auth);
375 return noErr;
376 } else
377 return errAuthorizationExternalizeNotAllowed;
378 }
379
380 OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm,
381 AuthorizationBlob &authBlob)
382 {
383 // interpret the external form
384 const AuthorizationExternalBlob &extBlob =
385 reinterpret_cast<const AuthorizationExternalBlob &>(extForm);
386
387 // locate source authorization
388 AuthorizationToken &sourceAuth = AuthorizationToken::find(extBlob.blob);
389
390 // check for permission and do it
391 if (sourceAuth.mayInternalize(Server::process(), true)) {
392 StLock<Mutex> _(*this);
393 authBlob = extBlob.blob;
394 Server::process().addAuthorization(&sourceAuth);
395 secdebug("SSauth", "Authorization %p internalized", &sourceAuth);
396 return noErr;
397 } else
398 return errAuthorizationInternalizeNotAllowed;
399 }
400
401
402 //
403 // Accessor method for setting audit session flags.
404 //
405 void Session::setAttributes(SessionAttributeBits bits)
406 {
407 StLock<Mutex> _(*this);
408 updateAudit();
409 // assert((bits & ~settableAttributes) == 0);
410 mAudit.ai_flags = bits;
411 mAudit.set();
412 }
413
414 //
415 // The default session setup operation always fails.
416 // Subclasses can override this to support session setup calls.
417 //
418 void Session::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
419 {
420 MacOSError::throwMe(errSessionAuthorizationDenied);
421 }
422
423
424 //
425 // Authorization database I/O
426 //
427 OSStatus Session::authorizationdbGet(AuthorizationString inRightName, CFDictionaryRef *rightDict)
428 {
429 string rightName(inRightName);
430 return Server::authority().getRule(rightName, rightDict);
431 }
432
433
434 OSStatus Session::authorizationdbSet(const AuthorizationBlob &authBlob, AuthorizationString inRightName, CFDictionaryRef rightDict)
435 {
436 CredentialSet resultCreds;
437 AuthorizationToken &auth = authorization(authBlob);
438 CredentialSet effective;
439
440 {
441 StLock<Mutex> _(mCredsLock);
442 effective = auth.effectiveCreds();
443 }
444
445 OSStatus result = Server::authority().setRule(inRightName, rightDict, &effective, &resultCreds, auth);
446
447 {
448 StLock<Mutex> _(mCredsLock);
449 mergeCredentials(resultCreds);
450 auth.mergeCredentials(resultCreds);
451 }
452
453 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%d)",
454 &authorization(authBlob), inRightName, int32_t(result));
455 return result;
456 }
457
458
459 OSStatus Session::authorizationdbRemove(const AuthorizationBlob &authBlob, AuthorizationString inRightName)
460 {
461 CredentialSet resultCreds;
462 AuthorizationToken &auth = authorization(authBlob);
463 CredentialSet effective;
464
465 {
466 StLock<Mutex> _(mCredsLock);
467 effective = auth.effectiveCreds();
468 }
469
470 OSStatus result = Server::authority().removeRule(inRightName, &effective, &resultCreds, auth);
471
472 {
473 StLock<Mutex> _(mCredsLock);
474 mergeCredentials(resultCreds);
475 auth.mergeCredentials(resultCreds);
476 }
477
478 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%d)",
479 &authorization(authBlob), inRightName, int32_t(result));
480 return result;
481 }
482
483
484 //
485 // Merge a set of credentials into the shared-session credential pool
486 //
487 // must hold mCredsLock
488 void Session::mergeCredentials(CredentialSet &creds)
489 {
490 secdebug("SSsession", "%p merge creds @%p", this, &creds);
491 CredentialSet updatedCredentials = creds;
492 for (CredentialSet::const_iterator it = creds.begin(); it != creds.end(); it++)
493 if ((*it)->isShared() && (*it)->isValid()) {
494 CredentialSet::iterator old = mSessionCreds.find(*it);
495 if (old == mSessionCreds.end()) {
496 mSessionCreds.insert(*it);
497 } else {
498 // replace "new" with "old" in input set to retain synchronization
499 (*old)->merge(**it);
500 updatedCredentials.erase(*it);
501 updatedCredentials.insert(*old);
502 }
503 }
504 creds.swap(updatedCredentials);
505 }
506
507
508 //
509 // Locate an AuthorizationToken given a blob
510 //
511 AuthorizationToken &Session::authorization(const AuthorizationBlob &blob)
512 {
513 AuthorizationToken &auth = AuthorizationToken::find(blob);
514 Server::process().checkAuthorization(&auth);
515 return auth;
516 }
517
518 //
519 // Run the Authorization engine to check if a given right has been authorized,
520 // independent of an external client request.
521 //
522 OSStatus Session::authCheckRight(string &rightName, Connection &connection, bool allowUI)
523 {
524 // dummy up the arguments for authCreate()
525 AuthorizationItem rightItem = { rightName.c_str(), 0, NULL, 0 };
526 AuthorizationItemSet rightItemSet = { 1, &rightItem };
527 AuthItemSet rightAuthItemSet(&rightItemSet);
528 AuthItemSet envAuthItemSet(kAuthorizationEmptyEnvironment);
529 AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagExtendRights;
530 if (true == allowUI)
531 flags |= kAuthorizationFlagInteractionAllowed;
532 AuthorizationBlob dummyHandle;
533 const audit_token_t *at = connection.auditToken();
534
535 return authCreate(rightAuthItemSet, envAuthItemSet, flags, dummyHandle, *at);
536 }
537
538 // for places within securityd that don't want to #include
539 // <libsecurity_authorization/Authorization.h> or to fuss about exceptions
540 bool Session::isRightAuthorized(string &rightName, Connection &connection, bool allowUI)
541 {
542 bool isAuthorized = false;
543
544 try {
545 OSStatus status = authCheckRight(rightName, connection, allowUI);
546 if (errAuthorizationSuccess == status)
547 isAuthorized = true;
548 }
549 catch (...) {
550 }
551 return isAuthorized;
552 }
553
554 RefPointer<AuthHostInstance>
555 Session::authhost(const AuthHostType hostType, const bool restart)
556 {
557 StLock<Mutex> _(mAuthHostLock);
558
559 if (hostType == privilegedAuthHost)
560 {
561 if (restart || !mAuthHost || (mAuthHost->state() != Security::UnixPlusPlus::Child::alive))
562 {
563 if (mAuthHost)
564 PerSession::kill(*mAuthHost);
565 mAuthHost = new AuthHostInstance(*this, hostType);
566 }
567 return mAuthHost;
568 }
569 else /* if (hostType == securityAgent) */
570 {
571 if (restart || !mSecurityAgent || (mSecurityAgent->state() != Security::UnixPlusPlus::Child::alive))
572 {
573 if (mSecurityAgent)
574 PerSession::kill(*mSecurityAgent);
575 mSecurityAgent = new AuthHostInstance(*this, hostType);
576 }
577 return mSecurityAgent;
578 }
579 }
580
581 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict)
582 {
583 if (Server::process().uid() != 0)
584 MacOSError::throwMe(errSessionAuthorizationDenied);
585 StLock<Mutex> _(*this);
586 mSessionAgentPrefs = userPrefsDict;
587 }
588
589 CFDataRef DynamicSession::copyUserPrefs()
590 {
591 StLock<Mutex> _(*this);
592 if (mSessionAgentPrefs)
593 CFRetain(mSessionAgentPrefs);
594 return mSessionAgentPrefs;
595 }
596
597
598 //
599 // Debug dumping
600 //
601 #if defined(DEBUGDUMP)
602
603 void Session::dumpNode()
604 {
605 PerSession::dumpNode();
606 Debug::dump(" auid=%d attrs=%#x authhost=%p securityagent=%p",
607 this->sessionId(), uint32_t(this->attributes()), mAuthHost, mSecurityAgent);
608 }
609
610 #endif //DEBUGDUMP