X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/libsecurity_mds/lib/MDSSession.cpp diff --git a/libsecurity_mds/lib/MDSSession.cpp b/libsecurity_mds/lib/MDSSession.cpp deleted file mode 100644 index 2c19f9b3..00000000 --- a/libsecurity_mds/lib/MDSSession.cpp +++ /dev/null @@ -1,1867 +0,0 @@ -/* - * 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. - */ - - -#include "MDSSession.h" - -#include -#include "MDSModule.h" -#include "MDSAttrParser.h" -#include "MDSAttrUtils.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace CssmClient; - -/* - * The layout of the various MDS DB files on disk is as follows: - * - * /var/db/mds -- owner = root, mode = 01777, world writable, sticky - * system/ -- owner = root, mode = 0755 - * mdsObject.db -- owner = root, mode = 0644, object DB - * mdsDirectory.db -- owner = root, mode = 0644, MDS directory DB - * mds.lock -- temporary, owner = root, protects creation of and - * updates to previous two files - * mds.install.lock -- owner = root, protects MDS_Install operation - * / -- owner = , mode = 0700 - * mdsObject.db -- owner = , mode = 000, object DB - * mdsDirectory.db -- owner = , mode = 000, MDS directory DB - * mds.lock -- owner = , protects updates of previous two files - * - * The /var/db/mds/system directory is created at OS install time. The DB files in - * it are created by root at MDS_Install time. The ownership and mode of this directory and - * of its parent is also re-checked and forced to the correct state at MDS_Install time. - * Each user has their own private directory with two DB files and a lock. The first time - * a user accesses MDS, the per-user directory is created and the per-user DB files are - * created as copies of the system DB files. Fcntl() with a F_RDLCK is used to lock the system - * DB files when they are the source of these copies; this is the same mechanism - * used by the underlying AtomicFile. - * - * The sticky bit in /var/db/mds ensures that users cannot modify other userss private - * MDS directories. - */ -namespace Security -{ - -/* - * Nominal location of Security.framework. - */ -#define MDS_SYSTEM_PATH "/System/Library/Frameworks" -#define MDS_SYSTEM_FRAME "Security.framework" - -/* - * Nominal location of standard plugins. - */ -#define MDS_BUNDLE_PATH "/System/Library/Security" -#define MDS_BUNDLE_EXTEN ".bundle" - - -/* - * Location of MDS database and lock files. - */ -#define MDS_BASE_DB_DIR "/private/var/db/mds" -#define MDS_SYSTEM_DB_COMP "system" -#define MDS_SYSTEM_DB_DIR MDS_BASE_DB_DIR "/" MDS_SYSTEM_DB_COMP -#define MDS_USER_DB_COMP "mds" - -#define MDS_LOCK_FILE_NAME "mds.lock" -#define MDS_INSTALL_LOCK_NAME "mds.install.lock" -#define MDS_OBJECT_DB_NAME "mdsObject.db" -#define MDS_DIRECT_DB_NAME "mdsDirectory.db" - -#define MDS_INSTALL_LOCK_PATH MDS_SYSTEM_DB_DIR "/" MDS_INSTALL_LOCK_NAME -#define MDS_OBJECT_DB_PATH MDS_SYSTEM_DB_DIR "/" MDS_OBJECT_DB_NAME -#define MDS_DIRECT_DB_PATH MDS_SYSTEM_DB_DIR "/" MDS_DIRECT_DB_NAME - -/* hard coded modes and a symbolic UID for root */ -#define MDS_BASE_DB_DIR_MODE (mode_t)0755 -#define MDS_SYSTEM_DB_DIR_MODE (mode_t)0755 -#define MDS_SYSTEM_DB_MODE (mode_t)0644 -#define MDS_USER_DB_DIR_MODE (mode_t)0700 -#define MDS_USER_DB_MODE (mode_t)0600 -#define MDS_SYSTEM_UID (uid_t)0 - -/* - * Location of per-user bundles, relative to home directory. - * Per-user DB files are in MDS_BASE_DB_DIR//. - */ -#define MDS_USER_BUNDLE "Library/Security" - -/* time to wait in ms trying to acquire lock */ -#define DB_LOCK_TIMEOUT (2 * 1000) - -/* Minimum interval, in seconds, between rescans for plugin changes */ -#define MDS_SCAN_INTERVAL 5 - -/* trace file I/O */ -#define MSIoDbg(args...) secdebug("MDS_IO", ## args) - -/* Trace cleanDir() */ -#define MSCleanDirDbg(args...) secdebug("MDS_CleanDir", ## args) - -static std::string GetMDSBaseDBDir(bool isRoot) -{ - // what we return depends on whether or not we are root - string retValue; - if (isRoot) - { - retValue = MDS_SYSTEM_DB_DIR; - } - else - { - char strBuffer[PATH_MAX + 1]; - size_t result = confstr(_CS_DARWIN_USER_CACHE_DIR, strBuffer, sizeof(strBuffer)); - if (result == 0) - { - // we have an error, log it - syslog(LOG_CRIT, "confstr on _CS_DARWIN_USER_CACHE_DIR returned an error."); - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); - } - - retValue = strBuffer; - } - - return retValue; -} - - - -static std::string GetMDSDBDir() -{ - string retValue; - bool isRoot = geteuid() == 0; - - if (isRoot) - { - retValue = MDS_SYSTEM_DB_DIR; - } - else - { - retValue = GetMDSBaseDBDir(isRoot) + "/" + MDS_USER_DB_COMP; - } - - return retValue; -} - - - -static std::string GetMDSObjectDBPath() -{ - return GetMDSDBDir() + "/" + MDS_OBJECT_DB_NAME; -} - - - -static std::string GetMDSDirectDBPath() -{ - return GetMDSDBDir() + "/" + MDS_DIRECT_DB_PATH; -} - - - -static std::string GetMDSDBLockPath() -{ - return GetMDSDBDir() + "/" + MDS_LOCK_FILE_NAME; -} - - - -/* - * Given a path to a directory, remove everything in the directory except for the optional - * keepFileNames. Returns 0 on success, else an errno. - */ -static int cleanDir( - const char *dirPath, - const char **keepFileNames, // array of strings, size numKeepFileNames - unsigned numKeepFileNames) -{ - DIR *dirp; - struct dirent *dp; - char fullPath[MAXPATHLEN]; - int rtn = 0; - - MSCleanDirDbg("cleanDir(%s) top", dirPath); - if ((dirp = opendir(dirPath)) == NULL) { - rtn = errno; - MSCleanDirDbg("opendir(%s) returned %d", dirPath, rtn); - return rtn; - } - - for(;;) { - bool skip = false; - const char *d_name = NULL; - - /* this block is for breaking on unqualified entries */ - do { - errno = 0; - dp = readdir(dirp); - if(dp == NULL) { - /* end of directory or error */ - rtn = errno; - if(rtn) { - MSCleanDirDbg("cleanDir(%s): readdir err %d", dirPath, rtn); - } - break; - } - d_name = dp->d_name; - - /* skip "." and ".." */ - if( (d_name[0] == '.') && - ( (d_name[1] == '\0') || - ( (d_name[1] == '.') && (d_name[2] == '\0') ) ) ) { - skip = true; - break; - } - - /* skip entries in keepFileNames */ - for(unsigned dex=0; dexd_type == DT_DIR) { - /* directory. Clean it, then delete. */ - MSCleanDirDbg("cleanDir recursing for dir %s", fullPath); - rtn = cleanDir(fullPath, NULL, 0); - if(rtn) { - break; - } - MSCleanDirDbg("cleanDir deleting dir %s", fullPath); - if(rmdir(fullPath)) { - rtn = errno; - MSCleanDirDbg("unlink(%s) returned %d", fullPath, rtn); - break; - } - } - else { - MSCleanDirDbg("cleanDir deleting file %s", fullPath); - if(unlink(fullPath)) { - rtn = errno; - MSCleanDirDbg("unlink(%s) returned %d", fullPath, rtn); - break; - } - } - - /* - * Back to beginning of directory for clean scan. - * Normally we'd just do a rewinddir() here but the RAMDisk filesystem, - * used when booting from DVD, does not implement that properly. - */ - closedir(dirp); - if ((dirp = opendir(dirPath)) == NULL) { - rtn = errno; - MSCleanDirDbg("opendir(%s) returned %d", dirPath, rtn); - return rtn; - } - } /* main loop */ - - closedir(dirp); - return rtn; -} - -/* - * Determine if a file exists as regular file with specified owner. Returns true if so. - * If the purge argument is true, and there is something at the specified path that - * doesn't meet spec, we do everything we can to delete it. If that fails we throw - * CssmError(CSSM_ERRCODE_MDS_ERROR). If the delete succeeds we return false. - * Returns the stat info on success for further processing by caller. - */ -static bool doesFileExist( - const char *filePath, - uid_t forUid, - bool purge, - struct stat &sb) // RETURNED -{ - MSIoDbg("stat %s in doesFileExist", filePath); - if(lstat(filePath, &sb)) { - /* doesn't exist or we can't even get to it. */ - if(errno == ENOENT) { - return false; - } - if(purge) { - /* If we can't stat it we sure can't delete it. */ - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); - } - return false; - } - - /* it's there...how does it look? */ - mode_t fileType = sb.st_mode & S_IFMT; - if((fileType == S_IFREG) && (sb.st_uid == forUid)) { - return true; - } - if(!purge) { - return false; - } - - /* not what we want: get rid of it. */ - if(fileType == S_IFDIR) { - /* directory: clean then remove */ - if(cleanDir(filePath, NULL, 0)) { - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); - } - if(rmdir(filePath)) { - MSDebug("rmdir(%s) returned %d", filePath, errno); - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); - } - } - else { - if(unlink(filePath)) { - MSDebug("unlink(%s) returned %d", filePath, errno); - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); - } - } - - /* caller should be somewhat happy */ - return false; -} - -/* - * Determine if both of the specified DB files exist as accessible regular files with specified - * owner. Returns true if they do. - * - * If the purge argument is true, we'll ensure that either both files exist with - * the right owner, or neither of the files exist on exit. An error on that operation - * throws a CSSM_ERRCODE_MDS_ERROR CssmError exception (i.e., we're hosed). - * Returns the stat info for both files on success for further processing by caller. - */ -static bool doFilesExist( - const char *objDbFile, - const char *directDbFile, - uid_t forUid, - bool purge, // false means "passive" check - struct stat &objDbSb, // RETURNED - struct stat &directDbSb) // RETURNED - -{ - bool objectExist = doesFileExist(objDbFile, forUid, purge, objDbSb); - bool directExist = doesFileExist(directDbFile, forUid, purge, directDbSb); - if(objectExist && directExist) { - return true; - } - else if(!purge) { - return false; - } - - /* - * At least one does not exist - ensure neither of them do. - * Note that if we got this far, we know the one that exists is a regular file - * so it's safe to just unlink it. - */ - if(objectExist) { - if(unlink(objDbFile)) { - MSDebug("unlink(%s) returned %d", objDbFile, errno); - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); - } - } - if(directExist) { - if(unlink(directDbFile)) { - MSDebug("unlink(%s) returned %d", directDbFile, errno); - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); - } - } - return false; -} - -/* - * Determine if specified directory exists with specified owner and mode. - * Returns true if copacetic, else returns false and also indicates - * via the directStatus out param what went wrong. - */ -typedef enum { - MDS_NotPresent, /* nothing there */ - MDS_NotDirectory, /* not a directory */ - MDS_BadOwnerMode, /* wrong owner or mode */ - MDS_Access /* couldn't search the directories */ -} MdsDirectStatus; - -static bool doesDirectExist( - const char *dirPath, - uid_t forUid, - mode_t mode, - MdsDirectStatus &directStatus) /* RETURNED */ -{ - struct stat sb; - - MSIoDbg("stat %s in doesDirectExist", dirPath); - if (lstat(dirPath, &sb)) { - int err = errno; - switch(err) { - case EACCES: - directStatus = MDS_Access; - break; - case ENOENT: - directStatus = MDS_NotPresent; - break; - /* Any others? Is this a good SWAG to handle the default? */ - default: - directStatus = MDS_NotDirectory; - break; - } - return false; - } - mode_t fileType = sb.st_mode & S_IFMT; - if(fileType != S_IFDIR) { - directStatus = MDS_NotDirectory; - return false; - } - if(sb.st_uid != forUid) { - directStatus = MDS_BadOwnerMode; - return false; - } - if((sb.st_mode & 07777) != mode) { - directStatus = MDS_BadOwnerMode; - return false; - } - return true; -} - -/* - * Create specified directory if it doesn't already exist. If there is something - * already there that doesn't meet spec (not a directory, wrong mode, wrong owner) - * we'll do everything we can do delete what is there and then try to create it - * correctly. - * - * Returns an errno on any unrecoverable error. - */ -static int createDir( - const char *dirPath, - uid_t forUid, // for checking - we don't try to set this - mode_t dirMode) -{ - MdsDirectStatus directStatus; - - if(doesDirectExist(dirPath, forUid, dirMode, directStatus)) { - /* we're done */ - return 0; - } - - /* - * Attempt recovery if there is *something* there. - * Anything other than "not present" should be considered to be a possible - * attack; syslog it. - */ - int rtn; - switch(directStatus) { - case MDS_NotPresent: - /* normal trivial case: proceed. */ - break; - - case MDS_NotDirectory: - /* there's a file or symlink in the way */ - if(unlink(dirPath)) { - rtn = errno; - MSDebug("createDir(%s): unlink() returned %d", dirPath, rtn); - return rtn; - } - break; - - case MDS_BadOwnerMode: - /* - * It's a directory; try to clean it out (which may well fail if we're - * not root). - */ - rtn = cleanDir(dirPath, NULL, 0); - if(rtn) { - return rtn; - } - if(rmdir(dirPath)) { - rtn = errno; - MSDebug("createDir(%s): rmdir() returned %d", dirPath, rtn); - return rtn; - } - /* good to go */ - break; - - case MDS_Access: /* hopeless */ - MSDebug("createDir(%s): access failure, bailing", dirPath); - return EACCES; - } - rtn = mkdir(dirPath, dirMode); - if(rtn) { - rtn = errno; - MSDebug("createDir(%s): mkdir() returned %d", dirPath, errno); - } - else { - /* make sure umask does't trick us */ - rtn = chmod(dirPath, dirMode); - if(rtn) { - MSDebug("chmod(%s) returned %d", dirPath, errno); - } - } - return rtn; -} - -/* - * Create an MDS session. - */ -MDSSession::MDSSession (const Guid *inCallerGuid, - const CSSM_MEMORY_FUNCS &inMemoryFunctions) : - DatabaseSession(MDSModule::get().databaseManager()), - mCssmMemoryFunctions (inMemoryFunctions), - mModule(MDSModule::get()) -{ - MSDebug("MDSSession::MDSSession"); - - mCallerGuidPresent = inCallerGuid != nil; - if (mCallerGuidPresent) { - mCallerGuid = *inCallerGuid; - } -} - -MDSSession::~MDSSession () -{ - MSDebug("MDSSession::~MDSSession"); -} - -void -MDSSession::terminate () -{ - MSDebug("MDSSession::terminate"); - closeAll(); -} - -const char* kExceptionDeletePath = "messages"; - - -/* - * Called by security server via MDS_Install(). - */ -void -MDSSession::install () -{ - // - // Installation requires root - // - if(geteuid() != (uid_t)0) { - CssmError::throwMe(CSSMERR_DL_OS_ACCESS_DENIED); - } - - // - // install() is only (legitimately) called from securityd. - // Mark "server mode" so we don't end up waiting for ourselves when the databases open. - // - mModule.setServerMode(); - - try { - /* ensure MDS base directory exists with correct permissions */ - if(createDir(MDS_BASE_DB_DIR, MDS_SYSTEM_UID, MDS_BASE_DB_DIR_MODE)) { - MSDebug("Error creating base MDS dir; aborting."); - CssmError::throwMe(CSSMERR_DL_OS_ACCESS_DENIED); - } - - /* ensure the the system MDS DB directory exists with correct permissions */ - if(createDir(MDS_SYSTEM_DB_DIR, MDS_SYSTEM_UID, MDS_SYSTEM_DB_DIR_MODE)) { - MSDebug("Error creating system MDS dir; aborting."); - CssmError::throwMe(CSSMERR_DL_OS_ACCESS_DENIED); - } - - LockHelper lh; - - if(!lh.obtainLock(MDS_INSTALL_LOCK_PATH, DB_LOCK_TIMEOUT)) { - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); - } - - /* - * We own the whole MDS system. Clean everything out except for our lock - * (and the directory it's in :-) - */ - - const char *savedFile = MDS_INSTALL_LOCK_NAME; - if(cleanDir(MDS_SYSTEM_DB_DIR, &savedFile, 1)) { - /* this should never happen - we're root */ - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); - } - - const char *savedFiles[] = {MDS_SYSTEM_DB_COMP, kExceptionDeletePath}; - if(cleanDir(MDS_BASE_DB_DIR, savedFiles, 2)) { - /* this should never happen - we're root */ - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); - } - - /* - * Do initial population of system DBs. - */ - createSystemDatabases(CSSM_FALSE, MDS_SYSTEM_DB_MODE); - DbFilesInfo dbFiles(*this, MDS_SYSTEM_DB_DIR); - dbFiles.updateSystemDbInfo(MDS_SYSTEM_PATH, MDS_BUNDLE_PATH); - } - catch(...) { - throw; - } -} - -// -// In this implementation, the uninstall() call is not supported since -// we do not allow programmatic deletion of the MDS databases. -// - -void -MDSSession::uninstall () -{ - CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); -} - -/* - * Common private open routine given a full specified path. - */ -CSSM_DB_HANDLE MDSSession::dbOpen(const char *dbName, bool batched) -{ - static CSSM_APPLEDL_OPEN_PARAMETERS batchOpenParams = { - sizeof(CSSM_APPLEDL_OPEN_PARAMETERS), - CSSM_APPLEDL_OPEN_PARAMETERS_VERSION, - CSSM_FALSE, // do not auto-commit - 0 // mask - do not use following fields - }; - - MSDebug("Opening %s%s", dbName, batched ? " in batch mode" : ""); - MSIoDbg("open %s in dbOpen(name, batched)", dbName); - CSSM_DB_HANDLE dbHand; - DatabaseSession::DbOpen(dbName, - NULL, // DbLocation - CSSM_DB_ACCESS_READ, - NULL, // AccessCred - hopefully optional - batched ? &batchOpenParams : NULL, - dbHand); - return dbHand; -} - -/* DatabaseSession routines we need to override */ -void MDSSession::DbOpen(const char *DbName, - const CSSM_NET_ADDRESS *DbLocation, - CSSM_DB_ACCESS_TYPE AccessRequest, - const AccessCredentials *AccessCred, - const void *OpenParameters, - CSSM_DB_HANDLE &DbHandle) -{ - if (!mModule.serverMode()) { - /* - * Make sure securityd has finished initializing (system) MDS data. - * Note that activate() only does IPC once and retains global state after that. - */ - SecurityServer::ClientSession client(Allocator::standard(), Allocator::standard()); - client.activate(); /* contact securityd - won't return until MDS is ready */ - } - - /* make sure DBs are up-to-date */ - updateDataBases(); - - /* - * Only task here is map incoming DbName - specified in the CDSA - * spec - to a filename we actually use (which is a path to either - * a system MDS DB file or a per-user MDS DB file). - */ - if(DbName == NULL) { - CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME); - } - const char *dbName; - if(!strcmp(DbName, MDS_OBJECT_DIRECTORY_NAME)) { - dbName = MDS_OBJECT_DB_NAME; - } - else if(!strcmp(DbName, MDS_CDSA_DIRECTORY_NAME)) { - dbName = MDS_DIRECT_DB_NAME; - } - else { - CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME); - } - char fullPath[MAXPATHLEN]; - dbFullPath(dbName, fullPath); - MSIoDbg("open %s in dbOpen(name, loc, accessReq...)", dbName); - DatabaseSession::DbOpen(fullPath, DbLocation, AccessRequest, AccessCred, - OpenParameters, DbHandle); -} - -CSSM_HANDLE MDSSession::DataGetFirst(CSSM_DB_HANDLE DBHandle, - const CssmQuery *Query, - CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR Attributes, - CssmData *Data, - CSSM_DB_UNIQUE_RECORD_PTR &UniqueId) -{ - updateDataBases(); - return DatabaseSession::DataGetFirst(DBHandle, Query, Attributes, Data, UniqueId); -} - - -void -MDSSession::GetDbNames(CSSM_NAME_LIST_PTR &outNameList) -{ - outNameList = new CSSM_NAME_LIST[1]; - outNameList->NumStrings = 2; - outNameList->String = new char*[2]; - outNameList->String[0] = MDSCopyCstring(MDS_OBJECT_DIRECTORY_NAME); - outNameList->String[1] = MDSCopyCstring(MDS_CDSA_DIRECTORY_NAME); -} - -void -MDSSession::FreeNameList(CSSM_NAME_LIST &inNameList) -{ - delete [] inNameList.String[0]; - delete [] inNameList.String[1]; - delete [] inNameList.String; -} - -void MDSSession::GetDbNameFromHandle(CSSM_DB_HANDLE DBHandle, - char **DbName) -{ - printf("GetDbNameFromHandle: code on demand\n"); - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); -} - -// -// Attempt to obtain an exclusive lock over the the MDS databases. The -// parameter is the maximum amount of time, in milliseconds, to spend -// trying to obtain the lock. A value of zero means to return failure -// right away if the lock cannot be obtained. -// -bool -MDSSession::LockHelper::obtainLock( - const char *lockFile, // e.g. MDS_INSTALL_LOCK_PATH - int timeout) // default 0 -{ - mFD = -1; - for(;;) { - secdebug("mdslock", "obtainLock: calling open(%s)", lockFile); - mFD = open(lockFile, O_EXLOCK | O_CREAT | O_RDWR, 0644); - if(mFD == -1) { - int err = errno; - secdebug("mdslock", "obtainLock: open error %d", errno); - if(err == EINTR) { - /* got a signal, go again */ - continue; - } - else { - /* theoretically should never happen */ - return false; - } - } - else { - secdebug("mdslock", "obtainLock: success"); - return true; - } - } - - /* not reached */ - return false; -} - -// -// Release the exclusive lock over the MDS databases. If this session -// does not hold the lock, this method does nothing. -// - -MDSSession::LockHelper::~LockHelper() -{ - secdebug("mdslock", "releaseLock"); - if (mFD == -1) - { - return; - } - - flock(mFD, LOCK_UN); - close(mFD); - mFD = -1; -} - -/* given DB file name, fill in fully specified path */ -void MDSSession::dbFullPath( - const char *dbName, - char fullPath[MAXPATHLEN+1]) -{ - mModule.getDbPath(fullPath); - assert(fullPath[0] != '\0'); - strcat(fullPath, "/"); - strcat(fullPath, dbName); -} - -/* - * See if any per-user bundles exist in specified directory. Returns true if so. - * First the check for one entry.... - */ -static bool isBundle( - const struct dirent *dp) -{ - if(dp == NULL) { - return false; - } - /* NFS directories show up as DT_UNKNOWN */ - switch(dp->d_type) { - case DT_UNKNOWN: - case DT_DIR: - break; - default: - return false; - } - int suffixLen = strlen(MDS_BUNDLE_EXTEN); - size_t len = strlen(dp->d_name); - - return (len >= suffixLen) && - !strcmp(dp->d_name + len - suffixLen, MDS_BUNDLE_EXTEN); -} - -/* now the full directory search */ -static bool checkUserBundles( - const char *bundlePath) -{ - MSDebug("searching for user bundles in %s", bundlePath); - DIR *dir = opendir(bundlePath); - if (dir == NULL) { - return false; - } - struct dirent *dp; - bool rtn = false; - while ((dp = readdir(dir)) != NULL) { - if(isBundle(dp)) { - /* any other checking to do? */ - rtn = true; - break; - } - } - closedir(dir); - MSDebug("...%s bundle(s) found", rtn ? "" : "No"); - return rtn; -} - -#define COPY_BUF_SIZE 1024 - -/* - * Single file copy with locking. - * Ensures that the source is a regular file with specified owner. - * Caller specifies mode of destination file. - * Throws a CssmError if the source file doesn't meet spec; throws a - * UnixError on any other error (which is generally recoverable by - * having the user MDS session use the system DB files). - */ -static void safeCopyFile( - const char *fromPath, - uid_t fromUid, - const char *toPath, - mode_t toMode) -{ - int error = 0; - bool haveLock = false; - int destFd = 0; - int srcFd = 0; - struct stat sb; - char tmpToPath[MAXPATHLEN+1]; - - MSIoDbg("open %s, %s in safeCopyFile", fromPath, toPath); - - if(!doesFileExist(fromPath, fromUid, false, sb)) { - MSDebug("safeCopyFile: bad system DB file %s", fromPath); - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); - } - - /* create temp destination */ - snprintf(tmpToPath, sizeof(tmpToPath), "%s_", toPath); - destFd = open(tmpToPath, O_WRONLY | O_APPEND | O_CREAT | O_TRUNC | O_EXCL, toMode); - if(destFd < 0) { - error = errno; - MSDebug("Error %d opening user DB file %s\n", error, tmpToPath); - UnixError::throwMe(error); - } - - struct flock fl; - try { - /* don't get tripped up by umask */ - if(fchmod(destFd, toMode)) { - error = errno; - MSDebug("Error %d chmoding user DB file %s\n", error, tmpToPath); - UnixError::throwMe(error); - } - - /* open source for reading */ - srcFd = open(fromPath, O_RDONLY, 0); - if(srcFd < 0) { - error = errno; - MSDebug("Error %d opening system DB file %s\n", error, fromPath); - UnixError::throwMe(error); - } - - /* acquire the same kind of lock AtomicFile uses */ - fl.l_start = 0; - fl.l_len = 1; - fl.l_pid = getpid(); - fl.l_type = F_RDLCK; // AtomicFile gets F_WRLCK - fl.l_whence = SEEK_SET; - - // Keep trying to obtain the lock if we get interupted. - for (;;) { - if (::fcntl(srcFd, F_SETLKW, &fl) == -1) { - error = errno; - if (error == EINTR) { - error = 0; - continue; - } - MSDebug("Error %d locking system DB file %s\n", error, fromPath); - UnixError::throwMe(error); - } - else { - break; - haveLock = true; - } - } - - /* copy */ - char buf[COPY_BUF_SIZE]; - while(1) { - ssize_t bytesRead = read(srcFd, buf, COPY_BUF_SIZE); - if(bytesRead == 0) { - break; - } - if(bytesRead < 0) { - error = errno; - MSDebug("Error %d reading system DB file %s\n", error, fromPath); - UnixError::throwMe(error); - } - ssize_t bytesWritten = write(destFd, buf, bytesRead); - if(bytesWritten < 0) { - error = errno; - MSDebug("Error %d writing user DB file %s\n", error, tmpToPath); - UnixError::throwMe(error); - } - } - } - catch(...) { - /* error is nonzero, we'll re-throw below...still have some cleanup */ - } - - /* unlock source and close both */ - if(haveLock) { - fl.l_type = F_UNLCK; - if (::fcntl(srcFd, F_SETLK, &fl) == -1) { - MSDebug("Error %d unlocking system DB file %s\n", errno, fromPath); - } - } - MSIoDbg("close %s, %s in safeCopyFile", fromPath, tmpToPath); - if(srcFd) { - close(srcFd); - } - if(destFd) { - close(destFd); - } - if(error == 0) { - /* commit temp file */ - if(::rename(tmpToPath, toPath)) { - error = errno; - MSDebug("Error %d committing %s\n", error, toPath); - } - } - if(error) { - UnixError::throwMe(error); - } -} - -/* - * Copy system DB files to specified user dir. Caller holds user DB lock. - * Throws a UnixError on error. - */ -static void copySystemDbs( - const char *userDbFileDir) -{ - char toPath[MAXPATHLEN+1]; - - snprintf(toPath, sizeof(toPath), "%s/%s", userDbFileDir, MDS_OBJECT_DB_NAME); - safeCopyFile(MDS_OBJECT_DB_PATH, MDS_SYSTEM_UID, toPath, MDS_USER_DB_MODE); - snprintf(toPath, sizeof(toPath), "%s/%s", userDbFileDir, MDS_DIRECT_DB_NAME); - safeCopyFile(MDS_DIRECT_DB_PATH, MDS_SYSTEM_UID, toPath, MDS_USER_DB_MODE); -} - -/* - * Ensure current DB files exist and are up-to-date. - * Called from MDSSession constructor and from DataGetFirst, DbOpen, and any - * other public functions which access a DB from scratch. - */ -void MDSSession::updateDataBases() -{ - RecursionBlock::Once once(mUpdating); - if (once()) - return; // already updating; don't recurse - - uid_t ourUid = geteuid(); - bool isRoot = (ourUid == 0); - - /* if we scanned recently, we're done */ - double delta = mModule.timeSinceLastScan(); - if(delta < (double)MDS_SCAN_INTERVAL) { - return; - } - - /* - * If we're root, the first thing we do is to ensure that system DBs are present. - * Note that this is a necessary artifact of the problem behind Radar 3800811. - * When that is fixed, install() should ONLY be called from the public MDS_Install() - * routine. - * Anyway, if we *do* have to install here, we're done. - */ - if(isRoot && !systemDatabasesPresent(false)) { - install(); - mModule.setDbPath(MDS_SYSTEM_DB_DIR); - mModule.lastScanIsNow(); - return; - } - - /* - * Obtain various per-user paths. Root is a special case but follows most - * of the same logic from here on. - */ - std::string userDBFileDir = GetMDSDBDir(); - std::string userObjDBFilePath = GetMDSObjectDBPath(); - std::string userDirectDBFilePath = GetMDSDirectDBPath(); - char userBundlePath[MAXPATHLEN+1]; - std::string userDbLockPath = GetMDSDBLockPath(); - - /* this means "no user bundles" */ - userBundlePath[0] = '\0'; - if(!isRoot) { - char *userHome = getenv("HOME"); - if((userHome == NULL) || - (strlen(userHome) + strlen(MDS_USER_BUNDLE) + 2) > sizeof(userBundlePath)) { - /* Can't check for user bundles */ - MSDebug("missing or invalid HOME; skipping user bundle check"); - } - /* TBD: any other checking of userHome? */ - else { - snprintf(userBundlePath, sizeof(userBundlePath), - "%s/%s", userHome, MDS_USER_BUNDLE); - } - } - - /* - * Create the per-user directory...that's where the lock we'll be using lives. - */ - if(!isRoot) { - if(createDir(userDBFileDir.c_str(), ourUid, MDS_USER_DB_DIR_MODE)) { - /* - * We'll just have to limp along using the read-only system DBs. - * Note that this protects (somewhat) against the DoS attack in - * Radar 3801292. The only problem is that this user won't be able - * to use per-user bundles. - */ - MSDebug("Error creating user DBs; using system DBs"); - mModule.setDbPath(MDS_SYSTEM_DB_DIR); - return; - } - } - - /* always release userLockFd no matter what happens */ - LockHelper lh; - - if(!lh.obtainLock(userDbLockPath.c_str(), DB_LOCK_TIMEOUT)) { - CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR); - } - try { - if(!isRoot) { - try { - /* - * We copy the system DBs to the per-user DBs in two cases: - * -- user DBs don't exist, or - * -- system DBs have changed since the the last update to the user DBs. - * This happens on smart card insertion and removal. - */ - bool doCopySystem = false; - struct stat userObjStat, userDirectStat; - if(!doFilesExist(userObjDBFilePath.c_str(), userDirectDBFilePath.c_str(), ourUid, true, - userObjStat, userDirectStat)) { - doCopySystem = true; - } - else { - /* compare the two mdsDirectory.db files */ - MSIoDbg("stat %s, %s in updateDataBases", - MDS_DIRECT_DB_PATH, userDirectDBFilePath.c_str()); - struct stat sysStat; - if (!stat(MDS_DIRECT_DB_PATH, &sysStat)) { - doCopySystem = (sysStat.st_mtimespec.tv_sec > userDirectStat.st_mtimespec.tv_sec) || - ((sysStat.st_mtimespec.tv_sec == userDirectStat.st_mtimespec.tv_sec) && - (sysStat.st_mtimespec.tv_nsec > userDirectStat.st_mtimespec.tv_nsec)); - if(doCopySystem) { - MSDebug("user DB files obsolete at %s", userDBFileDir.c_str()); - } - } - } - if(doCopySystem) { - /* copy system DBs to user DBs */ - MSDebug("copying system DBs to user at %s", userDBFileDir.c_str()); - copySystemDbs(userDBFileDir.c_str()); - } - else { - MSDebug("Using existing user DBs at %s", userDBFileDir.c_str()); - } - } - catch(const CssmError &cerror) { - /* - * Bad system DB file detected. Fatal. - */ - throw; - } - catch(...) { - /* - * Error on delete or create user DBs; fall back on system DBs. - */ - MSDebug("doFilesExist(purge) error; using system DBs"); - mModule.setDbPath(MDS_SYSTEM_DB_DIR); - return; - } - } - else { - MSDebug("Using system DBs only"); - } - - /* - * Update per-user DBs from both bundle sources (System bundles, user bundles) - * as appropriate. - */ - DbFilesInfo dbFiles(*this, userDBFileDir.c_str()); - dbFiles.removeOutdatedPlugins(); - dbFiles.updateSystemDbInfo(NULL, MDS_BUNDLE_PATH); - if(userBundlePath[0]) { - /* skip for invalid or missing $HOME... */ - if(checkUserBundles(userBundlePath)) { - dbFiles.updateForBundleDir(userBundlePath); - } - } - mModule.setDbPath(userDBFileDir.c_str()); - } /* main block protected by mLockFd */ - catch(...) { - throw; - } - mModule.lastScanIsNow(); -} - -/* - * Remove all records with specified guid (a.k.a. ModuleID) from specified DB. - */ -void MDSSession::removeRecordsForGuid( - const char *guid, - CSSM_DB_HANDLE dbHand) -{ - // tell the DB to flush its intermediate data to disk - PassThrough(dbHand, CSSM_APPLEFILEDL_COMMIT, NULL, NULL); - CssmClient::Query query = Attribute("ModuleID") == guid; - clearRecords(dbHand, query.cssmQuery()); -} - - -void MDSSession::clearRecords(CSSM_DB_HANDLE dbHand, const CssmQuery &query) -{ - CSSM_DB_UNIQUE_RECORD_PTR record = NULL; - CSSM_HANDLE resultHand = DataGetFirst(dbHand, - &query, - NULL, - NULL, // No data - record); - if (resultHand == CSSM_INVALID_HANDLE) - return; // no matches - try { - do { - DataDelete(dbHand, *record); - FreeUniqueRecord(dbHand, *record); - record = NULL; - } while (DataGetNext(dbHand, - resultHand, - NULL, - NULL, - record)); - } catch (...) { - if (record) - FreeUniqueRecord(dbHand, *record); - DataAbortQuery(dbHand, resultHand); - } -} - - -/* - * Determine if system databases are present. - * If the purge argument is true, we'll ensure that either both or neither - * DB files exist on exit; in that case caller must be holding MDS_INSTALL_LOCK_PATH. - */ -bool MDSSession::systemDatabasesPresent(bool purge) -{ - bool rtn = false; - - try { - /* - * This can throw on a failed attempt to delete sole existing file.... - * But if that happens while we're root, our goose is fully cooked. - */ - struct stat objDbSb, directDbSb; - if(doFilesExist(MDS_OBJECT_DB_PATH, MDS_DIRECT_DB_PATH, - MDS_SYSTEM_UID, purge, objDbSb, directDbSb)) { - rtn = true; - } - } - catch(...) { - - } - return rtn; -} - -/* - * Given a DB name (which is used as an absolute path) and an array of - * RelationInfos, create a DB. - */ -void -MDSSession::createSystemDatabase( - const char *dbName, - const RelationInfo *relationInfo, - unsigned numRelations, - CSSM_BOOL autoCommit, - mode_t mode, - CSSM_DB_HANDLE &dbHand) // RETURNED -{ - CSSM_DBINFO dbInfo; - CSSM_DBINFO_PTR dbInfoP = &dbInfo; - - memset(dbInfoP, 0, sizeof(CSSM_DBINFO)); - dbInfoP->NumberOfRecordTypes = numRelations; - dbInfoP->IsLocal = CSSM_TRUE; // TBD - what does this mean? - dbInfoP->AccessPath = NULL; // TBD - - /* alloc numRelations elements for parsingModule, recordAttr, and recordIndex - * info arrays */ - unsigned size = sizeof(CSSM_DB_PARSING_MODULE_INFO) * numRelations; - dbInfoP->DefaultParsingModules = (CSSM_DB_PARSING_MODULE_INFO_PTR)malloc(size); - memset(dbInfoP->DefaultParsingModules, 0, size); - size = sizeof(CSSM_DB_RECORD_ATTRIBUTE_INFO) * numRelations; - dbInfoP->RecordAttributeNames = (CSSM_DB_RECORD_ATTRIBUTE_INFO_PTR)malloc(size); - memset(dbInfoP->RecordAttributeNames, 0, size); - size = sizeof(CSSM_DB_RECORD_INDEX_INFO) * numRelations; - dbInfoP->RecordIndexes = (CSSM_DB_RECORD_INDEX_INFO_PTR)malloc(size); - memset(dbInfoP->RecordIndexes, 0, size); - - /* cook up attribute and index info for each relation */ - unsigned relation; - for(relation=0; relationRecordAttributeNames[relation]; // dest 1 - CSSM_DB_RECORD_INDEX_INFO_PTR indexInfo = - &dbInfoP->RecordIndexes[relation]; // dest 2 - - attrInfo->DataRecordType = relp->DataRecordType; - attrInfo->NumberOfAttributes = relp->NumberOfAttributes; - attrInfo->AttributeInfo = (CSSM_DB_ATTRIBUTE_INFO_PTR)relp->AttributeInfo; - - indexInfo->DataRecordType = relp->DataRecordType; - indexInfo->NumberOfIndexes = relp->NumberOfIndexes; - indexInfo->IndexInfo = (CSSM_DB_INDEX_INFO_PTR)relp->IndexInfo; - } - - /* set autocommit and mode */ - CSSM_APPLEDL_OPEN_PARAMETERS openParams; - memset(&openParams, 0, sizeof(openParams)); - openParams.length = sizeof(openParams); - openParams.version = CSSM_APPLEDL_OPEN_PARAMETERS_VERSION; - openParams.autoCommit = autoCommit; - openParams.mask = kCSSM_APPLEDL_MASK_MODE; - openParams.mode = mode; - - try { - DbCreate(dbName, - NULL, // DbLocation - *dbInfoP, - CSSM_DB_ACCESS_READ | CSSM_DB_ACCESS_WRITE, - NULL, // CredAndAclEntry - &openParams, - dbHand); - } - catch(...) { - MSDebug("Error on DbCreate"); - free(dbInfoP->DefaultParsingModules); - free(dbInfoP->RecordAttributeNames); - free(dbInfoP->RecordIndexes); - throw; - } - free(dbInfoP->DefaultParsingModules); - free(dbInfoP->RecordAttributeNames); - free(dbInfoP->RecordIndexes); - -} - -/* - * Create system databases from scratch if they do not already exist. - * MDS_INSTALL_LOCK_PATH held on entry and exit. MDS_SYSTEM_DB_DIR assumed to - * exist (that's our caller's job, before acquiring MDS_INSTALL_LOCK_PATH). - * Returns true if we actually built the files, false if they already - * existed. - */ -bool MDSSession::createSystemDatabases( - CSSM_BOOL autoCommit, - mode_t mode) -{ - CSSM_DB_HANDLE objectDbHand = 0; - CSSM_DB_HANDLE directoryDbHand = 0; - - assert(geteuid() == (uid_t)0); - if(systemDatabasesPresent(true)) { - /* both databases exist as regular files with correct owner - we're done */ - MSDebug("system DBs already exist"); - return false; - } - - /* create two DBs - any exception here results in deleting both of them */ - MSDebug("Creating MDS DBs"); - try { - createSystemDatabase(MDS_OBJECT_DB_PATH, &kObjectRelation, 1, - autoCommit, mode, objectDbHand); - MSIoDbg("close objectDbHand in createSystemDatabases"); - DbClose(objectDbHand); - objectDbHand = 0; - createSystemDatabase(MDS_DIRECT_DB_PATH, kMDSRelationInfo, kNumMdsRelations, - autoCommit, mode, directoryDbHand); - MSIoDbg("close directoryDbHand in createSystemDatabases"); - DbClose(directoryDbHand); - directoryDbHand = 0; - } - catch (...) { - MSDebug("Error creating MDS DBs - deleting both DB files"); - unlink(MDS_OBJECT_DB_PATH); - unlink(MDS_DIRECT_DB_PATH); - throw; - } - return true; -} - -/* - * DbFilesInfo helper class - */ - -/* Note both DB files MUST exist at construction time */ -MDSSession::DbFilesInfo::DbFilesInfo( - MDSSession &session, - const char *dbPath) : - mSession(session), - mObjDbHand(0), - mDirectDbHand(0), - mLaterTimestamp(0) -{ - assert(strlen(dbPath) < MAXPATHLEN); - strcpy(mDbPath, dbPath); - - /* stat the two DB files, snag the later timestamp */ - char path[MAXPATHLEN]; - sprintf(path, "%s/%s", mDbPath, MDS_OBJECT_DB_NAME); - struct stat sb; - MSIoDbg("stat %s in DbFilesInfo()", path); - int rtn = ::stat(path, &sb); - if(rtn) { - int error = errno; - MSDebug("Error %d statting DB file %s", error, path); - UnixError::throwMe(error); - } - mLaterTimestamp = sb.st_mtimespec.tv_sec; - sprintf(path, "%s/%s", mDbPath, MDS_DIRECT_DB_NAME); - MSIoDbg("stat %s in DbFilesInfo()", path); - rtn = ::stat(path, &sb); - if(rtn) { - int error = errno; - MSDebug("Error %d statting DB file %s", error, path); - UnixError::throwMe(error); - } - if(sb.st_mtimespec.tv_sec > mLaterTimestamp) { - mLaterTimestamp = sb.st_mtimespec.tv_sec; - } -} - -MDSSession::DbFilesInfo::~DbFilesInfo() -{ - if(mObjDbHand != 0) { - /* autocommit on, henceforth */ - mSession.PassThrough(mObjDbHand, - CSSM_APPLEFILEDL_COMMIT, NULL, NULL); - MSIoDbg("close objectDbHand in ~DbFilesInfo()"); - mSession.DbClose(mObjDbHand); - mObjDbHand = 0; - } - if(mDirectDbHand != 0) { - mSession.PassThrough(mDirectDbHand, - CSSM_APPLEFILEDL_COMMIT, NULL, NULL); - MSIoDbg("close mDirectDbHand in ~DbFilesInfo()"); - mSession.DbClose(mDirectDbHand); - mDirectDbHand = 0; - } -} - -/* lazy evaluation of both DB handlesÊ*/ -CSSM_DB_HANDLE MDSSession::DbFilesInfo::objDbHand() -{ - if(mObjDbHand != 0) { - return mObjDbHand; - } - char fullPath[MAXPATHLEN + 1]; - sprintf(fullPath, "%s/%s", mDbPath, MDS_OBJECT_DB_NAME); - MSIoDbg("open %s in objDbHand()", fullPath); - mObjDbHand = mSession.dbOpen(fullPath, true); // batch mode - return mObjDbHand; -} - -CSSM_DB_HANDLE MDSSession::DbFilesInfo::directDbHand() -{ - if(mDirectDbHand != 0) { - return mDirectDbHand; - } - char fullPath[MAXPATHLEN + 1]; - sprintf(fullPath, "%s/%s", mDbPath, MDS_DIRECT_DB_NAME); - MSIoDbg("open %s in directDbHand()", fullPath); - mDirectDbHand = mSession.dbOpen(fullPath, true); // batch mode - return mDirectDbHand; -} - -/* - * Update the info for Security.framework and the system bundles. - */ -void MDSSession::DbFilesInfo::updateSystemDbInfo( - const char *systemPath, // e.g., /System/Library/Frameworks - const char *bundlePath) // e.g., /System/Library/Security -{ - /* - * Security.framework - CSSM and built-in modules - only for initial population of - * system DB files. - */ - if (systemPath) { - string path; - if (CFRef me = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"))) - if (CFRef url = CFBundleCopyBundleURL(me)) - if (CFRef cfpath = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle)) - path = cfString(cfpath); // path to my bundle - - if (path.empty()) // use system default - path = string(systemPath) + "/" MDS_SYSTEM_FRAME; - updateForBundle(path.c_str()); - } - - /* Standard loadable bundles */ - updateForBundleDir(bundlePath); -} - - -MDSSession::DbFilesInfo::TbdRecord::TbdRecord( - const CSSM_DATA &guid) -{ - assert(guid.Length <= MAX_GUID_LEN); - assert(guid.Length != 0); - memmove(mGuid, guid.Data, guid.Length); - if(mGuid[guid.Length - 1] != '\0') { - mGuid[guid.Length] = '\0'; - } -} - -/* - * Test if plugin specified by pluginPath needs to be deleted from DBs. - * If so, add to tbdVector. - */ -void MDSSession::DbFilesInfo::checkOutdatedPlugin( - const CSSM_DATA &pathValue, - const CSSM_DATA &guidValue, - TbdVector &tbdVector) -{ - /* stat the specified plugin */ - struct stat sb; - bool obsolete = false; - string path = CssmData::overlay(pathValue).toString(); - if (!path.empty() && path[0] == '*') { - /* builtin pseudo-path; never obsolete this */ - return; - } - MSIoDbg("stat %s in checkOutdatedPlugin()", path.c_str()); - int rtn = ::stat(path.c_str(), &sb); - if(rtn) { - /* not there or inaccessible; delete */ - obsolete = true; - } - else if(sb.st_mtimespec.tv_sec > mLaterTimestamp) { - /* timestamp of plugin's main directory later than that of DBs */ - obsolete = true; - } - if(obsolete) { - TbdRecord *tbdRecord = new TbdRecord(guidValue); - tbdVector.push_back(tbdRecord); - MSDebug("checkOutdatedPlugin: flagging %s obsolete", path.c_str()); - } -} - -/* - * Examine dbFiles.objDbHand; remove all fields associated with any bundle - * i.e., with any path) which are either not present on disk, or which - * have changed since dbFiles.laterTimestamp(). - */ -void MDSSession::DbFilesInfo::removeOutdatedPlugins() -{ - CSSM_QUERY query; - CSSM_DB_UNIQUE_RECORD_PTR record = NULL; - CSSM_HANDLE resultHand; - CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; - CSSM_DB_ATTRIBUTE_DATA theAttrs[2]; - CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo; - TbdVector tbdRecords; - - /* - * First, scan object directory. All we need are the path and GUID attributes. - */ - recordAttrs.DataRecordType = MDS_OBJECT_RECORDTYPE; - recordAttrs.SemanticInformation = 0; - recordAttrs.NumberOfAttributes = 2; - recordAttrs.AttributeData = theAttrs; - - attrInfo = &theAttrs[0].Info; - attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; - attrInfo->Label.AttributeName = (char*) "ModuleID"; - attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING; - theAttrs[0].NumberOfValues = 0; - theAttrs[0].Value = NULL; - attrInfo = &theAttrs[1].Info; - attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; - attrInfo->Label.AttributeName = (char*) "Path"; - attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING; - theAttrs[1].NumberOfValues = 0; - theAttrs[1].Value = NULL; - - /* just search by recordType, no predicates */ - query.RecordType = MDS_OBJECT_RECORDTYPE; - query.Conjunctive = CSSM_DB_NONE; - query.NumSelectionPredicates = 0; - query.SelectionPredicate = NULL; - query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? - query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? - query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA...FIXME - used? - - CssmQuery perryQuery(query); - try { - resultHand = mSession.DataGetFirst(objDbHand(), - &perryQuery, - &recordAttrs, - NULL, // No data - record); - } - catch(...) { - MSDebug("removeOutdatedPlugins: DataGetFirst threw"); - return; // ??? - } - if(record) { - mSession.FreeUniqueRecord(mObjDbHand, *record); - } - if(resultHand) { - if(theAttrs[0].NumberOfValues && theAttrs[1].NumberOfValues) { - checkOutdatedPlugin(*theAttrs[1].Value, *theAttrs[0].Value, - tbdRecords); - } - else { - MSDebug("removeOutdatedPlugins: incomplete record found (1)!"); - } - for(unsigned dex=0; dex<2; dex++) { - CSSM_DB_ATTRIBUTE_DATA *attr = &theAttrs[dex]; - for (unsigned attrDex=0; attrDexNumberOfValues; attrDex++) { - if(attr->Value[attrDex].Data) { - mSession.free(attr->Value[attrDex].Data); - } - } - if(attr->Value) { - mSession.free(attr->Value); - } - } - } - else { - /* empty Object DB - we're done */ - MSDebug("removeOutdatedPlugins: empty object DB"); - return; - } - - /* now the rest of the object DB records */ - for(;;) { - bool brtn = mSession.DataGetNext(objDbHand(), - resultHand, - &recordAttrs, - NULL, - record); - if(!brtn) { - /* end of data */ - break; - } - if(record) { - mSession.FreeUniqueRecord(mObjDbHand, *record); - } - if(theAttrs[0].NumberOfValues && theAttrs[1].NumberOfValues) { - checkOutdatedPlugin(*theAttrs[1].Value, - *theAttrs[0].Value, - tbdRecords); - } - else { - MSDebug("removeOutdatedPlugins: incomplete record found (2)!"); - } - for(unsigned dex=0; dex<2; dex++) { - CSSM_DB_ATTRIBUTE_DATA *attr = &theAttrs[dex]; - for (unsigned attrDex=0; attrDexNumberOfValues; attrDex++) { - if(attr->Value[attrDex].Data) { - mSession.free(attr->Value[attrDex].Data); - } - } - if(attr->Value) { - mSession.free(attr->Value); - } - } - } - /* no DataAbortQuery needed; we scanned until completion */ - - /* - * We have a vector of plugins to be deleted. Remove all records from both - * DBs associated with the plugins, as specified by guid. - */ - size_t numRecords = tbdRecords.size(); - for(size_t i=0; iguid(), objDbHand()); - mSession.removeRecordsForGuid(tbdRecord->guid(), directDbHand()); - } - for(size_t i=0; id_name); - updateForBundle(fullPath); - } - } - closedir(dir); -} - -/* - * lookup by path - just returns true if there is a record assoociated with the path - * in mObjDbHand. - */ -bool MDSSession::DbFilesInfo::lookupForPath( - const char *path) -{ - CSSM_QUERY query; - CSSM_DB_UNIQUE_RECORD_PTR record = NULL; - CSSM_HANDLE resultHand = 0; - CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; - CSSM_DB_ATTRIBUTE_DATA theAttr; - CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo = &theAttr.Info; - CSSM_SELECTION_PREDICATE predicate; - CSSM_DATA predData; - - recordAttrs.DataRecordType = MDS_OBJECT_RECORDTYPE; - recordAttrs.SemanticInformation = 0; - recordAttrs.NumberOfAttributes = 1; - recordAttrs.AttributeData = &theAttr; - - attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; - attrInfo->Label.AttributeName = (char*) "Path"; - attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING; - - theAttr.NumberOfValues = 0; - theAttr.Value = NULL; - - predicate.DbOperator = CSSM_DB_EQUAL; - predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; - predicate.Attribute.Info.Label.AttributeName = (char*) "Path"; - predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING; - predData.Data = (uint8 *)path; - predData.Length = strlen(path); - predicate.Attribute.Value = &predData; - predicate.Attribute.NumberOfValues = 1; - - query.RecordType = MDS_OBJECT_RECORDTYPE; - query.Conjunctive = CSSM_DB_NONE; - query.NumSelectionPredicates = 1; - query.SelectionPredicate = &predicate; - query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? - query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? - query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA...FIXME - used? - - bool ourRtn = true; - try { - CssmQuery perryQuery(query); - resultHand = mSession.DataGetFirst(objDbHand(), - &perryQuery, - &recordAttrs, - NULL, // No data - record); - } - catch (...) { - ourRtn = false; - } - if(record) { - mSession.FreeUniqueRecord(mObjDbHand, *record); - } - else { - ourRtn = false; - } - if(resultHand && ourRtn) { - /* more resulting pending; terminate the search */ - try { - mSession.DataAbortQuery(mObjDbHand, resultHand); - } - catch(...) { - MSDebug("exception on DataAbortQuery in lookupForPath"); - } - } - for(unsigned dex=0; dex(makeCFURL(file)), CFTempString(subdir)); - } - catch (const CssmError &err) { - const char *guid = parser.guid(); - if (guid) { - removeRecordsForGuid(guid, dbFiles.objDbHand()); - removeRecordsForGuid(guid, dbFiles.directDbHand()); - } - } -} - - -// -// Private API: Remove all records for a guid/subservice -// -// Note: Multicursors searching for SSID fail because not all records in the -// database have this attribute. So we have to explicitly run through all tables -// that do. -// -void MDSSession::removeSubservice(const char *guid, uint32 ssid) -{ - DbFilesInfo dbFiles(*this, MDS_SYSTEM_DB_DIR); - - CssmClient::Query query = - Attribute("ModuleID") == guid && - Attribute("SSID") == ssid; - - // only CSP and DL tables are cleared here - // (this function is private to securityd, which only handles those types) - clearRecords(dbFiles.directDbHand(), - CssmQuery(query.cssmQuery(), MDS_CDSADIR_CSP_PRIMARY_RECORDTYPE)); - clearRecords(dbFiles.directDbHand(), - CssmQuery(query.cssmQuery(), MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE)); - clearRecords(dbFiles.directDbHand(), - CssmQuery(query.cssmQuery(), MDS_CDSADIR_CSP_ENCAPSULATED_PRODUCT_RECORDTYPE)); - clearRecords(dbFiles.directDbHand(), - CssmQuery(query.cssmQuery(), MDS_CDSADIR_CSP_SC_INFO_RECORDTYPE)); - clearRecords(dbFiles.directDbHand(), - CssmQuery(query.cssmQuery(), MDS_CDSADIR_DL_PRIMARY_RECORDTYPE)); - clearRecords(dbFiles.directDbHand(), - CssmQuery(query.cssmQuery(), MDS_CDSADIR_DL_ENCAPSULATED_PRODUCT_RECORDTYPE)); -} - - -} // end namespace Security