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