/* * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the * specific language governing rights and limitations under the License. */ // // session - authentication session domains // // A Session is defined by a mach_init bootstrap dictionary. These dictionaries are // hierarchical and inherited, so they work well for characterization of processes // that "belong" together. (Of course, if your mach_init is broken, you're in bad shape.) // // Sessions are multi-threaded objects. // #include "session.h" #include "connection.h" #include "server.h" // // The static session map // Session::SessionMap Session::sessionMap; Mutex Session::sessionMapLock; // // Create a Session object from initial parameters (create) // Session::Session(Bootstrap bootstrap, SessionAttributeBits attrs) : mBootstrap(bootstrap), mAttributes(attrs), mProcessCount(0), mAuthCount(0), mDying(false) { debug("SSsession", "%p CREATED: handle=0x%lx bootstrap=%d attrs=0x%lx", this, handle(), mBootstrap.port(), mAttributes); } RootSession::RootSession() : Session(Bootstrap(), sessionIsRoot | sessionWasInitialized) { // self-install sessionMap[mBootstrap] = this; } DynamicSession::DynamicSession(Bootstrap bootstrap) : Session(bootstrap) { Server::active().notifyIfDead(bootstrapPort()); } // // Destroy a Session // Session::~Session() { assert(mProcessCount == 0); // can't die with processes still alive Database::lockAllDatabases(); debug("SSsession", "%p DESTROYED: handle=0x%lx bootstrap=%d", this, handle(), mBootstrap.port()); } // // Retrieve or create a session object // Session &Session::find(Bootstrap bootstrap, bool makeNew) { StLock _(sessionMapLock); Session * &slot = sessionMap[bootstrap]; if (slot == NULL) if (makeNew) slot = new DynamicSession(bootstrap); else Authorization::Error::throwMe(errAuthorizationInvalidRef); return *slot; } Session &Session::find(SecuritySessionId id) { switch (id) { case callerSecuritySession: return Server::connection().process.session; default: return findHandle(id); } } // // Act on a death notification for a session's (sub)bootstrap port. // We may not destroy the Session outright here (due to processes that use it), // but we do clear out its accumulated wealth. // void Session::eliminate(Bootstrap bootstrap) { // remove session from session map StLock _(sessionMapLock); SessionMap::iterator it = sessionMap.find(bootstrap); assert(it != sessionMap.end()); Session *session = it->second; sessionMap.erase(it); // clear resources if (session->clearResources()) delete session; else debug("SSsession", "session %p zombified for %d processes and %d auths", session, int(session->mProcessCount), int(session->mAuthCount)); } bool Session::clearResources() { StLock _(mLock); // this session is now officially dying mDying = true; // invalidate shared credentials IFDEBUG(if (!mSessionCreds.empty()) debug("SSauth", "session %p clearing %d shared credentials", this, int(mSessionCreds.size()))); for (CredentialSet::iterator it = mSessionCreds.begin(); it != mSessionCreds.end(); it++) (*it)->invalidate(); // let the caller know if we are ready to die NOW return mProcessCount == 0 && mAuthCount == 0; } // // Process management // void Session::addProcess(Process *) { StLock _(mLock); mProcessCount++; } bool Session::removeProcess(Process *) { StLock _(mLock); assert(mProcessCount > 0); return --mProcessCount == 0 && mDying && mAuthCount == 0; } // // Authorization retention management. // void Session::addAuthorization(AuthorizationToken *) { StLock _(mLock); mAuthCount++; } bool Session::removeAuthorization(AuthorizationToken *) { StLock _(mLock); assert(mAuthCount > 0); return --mAuthCount == 0 && mDying && mProcessCount == 0; } // // Authorization operations // OSStatus Session::authCreate(const RightSet &rights, const AuthorizationEnvironment *environment, AuthorizationFlags flags, AuthorizationBlob &newHandle) { // invoke the authorization computation engine CredentialSet resultCreds; // this will acquire mLock, so we delay acquiring it auto_ptr auth(new AuthorizationToken(*this, resultCreds)); OSStatus result = Server::authority().authorize(rights, environment, flags, &mSessionCreds, &resultCreds, NULL, *auth); newHandle = auth->handle(); { StLock _(mLock); // merge resulting creds into shared pool if ((flags & kAuthorizationFlagExtendRights) && !(flags & kAuthorizationFlagDestroyRights)) { mergeCredentials(resultCreds); auth->mergeCredentials(resultCreds); } } // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to // not be destroyed anymore since it's destructor asserts it has no processes Server::connection().process.addAuthorization(auth.get()); auth.release(); return result; } void Session::authFree(const AuthorizationBlob &authBlob, AuthorizationFlags flags) { AuthorizationToken::Deleter deleter(authBlob); AuthorizationToken &auth = deleter; if (flags & kAuthorizationFlagDestroyRights) { // explicitly invalidate all shared credentials and remove them from the session for (CredentialSet::const_iterator it = auth.begin(); it != auth.end(); it++) if ((*it)->isShared()) (*it)->invalidate(); } // now get rid of the authorization itself if (Server::connection().process.removeAuthorization(&auth)) deleter.remove(); } OSStatus Session::authGetRights(const AuthorizationBlob &authBlob, const RightSet &rights, const AuthorizationEnvironment *environment, AuthorizationFlags flags, MutableRightSet &grantedRights) { StLock _(mLock); CredentialSet resultCreds; AuthorizationToken &auth = authorization(authBlob); CredentialSet effective = auth.effectiveCreds(); OSStatus result = Server::authority().authorize(rights, environment, flags, &effective, &resultCreds, &grantedRights, auth); // merge resulting creds into shared pool if ((flags & kAuthorizationFlagExtendRights) && !(flags & kAuthorizationFlagDestroyRights)) { mergeCredentials(resultCreds); auth.mergeCredentials(resultCreds); } IFDEBUG(debug("SSauth", "Authorization %p copyRights asked for %d got %d", &authorization(authBlob), int(rights.size()), int(grantedRights.size()))); return result; } OSStatus Session::authGetInfo(const AuthorizationBlob &authBlob, const char *tag, MutableRightSet &grantedRights) { StLock _(mLock); AuthorizationToken &auth = authorization(authBlob); debug("SSauth", "Authorization %p get-info not implemented", &auth); if (tag) { // no such tag (no info support) return errAuthorizationInvalidTag; } else { // return no tags (no info support) grantedRights = RightSet(); // return no entries return noErr; } } OSStatus Session::authExternalize(const AuthorizationBlob &authBlob, AuthorizationExternalForm &extForm) { StLock _(mLock); const AuthorizationToken &auth = authorization(authBlob); if (auth.mayExternalize(Server::connection().process)) { memset(&extForm, 0, sizeof(extForm)); AuthorizationExternalBlob &extBlob = reinterpret_cast(extForm); extBlob.blob = auth.handle(); extBlob.session = bootstrapPort(); debug("SSauth", "Authorization %p externalized", &auth); return noErr; } else return errAuthorizationExternalizeNotAllowed; } OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm, AuthorizationBlob &authBlob) { StLock _(mLock); // interpret the external form const AuthorizationExternalBlob &extBlob = reinterpret_cast(extForm); // locate source authorization AuthorizationToken &sourceAuth = AuthorizationToken::find(extBlob.blob); // check for permission and do it if (sourceAuth.mayInternalize(Server::connection().process, true)) { authBlob = extBlob.blob; Server::connection().process.addAuthorization(&sourceAuth); mAuthCount++; debug("SSauth", "Authorization %p internalized", &sourceAuth); return noErr; } else return errAuthorizationInternalizeNotAllowed; } // // Set up a (new-ish) Session. // This call must be made from a process within the session, and it must be the first // such process to make the call. // void Session::setup(SessionCreationFlags flags, SessionAttributeBits attrs) { // check current process object - it may have been cached before the client's bootstrap switch Process *process = &Server::connection().process; if (process->taskPort().bootstrap() != process->session.bootstrapPort()) process = Server::active().resetConnection(); process->session.setupAttributes(attrs); } void Session::setupAttributes(SessionAttributeBits attrs) { debug("SSsession", "%p setup attrs=0x%lx", this, attrs); if (attrs & ~settableAttributes) MacOSError::throwMe(errSessionInvalidAttributes); if (attribute(sessionWasInitialized)) MacOSError::throwMe(errSessionAuthorizationDenied); setAttributes(attrs | sessionWasInitialized); } // // Merge a set of credentials into the shared-session credential pool // void Session::mergeCredentials(CredentialSet &creds) { for (CredentialSet::const_iterator it = creds.begin(); it != creds.end(); it++) if (((*it)->isShared() && (*it)->isValid())) { CredentialSet::iterator old = mSessionCreds.find(*it); if (old == mSessionCreds.end()) { mSessionCreds.insert(*it); } else { // replace "new" with "old" in input set to retain synchronization (*old)->merge(**it); creds.erase(it); creds.insert(*old); } } } // // Locate an AuthorizationToken given a blob // AuthorizationToken &Session::authorization(const AuthorizationBlob &blob) { return AuthorizationToken::find(blob); }