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