]> git.saurik.com Git - apple/security.git/blob - cdsa/mds/MDSSession.cpp
Security-54.1.tar.gz
[apple/security.git] / cdsa / mds / MDSSession.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 #include "MDSSession.h"
20
21 #include <Security/DbContext.h>
22 #include "MDSModule.h"
23 #include "MDSAttrParser.h"
24 #include "MDSAttrUtils.h"
25
26 #include <memory>
27 #include <Security/cssmerr.h>
28 #include <Security/utilities.h>
29 #include <Security/logging.h>
30 #include <Security/debugging.h>
31 #include <Security/mds_schema.h>
32
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <dirent.h>
36 #include <fcntl.h>
37 #include <assert.h>
38 #include <time.h>
39
40 /*
41 * The layout of the various MDS DB files on disk is as follows:
42 *
43 * /var/tmp/mds -- owner = root, mode = 01777, world writable, sticky
44 * mdsObject.db -- owner = root, mode = 0644, object DB
45 * mdsDirectory.db -- owner = root, mode = 0644, MDS directory DB
46 * mds.lock -- temporary, owner = root, protects creation of
47 * previous two files
48 * <uid>/ -- owner = <uid>, mode = 0644
49 * mdsObject.db -- owner = <uid>, mode = 0644, object DB
50 * mdsDirectory.db -- owner = <uid>, mode = 0644, MDS directory DB
51 * mds.lock -- temporary, owner = <uid>, protects creation of
52 * previous two files
53 *
54 * The /var/tmp/mds directory and the two db files in it are created by root
55 * via SS or an AEWP call. Each user except for root has their own private
56 * directory with two DB files and a lock. The first time a user accesses MDS,
57 * the per-user directory is created and the per-user DB files are created as
58 * copies of the system DB files. Fcntl() with a F_RDLCK is used to lock the system
59 * DB files when they are the source of these copies; this is the same mechanism
60 * used by the underlying AtomincFile.
61 *
62 * The sticky bit in /var/tmp/mds ensures that users cannot delete, rename, and/or
63 * replace the root-owned DB files in that directory, and that users can not
64 * modify other user's private MDS directories.
65 */
66 namespace Security
67 {
68
69 /*
70 * Nominal location of Security.framework.
71 */
72 #define MDS_SYSTEM_PATH "/System/Library/Frameworks"
73 #define MDS_SYSTEM_FRAME "Security.framework"
74
75 /*
76 * Nominal location of standard plugins.
77 */
78 #define MDS_BUNDLE_PATH "/System/Library/Security"
79 #define MDS_BUNDLE_EXTEN ".bundle"
80
81
82 /*
83 * Location of system MDS database and lock files.
84 */
85 #define MDS_SYSTEM_DB_DIR "/private/var/tmp/mds"
86 #define MDS_LOCK_FILE_NAME "mds.lock"
87 #define MDS_OBJECT_DB_NAME "mdsObject.db"
88 #define MDS_DIRECT_DB_NAME "mdsDirectory.db"
89 #define MDS_LOCK_FILE_PATH MDS_SYSTEM_DB_DIR "/" MDS_LOCK_FILE_NAME
90 #define MDS_OBJECT_DB_PATH MDS_SYSTEM_DB_DIR "/" MDS_OBJECT_DB_NAME
91 #define MDS_DIRECT_DB_PATH MDS_SYSTEM_DB_DIR "/" MDS_DIRECT_DB_NAME
92
93 /*
94 * Location of per-user bundles, relative to home directory.
95 * PEr-user DB files are in MDS_SYSTEM_DB_DIR/<uid>/.
96 */
97 #define MDS_USER_DB_DIR "Library/Security"
98 #define MDS_USER_BUNDLE "Library/Security"
99
100 /* time to wait in ms trying to acquire lock */
101 #define DB_LOCK_TIMEOUT (2 * 1000)
102
103 /* Minimum interval, in seconds, between rescans for plugin changes */
104 #define MDS_SCAN_INTERVAL 10
105
106 /* initial debug - start from scratch each time */
107 #define START_FROM_SCRATCH 0
108
109 /* debug - skip file-level locking */
110 #define SKIP_FILE_LOCKING 0
111
112 /* Only allow root to create and update system DB files - in the final config this
113 * will be true */
114 #define SYSTEM_MDS_ROOT_ONLY 0
115
116 /*
117 * Early development; no Security Server/root involvement with system DB creation.
118 * If this is true, SYSTEM_MDS_ROOT_ONLY must be false (though both can be
119 * false for intermediate testing).
120 */
121 #define SYSTEM_DBS_VIA_USER 1
122
123 /* when true, turn autocommit off when building system DB */
124 #define AUTO_COMMIT_OPT 1
125
126
127 /*
128 * Determine if both of the specified DB files exist as
129 * accessible regular files. Returns true if they do. If the purge argument
130 * is true, we'll ensure that either both or neither of the files exist on
131 * exit.
132 */
133 static bool doFilesExist(
134 const char *objDbFile,
135 const char *directDbFile,
136 bool purge) // false means "passive" check
137 {
138 struct stat sb;
139 bool objectExist = false;
140 bool directExist = false;
141
142 if (stat(objDbFile, &sb) == 0) {
143 /* Object DB exists */
144 if(!(sb.st_mode & S_IFREG)) {
145 MSDebug("deleting non-regular file %s", objDbFile);
146 if(purge && unlink(objDbFile)) {
147 MSDebug("unlink(%s) returned %d", objDbFile, errno);
148 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
149 }
150 }
151 else {
152 objectExist = true;
153 }
154 }
155 if (stat(directDbFile, &sb) == 0) {
156 /* directory DB exists */
157 if(!(sb.st_mode & S_IFREG)) {
158 MSDebug("deleting non-regular file %s", directDbFile);
159 if(purge & unlink(directDbFile)) {
160 MSDebug("unlink(%s) returned %d", directDbFile, errno);
161 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
162 }
163 }
164 directExist = true;
165 }
166 if(objectExist && directExist) {
167 /* both databases exist as regular files */
168 return true;
169 }
170 else if(!purge) {
171 return false;
172 }
173
174 /* at least one does not exist - ensure neither of them do */
175 if(objectExist) {
176 if(unlink(objDbFile)) {
177 MSDebug("unlink(%s) returned %d", objDbFile, errno);
178 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
179 }
180 }
181 if(directExist) {
182 if(unlink(directDbFile)) {
183 MSDebug("unlink(%s) returned %d", directDbFile, errno);
184 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
185 }
186 }
187 return false;
188 }
189
190 /*
191 * Determine if specified directory exists.
192 */
193 static bool doesDirectExist(
194 const char *dirPath)
195 {
196 struct stat sb;
197
198 if (stat(dirPath, &sb)) {
199 return false;
200 }
201 if(!(sb.st_mode & S_IFDIR)) {
202 return false;
203 }
204 return true;
205 }
206
207 /*
208 * Create specified directory if it doesn't already exist.
209 * Zero for mode means "use the default provided by 0755 modified by umask".
210 */
211 static int createDir(
212 const char *dirPath,
213 mode_t dirMode = 0)
214 {
215 if(doesDirectExist(dirPath)) {
216 return 0;
217 }
218 int rtn = mkdir(dirPath, 0755);
219 if(rtn) {
220 if(errno == EEXIST) {
221 /* this one's OK */
222 rtn = 0;
223 }
224 else {
225 rtn = errno;
226 MSDebug("mkdir(%s) returned %d", dirPath, errno);
227 }
228 }
229 if((rtn == 0) && (dirMode != 0)) {
230 rtn = chmod(dirPath, dirMode);
231 if(rtn) {
232 MSDebug("chmod(%s) returned %d", dirPath, errno);
233 }
234 }
235 return rtn;
236 }
237
238 /*
239 * Create an MDS session.
240 */
241 MDSSession::MDSSession (const Guid *inCallerGuid,
242 const CSSM_MEMORY_FUNCS &inMemoryFunctions) :
243 DatabaseSession(MDSModule::get().databaseManager()),
244 mCssmMemoryFunctions (inMemoryFunctions),
245 mModule(MDSModule::get()),
246 mLockFd(-1)
247 {
248 MSDebug("MDSSession::MDSSession");
249
250 #if START_FROM_SCRATCH
251 unlink(MDS_LOCK_FILE_PATH);
252 unlink(MDS_OBJECT_DB_PATH);
253 unlink(MDS_DIRECT_DB_PATH);
254 #endif
255
256 mCallerGuidPresent = inCallerGuid != nil;
257 if (mCallerGuidPresent)
258 mCallerGuid = *inCallerGuid;
259
260 /*
261 * Create DB files if necessary; make sure they are up-to-date
262 */
263 // no! done in either install or open! updateDataBases();
264 }
265
266 MDSSession::~MDSSession ()
267 {
268 MSDebug("MDSSession::~MDSSession");
269 releaseLock(mLockFd);
270 }
271
272 void
273 MDSSession::terminate ()
274 {
275 MSDebug("MDSSession::terminate");
276 releaseLock(mLockFd);
277 closeAll();
278 }
279
280 /*
281 * Called by security server or AEWP-executed privileged tool.
282 */
283 void
284 MDSSession::install ()
285 {
286 if((getuid() != (uid_t)0) && SYSTEM_MDS_ROOT_ONLY) {
287 CssmError::throwMe(CSSMERR_DL_OS_ACCESS_DENIED);
288 }
289
290 int sysFdLock = -1;
291 try {
292 /* before we obtain the lock, ensure the the system MDS DB directory exists */
293 if(createDir(MDS_SYSTEM_DB_DIR, 01777)) {
294 MSDebug("Error creating system MDS dir; aborting.");
295 CssmError::throwMe(CSSMERR_DL_OS_ACCESS_DENIED);
296 }
297
298 if(!obtainLock(MDS_LOCK_FILE_PATH, sysFdLock, DB_LOCK_TIMEOUT)) {
299 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
300 }
301 if(!systemDatabasesPresent(true)) {
302 bool created = createSystemDatabases();
303 if(created) {
304 /*
305 * Skip possible race condition in which this is called twice,
306 * both via SS by user procs who say "no system DBs present"
307 * in their updateDataBases() method.
308 *
309 * Do initial population of system DBs.
310 */
311 DbFilesInfo dbFiles(*this, MDS_SYSTEM_DB_DIR);
312 #if AUTO_COMMIT_OPT
313 dbFiles.autoCommit(CSSM_FALSE);
314 #endif
315 dbFiles.updateSystemDbInfo(MDS_SYSTEM_PATH, MDS_BUNDLE_PATH);
316 }
317 }
318 }
319 catch(...) {
320 if(sysFdLock != -1) {
321 releaseLock(sysFdLock);
322 }
323 throw;
324 }
325 releaseLock(sysFdLock);
326 }
327
328 //
329 // In this implementation, the uninstall() call is not supported since
330 // we do not allow programmatic deletion of the MDS databases.
331 //
332
333 void
334 MDSSession::uninstall ()
335 {
336 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
337 }
338
339 /*
340 * Common private open routine given a full specified path.
341 *
342 * FIXME: both of these dbOpen routines leak like crazy even though
343 * we know we close properly.
344 * Typical stack trace (from MallocDebug) of a leak is
345 *
346 * DatabaseSession::DbOpen(char const *, cssm_net_address const...)
347 * DatabaseManager::dbOpen(Security::DatabaseSession &, ...)
348 * Database::_dbOpen(Security::DatabaseSession &, unsigned long, ...)
349 * AppleDatabase::dbOpen(Security::DbContext &)
350 * DbModifier::openDatabase(void)
351 * DbModifier::getDbVersion(void)
352 * DbVersion::DbVersion(Security::AtomicFile &, ...)
353 * DbVersion::open(void)
354 * MetaRecord::unpackRecord(Security::ReadSection const &, ...)
355 * MetaRecord::unpackAttribute(Security::ReadSection const &, ...)
356 * MetaAttribute::unpackAttribute(Security::ReadSection const &, ..)
357 * TypedMetaAttribute<Security::StringValue>::unpackValue(...)
358 * TrackingAllocator::malloc(unsigned long)
359 */
360 CSSM_DB_HANDLE MDSSession::dbOpen(
361 const char *dbName)
362 {
363 MSDebug("Opening %s", dbName);
364 CSSM_DB_HANDLE dbHand;
365 DatabaseSession::DbOpen(dbName,
366 NULL, // DbLocation
367 CSSM_DB_ACCESS_READ,
368 NULL, // AccessCred - hopefully optional
369 NULL, // OpenParameters
370 dbHand);
371 return dbHand;
372 }
373
374
375 /* DatabaseSession routines we need to override */
376 void MDSSession::DbOpen(const char *DbName,
377 const CSSM_NET_ADDRESS *DbLocation,
378 CSSM_DB_ACCESS_TYPE AccessRequest,
379 const AccessCredentials *AccessCred,
380 const void *OpenParameters,
381 CSSM_DB_HANDLE &DbHandle)
382 {
383 /* make sure DBs are up-to-date */
384 updateDataBases();
385
386 /*
387 * Only task here is map incoming DbName - specified in the CDSA
388 * spec - to a filename we actually use (which is a path to either
389 * a system MDS DB file or a per-user MDS DB file).
390 */
391 if(DbName == NULL) {
392 CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME);
393 }
394 const char *dbName;
395 if(!strcmp(DbName, MDS_OBJECT_DIRECTORY_NAME)) {
396 dbName = MDS_OBJECT_DB_NAME;
397 }
398 else if(!strcmp(DbName, MDS_CDSA_DIRECTORY_NAME)) {
399 dbName = MDS_DIRECT_DB_NAME;
400 }
401 else {
402 CssmError::throwMe(CSSMERR_DL_INVALID_DB_NAME);
403 }
404 char fullPath[MAXPATHLEN];
405 dbFullPath(dbName, fullPath);
406 DatabaseSession::DbOpen(fullPath, DbLocation, AccessRequest, AccessCred,
407 OpenParameters, DbHandle);
408 }
409
410 void
411 MDSSession::GetDbNames(CSSM_NAME_LIST_PTR &outNameList)
412 {
413 outNameList = new CSSM_NAME_LIST[1];
414 outNameList->NumStrings = 2;
415 outNameList->String = new (char *)[2];
416 outNameList->String[0] = MDSCopyCstring(MDS_OBJECT_DIRECTORY_NAME);
417 outNameList->String[1] = MDSCopyCstring(MDS_CDSA_DIRECTORY_NAME);
418 }
419
420 void
421 MDSSession::FreeNameList(CSSM_NAME_LIST &inNameList)
422 {
423 delete [] inNameList.String[0];
424 delete [] inNameList.String[1];
425 delete [] inNameList.String;
426 }
427
428 void MDSSession::GetDbNameFromHandle(CSSM_DB_HANDLE DBHandle,
429 char **DbName)
430 {
431 printf("GetDbNameFromHandle: code on demand\n");
432 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
433 }
434
435 //
436 // Attempt to obtain an exclusive lock over the the MDS databases. The
437 // parameter is the maximum amount of time, in milliseconds, to spend
438 // trying to obtain the lock. A value of zero means to return failure
439 // right away if the lock cannot be obtained.
440 //
441 bool
442 MDSSession::obtainLock(
443 const char *lockFile, // e.g. MDS_LOCK_FILE_PATH
444 int &fd, // IN/OUT
445 int timeout) // default 0
446 {
447 #if SKIP_FILE_LOCKING
448 return true;
449 #else
450
451 static const int kRetryDelay = 250; // ms
452
453 fd = open(MDS_LOCK_FILE_PATH, O_CREAT | O_EXCL, 0544);
454 while (fd == -1 && timeout >= kRetryDelay) {
455 timeout -= kRetryDelay;
456 usleep(1000 * kRetryDelay);
457 mLockFd = open(MDS_LOCK_FILE_PATH, O_CREAT | O_EXCL, 0544);
458 }
459
460 return (fd != -1);
461 #endif /* SKIP_FILE_LOCKING */
462 }
463
464 //
465 // Release the exclusive lock over the MDS databases. If this session
466 // does not hold the lock, this method does nothing.
467 //
468
469 void
470 MDSSession::releaseLock(int &fd)
471 {
472 #if !SKIP_FILE_LOCKING
473 if (fd != -1) {
474 close(fd);
475 unlink(MDS_LOCK_FILE_PATH);
476 fd = -1;
477 }
478 #endif
479 }
480
481 /* given DB file name, fill in fully specified path */
482 void MDSSession::dbFullPath(
483 const char *dbName,
484 char fullPath[MAXPATHLEN+1])
485 {
486 mModule.getDbPath(fullPath);
487 assert(fullPath[0] != '\0');
488 strcat(fullPath, "/");
489 strcat(fullPath, dbName);
490 }
491
492 /*
493 * See if any per-user bundles exist in specified directory. Returns true if so.
494 * First the check for one entry....
495 */
496 static bool isBundle(
497 const struct dirent *dp)
498 {
499 if(dp == NULL) {
500 return false;
501 }
502 /* NFS directories show up as DT_UNKNOWN */
503 switch(dp->d_type) {
504 case DT_UNKNOWN:
505 case DT_DIR:
506 break;
507 default:
508 return false;
509 }
510 int suffixLen = strlen(MDS_BUNDLE_EXTEN);
511 int len = strlen(dp->d_name);
512
513 return (len >= suffixLen) &&
514 !strcmp(dp->d_name + len - suffixLen, MDS_BUNDLE_EXTEN);
515 }
516
517 /* now the full directory search */
518 static bool checkUserBundles(
519 const char *bundlePath)
520 {
521 MSDebug("searching for user bundles in %s", bundlePath);
522 DIR *dir = opendir(bundlePath);
523 if (dir == NULL) {
524 return false;
525 }
526 struct dirent *dp;
527 bool rtn = false;
528 while ((dp = readdir(dir)) != NULL) {
529 if(isBundle(dp)) {
530 /* any other checking to do? */
531 rtn = true;
532 break;
533 }
534 }
535 closedir(dir);
536 MSDebug("...%s bundle(s) found", rtn ? "" : "No");
537 return rtn;
538 }
539
540 #define COPY_BUF_SIZE 1024
541
542 /* Single file copy with locking */
543 static void safeCopyFile(
544 const char *fromPath,
545 const char *toPath)
546 {
547 /* open source for reading */
548 int srcFd = open(fromPath, O_RDONLY, 0);
549 if(srcFd < 0) {
550 /* FIXME - what error would we see if the file is locked for writing
551 * by someone else? We definitely have to handle that. */
552 int error = errno;
553 MSDebug("Error %d opening system DB file %s\n", error, fromPath);
554 UnixError::throwMe(error);
555 }
556
557 /* acquire the same kind of lock AtomicFile uses */
558 struct flock fl;
559 fl.l_start = 0;
560 fl.l_len = 1;
561 fl.l_pid = getpid();
562 fl.l_type = F_RDLCK; // AtomicFile gets F_WRLCK
563 fl.l_whence = SEEK_SET;
564
565 // Keep trying to obtain the lock if we get interupted.
566 for (;;) {
567 if (::fcntl(srcFd, F_SETLKW, reinterpret_cast<int>(&fl)) == -1) {
568 int error = errno;
569 if (error == EINTR) {
570 continue;
571 }
572 MSDebug("Error %d locking system DB file %s\n", error, fromPath);
573 UnixError::throwMe(error);
574 }
575 else {
576 break;
577 }
578 }
579
580 /* create destination */
581 int destFd = open(toPath, O_WRONLY | O_APPEND | O_CREAT | O_TRUNC | O_EXCL, 0644);
582 if(destFd < 0) {
583 int error = errno;
584 MSDebug("Error %d opening user DB file %s\n", error, toPath);
585 UnixError::throwMe(error);
586 }
587
588 /* copy */
589 char buf[COPY_BUF_SIZE];
590 while(1) {
591 int bytesRead = read(srcFd, buf, COPY_BUF_SIZE);
592 if(bytesRead == 0) {
593 break;
594 }
595 if(bytesRead < 0) {
596 int error = errno;
597 MSDebug("Error %d reading system DB file %s\n", error, fromPath);
598 UnixError::throwMe(error);
599 }
600 int bytesWritten = write(destFd, buf, bytesRead);
601 if(bytesWritten < 0) {
602 int error = errno;
603 MSDebug("Error %d writing user DB file %s\n", error, toPath);
604 UnixError::throwMe(error);
605 }
606 }
607
608 /* unlock source and close both */
609 fl.l_type = F_UNLCK;
610 if (::fcntl(srcFd, F_SETLK, reinterpret_cast<int>(&fl)) == -1) {
611 MSDebug("Error %d unlocking system DB file %s\n", errno, fromPath);
612 }
613 close(srcFd);
614 close(destFd);
615 }
616
617 /* Copy system DB files to specified user dir. */
618 static void copySystemDbs(
619 const char *userDbFileDir)
620 {
621 char toPath[MAXPATHLEN+1];
622
623 sprintf(toPath, "%s/%s", userDbFileDir, MDS_OBJECT_DB_NAME);
624 safeCopyFile(MDS_OBJECT_DB_PATH, toPath);
625 sprintf(toPath, "%s/%s", userDbFileDir, MDS_DIRECT_DB_NAME);
626 safeCopyFile(MDS_DIRECT_DB_PATH, toPath);
627 }
628
629 /*
630 * Ensure current DB files exist and are up-to-date.
631 * Called from MDSSession constructor and from DataGetFirst, DbOpen, and any
632 * other public functions which access a DB from scratch.
633 */
634 void MDSSession::updateDataBases()
635 {
636 bool isRoot = (getuid() == (uid_t)0);
637 bool createdSystemDb = false;
638
639 /*
640 * The first thing we do is to ensure that system DBs are present.
641 * This call right here is the reason for the purge argument in
642 * systemDatabasesPresent(); if we're a user proc, we can't grab the system
643 * MDS lock.
644 */
645 if(!systemDatabasesPresent(false)) {
646 if(isRoot || SYSTEM_DBS_VIA_USER) {
647 /* Either doing actual MDS op as root, or development case:
648 * install as current user */
649 install();
650 }
651 else {
652 /* This path TBD; it involves either a SecurityServer RPC or
653 * a privileged tool exec'd via AEWP. */
654 assert(0);
655 }
656 /* remember this - we have to delete possible existing user DBs */
657 createdSystemDb = true;
658 }
659
660 /* if we scanned recently, we're done */
661 double delta = mModule.timeSinceLastScan();
662 if(delta < (double)MDS_SCAN_INTERVAL) {
663 return;
664 }
665
666 /*
667 * Obtain various per-user paths. Root is a special case but follows most
668 * of the same logic from here on.
669 */
670 char userDbFileDir[MAXPATHLEN+1];
671 char userObjDbFilePath[MAXPATHLEN+1];
672 char userDirectDbFilePath[MAXPATHLEN+1];
673 char userBundlePath[MAXPATHLEN+1];
674 char userDbLockPath[MAXPATHLEN+1];
675
676 if(isRoot) {
677 strcat(userDbFileDir, MDS_SYSTEM_DB_DIR);
678 /* no userBundlePath */
679 }
680 else {
681 char *userHome = getenv("HOME");
682 if(userHome == NULL) {
683 /* FIXME - what now, batman? */
684 MSDebug("updateDataBases: no HOME");
685 userHome = "/";
686 }
687 sprintf(userBundlePath, "%s/%s", userHome, MDS_USER_BUNDLE);
688
689 /* DBs go in a per-UID directory in the system MDS DB directory */
690 sprintf(userDbFileDir, "%s/%d", MDS_SYSTEM_DB_DIR, (int)(getuid()));
691 }
692 sprintf(userObjDbFilePath, "%s/%s", userDbFileDir, MDS_OBJECT_DB_NAME);
693 sprintf(userDirectDbFilePath, "%s/%s", userDbFileDir, MDS_DIRECT_DB_NAME);
694 sprintf(userDbLockPath, "%s/%s", userDbFileDir, MDS_LOCK_FILE_NAME);
695
696 /*
697 * Create the per-user directory first...that's where the lock we'll be using
698 * lives. Our createDir() is tolerant of EEXIST errors.
699 */
700 if(!isRoot) {
701 if(createDir(userDbFileDir)) {
702 /* We'll just have to limp along using the read-only system DBs */
703 Syslog::alert("Error creating %s", userDbFileDir);
704 MSDebug("Error creating user DBs; using system DBs");
705 mModule.setDbPath(MDS_SYSTEM_DB_DIR);
706 return;
707 }
708 }
709
710 /* always release mLockFd no matter what happens */
711 if(!obtainLock(userDbLockPath, mLockFd, DB_LOCK_TIMEOUT)) {
712 CssmError::throwMe(CSSM_ERRCODE_MDS_ERROR);
713 }
714 try {
715 if(!isRoot) {
716 if(createdSystemDb) {
717 /* initial creation of system DBs by user - start from scratch */
718 unlink(userObjDbFilePath);
719 unlink(userDirectDbFilePath);
720 }
721
722 /*
723 * System DBs exist and are as up-to-date as we are allowed to make them.
724 * Create per-user DBs if they don't exist.
725 */
726 if(createdSystemDb || //Êoptimization - if this is true, the
727 // per-user DBs do not exist since we just
728 // deleted them
729 !doFilesExist(userObjDbFilePath, userDirectDbFilePath,
730 true)) {
731
732 /* copy system DBs to user DBs */
733 MSDebug("copying system DBs to user at %s", userDbFileDir);
734 copySystemDbs(userDbFileDir);
735 }
736 else {
737 MSDebug("Using existing user DBs at %s", userDbFileDir);
738 }
739 }
740 else {
741 MSDebug("Using system DBs only");
742 }
743
744 /*
745 * Update per-user DBs from all three sources (System.framework,
746 * System bundles, user bundles) as appropriate. Note that if we
747 * just created the system DBs, we don't have to update with
748 * respect to system framework or system bundles.
749 */
750 DbFilesInfo dbFiles(*this, userDbFileDir);
751 if(!createdSystemDb) {
752 dbFiles.removeOutdatedPlugins();
753 dbFiles.updateSystemDbInfo(MDS_SYSTEM_PATH, MDS_BUNDLE_PATH);
754 }
755 if(!isRoot) {
756 /* root doesn't have user bundles */
757 if(checkUserBundles(userBundlePath)) {
758 dbFiles.updateForBundleDir(userBundlePath);
759 }
760 }
761 mModule.setDbPath(userDbFileDir);
762 } /* main block protected by mLockFd */
763 catch(...) {
764 releaseLock(mLockFd);
765 throw;
766 }
767 mModule.lastScanIsNow();
768 releaseLock(mLockFd);
769 }
770
771 /*
772 * Remove all records with specified guid (a.k.a. ModuleID) from specified DB.
773 */
774 void MDSSession::removeRecordsForGuid(
775 const char *guid,
776 CSSM_DB_HANDLE dbHand)
777 {
778 CSSM_QUERY query;
779 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
780 CSSM_HANDLE resultHand;
781 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
782 CSSM_SELECTION_PREDICATE predicate;
783 CSSM_DATA predData;
784
785 /* don't want any attributes back, just a record ptr */
786 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_ANY;
787 recordAttrs.SemanticInformation = 0;
788 recordAttrs.NumberOfAttributes = 0;
789 recordAttrs.AttributeData = NULL;
790
791 /* one predicate, == guid */
792 predicate.DbOperator = CSSM_DB_EQUAL;
793 predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
794 predicate.Attribute.Info.Label.AttributeName = "ModuleID";
795 predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
796 predData.Data = (uint8 *)guid;
797 predData.Length = strlen(guid) + 1;
798 predicate.Attribute.Value = &predData;
799 predicate.Attribute.NumberOfValues = 1;
800
801 query.RecordType = CSSM_DL_DB_RECORD_ANY;
802 query.Conjunctive = CSSM_DB_NONE;
803 query.NumSelectionPredicates = 1;
804 query.SelectionPredicate = &predicate;
805 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful?
806 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful?
807 query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA...FIXME - used?
808
809 /*
810 * Each search starts from scratch - not sure if we can delete a record
811 * associated with an active query and continue on with that query.
812 */
813 try {
814 for(;;) {
815 DLQuery perryQuery(query);
816 resultHand = DataGetFirst(dbHand,
817 &perryQuery,
818 &recordAttrs,
819 NULL, // No data
820 record);
821 if(resultHand) {
822 try {
823 MSDebug("...deleting a record for guid %s", guid);
824 DataDelete(dbHand, *record);
825 DataAbortQuery(dbHand, resultHand);
826 }
827 catch(...) {
828 MSDebug("exception (1) while deleting record for guid %s", guid);
829 /* proceed.... */
830 }
831 }
832 else if(record) {
833 FreeUniqueRecord(dbHand, *record);
834 break;
835 }
836 } /* main loop */
837 }
838 catch (...) {
839 MSDebug("exception (2) while deleting record for guid %s", guid);
840 }
841 }
842
843 /*
844 * Determine if system databases are present.
845 * If the purge argument is true, we'll ensure that either both or neither
846 * DB files exist on exit; in that case caller need to hold MDS_LOCK_FILE_PATH.
847 */
848 bool MDSSession::systemDatabasesPresent(bool purge)
849 {
850 bool rtn = false;
851
852 try {
853 /* this can throw on a failed attempt to delete sole existing file */
854 if(doFilesExist(MDS_OBJECT_DB_PATH, MDS_DIRECT_DB_PATH, purge)) {
855 rtn = true;
856 }
857 }
858 catch(...) {
859
860 }
861 return rtn;
862 }
863
864 /*
865 * Given a DB name (which is used as an absolute path) and an array of
866 * RelationInfos, create a DB.
867 */
868 void
869 MDSSession::createSystemDatabase(
870 const char *dbName,
871 const RelationInfo *relationInfo,
872 unsigned numRelations,
873 CSSM_DB_HANDLE &dbHand) // RETURNED
874 {
875 CSSM_DBINFO dbInfo;
876 CSSM_DBINFO_PTR dbInfoP = &dbInfo;
877
878 memset(dbInfoP, 0, sizeof(CSSM_DBINFO));
879 dbInfoP->NumberOfRecordTypes = numRelations;
880 dbInfoP->IsLocal = CSSM_TRUE; // TBD - what does this mean?
881 dbInfoP->AccessPath = NULL; // TBD
882
883 /* alloc numRelations elements for parsingModule, recordAttr, and recordIndex
884 * info arrays */
885 unsigned size = sizeof(CSSM_DB_PARSING_MODULE_INFO) * numRelations;
886 dbInfoP->DefaultParsingModules = (CSSM_DB_PARSING_MODULE_INFO_PTR)malloc(size);
887 memset(dbInfoP->DefaultParsingModules, 0, size);
888 size = sizeof(CSSM_DB_RECORD_ATTRIBUTE_INFO) * numRelations;
889 dbInfoP->RecordAttributeNames = (CSSM_DB_RECORD_ATTRIBUTE_INFO_PTR)malloc(size);
890 memset(dbInfoP->RecordAttributeNames, 0, size);
891 size = sizeof(CSSM_DB_RECORD_INDEX_INFO) * numRelations;
892 dbInfoP->RecordIndexes = (CSSM_DB_RECORD_INDEX_INFO_PTR)malloc(size);
893 memset(dbInfoP->RecordIndexes, 0, size);
894
895 /* cook up attribute and index info for each relation */
896 unsigned relation;
897 for(relation=0; relation<numRelations; relation++) {
898 const struct RelationInfo *relp = &relationInfo[relation]; // source
899 CSSM_DB_RECORD_ATTRIBUTE_INFO_PTR attrInfo =
900 &dbInfoP->RecordAttributeNames[relation]; // dest 1
901 CSSM_DB_RECORD_INDEX_INFO_PTR indexInfo =
902 &dbInfoP->RecordIndexes[relation]; // dest 2
903
904 attrInfo->DataRecordType = relp->DataRecordType;
905 attrInfo->NumberOfAttributes = relp->NumberOfAttributes;
906 attrInfo->AttributeInfo = (CSSM_DB_ATTRIBUTE_INFO_PTR)relp->AttributeInfo;
907
908 indexInfo->DataRecordType = relp->DataRecordType;
909 indexInfo->NumberOfIndexes = relp->NumberOfIndexes;
910 indexInfo->IndexInfo = (CSSM_DB_INDEX_INFO_PTR)relp->IndexInfo;
911 }
912
913 try {
914 DbCreate(dbName,
915 NULL, // DbLocation
916 *dbInfoP,
917 CSSM_DB_ACCESS_READ | CSSM_DB_ACCESS_WRITE,
918 NULL, // CredAndAclEntry
919 NULL, // OpenParameters
920 dbHand);
921 }
922 catch(...) {
923 MSDebug("Error on DbCreate");
924 free(dbInfoP->DefaultParsingModules);
925 free(dbInfoP->RecordAttributeNames);
926 free(dbInfoP->RecordIndexes);
927 throw;
928 }
929 free(dbInfoP->DefaultParsingModules);
930 free(dbInfoP->RecordAttributeNames);
931 free(dbInfoP->RecordIndexes);
932
933 }
934
935 /*
936 * Create system databases from scratch if they do not already exist.
937 * MDS_LOCK_FILE_PATH held on entry and exit. MDS_SYSTEM_DB_DIR assumed to
938 * exist (that's our caller's job, before acquiring MDS_LOCK_FILE_PATH).
939 * Returns true if we actually built the files, false if they already
940 * existed.
941 */
942 bool MDSSession::createSystemDatabases()
943 {
944 CSSM_DB_HANDLE objectDbHand = 0;
945 CSSM_DB_HANDLE directoryDbHand = 0;
946
947 assert((getuid() == (uid_t)0) || !SYSTEM_MDS_ROOT_ONLY);
948 if(systemDatabasesPresent(true)) {
949 /* both databases exist as regular files - we're done */
950 MSDebug("system DBs already exist");
951 return false;
952 }
953
954 /* create two DBs - any exception here results in deleting both of them */
955 MSDebug("Creating MDS DBs");
956 try {
957 createSystemDatabase(MDS_OBJECT_DB_PATH, &kObjectRelation, 1, objectDbHand);
958 DbClose(objectDbHand);
959 objectDbHand = 0;
960 createSystemDatabase(MDS_DIRECT_DB_PATH, kMDSRelationInfo, kNumMdsRelations,
961 directoryDbHand);
962 DbClose(directoryDbHand);
963 directoryDbHand = 0;
964 }
965 catch (...) {
966 MSDebug("Error creating MDS DBs - deleting both DB files");
967 unlink(MDS_OBJECT_DB_PATH);
968 unlink(MDS_DIRECT_DB_PATH);
969 throw;
970 }
971 return true;
972 }
973
974 /*
975 * DbFilesInfo helper class
976 */
977
978 /* Note both DB files MUST exist at construction time */
979 MDSSession::DbFilesInfo::DbFilesInfo(
980 MDSSession &session,
981 const char *dbPath) :
982 mSession(session),
983 mObjDbHand(0),
984 mDirectDbHand(0),
985 mLaterTimestamp(0)
986 {
987 assert(strlen(dbPath) < MAXPATHLEN);
988 strcpy(mDbPath, dbPath);
989
990 /* stat the two DB files, snag the later timestamp */
991 char path[MAXPATHLEN];
992 sprintf(path, "%s/%s", mDbPath, MDS_OBJECT_DB_NAME);
993 struct stat sb;
994 int rtn = ::stat(path, &sb);
995 if(rtn) {
996 int error = errno;
997 MSDebug("Error %d statting DB file %s", error, path);
998 UnixError::throwMe(error);
999 }
1000 mLaterTimestamp = sb.st_mtimespec.tv_sec;
1001 sprintf(path, "%s/%s", mDbPath, MDS_DIRECT_DB_NAME);
1002 rtn = ::stat(path, &sb);
1003 if(rtn) {
1004 int error = errno;
1005 MSDebug("Error %d statting DB file %s", error, path);
1006 UnixError::throwMe(error);
1007 }
1008 if(sb.st_mtimespec.tv_sec > mLaterTimestamp) {
1009 mLaterTimestamp = sb.st_mtimespec.tv_sec;
1010 }
1011 }
1012
1013 #define AUTO_COMMIT_OFF_ON_CLOSE 1
1014
1015 MDSSession::DbFilesInfo::~DbFilesInfo()
1016 {
1017 if(mObjDbHand != 0) {
1018 #if AUTO_COMMIT_OPT && AUTO_COMMIT_OFF_ON_CLOSE
1019 mSession.PassThrough(mObjDbHand,
1020 CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
1021 reinterpret_cast<void *>(CSSM_TRUE),
1022 NULL);
1023 #endif
1024 mSession.DbClose(mObjDbHand);
1025 mObjDbHand = 0;
1026 }
1027 if(mDirectDbHand != 0) {
1028 #if AUTO_COMMIT_OPT && AUTO_COMMIT_OFF_ON_CLOSE
1029 mSession.PassThrough(mDirectDbHand,
1030 CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
1031 reinterpret_cast<void *>(CSSM_TRUE),
1032 NULL);
1033 #endif
1034 mSession.DbClose(mDirectDbHand);
1035 mDirectDbHand = 0;
1036 }
1037 }
1038
1039 /* lazy evaluation of both DB handlesÊ*/
1040 CSSM_DB_HANDLE MDSSession::DbFilesInfo::objDbHand()
1041 {
1042 if(mObjDbHand != 0) {
1043 return mObjDbHand;
1044 }
1045 char fullPath[MAXPATHLEN + 1];
1046 sprintf(fullPath, "%s/%s", mDbPath, MDS_OBJECT_DB_NAME);
1047 mObjDbHand = mSession.dbOpen(fullPath);
1048 return mObjDbHand;
1049 }
1050
1051 CSSM_DB_HANDLE MDSSession::DbFilesInfo::directDbHand()
1052 {
1053 if(mDirectDbHand != 0) {
1054 return mDirectDbHand;
1055 }
1056 char fullPath[MAXPATHLEN + 1];
1057 sprintf(fullPath, "%s/%s", mDbPath, MDS_DIRECT_DB_NAME);
1058 mDirectDbHand = mSession.dbOpen(fullPath);
1059 return mDirectDbHand;
1060 }
1061
1062 /*
1063 * Update the info for System.framework and the system bundles.
1064 */
1065 void MDSSession::DbFilesInfo::updateSystemDbInfo(
1066 const char *systemPath, // e.g., /System/Library/Frameworks
1067 const char *bundlePath) // e.g., /System/Library/Security
1068 {
1069 /* System.framework - CSSM and built-in modules */
1070 char fullPath[MAXPATHLEN];
1071 sprintf(fullPath, "%s/%s", systemPath, MDS_SYSTEM_FRAME);
1072 updateForBundle(fullPath);
1073
1074 /* Standard loadable bundles */
1075 updateForBundleDir(bundlePath);
1076 }
1077
1078
1079 MDSSession::DbFilesInfo::TbdRecord::TbdRecord(
1080 const CSSM_DATA &guid)
1081 {
1082 assert(guid.Length <= MAX_GUID_LEN);
1083 assert(guid.Length != 0);
1084 memmove(mGuid, guid.Data, guid.Length);
1085 if(mGuid[guid.Length - 1] != '\0') {
1086 mGuid[guid.Length] = '\0';
1087 }
1088 }
1089
1090 /*
1091 * Test if plugin specified by pluginPath needs to be deleted from DBs.
1092 * If so, add to tbdVector.
1093 */
1094 void MDSSession::DbFilesInfo::checkOutdatedPlugin(
1095 const CSSM_DATA &pathValue,
1096 const CSSM_DATA &guidValue,
1097 TbdVector &tbdVector)
1098 {
1099 /* stat the specified plugin */
1100 struct stat sb;
1101 bool obsolete = false;
1102 int rtn = ::stat((char *)pathValue.Data, &sb);
1103 if(rtn) {
1104 /* not there or inaccessible; delete */
1105 obsolete = true;
1106 }
1107 else if(sb.st_mtimespec.tv_sec > mLaterTimestamp) {
1108 /* timestamp of plugin's main directory later than that of DBs */
1109 obsolete = true;
1110 }
1111 if(obsolete) {
1112 TbdRecord *tbdRecord = new TbdRecord(guidValue);
1113 tbdVector.push_back(tbdRecord);
1114 MSDebug("checkOutdatedPlugin: flagging %s obsolete", pathValue.Data);
1115 }
1116 }
1117
1118 /*
1119 * Examine dbFiles.objDbHand; remove all fields associated with any bundle
1120 * i.e., with any path) which are either not present on disk, or which
1121 * have changed since dbFiles.laterTimestamp().
1122 */
1123 void MDSSession::DbFilesInfo::removeOutdatedPlugins()
1124 {
1125 CSSM_QUERY query;
1126 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
1127 CSSM_HANDLE resultHand;
1128 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
1129 CSSM_DB_ATTRIBUTE_DATA theAttrs[2];
1130 CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo;
1131 TbdVector tbdRecords;
1132
1133 /*
1134 * First, scan object directory. All we need are the path and GUID attributes.
1135 */
1136 recordAttrs.DataRecordType = MDS_OBJECT_RECORDTYPE;
1137 recordAttrs.SemanticInformation = 0;
1138 recordAttrs.NumberOfAttributes = 2;
1139 recordAttrs.AttributeData = theAttrs;
1140
1141 attrInfo = &theAttrs[0].Info;
1142 attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
1143 attrInfo->Label.AttributeName = "ModuleID";
1144 attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
1145 theAttrs[0].NumberOfValues = 0;
1146 theAttrs[0].Value = NULL;
1147 attrInfo = &theAttrs[1].Info;
1148 attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
1149 attrInfo->Label.AttributeName = "Path";
1150 attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
1151 theAttrs[1].NumberOfValues = 0;
1152 theAttrs[1].Value = NULL;
1153
1154 /* just search by recordType, no predicates */
1155 query.RecordType = MDS_OBJECT_RECORDTYPE;
1156 query.Conjunctive = CSSM_DB_NONE;
1157 query.NumSelectionPredicates = 0;
1158 query.SelectionPredicate = NULL;
1159 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful?
1160 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful?
1161 query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA...FIXME - used?
1162
1163 DLQuery perryQuery(query);
1164 try {
1165 resultHand = mSession.DataGetFirst(objDbHand(),
1166 &perryQuery,
1167 &recordAttrs,
1168 NULL, // No data
1169 record);
1170 }
1171 catch(...) {
1172 MSDebug("removeOutdatedPlugins: DataGetFirst threw");
1173 return; // ???
1174 }
1175 if(record) {
1176 mSession.FreeUniqueRecord(mObjDbHand, *record);
1177 }
1178 if(resultHand) {
1179 if(theAttrs[0].NumberOfValues && theAttrs[1].NumberOfValues) {
1180 checkOutdatedPlugin(*theAttrs[1].Value, *theAttrs[0].Value,
1181 tbdRecords);
1182 }
1183 else {
1184 MSDebug("removeOutdatedPlugins: incomplete record found (1)!");
1185 }
1186 for(unsigned dex=0; dex<2; dex++) {
1187 if(theAttrs[dex].Value) {
1188 if(theAttrs[dex].Value->Data) {
1189 mSession.free(theAttrs[dex].Value->Data);
1190 }
1191 mSession.free(theAttrs[dex].Value);
1192 }
1193 }
1194 }
1195 else {
1196 /* empty Object DB - we're done */
1197 MSDebug("removeOutdatedPlugins: empty object DB");
1198 return;
1199 }
1200
1201 /* now the rest of the object DB records */
1202 for(;;) {
1203 bool brtn = mSession.DataGetNext(objDbHand(),
1204 resultHand,
1205 &recordAttrs,
1206 NULL,
1207 record);
1208 if(!brtn) {
1209 /* end of data */
1210 break;
1211 }
1212 if(record) {
1213 mSession.FreeUniqueRecord(mObjDbHand, *record);
1214 }
1215 if(theAttrs[0].NumberOfValues && theAttrs[1].NumberOfValues) {
1216 checkOutdatedPlugin(*theAttrs[1].Value,
1217 *theAttrs[0].Value,
1218 tbdRecords);
1219 }
1220 else {
1221 MSDebug("removeOutdatedPlugins: incomplete record found (2)!");
1222 }
1223 for(unsigned dex=0; dex<2; dex++) {
1224 if(theAttrs[dex].Value) {
1225 if(theAttrs[dex].Value->Data) {
1226 mSession.free(theAttrs[dex].Value->Data);
1227 }
1228 mSession.free(theAttrs[dex].Value);
1229 }
1230 }
1231 }
1232 /* no DataAbortQuery needed; we scanned until completion */
1233
1234 /*
1235 * We have a vector of plugins to be deleted. Remove all records from both
1236 * DBs associated with the plugins, as specified by guid.
1237 */
1238 unsigned numRecords = tbdRecords.size();
1239 for(unsigned i=0; i<numRecords; i++) {
1240 TbdRecord *tbdRecord = tbdRecords[i];
1241 mSession.removeRecordsForGuid(tbdRecord->guid(), objDbHand());
1242 mSession.removeRecordsForGuid(tbdRecord->guid(), directDbHand());
1243 }
1244 for(unsigned i=0; i<numRecords; i++) {
1245 delete tbdRecords[i];
1246 }
1247 }
1248
1249
1250 /*
1251 * Update DBs for all bundles in specified directory.
1252 */
1253 void MDSSession::DbFilesInfo::updateForBundleDir(
1254 const char *bundleDirPath)
1255 {
1256 /* do this with readdir(); CFBundleCreateBundlesFromDirectory is
1257 * much too heavyweight */
1258 MSDebug("...updating DBs for dir %s", bundleDirPath);
1259 DIR *dir = opendir(bundleDirPath);
1260 if (dir == NULL) {
1261 MSDebug("updateForBundleDir: error %d opening %s", errno, bundleDirPath);
1262 return;
1263 }
1264 struct dirent *dp;
1265 char fullPath[MAXPATHLEN];
1266 while ((dp = readdir(dir)) != NULL) {
1267 if(isBundle(dp)) {
1268 sprintf(fullPath, "%s/%s", bundleDirPath, dp->d_name);
1269 updateForBundle(fullPath);
1270 }
1271 }
1272 closedir(dir);
1273 }
1274
1275 /*
1276 * lookup by path - just returns true if there is a record assoociated with the path
1277 * in mObjDbHand.
1278 */
1279 bool MDSSession::DbFilesInfo::lookupForPath(
1280 const char *path)
1281 {
1282 CSSM_QUERY query;
1283 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
1284 CSSM_HANDLE resultHand = 0;
1285 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
1286 CSSM_DB_ATTRIBUTE_DATA theAttr;
1287 CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo = &theAttr.Info;
1288 CSSM_SELECTION_PREDICATE predicate;
1289 CSSM_DATA predData;
1290
1291 recordAttrs.DataRecordType = MDS_OBJECT_RECORDTYPE;
1292 recordAttrs.SemanticInformation = 0;
1293 recordAttrs.NumberOfAttributes = 1;
1294 recordAttrs.AttributeData = &theAttr;
1295
1296 attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
1297 attrInfo->Label.AttributeName = "Path";
1298 attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
1299
1300 theAttr.NumberOfValues = 0;
1301 theAttr.Value = NULL;
1302
1303 predicate.DbOperator = CSSM_DB_EQUAL;
1304 predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
1305 predicate.Attribute.Info.Label.AttributeName = "Path";
1306 predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
1307 predData.Data = (uint8 *)path;
1308 predData.Length = strlen(path) + 1;
1309 predicate.Attribute.Value = &predData;
1310 predicate.Attribute.NumberOfValues = 1;
1311
1312 query.RecordType = MDS_OBJECT_RECORDTYPE;
1313 query.Conjunctive = CSSM_DB_NONE;
1314 query.NumSelectionPredicates = 1;
1315 query.SelectionPredicate = &predicate;
1316 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful?
1317 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful?
1318 query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA...FIXME - used?
1319
1320 bool ourRtn = true;
1321 try {
1322 DLQuery perryQuery(query);
1323 resultHand = mSession.DataGetFirst(objDbHand(),
1324 &perryQuery,
1325 &recordAttrs,
1326 NULL, // No data
1327 record);
1328 }
1329 catch (...) {
1330 ourRtn = false;
1331 }
1332 if(record) {
1333 mSession.FreeUniqueRecord(mObjDbHand, *record);
1334 }
1335 else {
1336 ourRtn = false;
1337 }
1338 if(resultHand && ourRtn) {
1339 /* more resulting pending; terminate the search */
1340 try {
1341 mSession.DataAbortQuery(mObjDbHand, resultHand);
1342 }
1343 catch(...) {
1344 MSDebug("exception on DataAbortQuery in lookupForPath");
1345 }
1346 }
1347 if(theAttr.Value) {
1348 if(theAttr.Value->Data) {
1349 mSession.free(theAttr.Value->Data);
1350 }
1351 mSession.free(theAttr.Value);
1352 }
1353 return ourRtn;
1354 }
1355
1356 /* update entry for one bundle, which is known to exist */
1357 void MDSSession::DbFilesInfo::updateForBundle(
1358 const char *bundlePath)
1359 {
1360 MSDebug("...updating DBs for bundle %s", bundlePath);
1361
1362 /* Quick lookup - do we have ANY entry for a bundle with this path? */
1363 if(lookupForPath(bundlePath)) {
1364 /* Yep, we're done */
1365 return;
1366 }
1367 MDSAttrParser parser(bundlePath,
1368 mSession,
1369 objDbHand(),
1370 directDbHand());
1371 parser.parseAttrs();
1372 }
1373
1374 /* DB autocommit on/off */
1375 void MDSSession::DbFilesInfo::autoCommit(CSSM_BOOL val)
1376 {
1377 try {
1378 mSession.PassThrough(objDbHand(),
1379 CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
1380 reinterpret_cast<void *>(val),
1381 NULL);
1382 mSession.PassThrough(directDbHand(),
1383 CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
1384 reinterpret_cast<void *>(val),
1385 NULL);
1386 }
1387 catch (...) {
1388 MSDebug("DbFilesInfo::autoCommit error!");
1389 /* but proceed */
1390 }
1391 }
1392
1393
1394 } // end namespace Security