/* * 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. */ // // process - track a single client process and its belongings // #include "process.h" #include "server.h" #include "session.h" #include "authority.h" #include "flippers.h" // // Construct a Process object. // Process::Process(Port servicePort, TaskPort taskPort, const ClientSetupInfo *info, const char *identity, uid_t uid, gid_t gid) : session(Session::find(servicePort)), mBusyCount(0), mDying(false), mTaskPort(taskPort), mByteFlipped(false), mUid(uid), mGid(gid), mClientIdent(deferred) { // examine info passed assert(info); uint32 pversion = info->version; if (pversion == SSPROTOVERSION) { // correct protocol, same byte order, cool } else { Flippers::flip(pversion); if (pversion == SSPROTOVERSION) { // correct protocol, reversed byte order mByteFlipped = true; } else { // unsupported protocol version CssmError::throwMe(CSSM_ERRCODE_INCOMPATIBLE_VERSION); } } // let's take a look at our wannabe client... mPid = mTaskPort.pid(); // register with the session session.addProcess(this); secdebug("SS", "New process %p(%d) uid=%d gid=%d session=%p TP=%d %sfor %s", this, mPid, mUid, mGid, &session, mTaskPort.port(), mByteFlipped ? "FLIP " : "", (identity && identity[0]) ? identity : "(unknown)"); try { mClientCode = CodeSigning::OSXCode::decode(identity); } catch (...) { secdebug("SS", "process %p(%d) identity decode threw exception", this, pid()); } if (!mClientCode) { mClientIdent = unknown; // no chance to squeeze a code identity from this secdebug("SS", "process %p(%d) no clientCode - marked anonymous", this, pid()); } } Process::~Process() { assert(mBusyCount == 0); // mustn't die with Connections referencing us // tell all our authorizations that we're gone IFDEBUG(if (!mAuthorizations.empty()) secdebug("SS", "Process %p(%d) clearing %d authorizations", this, mPid, int(mAuthorizations.size()))); for (AuthorizationSet::iterator it = mAuthorizations.begin(); it != mAuthorizations.end(); ) { AuthorizationToken *auth = *it; while (++it != mAuthorizations.end() && *it == auth) ; // Skip duplicates if (auth->endProcess(*this)) delete auth; } // remove all database handles that belong to this process IFDEBUG(if (!mDatabases.empty()) secdebug("SS", "Process %p(%d) clearing %d database handles", this, mPid, int(mDatabases.size()))); for (DatabaseSet::iterator it = mDatabases.begin(); it != mDatabases.end(); it++) delete *it; // no need to lock here; the client process has no more active threads secdebug("SS", "Process %p(%d) has died", this, mPid); // release our name for the process's task port if (mTaskPort) mTaskPort.destroy(); // deregister from session if (session.removeProcess(this)) delete &session; } bool Process::kill(bool keepTaskPort) { StLock _(mLock); if (keepTaskPort) mTaskPort = Port(); // clear port so we don't destroy it later if (mBusyCount == 0) { return true; // destroy me now } else { secdebug("SS", "Process %p(%d) destruction deferred for %d busy connections", this, mPid, int(mBusyCount)); mDying = true; return false; // destroy me later } } // // Connection management // void Process::beginConnection(Connection &) { StLock _(mLock); mBusyCount++; } bool Process::endConnection(Connection &) { StLock _(mLock); return --mBusyCount == 0 && mDying; } // // Database management // void Process::addDatabase(Database *database) { StLock _(mLock); mDatabases.insert(database); } void Process::removeDatabase(Database *database) { StLock _(mLock); assert(mDatabases.find(database) != mDatabases.end()); mDatabases.erase(database); } // // CodeSignatures implementation of Identity. // The caller must make sure we have a valid (not necessarily hash-able) clientCode(). // string Process::getPath() const { assert(mClientCode); return mClientCode->canonicalPath(); } const CssmData Process::getHash(CodeSigning::OSXSigner &signer) const { switch (mClientIdent) { case deferred: try { // try to calculate our signature hash (first time use) mCachedSignature.reset(mClientCode->sign(signer)); assert(mCachedSignature.get()); mClientIdent = known; secdebug("SS", "process %p(%d) code signature computed", this, pid()); break; } catch (...) { // couldn't get client signature (unreadable, gone, hack attack, ...) mClientIdent = unknown; secdebug("SS", "process %p(%d) no code signature - anonymous", this, pid()); CssmError::throwMe(CSSM_ERRCODE_INSUFFICIENT_CLIENT_IDENTIFICATION); } case known: assert(mCachedSignature.get()); break; case unknown: CssmError::throwMe(CSSM_ERRCODE_INSUFFICIENT_CLIENT_IDENTIFICATION); } return CssmData(*mCachedSignature); } // // Authorization set maintainance // void Process::addAuthorization(AuthorizationToken *auth) { assert(auth); StLock _(mLock); mAuthorizations.insert(auth); auth->addProcess(*this); } void Process::checkAuthorization(AuthorizationToken *auth) { assert(auth); StLock _(mLock); if (mAuthorizations.find(auth) == mAuthorizations.end()) MacOSError::throwMe(errAuthorizationInvalidRef); } bool Process::removeAuthorization(AuthorizationToken *auth) { assert(auth); StLock _(mLock); // we do everything with a single set lookup call... typedef AuthorizationSet::iterator Iter; Iter it = mAuthorizations.lower_bound(auth); bool isLast; if (it == mAuthorizations.end() || auth != *it) { Syslog::error("process is missing authorization to remove"); // temp. diagnostic isLast = true; } else { Iter next = it; ++next; // following element isLast = (next == mAuthorizations.end()) || auth != *next; mAuthorizations.erase(it); // remove first match } if (isLast) { if (auth->endProcess(*this)) // ... tell it to remove us, return true; // ... and tell the caller } return false; // keep the auth; it's still in use } // // Notification client maintainance // void Process::requestNotifications(Port port, Listener::Domain domain, Listener::EventMask events) { new Listener(*this, port, domain, events); } void Process::stopNotifications(Port port) { if (!Listener::remove(port)) CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE); //@@@ bad name (should be "no such callback") } void Process::postNotification(Listener::Domain domain, Listener::Event event, const CssmData &data) { Listener::notify(domain, event, data); }