/ OSX / libsecurity_manifest / lib / ManifestInternal.cpp
ManifestInternal.cpp
   1  /*
   2   *  Copyright (c) 2004-2006,2011-2014 Apple Inc. All Rights Reserved.
   3   *
   4   *  @APPLE_LICENSE_HEADER_START@
   5   *  
   6   *  This file contains Original Code and/or Modifications of Original Code
   7   *  as defined in and that are subject to the Apple Public Source License
   8   *  Version 2.0 (the 'License'). You may not use this file except in
   9   *  compliance with the License. Please obtain a copy of the License at
  10   *  http://www.opensource.apple.com/apsl/ and read it before using this
  11   *  file.
  12   *  
  13   *  The Original Code and all software distributed under the License are
  14   *  distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  15   *  EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  16   *  INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  17   *  FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  18   *  Please see the License for the specific language governing rights and
  19   *  limitations under the License.
  20   *  
  21   *  @APPLE_LICENSE_HEADER_END@
  22   */
  23  
  24  
  25  
  26  #include <stdlib.h>
  27  #include <sys/stat.h>
  28  #include <string.h>
  29  #include <sys/types.h>
  30  #include <dirent.h>
  31  #include <unistd.h>
  32  #include <sys/param.h>
  33  #include <sys/mount.h>
  34  #include <sys/uio.h>
  35  #include <security_utilities/cfutilities.h>
  36  #include <fts.h>
  37  #include <fcntl.h>
  38  #include <CommonCrypto/CommonDigest.h>
  39  
  40  #include "Manifest.h"
  41  
  42  ModuleNexus<CSSMInitializer> CSSMInitializer::mInstance;
  43  
  44  CSSMInitializer::CSSMInitializer () : mModule (gGuidAppleCSP), mCSP (mModule)
  45  {
  46  }
  47  
  48  
  49  
  50  CSSMInitializer::~CSSMInitializer ()
  51  {
  52  }
  53  
  54  
  55  
  56  CssmClient::CSP* CSSMInitializer::GetCSP ()
  57  {
  58  	return &mInstance().mCSP;
  59  }
  60  
  61  
  62  
  63  //==========================  MANIFEST ITEM LIST ==========================
  64  
  65  
  66  
  67  ManifestItemList::ManifestItemList ()
  68  {
  69  }
  70  
  71  
  72  
  73  ManifestItemList::~ManifestItemList ()
  74  {
  75  	// throw away all of the items in the list
  76  	iterator it = begin ();
  77  	while (it != end ())
  78  	{
  79  		delete *it++;
  80  	}
  81  }
  82  
  83  
  84  
  85  // return the path portion of a URL after checking to see if we support its scheme
  86  void ManifestItemList::DecodeURL (CFURLRef url, char *pathBuffer, CFIndex maxBufLen)
  87  {
  88  	// get the scheme from the url and check to make sure it is a "file" scheme
  89  	CFRef<CFStringRef> scheme (CFURLCopyScheme (url));
  90  	if (CFStringCompare (scheme, CFSTR("file"), 0) != 0)
  91  	{
  92  		// we only support file URL's
  93  		MacOSError::throwMe (errSecManifestNotSupported);
  94  	}
  95  	
  96  	// convert the url into a "real" path name
  97  	if (!CFURLGetFileSystemRepresentation (url, false, (UInt8*) pathBuffer, maxBufLen))
  98  	{
  99  		MacOSError::throwMe (errSecManifestNotEqual);
 100  	}
 101  }
 102  
 103  
 104  
 105  void ManifestItemList::AddFileSystemObject (char* path, StringSet& exceptions, bool isRoot, bool hasAppleDoubleResourceFork)
 106  {
 107  	// see if our path is in the exception list.  If it is, do nothing else
 108  	StringSet::iterator it = exceptions.find (path);
 109  	if (it != exceptions.end ())
 110  	{
 111  		secinfo ("manifest", "Did not add %s to the manifest.", path);
 112  		return;
 113  	}
 114  	
 115  	// now that we have the path, do a stat and see what we have
 116  	struct stat nodeStat;
 117  	int result = lstat (path, &nodeStat);
 118  	UnixError::check (result);
 119  	
 120  	FileSystemEntryItem* mItem;
 121  	
 122  	bool includeUserAndGroup = true;
 123  	
 124  	switch (nodeStat.st_mode & S_IFMT)
 125  	{
 126  		case S_IFDIR: // are we a directory?
 127  		{
 128  			ManifestDirectoryItem* dirItem = new ManifestDirectoryItem ();
 129  			dirItem->SetPath (path, exceptions, isRoot);
 130  			mItem = dirItem;
 131  		}
 132  		break;
 133  		
 134  		case S_IFREG:
 135  		{
 136  			ManifestFileItem* fileItem = new ManifestFileItem ();
 137  			fileItem->SetPath (path);
 138  			fileItem->ComputeRepresentations (nodeStat, hasAppleDoubleResourceFork);
 139  			mItem = fileItem;
 140  		}
 141  		break;
 142  		
 143  		case S_IFLNK:
 144  		{
 145  			ManifestSymLinkItem* symItem = new ManifestSymLinkItem ();
 146  			symItem->SetPath (path);
 147  			symItem->ComputeRepresentation ();
 148  			mItem = symItem;
 149  			nodeStat.st_mode = S_IFLNK;
 150  			includeUserAndGroup = false;
 151  		}
 152  		break;
 153  		
 154  		default:
 155  		{
 156  			ManifestOtherItem* otherItem = new ManifestOtherItem ();
 157  			otherItem->SetPath (path);
 158  			mItem = otherItem;
 159  		}
 160  		break;
 161  	}
 162  	
 163  	if (includeUserAndGroup) // should we set the info?
 164  	{
 165  		mItem->SetUID (nodeStat.st_uid);
 166  		mItem->SetGID (nodeStat.st_gid);
 167  	}
 168  	
 169  	mItem->SetMode (nodeStat.st_mode);
 170  
 171  	push_back (mItem);
 172  }
 173  
 174  
 175  
 176  void ManifestItemList::AddDataObject (CFDataRef object)
 177  {
 178  	// reconstruct the pointer
 179  	SHA1Digest digest;
 180  	CC_SHA1_CTX digestContext;
 181  	
 182  	CC_SHA1_Init (&digestContext);
 183  	
 184  	const UInt8* data = CFDataGetBytePtr (object);
 185  	CFIndex length = CFDataGetLength (object);
 186  	
 187  	CC_SHA1_Update (&digestContext, data, (CC_LONG)length);
 188  	CC_SHA1_Final (digest, &digestContext);
 189  	
 190  	ManifestDataBlobItem* db = new ManifestDataBlobItem ();
 191  
 192  	db->SetDigest (&digest);
 193  	db->SetLength (length);
 194  	
 195  	push_back (db);
 196  }
 197  
 198  
 199  
 200  void ManifestItemList::ConvertToStringSet (const char* path, CFArrayRef exceptionList, StringSet &exceptions)
 201  {
 202  	if (exceptionList != NULL)
 203  	{
 204  		std::string prefix = path;
 205  		
 206  		// put us in canonical form
 207  		if (prefix[prefix.length () - 1] != '/')
 208  		{
 209  			prefix += '/';
 210  		}
 211  		
 212  		// enumerate the list
 213  		CFIndex max = CFArrayGetCount (exceptionList);
 214  		CFIndex n;
 215  		
 216  		for (n = 0; n < max; ++n)
 217  		{
 218  			CFTypeRef dataRef = CFArrayGetValueAtIndex (exceptionList, n);
 219  			if (CFGetTypeID (dataRef) != CFStringGetTypeID ())
 220  			{
 221  				MacOSError::throwMe (errSecManifestInvalidException);
 222  			}
 223  			
 224  			// always prepend the prefix -- the spec says that all items in the exception list are relative to the root
 225  			std::string s = prefix + cfString (CFStringRef (dataRef));
 226  			secinfo ("manifest", "Uncanonicalized path is %s", s.c_str ());
 227  
 228  			// canonicalize the path and insert if successful.
 229  			char realPath [PATH_MAX];
 230  			if (realpath (s.c_str (), realPath) != NULL)
 231  			{
 232  				secinfo ("manifest", "Inserted path %s as an exception", realPath);
 233  				exceptions.insert (realPath);
 234  			}
 235  		}
 236  	}
 237  }
 238  
 239  
 240  
 241  void ManifestItemList::AddObject (CFTypeRef object, CFArrayRef exceptionList)
 242  {
 243  	// get the type of the object
 244  	CFTypeID objectID = CFGetTypeID (object);
 245  	
 246  	if (objectID == CFDataGetTypeID ())
 247  	{
 248  		AddDataObject ((CFDataRef) object);
 249  	}
 250  	else if (objectID == CFURLGetTypeID ())
 251  	{
 252  		StringSet exceptions;
 253  		
 254  		// get the path from the URL
 255  		char path [PATH_MAX];
 256  		DecodeURL ((CFURLRef) object, path, sizeof (path));
 257  
 258  		// canonicalize
 259  		char realPath [PATH_MAX];
 260  		if (realpath (path, realPath) == NULL)
 261  		{
 262  			UnixError::throwMe ();
 263  		}
 264  		
 265  		ConvertToStringSet (realPath, exceptionList, exceptions);
 266  
 267  		AddFileSystemObject (realPath, exceptions, true, false);
 268  	}
 269  	else
 270  	{
 271  		MacOSError::throwMe (errSecManifestNotEqual);
 272  	}
 273  }
 274  
 275  
 276  
 277  void RootItemList::Compare (RootItemList& item, bool compareOwnerAndGroup)
 278  {
 279  	// the number of items in the list has to be the same
 280  	unsigned numItems = (unsigned)size ();
 281  
 282  	if (numItems != item.size ())
 283  	{
 284  		MacOSError::throwMe (errSecManifestNotEqual);
 285  	}
 286  	
 287  	// for a root item list, items in the manifest MUST have the same creation order
 288  	unsigned i;
 289  	
 290  	for (i = 0; i < numItems; ++i)
 291  	{
 292  		 ManifestItem* item1 = (*this)[i];
 293  		 ManifestItem* item2 = item[i];
 294  		
 295  		if (item1->GetItemType () != item2->GetItemType ())
 296  		{
 297  			MacOSError::throwMe (errSecManifestNotEqual);
 298  		}
 299  		
 300  		item1->Compare (item2, compareOwnerAndGroup);
 301  	}
 302  }
 303  
 304  
 305  
 306  class CompareManifestFileItems
 307  {
 308  public:
 309  	bool operator () (ManifestItem *a, ManifestItem *b);
 310  };
 311  
 312  
 313  
 314  bool CompareManifestFileItems::operator () (ManifestItem *a, ManifestItem *b)
 315  {
 316  	FileSystemEntryItem *aa = static_cast<FileSystemEntryItem*>(a);
 317  	FileSystemEntryItem *bb = static_cast<FileSystemEntryItem*>(b);
 318  	
 319  	return strcmp (aa->GetName (), bb->GetName ()) < 0;
 320  }
 321  
 322  
 323  
 324  void FileSystemItemList::Compare (FileSystemItemList &a, bool compareOwnerAndGroup)
 325  {
 326  	unsigned numItems = (unsigned)size ();
 327  	
 328  	if (numItems != a.size ())
 329  	{
 330  		MacOSError::throwMe (errSecManifestNotEqual);
 331  	}
 332  
 333  	// sort the two lists
 334  	sort (begin (), end (), CompareManifestFileItems ());
 335  	sort (a.begin (), a.end (), CompareManifestFileItems ());
 336  	
 337  	// compare each item in the list
 338  	unsigned i;
 339  	for (i = 0; i < numItems; ++i)
 340  	{
 341  		ManifestItem *thisListPtr = (*this)[i];
 342  		ManifestItem *aListPtr = a[i];
 343  		if (thisListPtr->GetItemType () != aListPtr->GetItemType ())
 344  		{
 345  			MacOSError::throwMe (errSecManifestNotEqual);
 346  		}
 347  		thisListPtr->Compare (aListPtr, compareOwnerAndGroup);
 348  	}
 349  }
 350  
 351  
 352  
 353  //==========================  MANIFEST  ==========================
 354  
 355  
 356  
 357  ManifestInternal::ManifestInternal ()
 358  {
 359  }
 360  
 361  
 362  
 363  ManifestInternal::~ManifestInternal ()
 364  {
 365  	secinfo ("manifest", "Destroyed manifest internal %p", this);
 366  }
 367  
 368  
 369  
 370  void ManifestInternal::CompareManifests (ManifestInternal& m1,  ManifestInternal& m2, SecManifestCompareOptions options)
 371  {
 372  	if ((options & ~kSecManifestVerifyOwnerAndGroup) != 0)
 373  	{
 374  		MacOSError::throwMe (errSecUnimplemented); // we don't support these options
 375  	}
 376  	
 377  	m1.mManifestItems.Compare (m2.mManifestItems, (bool) options & kSecManifestVerifyOwnerAndGroup);
 378  }
 379  
 380  
 381  
 382  //==========================  MANIFEST ITEM  ==========================
 383  ManifestItem::~ManifestItem ()
 384  {
 385  }
 386  
 387  
 388  
 389  //==========================  DATA BLOB ITEM  ==========================
 390  ManifestDataBlobItem::ManifestDataBlobItem ()
 391  {
 392  }
 393  
 394  
 395  
 396  ManifestDataBlobItem::~ManifestDataBlobItem ()
 397  {
 398  }
 399  
 400  
 401  
 402  ManifestItemType ManifestDataBlobItem::GetItemType ()
 403  {
 404  	return kManifestDataBlobItemType;
 405  }
 406  
 407  
 408  
 409  const SHA1Digest* ManifestDataBlobItem::GetDigest ()
 410  {
 411  	return &mSHA1Digest;
 412  }
 413  
 414  
 415  
 416  void ManifestDataBlobItem::SetDigest (const SHA1Digest *sha1Digest)
 417  {
 418  	memcpy (&mSHA1Digest, sha1Digest, sizeof (SHA1Digest));
 419  }
 420  
 421  
 422  
 423  size_t ManifestDataBlobItem::GetLength ()
 424  {
 425  	return mLength;
 426  }
 427  
 428  
 429  
 430  void ManifestDataBlobItem::SetLength (size_t length)
 431  {
 432  	mLength = length;
 433  }
 434  
 435  
 436  
 437  void ManifestDataBlobItem::Compare (ManifestItem* item, bool compareOwnerAndGroup)
 438  {
 439  	ManifestDataBlobItem* i = static_cast<ManifestDataBlobItem*>(item);
 440  	if (memcmp (&i->mSHA1Digest, &mSHA1Digest, sizeof (SHA1Digest)) != 0)
 441  	{
 442  		MacOSError::throwMe (errSecManifestNotEqual);
 443  	}
 444  }
 445  
 446  
 447  
 448  //==========================  FILE SYSTEM ENTRY ITEM  ==========================
 449  
 450  
 451  
 452  FileSystemEntryItem::FileSystemEntryItem () : mUserID (0), mGroupID (0), mMode (0)
 453  {
 454  }
 455  
 456  
 457  
 458  FileSystemEntryItem::~FileSystemEntryItem ()
 459  {
 460  }
 461  
 462  
 463  
 464  void FileSystemEntryItem::SetName (char* name)
 465  {
 466  	mName = name;
 467  }
 468  
 469  
 470  
 471  static char* StringTail (char* path)
 472  {
 473  	 char* finger = path + strlen (path) - 1;
 474  	while (finger != path && *finger != '/')
 475  	{
 476  		finger -= 1;
 477  	}
 478  	
 479  	if (finger != path) // did find a separator
 480  	{
 481  		finger += 1;
 482  	}
 483  	
 484  	return finger;
 485  }
 486  
 487  
 488  
 489  void FileSystemEntryItem::SetPath (char* path)
 490  {
 491  	// save off the path
 492  	mPath = path;
 493  	
 494  	// while we are at it, extract that last name of the path name and save it off as the name
 495  	mName = StringTail (path);
 496  	secinfo ("manifest", "Created file item for %s with name %s", mPath.c_str (), mName.c_str ());
 497  }
 498  
 499  
 500  
 501  void FileSystemEntryItem::SetUID (uid_t uid)
 502  {
 503  	mUserID = uid;
 504  }
 505  
 506  
 507  
 508  void FileSystemEntryItem::SetGID (gid_t gid)
 509  {
 510  	mGroupID = gid;
 511  }
 512  
 513  
 514  
 515  void FileSystemEntryItem::SetMode (mode_t mode)
 516  {
 517  	mMode = mode;
 518  }
 519  
 520  
 521  
 522  uid_t FileSystemEntryItem::GetUID () const
 523  {
 524  	return mUserID;
 525  }
 526  
 527  
 528  gid_t FileSystemEntryItem::GetGID () const
 529  {
 530  	return mGroupID;
 531  }
 532  
 533  
 534  
 535  mode_t FileSystemEntryItem::GetMode () const
 536  {
 537  	return mMode;
 538  }
 539  
 540  
 541  
 542  const char* FileSystemEntryItem::GetName () const
 543  {
 544  	return (char*) mName.c_str ();
 545  }
 546  
 547  
 548  
 549  void FileSystemEntryItem::Compare (ManifestItem *aa, bool compareOwnerAndGroup)
 550  {
 551  	FileSystemEntryItem* a = static_cast<FileSystemEntryItem*>(aa);
 552  	
 553  	if (mName != a->mName || mMode != a->mMode)
 554  	{
 555  		MacOSError::throwMe (errSecManifestNotEqual);
 556  	}
 557  
 558  	if (compareOwnerAndGroup)
 559  	{
 560  		if (mUserID != a->mUserID || mGroupID != a->mGroupID)
 561  		{
 562  			MacOSError::throwMe (errSecManifestNotEqual);
 563  		}
 564  	}
 565  }
 566  
 567  
 568  
 569  //==========================  MANIFEST FILE ITEM  ==========================
 570  
 571  
 572  
 573  bool ManifestFileItem::FileSystemHasTrueForks (char* pathToFile)
 574  {
 575      // return true if volume to which path points supports true forked files
 576      struct statfs st;
 577      int result = statfs (pathToFile, &st);
 578      if (result != 0)
 579      {
 580          secinfo ("manifest", "Could not get statfs (error was %s)", strerror (errno));
 581          UnixError::throwMe ();
 582      }
 583      
 584      return strcmp (st.f_fstypename, "afpfs") == 0 || strcmp (st.f_fstypename, "hfs") == 0;
 585  }
 586  
 587  
 588  
 589  std::string ManifestFileItem::ResourceFileName (char* path)
 590  {
 591      std::string filePath;
 592  
 593      if (FileSystemHasTrueForks (path))
 594      {
 595          filePath = path;
 596  		
 597  		return filePath + "/rsrc";
 598      }
 599      else
 600      {
 601  		return "";
 602      }
 603  }
 604  
 605  
 606  
 607  bool ManifestFileItem::HasResourceFork (char* pathToFile, std::string &result, struct stat &st)
 608  {
 609      // try to get the stat on the file.  If it works, the file exists
 610  	result = ResourceFileName (pathToFile);
 611  	if (result.length () != 0)
 612  	{
 613  		int stresult = lstat (result.c_str (), &st);
 614  		if (stresult == 0)
 615  		{
 616  			return st.st_size != 0;
 617  		}
 618  	}
 619  	
 620      return false;
 621  }
 622  
 623  
 624  
 625  ManifestFileItem::ManifestFileItem () : mNumForks (1)
 626  {
 627  }
 628  
 629  
 630  
 631  ManifestFileItem::~ManifestFileItem ()
 632  {
 633  	secinfo ("manifest", "Destroyed manifest item %p for path %s", this, mPath.c_str ());
 634  }
 635  
 636  
 637  
 638  ManifestItemType ManifestFileItem::GetItemType () 
 639  {
 640  	return kManifestFileItemType;
 641  }
 642  
 643  
 644  
 645  u_int32_t ManifestFileItem::GetNumberOfForks () 
 646  {
 647  	return mNumForks;
 648  }
 649  
 650  
 651  
 652  void ManifestFileItem::SetNumberOfForks (u_int32_t numForks)
 653  {
 654  	mNumForks = numForks;
 655  }
 656  
 657  
 658  
 659  bool ManifestFileItem::FileIsMachOBinary (char* path)
 660  {
 661  	return false;
 662  }
 663  
 664  
 665  
 666  void ManifestFileItem::SetForkLength (int which, size_t length)
 667  {
 668  	mFileLengths[which] = length;
 669  }
 670  
 671  
 672  
 673  size_t ManifestFileItem::GetForkLength (int which)
 674  {
 675  	return mFileLengths[which];
 676  }
 677  
 678  
 679  
 680  void ManifestFileItem::ComputeRepresentations (struct stat &st, bool hasAppleDoubleResourceFork)
 681  {
 682  	// digest the data fork
 683  	mNumForks = 1;
 684  	ComputeDigestForFile ((char*) mPath.c_str (), mDigest[0], mFileLengths[0], st);
 685  	
 686  	struct stat stat2;
 687  	std::string resourceForkName;
 688  	if (hasAppleDoubleResourceFork)
 689  	{
 690  		mNumForks = 2;
 691  		
 692  		resourceForkName = mPath;
 693  		// walk back to find the beginning of the path and insert ._
 694                  int i = (int)resourceForkName.length () - 1;
 695  		while (i >= 0 && resourceForkName[i] != '/')
 696  		{
 697  			i -= 1;
 698  		}
 699  		
 700  		i += 1;
 701  		
 702  		resourceForkName.insert (i, "._");
 703  		
 704  		ComputeDigestForAppleDoubleResourceFork ((char*) resourceForkName.c_str(), mDigest[1], mFileLengths[1]);
 705  	}
 706  	else if (HasResourceFork ((char*) mPath.c_str (), resourceForkName, stat2))
 707  	{
 708  		mNumForks = 2;
 709  		ComputeDigestForFile ((char*) resourceForkName.c_str (), mDigest[1], mFileLengths[1], stat2);
 710  	}
 711  }
 712  
 713  
 714  
 715  static const int kReadChunkSize = 4096 * 4;
 716  
 717  
 718  
 719  static u_int32_t ExtractUInt32 (u_int8_t *&finger)
 720  {
 721  	u_int32_t result = 0;
 722  	int i;
 723  	for (i = 0; i < 4; ++i)
 724  	{
 725  		result = (result << 8) | *finger++;
 726  	}
 727  	
 728  	return result;
 729  }
 730  
 731  
 732  
 733  void ManifestFileItem::ComputeDigestForAppleDoubleResourceFork (char* name, SHA1Digest &digest, size_t &fileLength)
 734  {
 735  	secinfo ("manifest", "Creating digest for AppleDouble resource fork %s", name);
 736  
 737  	CC_SHA1_CTX digestContext;
 738  	CC_SHA1_Init (&digestContext);
 739  	
 740  	// bring the file into memory
 741  	int fileNo = open (name, O_RDONLY, 0);
 742  	if (fileNo == -1)
 743  	{
 744  		UnixError::throwMe ();
 745  	}
 746  	
 747  	// figure out how big the file is.
 748  	struct stat st;
 749  	int result = fstat (fileNo, &st);
 750  	if (result == -1)
 751  	{
 752  		UnixError::throwMe ();
 753  	}
 754  	
 755  	u_int8_t *buffer = new u_int8_t[st.st_size];
 756  	ssize_t bytesRead = read (fileNo, buffer, (size_t)st.st_size);
 757  	close (fileNo);
 758  	
 759  	if (bytesRead != st.st_size)
 760  	{
 761  		delete[] buffer;
 762  		UnixError::throwMe ();
 763  	}
 764  	
 765  	// walk the entry table to find the offset to our resource fork
 766  	u_int8_t *bufPtr = buffer + 24; // size of the header + filler
 767  	
 768  	// compute the number of entries in the file
 769  	int numEntries = (((int) bufPtr[0]) << 8) | (int) (bufPtr [1]);
 770  	bufPtr += 2;
 771  	
 772  	ssize_t length = 0;
 773  	ssize_t offset = 0;
 774  	
 775  	int i;
 776  	for (i = 0; i < numEntries; ++i)
 777  	{
 778  		// bufPtr points to an entry descriptor.  Four bytes for the ID, four for the offset, four for the length
 779  		ssize_t id = ExtractUInt32 (bufPtr);
 780  		offset = ExtractUInt32 (bufPtr);
 781  		length = ExtractUInt32 (bufPtr);
 782  		
 783  		if (id == 2) // is it the resource fork?
 784  		{
 785  			break;
 786  		}
 787  	}
 788  	
 789  	if (i >= numEntries) // did we run off the end?  This had better not happen
 790  	{
 791  		MacOSError::throwMe (errSecManifestNotSupported);
 792  	}
 793  	
 794  	fileLength = length;
 795  
 796  	// digest the data
 797  	CC_SHA1_Update (&digestContext, buffer + offset, (CC_LONG)length);
 798  	
 799  	// compute the SHA1 hash
 800  	CC_SHA1_Final (digest, &digestContext);
 801  	
 802  	delete[] buffer;
 803  }
 804  
 805  
 806  
 807  void ManifestFileItem::ComputeDigestForFile (char* name, SHA1Digest &digest, size_t &fileLength, struct stat &st)
 808  {
 809  	secinfo ("manifest", "Creating digest for %s", name);
 810  
 811  	// create a context for the digest operation
 812  	CC_SHA1_CTX digestContext;
 813  	CC_SHA1_Init (&digestContext);
 814  
 815  	
 816  	int fileNo = open (name, O_RDONLY, 0);
 817  	if (fileNo == -1)
 818  	{
 819  		UnixError::throwMe ();
 820  	}
 821  	
 822  	fileLength = (size_t)st.st_size;
 823  
 824  	if (st.st_size != 0)
 825  	{
 826  		// read the file
 827          std::vector<char> buffer(kReadChunkSize);
 828  
 829          ssize_t bytesRead;
 830          while ((bytesRead = read (fileNo, buffer.data(), kReadChunkSize)) != 0)
 831  		{
 832  			// digest the read data
 833  			CC_SHA1_Update (&digestContext, buffer.data(), (CC_LONG)bytesRead);
 834  		}
 835  		
 836  		// compute the SHA1 hash
 837  		CC_SHA1_Final (digest, &digestContext);
 838  	}
 839  
 840  	close (fileNo);
 841  }
 842  
 843  
 844  
 845  void ManifestFileItem::GetItemRepresentation (int whichFork, void* &itemRep, size_t &size) 
 846  {
 847  	itemRep = (void*) &mDigest[whichFork];
 848  	size = kSHA1DigestSize;
 849  }
 850  
 851  
 852  
 853  void ManifestFileItem::SetItemRepresentation (int whichFork, const void* itemRep, size_t size) 
 854  {
 855  	memcpy ((void*) &mDigest[whichFork], itemRep, size);
 856  }
 857  
 858  
 859  
 860  void ManifestFileItem::Compare (ManifestItem *manifestItem, bool compareOwnerAndGroup)
 861  {
 862  	FileSystemEntryItem::Compare (manifestItem, compareOwnerAndGroup);
 863  
 864  	ManifestFileItem* item = static_cast< ManifestFileItem*>(manifestItem);
 865  	
 866  	secinfo ("manifest", "Comparing file item %s against %s", GetName (), item->GetName ());
 867  
 868  	// the number of forks should be equal
 869  	if (mNumForks != item->mNumForks)
 870  	{
 871  		MacOSError::throwMe (errSecManifestNotEqual);
 872  	}
 873  	
 874  	// compare file lengths
 875  	int i;
 876  	for (i = 0; i < mNumForks; ++i)
 877  	{
 878  		if (mFileLengths[i] != item->mFileLengths[i])
 879  		{
 880  			MacOSError::throwMe (errSecManifestNotEqual);
 881  		}
 882  
 883  		if (memcmp (&mDigest[i], item->mDigest[i], kSHA1DigestSize) != 0)
 884  		{
 885  			MacOSError::throwMe (errSecManifestNotEqual);
 886  		}
 887  	}
 888  }
 889  
 890  
 891  
 892  //==========================  MANIFEST DIRECTORY ITEM  ==========================
 893  
 894  
 895  
 896  ManifestDirectoryItem::ManifestDirectoryItem ()
 897  {
 898  }
 899  
 900  
 901  
 902  ManifestDirectoryItem::~ManifestDirectoryItem ()
 903  {
 904  	secinfo ("manifest", "Destroyed directory item %p for path %s", this, mPath.c_str ());
 905  }
 906  
 907  
 908  const char* kAppleDoublePrefix = "._";
 909  const int kAppleDoublePrefixLength = 2;
 910  
 911  static int CompareFilenames (const FTSENT** a, const FTSENT** b)
 912  {
 913  	// ._name is always greater than name
 914  	// otherwise, ._ is ignored for sorting purposes
 915  	const char* aa = (*a)->fts_name;
 916  	const char* bb = (*b)->fts_name;
 917  	bool aHasPrefix = false;
 918  	
 919  	if (strncmp (aa, kAppleDoublePrefix, kAppleDoublePrefixLength) == 0) // do we have an appledouble prefix?
 920  	{
 921  		aHasPrefix = true;
 922  		aa += kAppleDoublePrefixLength;
 923  	}
 924  	
 925  	if (strncmp (bb, kAppleDoublePrefix, kAppleDoublePrefixLength) == 0) // do we have an appledouble prefix?
 926  	{
 927  		bb += kAppleDoublePrefixLength;
 928  	}
 929  	
 930  	int compare = strcmp (aa, bb);
 931  	
 932  	if (compare == 0 && aHasPrefix)
 933  	{
 934  		return 1;
 935  	}
 936  		
 937  	return compare;
 938  }
 939  
 940  
 941  
 942  const u_int8_t kAppleDoubleMagicNumber[] = {0x00, 0x05, 0x16, 0x07};
 943  
 944  
 945  
 946  static bool PathIsAppleDoubleFile (const char* path)
 947  {
 948  	// Open the file and check the "magic number".
 949  	int fRef = open (path, O_RDONLY, 0);
 950  	
 951  	u_int8_t buffer[4];
 952  	
 953  	// read the first four bytes of the file
 954  	ssize_t bytesRead = read(fRef, buffer, 4);
 955  	if (bytesRead == -1)
 956  	{
 957  		int err = errno;
 958  		close (fRef);
 959  		UnixError::throwMe (err);
 960  	}
 961  	
 962  	close (fRef);
 963  	
 964  	if (bytesRead != 4) // did we get enough bytes?
 965  	{
 966  		return false;
 967  	}
 968  	
 969  	// what we got had better be the proper magic number for this file type
 970  	int i;
 971  	for (i = 0; i < 4; ++i)
 972  	{
 973  		if (buffer[i] != kAppleDoubleMagicNumber[i])
 974  		{
 975  			return false;
 976  		}
 977  	}
 978  	
 979  	return true;
 980  }
 981  
 982  
 983  
 984  void ManifestDirectoryItem::SetPath (char* path, StringSet &exceptions, bool isRoot)
 985  {
 986  	if (isRoot)
 987  	{
 988  		mName = "/";
 989  		mPath = path;
 990  	}
 991  	else
 992  	{
 993  		FileSystemEntryItem::SetPath (path);
 994  	}
 995  	
 996  	secinfo ("manifest", "Added directory entry for %s with name %s", mPath.c_str (), mName.c_str ());
 997  	
 998  	// enumerate the contents of the directory.
 999  	char* path_argv[] = { path, NULL };
1000  	FTS* thisDir = fts_open (path_argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_NOSTAT | FTS_XDEV, CompareFilenames);
1001  	if (thisDir == NULL) // huh?  The file disappeared or isn't a directory any more
1002  	{
1003  		UnixError::throwMe ();
1004  	}
1005  	
1006  	(void)fts_read(thisDir);
1007  	FTSENT* dirEnt = fts_children (thisDir, FTS_NAMEONLY);
1008  	
1009  	while (dirEnt != NULL)
1010  	{
1011  		// get the next entry
1012  		FTSENT* dirEntNext = dirEnt->fts_link;
1013  		bool hasAppleDoubleResourceFork = false;
1014  
1015  		// see if it is an AppleDouble resource fork for this file
1016  		if (dirEntNext &&
1017  			strncmp (dirEntNext->fts_name, kAppleDoublePrefix, kAppleDoublePrefixLength) == 0 &&
1018  			strcmp (dirEnt->fts_name, dirEntNext->fts_name + kAppleDoublePrefixLength) == 0)
1019  		{
1020  			if (PathIsAppleDoubleFile ((mPath + "/" + dirEntNext->fts_name).c_str ()))
1021  			{
1022  				hasAppleDoubleResourceFork = true;
1023  				dirEntNext = dirEntNext->fts_link;
1024  			}
1025  		}
1026  		
1027  		// figure out what this is pointing to.
1028  		std::string fileName = mPath + "/" + dirEnt->fts_name;
1029  
1030  		mDirectoryItems.AddFileSystemObject ((char*) fileName.c_str(), exceptions, false, hasAppleDoubleResourceFork);
1031  		
1032  		dirEnt = dirEntNext;
1033  	}
1034  	
1035  	fts_close(thisDir);
1036  }
1037  
1038  
1039  
1040  ManifestItemType ManifestDirectoryItem::GetItemType () 
1041  {
1042  	return kManifestDirectoryItemType;
1043  }
1044  
1045  
1046  
1047  void ManifestDirectoryItem::Compare (ManifestItem* a, bool compareOwnerAndGroup)
1048  {
1049  	FileSystemEntryItem::Compare (a, compareOwnerAndGroup);
1050  	ManifestDirectoryItem* aa = static_cast<ManifestDirectoryItem*>(a);
1051  	secinfo ("manifest", "Comparing directory item %s against %s", GetName (), aa->GetName ());
1052  	mDirectoryItems.Compare (aa->mDirectoryItems, compareOwnerAndGroup);
1053  }
1054  
1055  
1056  
1057  //==========================  MANIFEST SYMLINK ITEM  ==========================
1058  
1059  
1060  
1061  ManifestSymLinkItem::ManifestSymLinkItem ()
1062  {
1063  }
1064  
1065  
1066  
1067  ManifestSymLinkItem::~ManifestSymLinkItem ()
1068  {
1069  	secinfo ("manifest", "Destroyed symlink item for %s", mPath.c_str ());
1070  }
1071  
1072  
1073  
1074  void ManifestSymLinkItem::ComputeRepresentation ()
1075  {
1076  	char path [FILENAME_MAX];
1077  	int result = (int)readlink (mPath.c_str (), path, sizeof (path));
1078  	secinfo ("manifest", "Read content %s for %s", path, mPath.c_str ());
1079  	
1080  	// create a digest context
1081  	CC_SHA1_CTX digestContext;
1082  	CC_SHA1_Init (&digestContext);
1083  	
1084  	// digest the data
1085  	CC_SHA1_Update (&digestContext, path, result);
1086  
1087  	// compute the result
1088  	CC_SHA1_Final (mDigest, &digestContext);
1089  	
1090  	UnixError::check (result);
1091  }
1092  
1093  
1094  
1095  const SHA1Digest* ManifestSymLinkItem::GetDigest () 
1096  {
1097  	return &mDigest;
1098  }
1099  
1100  
1101  
1102  void ManifestSymLinkItem::SetDigest (const SHA1Digest* digest)
1103  {
1104  	memcpy (mDigest, digest, sizeof (SHA1Digest));
1105  }
1106  
1107  
1108  
1109  ManifestItemType ManifestSymLinkItem::GetItemType () 
1110  {
1111  	return kManifestSymLinkItemType;
1112  }
1113  
1114  
1115  
1116  void ManifestSymLinkItem::Compare (ManifestItem *a, bool compareOwnerAndGroup)
1117  {
1118  	FileSystemEntryItem::Compare (a, compareOwnerAndGroup);
1119  	ManifestSymLinkItem* aa = static_cast<ManifestSymLinkItem*>(a);
1120  	secinfo ("manifest", "Comparing symlink item %s against %s", GetName (), aa->GetName ());
1121  	
1122  	// now compare the data
1123  	if (memcmp (&mDigest, &aa->mDigest, kSHA1DigestSize) != 0)
1124  	{
1125  		MacOSError::throwMe (errSecManifestNotEqual);
1126  	}
1127  }
1128  
1129  
1130  
1131  //==========================  MANIFEST OTHER ITEM  ==========================
1132  
1133  
1134  
1135  ManifestOtherItem::ManifestOtherItem ()
1136  {
1137  }
1138  
1139  
1140  
1141  ManifestOtherItem::~ManifestOtherItem ()
1142  {
1143  	secinfo ("manifest", "Destroyed other item for path %s", mPath.c_str ());
1144  }
1145  
1146  
1147  
1148  ManifestItemType ManifestOtherItem::GetItemType () 
1149  {
1150  	return kManifestOtherType;
1151  }
1152  
1153  
1154  
1155  void ManifestOtherItem::Compare (ManifestItem *a, bool compareOwnerAndGroup)
1156  {
1157  	FileSystemEntryItem::Compare (a, compareOwnerAndGroup);
1158  	secinfo ("manifest", "Comparing other item %s against %s", GetName (), static_cast<FileSystemEntryItem*>(a)->GetName ());
1159  }