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