2 * Copyright (c) 2000-2001,2011-2014 Apple Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
19 #include "MDSSession.h"
21 #include <security_cdsa_plugin/DbContext.h>
22 #include "MDSModule.h"
23 #include "MDSAttrParser.h"
24 #include "MDSAttrUtils.h"
27 #include <Security/cssmerr.h>
28 #include <security_utilities/logging.h>
29 #include <security_utilities/debugging.h>
30 #include <security_utilities/cfutilities.h>
31 #include <security_cdsa_client/dlquery.h>
32 #include <securityd_client/ssclient.h>
33 #include <Security/mds_schema.h>
34 #include <CoreFoundation/CFBundle.h>
36 #include <sys/types.h>
37 #include <sys/param.h>
46 using namespace CssmClient
;
49 * The layout of the various MDS DB files on disk is as follows:
51 * /var/db/mds -- owner = root, mode = 01777, world writable, sticky
52 * system/ -- owner = root, mode = 0755
53 * mdsObject.db -- owner = root, mode = 0644, object DB
54 * mdsDirectory.db -- owner = root, mode = 0644, MDS directory DB
55 * mds.lock -- temporary, owner = root, protects creation of and
56 * updates to previous two files
57 * mds.install.lock -- owner = root, protects MDS_Install operation
58 * <uid>/ -- owner = <uid>, mode = 0700
59 * mdsObject.db -- owner = <uid>, mode = 000, object DB
60 * mdsDirectory.db -- owner = <uid>, mode = 000, MDS directory DB
61 * mds.lock -- owner = <uid>, protects updates of previous two files
63 * The /var/db/mds/system directory is created at OS install time. The DB files in
64 * it are created by root at MDS_Install time. The ownership and mode of this directory and
65 * of its parent is also re-checked and forced to the correct state at MDS_Install time.
66 * Each user has their own private directory with two DB files and a lock. The first time
67 * a user accesses MDS, the per-user directory is created and the per-user DB files are
68 * created as copies of the system DB files. Fcntl() with a F_RDLCK is used to lock the system
69 * DB files when they are the source of these copies; this is the same mechanism
70 * used by the underlying AtomicFile.
72 * The sticky bit in /var/db/mds ensures that users cannot modify other users' private
79 * Nominal location of Security.framework.
81 #define MDS_SYSTEM_PATH "/System/Library/Frameworks"
82 #define MDS_SYSTEM_FRAME "Security.framework"
85 * Nominal location of standard plugins.
87 #define MDS_BUNDLE_PATH "/System/Library/Security"
88 #define MDS_BUNDLE_EXTEN ".bundle"
92 * Location of MDS database and lock files.
94 #define MDS_BASE_DB_DIR "/private/var/db/mds"
95 #define MDS_SYSTEM_DB_COMP "system"
96 #define MDS_SYSTEM_DB_DIR MDS_BASE_DB_DIR "/" MDS_SYSTEM_DB_COMP
97 #define MDS_USER_DB_COMP "mds"
99 #define MDS_LOCK_FILE_NAME "mds.lock"
100 #define MDS_INSTALL_LOCK_NAME "mds.install.lock"
101 #define MDS_OBJECT_DB_NAME "mdsObject.db"
102 #define MDS_DIRECT_DB_NAME "mdsDirectory.db"
104 #define MDS_INSTALL_LOCK_PATH MDS_SYSTEM_DB_DIR "/" MDS_INSTALL_LOCK_NAME
105 #define MDS_OBJECT_DB_PATH MDS_SYSTEM_DB_DIR "/" MDS_OBJECT_DB_NAME
106 #define MDS_DIRECT_DB_PATH MDS_SYSTEM_DB_DIR "/" MDS_DIRECT_DB_NAME
108 /* hard coded modes and a symbolic UID for root */
109 #define MDS_BASE_DB_DIR_MODE (mode_t)0755
110 #define MDS_SYSTEM_DB_DIR_MODE (mode_t)0755
111 #define MDS_SYSTEM_DB_MODE (mode_t)0644
112 #define MDS_USER_DB_DIR_MODE (mode_t)0700
113 #define MDS_USER_DB_MODE (mode_t)0600
114 #define MDS_SYSTEM_UID (uid_t)0
117 * Location of per-user bundles, relative to home directory.
118 * Per-user DB files are in MDS_BASE_DB_DIR/<uid>/.
120 #define MDS_USER_BUNDLE "Library/Security"
122 /* time to wait in ms trying to acquire lock */
123 #define DB_LOCK_TIMEOUT (2 * 1000)
125 /* Minimum interval, in seconds, between rescans for plugin changes */
126 #define MDS_SCAN_INTERVAL 5
129 #define MSIoDbg(args...) secinfo("MDS_IO", ## args)
131 /* Trace cleanDir() */
132 #define MSCleanDirDbg(args...) secinfo("MDS_CleanDir", ## args)
134 static std::string
GetMDSBaseDBDir(bool isRoot
)
136 // what we return depends on whether or not we are root
140 retValue
= MDS_SYSTEM_DB_DIR
;
144 char strBuffer
[PATH_MAX
+ 1];
145 size_t result
= confstr(_CS_DARWIN_USER_CACHE_DIR
, strBuffer
, sizeof(strBuffer
));
148 // we have an error, log it
149 syslog(LOG_CRIT
, "confstr on _CS_DARWIN_USER_CACHE_DIR returned an error.");
150 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
153 retValue
= strBuffer
;
161 static std::string
GetMDSDBDir()
164 bool isRoot
= geteuid() == 0;
168 retValue
= MDS_SYSTEM_DB_DIR
;
172 retValue
= GetMDSBaseDBDir(isRoot
) + "/" + MDS_USER_DB_COMP
;
180 static std::string
GetMDSObjectDBPath()
182 return GetMDSDBDir() + "/" + MDS_OBJECT_DB_NAME
;
187 static std::string
GetMDSDirectDBPath()
189 return GetMDSDBDir() + "/" + MDS_DIRECT_DB_PATH
;
194 static std::string
GetMDSDBLockPath()
196 return GetMDSDBDir() + "/" + MDS_LOCK_FILE_NAME
;
202 * Given a path to a directory, remove everything in the directory except for the optional
203 * keepFileNames. Returns 0 on success, else an errno.
207 const char **keepFileNames
, // array of strings, size numKeepFileNames
208 unsigned numKeepFileNames
)
212 char fullPath
[MAXPATHLEN
];
215 MSCleanDirDbg("cleanDir(%s) top", dirPath
);
216 if ((dirp
= opendir(dirPath
)) == NULL
) {
218 MSCleanDirDbg("opendir(%s) returned %d", dirPath
, rtn
);
224 const char *d_name
= NULL
;
226 /* this block is for breaking on unqualified entries */
231 /* end of directory or error */
234 MSCleanDirDbg("cleanDir(%s): readdir err %d", dirPath
, rtn
);
240 /* skip "." and ".." */
241 if( (d_name
[0] == '.') &&
242 ( (d_name
[1] == '\0') ||
243 ( (d_name
[1] == '.') && (d_name
[2] == '\0') ) ) ) {
248 /* skip entries in keepFileNames */
249 for(unsigned dex
=0; dex
<numKeepFileNames
; dex
++) {
250 if(!strcmp(keepFileNames
[dex
], d_name
)) {
256 if(rtn
|| (dp
== NULL
)) {
257 /* one way or another, we're done */
265 /* We have an entry to delete. Delete it, or recurse. */
267 snprintf(fullPath
, sizeof(fullPath
), "%s/%s", dirPath
, d_name
);
268 if(dp
->d_type
== DT_DIR
) {
269 /* directory. Clean it, then delete. */
270 MSCleanDirDbg("cleanDir recursing for dir %s", fullPath
);
271 rtn
= cleanDir(fullPath
, NULL
, 0);
275 MSCleanDirDbg("cleanDir deleting dir %s", fullPath
);
276 if(rmdir(fullPath
)) {
278 MSCleanDirDbg("unlink(%s) returned %d", fullPath
, rtn
);
283 MSCleanDirDbg("cleanDir deleting file %s", fullPath
);
284 if(unlink(fullPath
)) {
286 MSCleanDirDbg("unlink(%s) returned %d", fullPath
, rtn
);
292 * Back to beginning of directory for clean scan.
293 * Normally we'd just do a rewinddir() here but the RAMDisk filesystem,
294 * used when booting from DVD, does not implement that properly.
297 if ((dirp
= opendir(dirPath
)) == NULL
) {
299 MSCleanDirDbg("opendir(%s) returned %d", dirPath
, rtn
);
309 * Determine if a file exists as regular file with specified owner. Returns true if so.
310 * If the purge argument is true, and there is something at the specified path that
311 * doesn't meet spec, we do everything we can to delete it. If that fails we throw
312 * CssmError(CSSM_ERRCODE_MDS_ERROR). If the delete succeeds we return false.
313 * Returns the stat info on success for further processing by caller.
315 static bool doesFileExist(
316 const char *filePath
,
319 struct stat
&sb
) // RETURNED
321 MSIoDbg("stat %s in doesFileExist", filePath
);
322 if(lstat(filePath
, &sb
)) {
323 /* doesn't exist or we can't even get to it. */
324 if(errno
== ENOENT
) {
328 /* If we can't stat it we sure can't delete it. */
329 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
334 /* it's there...how does it look? */
335 mode_t fileType
= sb
.st_mode
& S_IFMT
;
336 if((fileType
== S_IFREG
) && (sb
.st_uid
== forUid
)) {
343 /* not what we want: get rid of it. */
344 if(fileType
== S_IFDIR
) {
345 /* directory: clean then remove */
346 if(cleanDir(filePath
, NULL
, 0)) {
347 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
349 if(rmdir(filePath
)) {
350 MSDebug("rmdir(%s) returned %d", filePath
, errno
);
351 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
355 if(unlink(filePath
)) {
356 MSDebug("unlink(%s) returned %d", filePath
, errno
);
357 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
361 /* caller should be somewhat happy */
366 * Determine if both of the specified DB files exist as accessible regular files with specified
367 * owner. Returns true if they do.
369 * If the purge argument is true, we'll ensure that either both files exist with
370 * the right owner, or neither of the files exist on exit. An error on that operation
371 * throws a CSSM_ERRCODE_MDS_ERROR CssmError exception (i.e., we're hosed).
372 * Returns the stat info for both files on success for further processing by caller.
374 static bool doFilesExist(
375 const char *objDbFile
,
376 const char *directDbFile
,
378 bool purge
, // false means "passive" check
379 struct stat
&objDbSb
, // RETURNED
380 struct stat
&directDbSb
) // RETURNED
383 bool objectExist
= doesFileExist(objDbFile
, forUid
, purge
, objDbSb
);
384 bool directExist
= doesFileExist(directDbFile
, forUid
, purge
, directDbSb
);
385 if(objectExist
&& directExist
) {
393 * At least one does not exist - ensure neither of them do.
394 * Note that if we got this far, we know the one that exists is a regular file
395 * so it's safe to just unlink it.
398 if(unlink(objDbFile
)) {
399 MSDebug("unlink(%s) returned %d", objDbFile
, errno
);
400 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
404 if(unlink(directDbFile
)) {
405 MSDebug("unlink(%s) returned %d", directDbFile
, errno
);
406 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
413 * Determine if specified directory exists with specified owner and mode.
414 * Returns true if copacetic, else returns false and also indicates
415 * via the directStatus out param what went wrong.
418 MDS_NotPresent
, /* nothing there */
419 MDS_NotDirectory
, /* not a directory */
420 MDS_BadOwnerMode
, /* wrong owner or mode */
421 MDS_Access
/* couldn't search the directories */
424 static bool doesDirectExist(
428 MdsDirectStatus
&directStatus
) /* RETURNED */
432 MSIoDbg("stat %s in doesDirectExist", dirPath
);
433 if (lstat(dirPath
, &sb
)) {
437 directStatus
= MDS_Access
;
440 directStatus
= MDS_NotPresent
;
442 /* Any others? Is this a good SWAG to handle the default? */
444 directStatus
= MDS_NotDirectory
;
449 mode_t fileType
= sb
.st_mode
& S_IFMT
;
450 if(fileType
!= S_IFDIR
) {
451 directStatus
= MDS_NotDirectory
;
454 if(sb
.st_uid
!= forUid
) {
455 directStatus
= MDS_BadOwnerMode
;
458 if((sb
.st_mode
& 07777) != mode
) {
459 directStatus
= MDS_BadOwnerMode
;
466 * Create specified directory if it doesn't already exist. If there is something
467 * already there that doesn't meet spec (not a directory, wrong mode, wrong owner)
468 * we'll do everything we can do delete what is there and then try to create it
471 * Returns an errno on any unrecoverable error.
473 static int createDir(
475 uid_t forUid
, // for checking - we don't try to set this
478 MdsDirectStatus directStatus
;
480 if(doesDirectExist(dirPath
, forUid
, dirMode
, directStatus
)) {
486 * Attempt recovery if there is *something* there.
487 * Anything other than "not present" should be considered to be a possible
491 switch(directStatus
) {
493 /* normal trivial case: proceed. */
496 case MDS_NotDirectory
:
497 /* there's a file or symlink in the way */
498 if(unlink(dirPath
)) {
500 MSDebug("createDir(%s): unlink() returned %d", dirPath
, rtn
);
505 case MDS_BadOwnerMode
:
507 * It's a directory; try to clean it out (which may well fail if we're
510 rtn
= cleanDir(dirPath
, NULL
, 0);
516 MSDebug("createDir(%s): rmdir() returned %d", dirPath
, rtn
);
522 case MDS_Access
: /* hopeless */
523 MSDebug("createDir(%s): access failure, bailing", dirPath
);
526 rtn
= mkdir(dirPath
, dirMode
);
529 MSDebug("createDir(%s): mkdir() returned %d", dirPath
, errno
);
532 /* make sure umask does't trick us */
533 rtn
= chmod(dirPath
, dirMode
);
535 MSDebug("chmod(%s) returned %d", dirPath
, errno
);
542 * Create an MDS session.
544 MDSSession::MDSSession (const Guid
*inCallerGuid
,
545 const CSSM_MEMORY_FUNCS
&inMemoryFunctions
) :
546 DatabaseSession(MDSModule::get().databaseManager()),
547 mCssmMemoryFunctions (inMemoryFunctions
),
548 mModule(MDSModule::get())
550 MSDebug("MDSSession::MDSSession");
552 mCallerGuidPresent
= inCallerGuid
!= nil
;
553 if (mCallerGuidPresent
) {
554 mCallerGuid
= *inCallerGuid
;
558 MDSSession::~MDSSession ()
560 MSDebug("MDSSession::~MDSSession");
564 MDSSession::terminate ()
566 MSDebug("MDSSession::terminate");
570 const char* kExceptionDeletePath
= "messages";
574 * Called by security server via MDS_Install().
577 MDSSession::install ()
580 // Installation requires root
582 if(geteuid() != (uid_t
)0) {
583 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
587 // install() is only (legitimately) called from securityd.
588 // Mark "server mode" so we don't end up waiting for ourselves when the databases open.
590 mModule
.setServerMode();
593 /* ensure MDS base directory exists with correct permissions */
594 if(createDir(MDS_BASE_DB_DIR
, MDS_SYSTEM_UID
, MDS_BASE_DB_DIR_MODE
)) {
595 MSDebug("Error creating base MDS dir; aborting.");
596 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
599 /* ensure the the system MDS DB directory exists with correct permissions */
600 if(createDir(MDS_SYSTEM_DB_DIR
, MDS_SYSTEM_UID
, MDS_SYSTEM_DB_DIR_MODE
)) {
601 MSDebug("Error creating system MDS dir; aborting.");
602 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
607 if(!lh
.obtainLock(MDS_INSTALL_LOCK_PATH
, DB_LOCK_TIMEOUT
)) {
608 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
612 * We own the whole MDS system. Clean everything out except for our lock
613 * (and the directory it's in :-)
616 const char *savedFile
= MDS_INSTALL_LOCK_NAME
;
617 if(cleanDir(MDS_SYSTEM_DB_DIR
, &savedFile
, 1)) {
618 /* this should never happen - we're root */
619 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
622 const char *savedFiles
[] = {MDS_SYSTEM_DB_COMP
, kExceptionDeletePath
};
623 if(cleanDir(MDS_BASE_DB_DIR
, savedFiles
, 2)) {
624 /* this should never happen - we're root */
625 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
629 * Do initial population of system DBs.
631 createSystemDatabases(CSSM_FALSE
, MDS_SYSTEM_DB_MODE
);
632 DbFilesInfo
dbFiles(*this, MDS_SYSTEM_DB_DIR
);
633 dbFiles
.updateSystemDbInfo(MDS_SYSTEM_PATH
, MDS_BUNDLE_PATH
);
641 // In this implementation, the uninstall() call is not supported since
642 // we do not allow programmatic deletion of the MDS databases.
646 MDSSession::uninstall ()
648 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
652 * Common private open routine given a full specified path.
654 CSSM_DB_HANDLE
MDSSession::dbOpen(const char *dbName
, bool batched
)
656 static CSSM_APPLEDL_OPEN_PARAMETERS batchOpenParams
= {
657 sizeof(CSSM_APPLEDL_OPEN_PARAMETERS
),
658 CSSM_APPLEDL_OPEN_PARAMETERS_VERSION
,
659 CSSM_FALSE
, // do not auto-commit
660 0 // mask - do not use following fields
663 MSDebug("Opening %s%s", dbName
, batched
? " in batch mode" : "");
664 MSIoDbg("open %s in dbOpen(name, batched)", dbName
);
665 CSSM_DB_HANDLE dbHand
;
666 DatabaseSession::DbOpen(dbName
,
669 NULL
, // AccessCred - hopefully optional
670 batched
? &batchOpenParams
: NULL
,
675 /* DatabaseSession routines we need to override */
676 void MDSSession::DbOpen(const char *DbName
,
677 const CSSM_NET_ADDRESS
*DbLocation
,
678 CSSM_DB_ACCESS_TYPE AccessRequest
,
679 const AccessCredentials
*AccessCred
,
680 const void *OpenParameters
,
681 CSSM_DB_HANDLE
&DbHandle
)
683 if (!mModule
.serverMode()) {
685 * Make sure securityd has finished initializing (system) MDS data.
686 * Note that activate() only does IPC once and retains global state after that.
688 SecurityServer::ClientSession
client(Allocator::standard(), Allocator::standard());
689 client
.activate(); /* contact securityd - won't return until MDS is ready */
692 /* make sure DBs are up-to-date */
696 * Only task here is map incoming DbName - specified in the CDSA
697 * spec - to a filename we actually use (which is a path to either
698 * a system MDS DB file or a per-user MDS DB file).
701 CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME
);
704 if(!strcmp(DbName
, MDS_OBJECT_DIRECTORY_NAME
)) {
705 dbName
= MDS_OBJECT_DB_NAME
;
707 else if(!strcmp(DbName
, MDS_CDSA_DIRECTORY_NAME
)) {
708 dbName
= MDS_DIRECT_DB_NAME
;
711 CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME
);
713 char fullPath
[MAXPATHLEN
];
714 dbFullPath(dbName
, fullPath
);
715 MSIoDbg("open %s in dbOpen(name, loc, accessReq...)", dbName
);
716 DatabaseSession::DbOpen(fullPath
, DbLocation
, AccessRequest
, AccessCred
,
717 OpenParameters
, DbHandle
);
720 CSSM_HANDLE
MDSSession::DataGetFirst(CSSM_DB_HANDLE DBHandle
,
721 const CssmQuery
*Query
,
722 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR Attributes
,
724 CSSM_DB_UNIQUE_RECORD_PTR
&UniqueId
)
727 return DatabaseSession::DataGetFirst(DBHandle
, Query
, Attributes
, Data
, UniqueId
);
732 MDSSession::GetDbNames(CSSM_NAME_LIST_PTR
&outNameList
)
734 outNameList
= new CSSM_NAME_LIST
[1];
735 outNameList
->NumStrings
= 2;
736 outNameList
->String
= new char*[2];
737 outNameList
->String
[0] = MDSCopyCstring(MDS_OBJECT_DIRECTORY_NAME
);
738 outNameList
->String
[1] = MDSCopyCstring(MDS_CDSA_DIRECTORY_NAME
);
742 MDSSession::FreeNameList(CSSM_NAME_LIST
&inNameList
)
744 delete [] inNameList
.String
[0];
745 delete [] inNameList
.String
[1];
746 delete [] inNameList
.String
;
749 void MDSSession::GetDbNameFromHandle(CSSM_DB_HANDLE DBHandle
,
752 printf("GetDbNameFromHandle: code on demand\n");
753 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
757 // Attempt to obtain an exclusive lock over the the MDS databases. The
758 // parameter is the maximum amount of time, in milliseconds, to spend
759 // trying to obtain the lock. A value of zero means to return failure
760 // right away if the lock cannot be obtained.
763 MDSSession::LockHelper::obtainLock(
764 const char *lockFile
, // e.g. MDS_INSTALL_LOCK_PATH
765 int timeout
) // default 0
769 secinfo("mdslock", "obtainLock: calling open(%s)", lockFile
);
770 mFD
= open(lockFile
, O_EXLOCK
| O_CREAT
| O_RDWR
, 0644);
773 secinfo("mdslock", "obtainLock: open error %d", errno
);
775 /* got a signal, go again */
779 /* theoretically should never happen */
784 secinfo("mdslock", "obtainLock: success");
791 // Release the exclusive lock over the MDS databases. If this session
792 // does not hold the lock, this method does nothing.
795 MDSSession::LockHelper::~LockHelper()
797 secinfo("mdslock", "releaseLock");
808 /* given DB file name, fill in fully specified path */
809 void MDSSession::dbFullPath(
811 char fullPath
[MAXPATHLEN
+1])
813 mModule
.getDbPath(fullPath
);
814 assert(fullPath
[0] != '\0');
815 strcat(fullPath
, "/");
816 strcat(fullPath
, dbName
);
820 * See if any per-user bundles exist in specified directory. Returns true if so.
821 * First the check for one entry....
823 static bool isBundle(
824 const struct dirent
*dp
)
829 /* NFS directories show up as DT_UNKNOWN */
837 int suffixLen
= strlen(MDS_BUNDLE_EXTEN
);
838 size_t len
= strlen(dp
->d_name
);
840 return (len
>= suffixLen
) &&
841 !strcmp(dp
->d_name
+ len
- suffixLen
, MDS_BUNDLE_EXTEN
);
844 /* now the full directory search */
845 static bool checkUserBundles(
846 const char *bundlePath
)
848 MSDebug("searching for user bundles in %s", bundlePath
);
849 DIR *dir
= opendir(bundlePath
);
855 while ((dp
= readdir(dir
)) != NULL
) {
857 /* any other checking to do? */
863 MSDebug("...%s bundle(s) found", rtn
? "" : "No");
867 #define COPY_BUF_SIZE 65536
870 * Single file copy with locking.
871 * Ensures that the source is a regular file with specified owner.
872 * Caller specifies mode of destination file.
873 * Throws a CssmError if the source file doesn't meet spec; throws a
874 * UnixError on any other error (which is generally recoverable by
875 * having the user MDS session use the system DB files).
877 static void safeCopyFile(
878 const char *fromPath
,
884 bool haveLock
= false;
888 char tmpToPath
[MAXPATHLEN
+1];
890 MSIoDbg("open %s, %s in safeCopyFile", fromPath
, toPath
);
892 if(!doesFileExist(fromPath
, fromUid
, false, sb
)) {
893 MSDebug("safeCopyFile: bad system DB file %s", fromPath
);
894 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
897 /* create temp destination */
898 snprintf(tmpToPath
, sizeof(tmpToPath
), "%s_", toPath
);
899 destFd
= open(tmpToPath
, O_WRONLY
| O_APPEND
| O_CREAT
| O_TRUNC
| O_EXCL
, toMode
);
902 MSDebug("Error %d opening user DB file %s\n", error
, tmpToPath
);
903 UnixError::throwMe(error
);
908 /* don't get tripped up by umask */
909 if(fchmod(destFd
, toMode
)) {
911 MSDebug("Error %d chmoding user DB file %s\n", error
, tmpToPath
);
912 UnixError::throwMe(error
);
915 /* open source for reading */
916 srcFd
= open(fromPath
, O_RDONLY
, 0);
919 MSDebug("Error %d opening system DB file %s\n", error
, fromPath
);
920 UnixError::throwMe(error
);
923 /* acquire the same kind of lock AtomicFile uses */
927 fl
.l_type
= F_RDLCK
; // AtomicFile gets F_WRLCK
928 fl
.l_whence
= SEEK_SET
;
930 // Keep trying to obtain the lock if we get interupted.
932 if (::fcntl(srcFd
, F_SETLKW
, &fl
) == -1) {
934 if (error
== EINTR
) {
938 MSDebug("Error %d locking system DB file %s\n", error
, fromPath
);
939 UnixError::throwMe(error
);
948 char *buf
= new char[COPY_BUF_SIZE
];
953 bytesRead
= read(srcFd
, buf
, COPY_BUF_SIZE
);
954 } while (bytesRead
< 0 && errno
== EINTR
);
962 MSDebug("Error %d reading system DB file %s\n", error
, fromPath
);
963 UnixError::throwMe(error
);
966 ssize_t bytesWritten
;
969 bytesWritten
= write(destFd
, buf
, bytesRead
);
970 } while (bytesWritten
< 0 && errno
== EINTR
);
972 if(bytesWritten
< 0) {
975 MSDebug("Error %d writing user DB file %s\n", error
, tmpToPath
);
976 UnixError::throwMe(error
);
982 /* error is nonzero, we'll re-throw below...still have some cleanup */
985 /* unlock source and close both */
988 if (::fcntl(srcFd
, F_SETLK
, &fl
) == -1) {
989 MSDebug("Error %d unlocking system DB file %s\n", errno
, fromPath
);
992 MSIoDbg("close %s, %s in safeCopyFile", fromPath
, tmpToPath
);
1000 /* commit temp file */
1001 if(::rename(tmpToPath
, toPath
)) {
1003 MSDebug("Error %d committing %s\n", error
, toPath
);
1007 UnixError::throwMe(error
);
1012 * Copy system DB files to specified user dir. Caller holds user DB lock.
1013 * Throws a UnixError on error.
1015 static void copySystemDbs(
1016 const char *userDbFileDir
)
1018 char toPath
[MAXPATHLEN
+1];
1020 snprintf(toPath
, sizeof(toPath
), "%s/%s", userDbFileDir
, MDS_OBJECT_DB_NAME
);
1021 safeCopyFile(MDS_OBJECT_DB_PATH
, MDS_SYSTEM_UID
, toPath
, MDS_USER_DB_MODE
);
1022 snprintf(toPath
, sizeof(toPath
), "%s/%s", userDbFileDir
, MDS_DIRECT_DB_NAME
);
1023 safeCopyFile(MDS_DIRECT_DB_PATH
, MDS_SYSTEM_UID
, toPath
, MDS_USER_DB_MODE
);
1027 * Ensure current DB files exist and are up-to-date.
1028 * Called from MDSSession constructor and from DataGetFirst, DbOpen, and any
1029 * other public functions which access a DB from scratch.
1031 void MDSSession::updateDataBases()
1033 RecursionBlock::Once
once(mUpdating
);
1035 return; // already updating; don't recurse
1037 uid_t ourUid
= geteuid();
1038 bool isRoot
= (ourUid
== 0);
1040 /* if we scanned recently, we're done */
1041 double delta
= mModule
.timeSinceLastScan();
1042 if(delta
< (double)MDS_SCAN_INTERVAL
) {
1047 * If we're root, the first thing we do is to ensure that system DBs are present.
1048 * Note that this is a necessary artifact of the problem behind Radar 3800811.
1049 * When that is fixed, install() should ONLY be called from the public MDS_Install()
1051 * Anyway, if we *do* have to install here, we're done.
1053 if(isRoot
&& !systemDatabasesPresent(false)) {
1055 mModule
.setDbPath(MDS_SYSTEM_DB_DIR
);
1056 mModule
.lastScanIsNow();
1061 * Obtain various per-user paths. Root is a special case but follows most
1062 * of the same logic from here on.
1064 std::string userDBFileDir
= GetMDSDBDir();
1065 std::string userObjDBFilePath
= GetMDSObjectDBPath();
1066 std::string userDirectDBFilePath
= GetMDSDirectDBPath();
1067 char userBundlePath
[MAXPATHLEN
+1];
1068 std::string userDbLockPath
= GetMDSDBLockPath();
1070 /* this means "no user bundles" */
1071 userBundlePath
[0] = '\0';
1073 char *userHome
= getenv("HOME");
1074 if((userHome
== NULL
) ||
1075 (strlen(userHome
) + strlen(MDS_USER_BUNDLE
) + 2) > sizeof(userBundlePath
)) {
1076 /* Can't check for user bundles */
1077 MSDebug("missing or invalid HOME; skipping user bundle check");
1079 /* TBD: any other checking of userHome? */
1081 snprintf(userBundlePath
, sizeof(userBundlePath
),
1082 "%s/%s", userHome
, MDS_USER_BUNDLE
);
1087 * Create the per-user directory...that's where the lock we'll be using lives.
1090 if(createDir(userDBFileDir
.c_str(), ourUid
, MDS_USER_DB_DIR_MODE
)) {
1092 * We'll just have to limp along using the read-only system DBs.
1093 * Note that this protects (somewhat) against the DoS attack in
1094 * Radar 3801292. The only problem is that this user won't be able
1095 * to use per-user bundles.
1097 MSDebug("Error creating user DBs; using system DBs");
1098 mModule
.setDbPath(MDS_SYSTEM_DB_DIR
);
1103 /* always release userLockFd no matter what happens */
1106 if(!lh
.obtainLock(userDbLockPath
.c_str(), DB_LOCK_TIMEOUT
)) {
1107 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
1113 * We copy the system DBs to the per-user DBs in two cases:
1114 * -- user DBs don't exist, or
1115 * -- system DBs have changed since the the last update to the user DBs.
1116 * This happens on smart card insertion and removal.
1118 bool doCopySystem
= false;
1119 struct stat userObjStat
, userDirectStat
;
1120 if(!doFilesExist(userObjDBFilePath
.c_str(), userDirectDBFilePath
.c_str(), ourUid
, true,
1121 userObjStat
, userDirectStat
)) {
1122 doCopySystem
= true;
1125 /* compare the two mdsDirectory.db files */
1126 MSIoDbg("stat %s, %s in updateDataBases",
1127 MDS_DIRECT_DB_PATH
, userDirectDBFilePath
.c_str());
1128 struct stat sysStat
;
1129 if (!stat(MDS_DIRECT_DB_PATH
, &sysStat
)) {
1130 doCopySystem
= (sysStat
.st_mtimespec
.tv_sec
> userDirectStat
.st_mtimespec
.tv_sec
) ||
1131 ((sysStat
.st_mtimespec
.tv_sec
== userDirectStat
.st_mtimespec
.tv_sec
) &&
1132 (sysStat
.st_mtimespec
.tv_nsec
> userDirectStat
.st_mtimespec
.tv_nsec
));
1134 MSDebug("user DB files obsolete at %s", userDBFileDir
.c_str());
1139 /* copy system DBs to user DBs */
1140 MSDebug("copying system DBs to user at %s", userDBFileDir
.c_str());
1141 copySystemDbs(userDBFileDir
.c_str());
1144 MSDebug("Using existing user DBs at %s", userDBFileDir
.c_str());
1147 catch(const CssmError
&cerror
) {
1149 * Bad system DB file detected. Fatal.
1155 * Error on delete or create user DBs; fall back on system DBs.
1157 MSDebug("doFilesExist(purge) error; using system DBs");
1158 mModule
.setDbPath(MDS_SYSTEM_DB_DIR
);
1163 MSDebug("Using system DBs only");
1167 * Update per-user DBs from both bundle sources (System bundles, user bundles)
1170 DbFilesInfo
dbFiles(*this, userDBFileDir
.c_str());
1171 dbFiles
.removeOutdatedPlugins();
1172 dbFiles
.updateSystemDbInfo(NULL
, MDS_BUNDLE_PATH
);
1173 if(userBundlePath
[0]) {
1174 /* skip for invalid or missing $HOME... */
1175 if(checkUserBundles(userBundlePath
)) {
1176 dbFiles
.updateForBundleDir(userBundlePath
);
1179 mModule
.setDbPath(userDBFileDir
.c_str());
1180 } /* main block protected by mLockFd */
1184 mModule
.lastScanIsNow();
1188 * Remove all records with specified guid (a.k.a. ModuleID) from specified DB.
1190 void MDSSession::removeRecordsForGuid(
1192 CSSM_DB_HANDLE dbHand
)
1194 // tell the DB to flush its intermediate data to disk
1195 PassThrough(dbHand
, CSSM_APPLEFILEDL_COMMIT
, NULL
, NULL
);
1196 CssmClient::Query query
= Attribute("ModuleID") == guid
;
1197 clearRecords(dbHand
, query
.cssmQuery());
1201 void MDSSession::clearRecords(CSSM_DB_HANDLE dbHand
, const CssmQuery
&query
)
1203 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
1204 CSSM_HANDLE resultHand
= DataGetFirst(dbHand
,
1209 if (resultHand
== CSSM_INVALID_HANDLE
)
1210 return; // no matches
1213 DataDelete(dbHand
, *record
);
1214 FreeUniqueRecord(dbHand
, *record
);
1216 } while (DataGetNext(dbHand
,
1223 FreeUniqueRecord(dbHand
, *record
);
1224 DataAbortQuery(dbHand
, resultHand
);
1230 * Determine if system databases are present.
1231 * If the purge argument is true, we'll ensure that either both or neither
1232 * DB files exist on exit; in that case caller must be holding MDS_INSTALL_LOCK_PATH.
1234 bool MDSSession::systemDatabasesPresent(bool purge
)
1240 * This can throw on a failed attempt to delete sole existing file....
1241 * But if that happens while we're root, our goose is fully cooked.
1243 struct stat objDbSb
, directDbSb
;
1244 if(doFilesExist(MDS_OBJECT_DB_PATH
, MDS_DIRECT_DB_PATH
,
1245 MDS_SYSTEM_UID
, purge
, objDbSb
, directDbSb
)) {
1256 * Given a DB name (which is used as an absolute path) and an array of
1257 * RelationInfos, create a DB.
1260 MDSSession::createSystemDatabase(
1262 const RelationInfo
*relationInfo
,
1263 unsigned numRelations
,
1264 CSSM_BOOL autoCommit
,
1266 CSSM_DB_HANDLE
&dbHand
) // RETURNED
1269 CSSM_DBINFO_PTR dbInfoP
= &dbInfo
;
1271 memset(dbInfoP
, 0, sizeof(CSSM_DBINFO
));
1272 dbInfoP
->NumberOfRecordTypes
= numRelations
;
1273 dbInfoP
->IsLocal
= CSSM_TRUE
; // TBD - what does this mean?
1274 dbInfoP
->AccessPath
= NULL
; // TBD
1276 /* alloc numRelations elements for parsingModule, recordAttr, and recordIndex
1278 unsigned size
= sizeof(CSSM_DB_PARSING_MODULE_INFO
) * numRelations
;
1279 dbInfoP
->DefaultParsingModules
= (CSSM_DB_PARSING_MODULE_INFO_PTR
)malloc(size
);
1280 memset(dbInfoP
->DefaultParsingModules
, 0, size
);
1281 size
= sizeof(CSSM_DB_RECORD_ATTRIBUTE_INFO
) * numRelations
;
1282 dbInfoP
->RecordAttributeNames
= (CSSM_DB_RECORD_ATTRIBUTE_INFO_PTR
)malloc(size
);
1283 memset(dbInfoP
->RecordAttributeNames
, 0, size
);
1284 size
= sizeof(CSSM_DB_RECORD_INDEX_INFO
) * numRelations
;
1285 dbInfoP
->RecordIndexes
= (CSSM_DB_RECORD_INDEX_INFO_PTR
)malloc(size
);
1286 memset(dbInfoP
->RecordIndexes
, 0, size
);
1288 /* cook up attribute and index info for each relation */
1290 for(relation
=0; relation
<numRelations
; relation
++) {
1291 const struct RelationInfo
*relp
= &relationInfo
[relation
]; // source
1292 CSSM_DB_RECORD_ATTRIBUTE_INFO_PTR attrInfo
=
1293 &dbInfoP
->RecordAttributeNames
[relation
]; // dest 1
1294 CSSM_DB_RECORD_INDEX_INFO_PTR indexInfo
=
1295 &dbInfoP
->RecordIndexes
[relation
]; // dest 2
1297 attrInfo
->DataRecordType
= relp
->DataRecordType
;
1298 attrInfo
->NumberOfAttributes
= relp
->NumberOfAttributes
;
1299 attrInfo
->AttributeInfo
= (CSSM_DB_ATTRIBUTE_INFO_PTR
)relp
->AttributeInfo
;
1301 indexInfo
->DataRecordType
= relp
->DataRecordType
;
1302 indexInfo
->NumberOfIndexes
= relp
->NumberOfIndexes
;
1303 indexInfo
->IndexInfo
= (CSSM_DB_INDEX_INFO_PTR
)relp
->IndexInfo
;
1306 /* set autocommit and mode */
1307 CSSM_APPLEDL_OPEN_PARAMETERS openParams
;
1308 memset(&openParams
, 0, sizeof(openParams
));
1309 openParams
.length
= sizeof(openParams
);
1310 openParams
.version
= CSSM_APPLEDL_OPEN_PARAMETERS_VERSION
;
1311 openParams
.autoCommit
= autoCommit
;
1312 openParams
.mask
= kCSSM_APPLEDL_MASK_MODE
;
1313 openParams
.mode
= mode
;
1319 CSSM_DB_ACCESS_READ
| CSSM_DB_ACCESS_WRITE
,
1320 NULL
, // CredAndAclEntry
1325 MSDebug("Error on DbCreate");
1326 free(dbInfoP
->DefaultParsingModules
);
1327 free(dbInfoP
->RecordAttributeNames
);
1328 free(dbInfoP
->RecordIndexes
);
1329 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR
);
1331 free(dbInfoP
->DefaultParsingModules
);
1332 free(dbInfoP
->RecordAttributeNames
);
1333 free(dbInfoP
->RecordIndexes
);
1338 * Create system databases from scratch if they do not already exist.
1339 * MDS_INSTALL_LOCK_PATH held on entry and exit. MDS_SYSTEM_DB_DIR assumed to
1340 * exist (that's our caller's job, before acquiring MDS_INSTALL_LOCK_PATH).
1341 * Returns true if we actually built the files, false if they already
1344 bool MDSSession::createSystemDatabases(
1345 CSSM_BOOL autoCommit
,
1348 CSSM_DB_HANDLE objectDbHand
= 0;
1349 CSSM_DB_HANDLE directoryDbHand
= 0;
1351 assert(geteuid() == (uid_t
)0);
1352 if(systemDatabasesPresent(true)) {
1353 /* both databases exist as regular files with correct owner - we're done */
1354 MSDebug("system DBs already exist");
1358 /* create two DBs - any exception here results in deleting both of them */
1359 MSDebug("Creating MDS DBs");
1361 createSystemDatabase(MDS_OBJECT_DB_PATH
, &kObjectRelation
, 1,
1362 autoCommit
, mode
, objectDbHand
);
1363 MSIoDbg("close objectDbHand in createSystemDatabases");
1364 DbClose(objectDbHand
);
1366 createSystemDatabase(MDS_DIRECT_DB_PATH
, kMDSRelationInfo
, kNumMdsRelations
,
1367 autoCommit
, mode
, directoryDbHand
);
1368 MSIoDbg("close directoryDbHand in createSystemDatabases");
1369 DbClose(directoryDbHand
);
1370 directoryDbHand
= 0;
1373 MSDebug("Error creating MDS DBs - deleting both DB files");
1374 unlink(MDS_OBJECT_DB_PATH
);
1375 unlink(MDS_DIRECT_DB_PATH
);
1382 * DbFilesInfo helper class
1385 /* Note both DB files MUST exist at construction time */
1386 MDSSession::DbFilesInfo::DbFilesInfo(
1387 MDSSession
&session
,
1388 const char *dbPath
) :
1394 assert(strlen(dbPath
) < MAXPATHLEN
);
1395 strcpy(mDbPath
, dbPath
);
1397 /* stat the two DB files, snag the later timestamp */
1398 char path
[MAXPATHLEN
];
1399 sprintf(path
, "%s/%s", mDbPath
, MDS_OBJECT_DB_NAME
);
1401 MSIoDbg("stat %s in DbFilesInfo()", path
);
1402 int rtn
= ::stat(path
, &sb
);
1405 MSDebug("Error %d statting DB file %s", error
, path
);
1406 UnixError::throwMe(error
);
1408 mLaterTimestamp
= sb
.st_mtimespec
.tv_sec
;
1409 sprintf(path
, "%s/%s", mDbPath
, MDS_DIRECT_DB_NAME
);
1410 MSIoDbg("stat %s in DbFilesInfo()", path
);
1411 rtn
= ::stat(path
, &sb
);
1414 MSDebug("Error %d statting DB file %s", error
, path
);
1415 UnixError::throwMe(error
);
1417 if(sb
.st_mtimespec
.tv_sec
> mLaterTimestamp
) {
1418 mLaterTimestamp
= sb
.st_mtimespec
.tv_sec
;
1422 MDSSession::DbFilesInfo::~DbFilesInfo()
1424 if(mObjDbHand
!= 0) {
1425 /* autocommit on, henceforth */
1426 mSession
.PassThrough(mObjDbHand
,
1427 CSSM_APPLEFILEDL_COMMIT
, NULL
, NULL
);
1428 MSIoDbg("close objectDbHand in ~DbFilesInfo()");
1429 mSession
.DbClose(mObjDbHand
);
1432 if(mDirectDbHand
!= 0) {
1433 mSession
.PassThrough(mDirectDbHand
,
1434 CSSM_APPLEFILEDL_COMMIT
, NULL
, NULL
);
1435 MSIoDbg("close mDirectDbHand in ~DbFilesInfo()");
1436 mSession
.DbClose(mDirectDbHand
);
1441 /* lazy evaluation of both DB handlesÊ*/
1442 CSSM_DB_HANDLE
MDSSession::DbFilesInfo::objDbHand()
1444 if(mObjDbHand
!= 0) {
1447 char fullPath
[MAXPATHLEN
+ 1];
1448 sprintf(fullPath
, "%s/%s", mDbPath
, MDS_OBJECT_DB_NAME
);
1449 MSIoDbg("open %s in objDbHand()", fullPath
);
1450 mObjDbHand
= mSession
.dbOpen(fullPath
, true); // batch mode
1454 CSSM_DB_HANDLE
MDSSession::DbFilesInfo::directDbHand()
1456 if(mDirectDbHand
!= 0) {
1457 return mDirectDbHand
;
1459 char fullPath
[MAXPATHLEN
+ 1];
1460 sprintf(fullPath
, "%s/%s", mDbPath
, MDS_DIRECT_DB_NAME
);
1461 MSIoDbg("open %s in directDbHand()", fullPath
);
1462 mDirectDbHand
= mSession
.dbOpen(fullPath
, true); // batch mode
1463 return mDirectDbHand
;
1467 * Update the info for Security.framework and the system bundles.
1469 void MDSSession::DbFilesInfo::updateSystemDbInfo(
1470 const char *systemPath
, // e.g., /System/Library/Frameworks
1471 const char *bundlePath
) // e.g., /System/Library/Security
1474 * Security.framework - CSSM and built-in modules - only for initial population of
1479 if (CFRef
<CFBundleRef
> me
= CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")))
1480 if (CFRef
<CFURLRef
> url
= CFBundleCopyBundleURL(me
))
1481 if (CFRef
<CFStringRef
> cfpath
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
))
1482 path
= cfString(cfpath
); // path to my bundle
1484 if (path
.empty()) // use system default
1485 path
= string(systemPath
) + "/" MDS_SYSTEM_FRAME
;
1486 updateForBundle(path
.c_str());
1489 /* Standard loadable bundles */
1490 updateForBundleDir(bundlePath
);
1494 MDSSession::DbFilesInfo::TbdRecord::TbdRecord(
1495 const CSSM_DATA
&guid
)
1497 assert(guid
.Length
<= MAX_GUID_LEN
);
1498 assert(guid
.Length
!= 0);
1499 memmove(mGuid
, guid
.Data
, guid
.Length
);
1500 if(mGuid
[guid
.Length
- 1] != '\0') {
1501 mGuid
[guid
.Length
] = '\0';
1506 * Test if plugin specified by pluginPath needs to be deleted from DBs.
1507 * If so, add to tbdVector.
1509 void MDSSession::DbFilesInfo::checkOutdatedPlugin(
1510 const CSSM_DATA
&pathValue
,
1511 const CSSM_DATA
&guidValue
,
1512 TbdVector
&tbdVector
)
1514 /* stat the specified plugin */
1516 bool obsolete
= false;
1517 string path
= CssmData::overlay(pathValue
).toString();
1518 if (!path
.empty() && path
[0] == '*') {
1519 /* builtin pseudo-path; never obsolete this */
1522 MSIoDbg("stat %s in checkOutdatedPlugin()", path
.c_str());
1523 int rtn
= ::stat(path
.c_str(), &sb
);
1525 /* not there or inaccessible; delete */
1528 else if(sb
.st_mtimespec
.tv_sec
> mLaterTimestamp
) {
1529 /* timestamp of plugin's main directory later than that of DBs */
1533 TbdRecord
*tbdRecord
= new TbdRecord(guidValue
);
1534 tbdVector
.push_back(tbdRecord
);
1535 MSDebug("checkOutdatedPlugin: flagging %s obsolete", path
.c_str());
1540 * Examine dbFiles.objDbHand; remove all fields associated with any bundle
1541 * i.e., with any path) which are either not present on disk, or which
1542 * have changed since dbFiles.laterTimestamp().
1544 void MDSSession::DbFilesInfo::removeOutdatedPlugins()
1547 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
1548 CSSM_HANDLE resultHand
;
1549 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
1550 CSSM_DB_ATTRIBUTE_DATA theAttrs
[2];
1551 CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo
;
1552 TbdVector tbdRecords
;
1555 * First, scan object directory. All we need are the path and GUID attributes.
1557 recordAttrs
.DataRecordType
= MDS_OBJECT_RECORDTYPE
;
1558 recordAttrs
.SemanticInformation
= 0;
1559 recordAttrs
.NumberOfAttributes
= 2;
1560 recordAttrs
.AttributeData
= theAttrs
;
1562 attrInfo
= &theAttrs
[0].Info
;
1563 attrInfo
->AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
1564 attrInfo
->Label
.AttributeName
= (char*) "ModuleID";
1565 attrInfo
->AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_STRING
;
1566 theAttrs
[0].NumberOfValues
= 0;
1567 theAttrs
[0].Value
= NULL
;
1568 attrInfo
= &theAttrs
[1].Info
;
1569 attrInfo
->AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
1570 attrInfo
->Label
.AttributeName
= (char*) "Path";
1571 attrInfo
->AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_STRING
;
1572 theAttrs
[1].NumberOfValues
= 0;
1573 theAttrs
[1].Value
= NULL
;
1575 /* just search by recordType, no predicates */
1576 query
.RecordType
= MDS_OBJECT_RECORDTYPE
;
1577 query
.Conjunctive
= CSSM_DB_NONE
;
1578 query
.NumSelectionPredicates
= 0;
1579 query
.SelectionPredicate
= NULL
;
1580 query
.QueryLimits
.TimeLimit
= 0; // FIXME - meaningful?
1581 query
.QueryLimits
.SizeLimit
= 1; // FIXME - meaningful?
1582 query
.QueryFlags
= 0; // CSSM_QUERY_RETURN_DATA...FIXME - used?
1584 CssmQuery
perryQuery(query
);
1586 resultHand
= mSession
.DataGetFirst(objDbHand(),
1593 MSDebug("removeOutdatedPlugins: DataGetFirst threw");
1597 mSession
.FreeUniqueRecord(mObjDbHand
, *record
);
1600 if(theAttrs
[0].NumberOfValues
&& theAttrs
[1].NumberOfValues
) {
1601 checkOutdatedPlugin(*theAttrs
[1].Value
, *theAttrs
[0].Value
,
1605 MSDebug("removeOutdatedPlugins: incomplete record found (1)!");
1607 for(unsigned dex
=0; dex
<2; dex
++) {
1608 CSSM_DB_ATTRIBUTE_DATA
*attr
= &theAttrs
[dex
];
1609 for (unsigned attrDex
=0; attrDex
<attr
->NumberOfValues
; attrDex
++) {
1610 if(attr
->Value
[attrDex
].Data
) {
1611 mSession
.free(attr
->Value
[attrDex
].Data
);
1615 mSession
.free(attr
->Value
);
1620 /* empty Object DB - we're done */
1621 MSDebug("removeOutdatedPlugins: empty object DB");
1625 /* now the rest of the object DB records */
1627 bool brtn
= mSession
.DataGetNext(objDbHand(),
1637 mSession
.FreeUniqueRecord(mObjDbHand
, *record
);
1639 if(theAttrs
[0].NumberOfValues
&& theAttrs
[1].NumberOfValues
) {
1640 checkOutdatedPlugin(*theAttrs
[1].Value
,
1645 MSDebug("removeOutdatedPlugins: incomplete record found (2)!");
1647 for(unsigned dex
=0; dex
<2; dex
++) {
1648 CSSM_DB_ATTRIBUTE_DATA
*attr
= &theAttrs
[dex
];
1649 for (unsigned attrDex
=0; attrDex
<attr
->NumberOfValues
; attrDex
++) {
1650 if(attr
->Value
[attrDex
].Data
) {
1651 mSession
.free(attr
->Value
[attrDex
].Data
);
1655 mSession
.free(attr
->Value
);
1659 /* no DataAbortQuery needed; we scanned until completion */
1662 * We have a vector of plugins to be deleted. Remove all records from both
1663 * DBs associated with the plugins, as specified by guid.
1665 size_t numRecords
= tbdRecords
.size();
1666 for(size_t i
=0; i
<numRecords
; i
++) {
1667 TbdRecord
*tbdRecord
= tbdRecords
[i
];
1668 mSession
.removeRecordsForGuid(tbdRecord
->guid(), objDbHand());
1669 mSession
.removeRecordsForGuid(tbdRecord
->guid(), directDbHand());
1671 for(size_t i
=0; i
<numRecords
; i
++) {
1672 delete tbdRecords
[i
];
1678 * Update DBs for all bundles in specified directory.
1680 void MDSSession::DbFilesInfo::updateForBundleDir(
1681 const char *bundleDirPath
)
1683 /* do this with readdir(); CFBundleCreateBundlesFromDirectory is
1684 * much too heavyweight */
1685 MSDebug("...updating DBs for dir %s", bundleDirPath
);
1686 DIR *dir
= opendir(bundleDirPath
);
1688 MSDebug("updateForBundleDir: error %d opening %s", errno
, bundleDirPath
);
1692 char fullPath
[MAXPATHLEN
];
1693 while ((dp
= readdir(dir
)) != NULL
) {
1695 sprintf(fullPath
, "%s/%s", bundleDirPath
, dp
->d_name
);
1696 updateForBundle(fullPath
);
1703 * lookup by path - just returns true if there is a record assoociated with the path
1706 bool MDSSession::DbFilesInfo::lookupForPath(
1710 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
1711 CSSM_HANDLE resultHand
= 0;
1712 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
1713 CSSM_DB_ATTRIBUTE_DATA theAttr
;
1714 CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo
= &theAttr
.Info
;
1715 CSSM_SELECTION_PREDICATE predicate
;
1718 recordAttrs
.DataRecordType
= MDS_OBJECT_RECORDTYPE
;
1719 recordAttrs
.SemanticInformation
= 0;
1720 recordAttrs
.NumberOfAttributes
= 1;
1721 recordAttrs
.AttributeData
= &theAttr
;
1723 attrInfo
->AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
1724 attrInfo
->Label
.AttributeName
= (char*) "Path";
1725 attrInfo
->AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_STRING
;
1727 theAttr
.NumberOfValues
= 0;
1728 theAttr
.Value
= NULL
;
1730 predicate
.DbOperator
= CSSM_DB_EQUAL
;
1731 predicate
.Attribute
.Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
1732 predicate
.Attribute
.Info
.Label
.AttributeName
= (char*) "Path";
1733 predicate
.Attribute
.Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_STRING
;
1734 predData
.Data
= (uint8
*)path
;
1735 predData
.Length
= strlen(path
);
1736 predicate
.Attribute
.Value
= &predData
;
1737 predicate
.Attribute
.NumberOfValues
= 1;
1739 query
.RecordType
= MDS_OBJECT_RECORDTYPE
;
1740 query
.Conjunctive
= CSSM_DB_NONE
;
1741 query
.NumSelectionPredicates
= 1;
1742 query
.SelectionPredicate
= &predicate
;
1743 query
.QueryLimits
.TimeLimit
= 0; // FIXME - meaningful?
1744 query
.QueryLimits
.SizeLimit
= 1; // FIXME - meaningful?
1745 query
.QueryFlags
= 0; // CSSM_QUERY_RETURN_DATA...FIXME - used?
1749 CssmQuery
perryQuery(query
);
1750 resultHand
= mSession
.DataGetFirst(objDbHand(),
1760 mSession
.FreeUniqueRecord(mObjDbHand
, *record
);
1765 if(resultHand
&& ourRtn
) {
1766 /* more resulting pending; terminate the search */
1768 mSession
.DataAbortQuery(mObjDbHand
, resultHand
);
1771 MSDebug("exception on DataAbortQuery in lookupForPath");
1774 for(unsigned dex
=0; dex
<theAttr
.NumberOfValues
; dex
++) {
1775 if(theAttr
.Value
[dex
].Data
) {
1776 mSession
.free(theAttr
.Value
[dex
].Data
);
1779 mSession
.free(theAttr
.Value
);
1783 /* update entry for one bundle, which is known to exist */
1784 void MDSSession::DbFilesInfo::updateForBundle(
1785 const char *bundlePath
)
1787 MSDebug("...updating DBs for bundle %s", bundlePath
);
1789 /* Quick lookup - do we have ANY entry for a bundle with this path? */
1790 if(lookupForPath(bundlePath
)) {
1791 /* Yep, we're done */
1794 MDSAttrParser
parser(bundlePath
,
1799 parser
.parseAttrs();
1801 catch (const CssmError
&err
) {
1802 // a corrupt MDS info file invalidates the entire plugin
1803 const char *guid
= parser
.guid();
1805 mSession
.removeRecordsForGuid(guid
, objDbHand());
1806 mSession
.removeRecordsForGuid(guid
, directDbHand());
1813 // Private API: add MDS records from contents of file
1814 // These files are typically written by securityd and handed to us in this call.
1816 void MDSSession::installFile(const MDS_InstallDefaults
*defaults
,
1817 const char *inBundlePath
, const char *subdir
, const char *file
)
1819 string bundlePath
= inBundlePath
? inBundlePath
: cfString(CFBundleGetMainBundle());
1820 DbFilesInfo
dbFiles(*this, MDS_SYSTEM_DB_DIR
);
1821 MDSAttrParser
parser(bundlePath
.c_str(),
1823 dbFiles
.objDbHand(),
1824 dbFiles
.directDbHand());
1825 parser
.setDefaults(defaults
);
1828 if (file
== NULL
) // parse a directory
1829 if (subdir
) // a particular directory
1830 parser
.parseAttrs(CFTempString(subdir
));
1831 else // all resources in bundle
1832 parser
.parseAttrs(NULL
);
1833 else // parse just one file
1834 parser
.parseFile(CFRef
<CFURLRef
>(makeCFURL(file
)), CFTempString(subdir
));
1836 catch (const CssmError
&err
) {
1837 const char *guid
= parser
.guid();
1839 removeRecordsForGuid(guid
, dbFiles
.objDbHand());
1840 removeRecordsForGuid(guid
, dbFiles
.directDbHand());
1847 // Private API: Remove all records for a guid/subservice
1849 // Note: Multicursors searching for SSID fail because not all records in the
1850 // database have this attribute. So we have to explicitly run through all tables
1853 void MDSSession::removeSubservice(const char *guid
, uint32 ssid
)
1855 DbFilesInfo
dbFiles(*this, MDS_SYSTEM_DB_DIR
);
1857 CssmClient::Query query
=
1858 Attribute("ModuleID") == guid
&&
1859 Attribute("SSID") == ssid
;
1861 // only CSP and DL tables are cleared here
1862 // (this function is private to securityd, which only handles those types)
1863 clearRecords(dbFiles
.directDbHand(),
1864 CssmQuery(query
.cssmQuery(), MDS_CDSADIR_CSP_PRIMARY_RECORDTYPE
));
1865 clearRecords(dbFiles
.directDbHand(),
1866 CssmQuery(query
.cssmQuery(), MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE
));
1867 clearRecords(dbFiles
.directDbHand(),
1868 CssmQuery(query
.cssmQuery(), MDS_CDSADIR_CSP_ENCAPSULATED_PRODUCT_RECORDTYPE
));
1869 clearRecords(dbFiles
.directDbHand(),
1870 CssmQuery(query
.cssmQuery(), MDS_CDSADIR_CSP_SC_INFO_RECORDTYPE
));
1871 clearRecords(dbFiles
.directDbHand(),
1872 CssmQuery(query
.cssmQuery(), MDS_CDSADIR_DL_PRIMARY_RECORDTYPE
));
1873 clearRecords(dbFiles
.directDbHand(),
1874 CssmQuery(query
.cssmQuery(), MDS_CDSADIR_DL_ENCAPSULATED_PRODUCT_RECORDTYPE
));
1878 } // end namespace Security