--- /dev/null
+/*
+ * Copyright (c) 2004-2006,2011-2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+#include <security_utilities/cfutilities.h>
+#include <fts.h>
+#include <fcntl.h>
+#include <CommonCrypto/CommonDigest.h>
+
+#include "Manifest.h"
+
+ModuleNexus<CSSMInitializer> CSSMInitializer::mInstance;
+
+CSSMInitializer::CSSMInitializer () : mModule (gGuidAppleCSP), mCSP (mModule)
+{
+}
+
+
+
+CSSMInitializer::~CSSMInitializer ()
+{
+}
+
+
+
+CssmClient::CSP* CSSMInitializer::GetCSP ()
+{
+ return &mInstance().mCSP;
+}
+
+
+
+//========================== MANIFEST ITEM LIST ==========================
+
+
+
+ManifestItemList::ManifestItemList ()
+{
+}
+
+
+
+ManifestItemList::~ManifestItemList ()
+{
+ // throw away all of the items in the list
+ iterator it = begin ();
+ while (it != end ())
+ {
+ delete *it++;
+ }
+}
+
+
+
+// return the path portion of a URL after checking to see if we support its scheme
+void ManifestItemList::DecodeURL (CFURLRef url, char *pathBuffer, CFIndex maxBufLen)
+{
+ // get the scheme from the url and check to make sure it is a "file" scheme
+ CFRef<CFStringRef> scheme (CFURLCopyScheme (url));
+ if (CFStringCompare (scheme, CFSTR("file"), 0) != 0)
+ {
+ // we only support file URL's
+ MacOSError::throwMe (errSecManifestNotSupported);
+ }
+
+ // convert the url into a "real" path name
+ if (!CFURLGetFileSystemRepresentation (url, false, (UInt8*) pathBuffer, maxBufLen))
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+}
+
+
+
+void ManifestItemList::AddFileSystemObject (char* path, StringSet& exceptions, bool isRoot, bool hasAppleDoubleResourceFork)
+{
+ // see if our path is in the exception list. If it is, do nothing else
+ StringSet::iterator it = exceptions.find (path);
+ if (it != exceptions.end ())
+ {
+ secdebug ("manifest", "Did not add %s to the manifest.", path);
+ return;
+ }
+
+ // now that we have the path, do a stat and see what we have
+ struct stat nodeStat;
+ int result = lstat (path, &nodeStat);
+ UnixError::check (result);
+
+ FileSystemEntryItem* mItem;
+
+ bool includeUserAndGroup = true;
+
+ switch (nodeStat.st_mode & S_IFMT)
+ {
+ case S_IFDIR: // are we a directory?
+ {
+ ManifestDirectoryItem* dirItem = new ManifestDirectoryItem ();
+ dirItem->SetPath (path, exceptions, isRoot);
+ mItem = dirItem;
+ }
+ break;
+
+ case S_IFREG:
+ {
+ ManifestFileItem* fileItem = new ManifestFileItem ();
+ fileItem->SetPath (path);
+ fileItem->ComputeRepresentations (nodeStat, hasAppleDoubleResourceFork);
+ mItem = fileItem;
+ }
+ break;
+
+ case S_IFLNK:
+ {
+ ManifestSymLinkItem* symItem = new ManifestSymLinkItem ();
+ symItem->SetPath (path);
+ symItem->ComputeRepresentation ();
+ mItem = symItem;
+ nodeStat.st_mode = S_IFLNK;
+ includeUserAndGroup = false;
+ }
+ break;
+
+ default:
+ {
+ ManifestOtherItem* otherItem = new ManifestOtherItem ();
+ otherItem->SetPath (path);
+ mItem = otherItem;
+ }
+ break;
+ }
+
+ if (includeUserAndGroup) // should we set the info?
+ {
+ mItem->SetUID (nodeStat.st_uid);
+ mItem->SetGID (nodeStat.st_gid);
+ }
+
+ mItem->SetMode (nodeStat.st_mode);
+
+ push_back (mItem);
+}
+
+
+
+void ManifestItemList::AddDataObject (CFDataRef object)
+{
+ // reconstruct the pointer
+ SHA1Digest digest;
+ CC_SHA1_CTX digestContext;
+
+ CC_SHA1_Init (&digestContext);
+
+ const UInt8* data = CFDataGetBytePtr (object);
+ CFIndex length = CFDataGetLength (object);
+
+ CC_SHA1_Update (&digestContext, data, (CC_LONG)length);
+ CC_SHA1_Final (digest, &digestContext);
+
+ ManifestDataBlobItem* db = new ManifestDataBlobItem ();
+
+ db->SetDigest (&digest);
+ db->SetLength (length);
+
+ push_back (db);
+}
+
+
+
+void ManifestItemList::ConvertToStringSet (const char* path, CFArrayRef exceptionList, StringSet &exceptions)
+{
+ if (exceptionList != NULL)
+ {
+ std::string prefix = path;
+
+ // put us in canonical form
+ if (prefix[prefix.length () - 1] != '/')
+ {
+ prefix += '/';
+ }
+
+ // enumerate the list
+ CFIndex max = CFArrayGetCount (exceptionList);
+ CFIndex n;
+
+ for (n = 0; n < max; ++n)
+ {
+ CFTypeRef dataRef = CFArrayGetValueAtIndex (exceptionList, n);
+ if (CFGetTypeID (dataRef) != CFStringGetTypeID ())
+ {
+ MacOSError::throwMe (errSecManifestInvalidException);
+ }
+
+ // always prepend the prefix -- the spec says that all items in the exception list are relative to the root
+ std::string s = prefix + cfString (CFStringRef (dataRef));
+ secdebug ("manifest", "Uncanonicalized path is %s", s.c_str ());
+
+ // canonicalize the path and insert if successful.
+ char realPath [PATH_MAX];
+ if (realpath (s.c_str (), realPath) != NULL)
+ {
+ secdebug ("manifest", "Inserted path %s as an exception", realPath);
+ exceptions.insert (realPath);
+ }
+ }
+ }
+}
+
+
+
+void ManifestItemList::AddObject (CFTypeRef object, CFArrayRef exceptionList)
+{
+ // get the type of the object
+ CFTypeID objectID = CFGetTypeID (object);
+
+ if (objectID == CFDataGetTypeID ())
+ {
+ AddDataObject ((CFDataRef) object);
+ }
+ else if (objectID == CFURLGetTypeID ())
+ {
+ StringSet exceptions;
+
+ // get the path from the URL
+ char path [PATH_MAX];
+ DecodeURL ((CFURLRef) object, path, sizeof (path));
+
+ // canonicalize
+ char realPath [PATH_MAX];
+ if (realpath (path, realPath) == NULL)
+ {
+ UnixError::throwMe ();
+ }
+
+ ConvertToStringSet (realPath, exceptionList, exceptions);
+
+ AddFileSystemObject (realPath, exceptions, true, false);
+ }
+ else
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+}
+
+
+
+void RootItemList::Compare (RootItemList& item, bool compareOwnerAndGroup)
+{
+ // the number of items in the list has to be the same
+ unsigned numItems = (unsigned)size ();
+
+ if (numItems != item.size ())
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+
+ // for a root item list, items in the manifest MUST have the same creation order
+ unsigned i;
+
+ for (i = 0; i < numItems; ++i)
+ {
+ ManifestItem* item1 = (*this)[i];
+ ManifestItem* item2 = item[i];
+
+ if (item1->GetItemType () != item2->GetItemType ())
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+
+ item1->Compare (item2, compareOwnerAndGroup);
+ }
+}
+
+
+
+class CompareManifestFileItems
+{
+public:
+ bool operator () (ManifestItem *a, ManifestItem *b);
+};
+
+
+
+bool CompareManifestFileItems::operator () (ManifestItem *a, ManifestItem *b)
+{
+ FileSystemEntryItem *aa = static_cast<FileSystemEntryItem*>(a);
+ FileSystemEntryItem *bb = static_cast<FileSystemEntryItem*>(b);
+
+ return strcmp (aa->GetName (), bb->GetName ()) < 0;
+}
+
+
+
+void FileSystemItemList::Compare (FileSystemItemList &a, bool compareOwnerAndGroup)
+{
+ unsigned numItems = (unsigned)size ();
+
+ if (numItems != a.size ())
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+
+ // sort the two lists
+ sort (begin (), end (), CompareManifestFileItems ());
+ sort (a.begin (), a.end (), CompareManifestFileItems ());
+
+ // compare each item in the list
+ unsigned i;
+ for (i = 0; i < numItems; ++i)
+ {
+ ManifestItem *thisListPtr = (*this)[i];
+ ManifestItem *aListPtr = a[i];
+ if (thisListPtr->GetItemType () != aListPtr->GetItemType ())
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+ thisListPtr->Compare (aListPtr, compareOwnerAndGroup);
+ }
+}
+
+
+
+//========================== MANIFEST ==========================
+
+
+
+ManifestInternal::ManifestInternal ()
+{
+}
+
+
+
+ManifestInternal::~ManifestInternal ()
+{
+ secdebug ("manifest", "Destroyed manifest internal %p", this);
+}
+
+
+
+void ManifestInternal::CompareManifests (ManifestInternal& m1, ManifestInternal& m2, SecManifestCompareOptions options)
+{
+ if ((options & ~kSecManifestVerifyOwnerAndGroup) != 0)
+ {
+ MacOSError::throwMe (errSecUnimplemented); // we don't support these options
+ }
+
+ m1.mManifestItems.Compare (m2.mManifestItems, (bool) options & kSecManifestVerifyOwnerAndGroup);
+}
+
+
+
+//========================== MANIFEST ITEM ==========================
+ManifestItem::~ManifestItem ()
+{
+}
+
+
+
+//========================== DATA BLOB ITEM ==========================
+ManifestDataBlobItem::ManifestDataBlobItem ()
+{
+}
+
+
+
+ManifestDataBlobItem::~ManifestDataBlobItem ()
+{
+}
+
+
+
+ManifestItemType ManifestDataBlobItem::GetItemType ()
+{
+ return kManifestDataBlobItemType;
+}
+
+
+
+const SHA1Digest* ManifestDataBlobItem::GetDigest ()
+{
+ return &mSHA1Digest;
+}
+
+
+
+void ManifestDataBlobItem::SetDigest (const SHA1Digest *sha1Digest)
+{
+ memcpy (&mSHA1Digest, sha1Digest, sizeof (SHA1Digest));
+}
+
+
+
+size_t ManifestDataBlobItem::GetLength ()
+{
+ return mLength;
+}
+
+
+
+void ManifestDataBlobItem::SetLength (size_t length)
+{
+ mLength = length;
+}
+
+
+
+void ManifestDataBlobItem::Compare (ManifestItem* item, bool compareOwnerAndGroup)
+{
+ ManifestDataBlobItem* i = static_cast<ManifestDataBlobItem*>(item);
+ if (memcmp (&i->mSHA1Digest, &mSHA1Digest, sizeof (SHA1Digest)) != 0)
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+}
+
+
+
+//========================== FILE SYSTEM ENTRY ITEM ==========================
+
+
+
+FileSystemEntryItem::FileSystemEntryItem () : mUserID (0), mGroupID (0), mMode (0)
+{
+}
+
+
+
+FileSystemEntryItem::~FileSystemEntryItem ()
+{
+}
+
+
+
+void FileSystemEntryItem::SetName (char* name)
+{
+ mName = name;
+}
+
+
+
+static char* StringTail (char* path)
+{
+ char* finger = path + strlen (path) - 1;
+ while (finger != path && *finger != '/')
+ {
+ finger -= 1;
+ }
+
+ if (finger != path) // did find a separator
+ {
+ finger += 1;
+ }
+
+ return finger;
+}
+
+
+
+void FileSystemEntryItem::SetPath (char* path)
+{
+ // save off the path
+ mPath = path;
+
+ // while we are at it, extract that last name of the path name and save it off as the name
+ mName = StringTail (path);
+ secdebug ("manifest", "Created file item for %s with name %s", mPath.c_str (), mName.c_str ());
+}
+
+
+
+void FileSystemEntryItem::SetUID (uid_t uid)
+{
+ mUserID = uid;
+}
+
+
+
+void FileSystemEntryItem::SetGID (gid_t gid)
+{
+ mGroupID = gid;
+}
+
+
+
+void FileSystemEntryItem::SetMode (mode_t mode)
+{
+ mMode = mode;
+}
+
+
+
+uid_t FileSystemEntryItem::GetUID () const
+{
+ return mUserID;
+}
+
+
+gid_t FileSystemEntryItem::GetGID () const
+{
+ return mGroupID;
+}
+
+
+
+mode_t FileSystemEntryItem::GetMode () const
+{
+ return mMode;
+}
+
+
+
+const char* FileSystemEntryItem::GetName () const
+{
+ return (char*) mName.c_str ();
+}
+
+
+
+void FileSystemEntryItem::Compare (ManifestItem *aa, bool compareOwnerAndGroup)
+{
+ FileSystemEntryItem* a = static_cast<FileSystemEntryItem*>(aa);
+
+ if (mName != a->mName || mMode != a->mMode)
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+
+ if (compareOwnerAndGroup)
+ {
+ if (mUserID != a->mUserID || mGroupID != a->mGroupID)
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+ }
+}
+
+
+
+//========================== MANIFEST FILE ITEM ==========================
+
+
+
+bool ManifestFileItem::FileSystemHasTrueForks (char* pathToFile)
+{
+ // return true if volume to which path points supports true forked files
+ struct statfs st;
+ int result = statfs (pathToFile, &st);
+ if (result != 0)
+ {
+ secdebug ("manifest", "Could not get statfs (error was %s)", strerror (errno));
+ UnixError::throwMe ();
+ }
+
+ return strcmp (st.f_fstypename, "afpfs") == 0 || strcmp (st.f_fstypename, "hfs") == 0;
+}
+
+
+
+std::string ManifestFileItem::ResourceFileName (char* path)
+{
+ std::string filePath;
+
+ if (FileSystemHasTrueForks (path))
+ {
+ filePath = path;
+
+ return filePath + "/rsrc";
+ }
+ else
+ {
+ return "";
+ }
+
+ return filePath;
+}
+
+
+
+bool ManifestFileItem::HasResourceFork (char* pathToFile, std::string &result, struct stat &st)
+{
+ // try to get the stat on the file. If it works, the file exists
+ result = ResourceFileName (pathToFile);
+ if (result.length () != 0)
+ {
+ int stresult = lstat (result.c_str (), &st);
+ if (stresult == 0)
+ {
+ return st.st_size != 0;
+ }
+ }
+
+ return false;
+}
+
+
+
+ManifestFileItem::ManifestFileItem () : mNumForks (1)
+{
+}
+
+
+
+ManifestFileItem::~ManifestFileItem ()
+{
+ secdebug ("manifest", "Destroyed manifest item %p for path %s", this, mPath.c_str ());
+}
+
+
+
+ManifestItemType ManifestFileItem::GetItemType ()
+{
+ return kManifestFileItemType;
+}
+
+
+
+u_int32_t ManifestFileItem::GetNumberOfForks ()
+{
+ return mNumForks;
+}
+
+
+
+void ManifestFileItem::SetNumberOfForks (u_int32_t numForks)
+{
+ mNumForks = numForks;
+}
+
+
+
+bool ManifestFileItem::FileIsMachOBinary (char* path)
+{
+ return false;
+}
+
+
+
+void ManifestFileItem::SetForkLength (int which, size_t length)
+{
+ mFileLengths[which] = length;
+}
+
+
+
+size_t ManifestFileItem::GetForkLength (int which)
+{
+ return mFileLengths[which];
+}
+
+
+
+void ManifestFileItem::ComputeRepresentations (struct stat &st, bool hasAppleDoubleResourceFork)
+{
+ // digest the data fork
+ mNumForks = 1;
+ ComputeDigestForFile ((char*) mPath.c_str (), mDigest[0], mFileLengths[0], st);
+
+ struct stat stat2;
+ std::string resourceForkName;
+ if (hasAppleDoubleResourceFork)
+ {
+ mNumForks = 2;
+
+ resourceForkName = mPath;
+ // walk back to find the beginning of the path and insert ._
+ int i = (int)resourceForkName.length () - 1;
+ while (i >= 0 && resourceForkName[i] != '/')
+ {
+ i -= 1;
+ }
+
+ i += 1;
+
+ resourceForkName.insert (i, "._");
+
+ ComputeDigestForAppleDoubleResourceFork ((char*) resourceForkName.c_str(), mDigest[1], mFileLengths[1]);
+ }
+ else if (HasResourceFork ((char*) mPath.c_str (), resourceForkName, stat2))
+ {
+ mNumForks = 2;
+ ComputeDigestForFile ((char*) resourceForkName.c_str (), mDigest[1], mFileLengths[1], stat2);
+ }
+}
+
+
+
+static const int kReadChunkSize = 4096 * 4;
+
+
+
+static u_int32_t ExtractUInt32 (u_int8_t *&finger)
+{
+ u_int32_t result = 0;
+ int i;
+ for (i = 0; i < 4; ++i)
+ {
+ result = (result << 8) | *finger++;
+ }
+
+ return result;
+}
+
+
+
+void ManifestFileItem::ComputeDigestForAppleDoubleResourceFork (char* name, SHA1Digest &digest, size_t &fileLength)
+{
+ secdebug ("manifest", "Creating digest for AppleDouble resource fork %s", name);
+
+ CC_SHA1_CTX digestContext;
+ CC_SHA1_Init (&digestContext);
+
+ // bring the file into memory
+ int fileNo = open (name, O_RDONLY, 0);
+ if (fileNo == -1)
+ {
+ UnixError::throwMe ();
+ }
+
+ // figure out how big the file is.
+ struct stat st;
+ int result = fstat (fileNo, &st);
+ if (result == -1)
+ {
+ UnixError::throwMe ();
+ }
+
+ u_int8_t *buffer = new u_int8_t[st.st_size];
+ ssize_t bytesRead = read (fileNo, buffer, (size_t)st.st_size);
+ close (fileNo);
+
+ if (bytesRead != st.st_size)
+ {
+ delete buffer;
+ UnixError::throwMe ();
+ }
+
+ // walk the entry table to find the offset to our resource fork
+ u_int8_t *bufPtr = buffer + 24; // size of the header + filler
+
+ // compute the number of entries in the file
+ int numEntries = (((int) bufPtr[0]) << 8) | (int) (bufPtr [1]);
+ bufPtr += 2;
+
+ ssize_t length = 0;
+ ssize_t offset = 0;
+
+ int i;
+ for (i = 0; i < numEntries; ++i)
+ {
+ // bufPtr points to an entry descriptor. Four bytes for the ID, four for the offset, four for the length
+ ssize_t id = ExtractUInt32 (bufPtr);
+ offset = ExtractUInt32 (bufPtr);
+ length = ExtractUInt32 (bufPtr);
+
+ if (id == 2) // is it the resource fork?
+ {
+ break;
+ }
+ }
+
+ if (i >= numEntries) // did we run off the end? This had better not happen
+ {
+ MacOSError::throwMe (errSecManifestNotSupported);
+ }
+
+ fileLength = length;
+
+ // digest the data
+ CC_SHA1_Update (&digestContext, buffer + offset, (CC_LONG)length);
+
+ // compute the SHA1 hash
+ CC_SHA1_Final (digest, &digestContext);
+
+ delete buffer;
+}
+
+
+
+void ManifestFileItem::ComputeDigestForFile (char* name, SHA1Digest &digest, size_t &fileLength, struct stat &st)
+{
+ secdebug ("manifest", "Creating digest for %s", name);
+
+ // create a context for the digest operation
+ CC_SHA1_CTX digestContext;
+ CC_SHA1_Init (&digestContext);
+
+
+ int fileNo = open (name, O_RDONLY, 0);
+ if (fileNo == -1)
+ {
+ UnixError::throwMe ();
+ }
+
+ fileLength = (size_t)st.st_size;
+
+ if (st.st_size != 0)
+ {
+ // read the file
+ char buffer[kReadChunkSize];
+
+ ssize_t bytesRead;
+ while ((bytesRead = read (fileNo, buffer, kReadChunkSize)) != 0)
+ {
+ // digest the read data
+ CC_SHA1_Update (&digestContext, buffer, (CC_LONG)bytesRead);
+ }
+
+ // compute the SHA1 hash
+ CC_SHA1_Final (digest, &digestContext);
+ }
+
+ close (fileNo);
+}
+
+
+
+void ManifestFileItem::GetItemRepresentation (int whichFork, void* &itemRep, size_t &size)
+{
+ itemRep = (void*) &mDigest[whichFork];
+ size = kSHA1DigestSize;
+}
+
+
+
+void ManifestFileItem::SetItemRepresentation (int whichFork, const void* itemRep, size_t size)
+{
+ memcpy ((void*) &mDigest[whichFork], itemRep, size);
+}
+
+
+
+void ManifestFileItem::Compare (ManifestItem *manifestItem, bool compareOwnerAndGroup)
+{
+ FileSystemEntryItem::Compare (manifestItem, compareOwnerAndGroup);
+
+ ManifestFileItem* item = static_cast< ManifestFileItem*>(manifestItem);
+
+ secdebug ("manifest", "Comparing file item %s against %s", GetName (), item->GetName ());
+
+ // the number of forks should be equal
+ if (mNumForks != item->mNumForks)
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+
+ // compare file lengths
+ int i;
+ for (i = 0; i < mNumForks; ++i)
+ {
+ if (mFileLengths[i] != item->mFileLengths[i])
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+
+ if (memcmp (&mDigest[i], item->mDigest[i], kSHA1DigestSize) != 0)
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+ }
+}
+
+
+
+//========================== MANIFEST DIRECTORY ITEM ==========================
+
+
+
+ManifestDirectoryItem::ManifestDirectoryItem ()
+{
+}
+
+
+
+ManifestDirectoryItem::~ManifestDirectoryItem ()
+{
+ secdebug ("manifest", "Destroyed directory item %p for path %s", this, mPath.c_str ());
+}
+
+
+const char* kAppleDoublePrefix = "._";
+const int kAppleDoublePrefixLength = 2;
+
+static int CompareFilenames (const FTSENT** a, const FTSENT** b)
+{
+ // ._name is always greater than name
+ // otherwise, ._ is ignored for sorting purposes
+ const char* aa = (*a)->fts_name;
+ const char* bb = (*b)->fts_name;
+ bool aHasPrefix = false;
+
+ if (strncmp (aa, kAppleDoublePrefix, kAppleDoublePrefixLength) == 0) // do we have an appledouble prefix?
+ {
+ aHasPrefix = true;
+ aa += kAppleDoublePrefixLength;
+ }
+
+ if (strncmp (bb, kAppleDoublePrefix, kAppleDoublePrefixLength) == 0) // do we have an appledouble prefix?
+ {
+ bb += kAppleDoublePrefixLength;
+ }
+
+ int compare = strcmp (aa, bb);
+
+ if (compare == 0 && aHasPrefix)
+ {
+ return 1;
+ }
+
+ return compare;
+}
+
+
+
+const u_int8_t kAppleDoubleMagicNumber[] = {0x00, 0x05, 0x16, 0x07};
+
+
+
+static bool PathIsAppleDoubleFile (const char* path)
+{
+ // Open the file and check the "magic number".
+ int fRef = open (path, O_RDONLY, 0);
+
+ u_int8_t buffer[4];
+
+ // read the first four bytes of the file
+ ssize_t bytesRead = read(fRef, buffer, 4);
+ if (bytesRead == -1)
+ {
+ int err = errno;
+ close (fRef);
+ UnixError::throwMe (err);
+ }
+
+ close (fRef);
+
+ if (bytesRead != 4) // did we get enough bytes?
+ {
+ return false;
+ }
+
+ // what we got had better be the proper magic number for this file type
+ int i;
+ for (i = 0; i < 4; ++i)
+ {
+ if (buffer[i] != kAppleDoubleMagicNumber[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+void ManifestDirectoryItem::SetPath (char* path, StringSet &exceptions, bool isRoot)
+{
+ if (isRoot)
+ {
+ mName = "/";
+ mPath = path;
+ }
+ else
+ {
+ FileSystemEntryItem::SetPath (path);
+ }
+
+ secdebug ("manifest", "Added directory entry for %s with name %s", mPath.c_str (), mName.c_str ());
+
+ // enumerate the contents of the directory.
+ char* path_argv[] = { path, NULL };
+ FTS* thisDir = fts_open (path_argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_NOSTAT | FTS_XDEV, CompareFilenames);
+ if (thisDir == NULL) // huh? The file disappeared or isn't a directory any more
+ {
+ UnixError::throwMe ();
+ }
+
+ (void)fts_read(thisDir);
+ FTSENT* dirEnt = fts_children (thisDir, FTS_NAMEONLY);
+
+ while (dirEnt != NULL)
+ {
+ // get the next entry
+ FTSENT* dirEntNext = dirEnt->fts_link;
+ bool hasAppleDoubleResourceFork = false;
+
+ // see if it is an AppleDouble resource fork for this file
+ if (dirEntNext &&
+ strncmp (dirEntNext->fts_name, kAppleDoublePrefix, kAppleDoublePrefixLength) == 0 &&
+ strcmp (dirEnt->fts_name, dirEntNext->fts_name + kAppleDoublePrefixLength) == 0)
+ {
+ if (PathIsAppleDoubleFile ((mPath + "/" + dirEntNext->fts_name).c_str ()))
+ {
+ hasAppleDoubleResourceFork = true;
+ dirEntNext = dirEntNext->fts_link;
+ }
+ }
+
+ // figure out what this is pointing to.
+ std::string fileName = mPath + "/" + dirEnt->fts_name;
+
+ mDirectoryItems.AddFileSystemObject ((char*) fileName.c_str(), exceptions, false, hasAppleDoubleResourceFork);
+
+ dirEnt = dirEntNext;
+ }
+
+ fts_close(thisDir);
+}
+
+
+
+ManifestItemType ManifestDirectoryItem::GetItemType ()
+{
+ return kManifestDirectoryItemType;
+}
+
+
+
+void ManifestDirectoryItem::Compare (ManifestItem* a, bool compareOwnerAndGroup)
+{
+ FileSystemEntryItem::Compare (a, compareOwnerAndGroup);
+ ManifestDirectoryItem* aa = static_cast<ManifestDirectoryItem*>(a);
+ secdebug ("manifest", "Comparing directory item %s against %s", GetName (), aa->GetName ());
+ mDirectoryItems.Compare (aa->mDirectoryItems, compareOwnerAndGroup);
+}
+
+
+
+//========================== MANIFEST SYMLINK ITEM ==========================
+
+
+
+ManifestSymLinkItem::ManifestSymLinkItem ()
+{
+}
+
+
+
+ManifestSymLinkItem::~ManifestSymLinkItem ()
+{
+ secdebug ("manifest", "Destroyed symlink item for %s", mPath.c_str ());
+}
+
+
+
+void ManifestSymLinkItem::ComputeRepresentation ()
+{
+ char path [FILENAME_MAX];
+ int result = (int)readlink (mPath.c_str (), path, sizeof (path));
+ secdebug ("manifest", "Read content %s for %s", path, mPath.c_str ());
+
+ // create a digest context
+ CC_SHA1_CTX digestContext;
+ CC_SHA1_Init (&digestContext);
+
+ // digest the data
+ CC_SHA1_Update (&digestContext, path, result);
+
+ // compute the result
+ CC_SHA1_Final (mDigest, &digestContext);
+
+ UnixError::check (result);
+}
+
+
+
+const SHA1Digest* ManifestSymLinkItem::GetDigest ()
+{
+ return &mDigest;
+}
+
+
+
+void ManifestSymLinkItem::SetDigest (const SHA1Digest* digest)
+{
+ memcpy (mDigest, digest, sizeof (SHA1Digest));
+}
+
+
+
+ManifestItemType ManifestSymLinkItem::GetItemType ()
+{
+ return kManifestSymLinkItemType;
+}
+
+
+
+void ManifestSymLinkItem::Compare (ManifestItem *a, bool compareOwnerAndGroup)
+{
+ FileSystemEntryItem::Compare (a, compareOwnerAndGroup);
+ ManifestSymLinkItem* aa = static_cast<ManifestSymLinkItem*>(a);
+ secdebug ("manifest", "Comparing symlink item %s against %s", GetName (), aa->GetName ());
+
+ // now compare the data
+ if (memcmp (&mDigest, &aa->mDigest, kSHA1DigestSize) != 0)
+ {
+ MacOSError::throwMe (errSecManifestNotEqual);
+ }
+}
+
+
+
+//========================== MANIFEST OTHER ITEM ==========================
+
+
+
+ManifestOtherItem::ManifestOtherItem ()
+{
+}
+
+
+
+ManifestOtherItem::~ManifestOtherItem ()
+{
+ secdebug ("manifest", "Destroyed other item for path %s", mPath.c_str ());
+}
+
+
+
+ManifestItemType ManifestOtherItem::GetItemType ()
+{
+ return kManifestOtherType;
+}
+
+
+
+void ManifestOtherItem::Compare (ManifestItem *a, bool compareOwnerAndGroup)
+{
+ FileSystemEntryItem::Compare (a, compareOwnerAndGroup);
+ secdebug ("manifest", "Comparing other item %s against %s", GetName (), static_cast<FileSystemEntryItem*>(a)->GetName ());
+}