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 42ModuleNexus<CSSMInitializer> CSSMInitializer::mInstance; 43 44CSSMInitializer::CSSMInitializer () : mModule (gGuidAppleCSP), mCSP (mModule) 45{ 46} 47 48 49 50CSSMInitializer::~CSSMInitializer () 51{ 52} 53 54 55 56CssmClient::CSP* CSSMInitializer::GetCSP () 57{ 58 return &mInstance().mCSP; 59} 60 61 62 63//========================== MANIFEST ITEM LIST ========================== 64 65 66 67ManifestItemList::ManifestItemList () 68{ 69} 70 71 72 73ManifestItemList::~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 86void 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 105void 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 secdebug ("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 176void 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 200void 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 secdebug ("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 secdebug ("manifest", "Inserted path %s as an exception", realPath); 233 exceptions.insert (realPath); 234 } 235 } 236 } 237} 238 239 240 241void 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 277void 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 306class CompareManifestFileItems 307{ 308public: 309 bool operator () (ManifestItem *a, ManifestItem *b); 310}; 311 312 313 314bool 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 324void 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 357ManifestInternal::ManifestInternal () 358{ 359} 360 361 362 363ManifestInternal::~ManifestInternal () 364{ 365 secdebug ("manifest", "Destroyed manifest internal %p", this); 366} 367 368 369 370void 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 ========================== 383ManifestItem::~ManifestItem () 384{ 385} 386 387 388 389//========================== DATA BLOB ITEM ========================== 390ManifestDataBlobItem::ManifestDataBlobItem () 391{ 392} 393 394 395 396ManifestDataBlobItem::~ManifestDataBlobItem () 397{ 398} 399 400 401 402ManifestItemType ManifestDataBlobItem::GetItemType () 403{ 404 return kManifestDataBlobItemType; 405} 406 407 408 409const SHA1Digest* ManifestDataBlobItem::GetDigest () 410{ 411 return &mSHA1Digest; 412} 413 414 415 416void ManifestDataBlobItem::SetDigest (const SHA1Digest *sha1Digest) 417{ 418 memcpy (&mSHA1Digest, sha1Digest, sizeof (SHA1Digest)); 419} 420 421 422 423size_t ManifestDataBlobItem::GetLength () 424{ 425 return mLength; 426} 427 428 429 430void ManifestDataBlobItem::SetLength (size_t length) 431{ 432 mLength = length; 433} 434 435 436 437void 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 452FileSystemEntryItem::FileSystemEntryItem () : mUserID (0), mGroupID (0), mMode (0) 453{ 454} 455 456 457 458FileSystemEntryItem::~FileSystemEntryItem () 459{ 460} 461 462 463 464void FileSystemEntryItem::SetName (char* name) 465{ 466 mName = name; 467} 468 469 470 471static 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 489void 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 secdebug ("manifest", "Created file item for %s with name %s", mPath.c_str (), mName.c_str ()); 497} 498 499 500 501void FileSystemEntryItem::SetUID (uid_t uid) 502{ 503 mUserID = uid; 504} 505 506 507 508void FileSystemEntryItem::SetGID (gid_t gid) 509{ 510 mGroupID = gid; 511} 512 513 514 515void FileSystemEntryItem::SetMode (mode_t mode) 516{ 517 mMode = mode; 518} 519 520 521 522uid_t FileSystemEntryItem::GetUID () const 523{ 524 return mUserID; 525} 526 527 528gid_t FileSystemEntryItem::GetGID () const 529{ 530 return mGroupID; 531} 532 533 534 535mode_t FileSystemEntryItem::GetMode () const 536{ 537 return mMode; 538} 539 540 541 542const char* FileSystemEntryItem::GetName () const 543{ 544 return (char*) mName.c_str (); 545} 546 547 548 549void 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 573bool 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 secdebug ("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 589std::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 return filePath; 605} 606 607 608 609bool ManifestFileItem::HasResourceFork (char* pathToFile, std::string &result, struct stat &st) 610{ 611 // try to get the stat on the file. If it works, the file exists 612 result = ResourceFileName (pathToFile); 613 if (result.length () != 0) 614 { 615 int stresult = lstat (result.c_str (), &st); 616 if (stresult == 0) 617 { 618 return st.st_size != 0; 619 } 620 } 621 622 return false; 623} 624 625 626 627ManifestFileItem::ManifestFileItem () : mNumForks (1) 628{ 629} 630 631 632 633ManifestFileItem::~ManifestFileItem () 634{ 635 secdebug ("manifest", "Destroyed manifest item %p for path %s", this, mPath.c_str ()); 636} 637 638 639 640ManifestItemType ManifestFileItem::GetItemType () 641{ 642 return kManifestFileItemType; 643} 644 645 646 647u_int32_t ManifestFileItem::GetNumberOfForks () 648{ 649 return mNumForks; 650} 651 652 653 654void ManifestFileItem::SetNumberOfForks (u_int32_t numForks) 655{ 656 mNumForks = numForks; 657} 658 659 660 661bool ManifestFileItem::FileIsMachOBinary (char* path) 662{ 663 return false; 664} 665 666 667 668void ManifestFileItem::SetForkLength (int which, size_t length) 669{ 670 mFileLengths[which] = length; 671} 672 673 674 675size_t ManifestFileItem::GetForkLength (int which) 676{ 677 return mFileLengths[which]; 678} 679 680 681 682void ManifestFileItem::ComputeRepresentations (struct stat &st, bool hasAppleDoubleResourceFork) 683{ 684 // digest the data fork 685 mNumForks = 1; 686 ComputeDigestForFile ((char*) mPath.c_str (), mDigest[0], mFileLengths[0], st); 687 688 struct stat stat2; 689 std::string resourceForkName; 690 if (hasAppleDoubleResourceFork) 691 { 692 mNumForks = 2; 693 694 resourceForkName = mPath; 695 // walk back to find the beginning of the path and insert ._ 696 int i = (int)resourceForkName.length () - 1; 697 while (i >= 0 && resourceForkName[i] != '/') 698 { 699 i -= 1; 700 } 701 702 i += 1; 703 704 resourceForkName.insert (i, "._"); 705 706 ComputeDigestForAppleDoubleResourceFork ((char*) resourceForkName.c_str(), mDigest[1], mFileLengths[1]); 707 } 708 else if (HasResourceFork ((char*) mPath.c_str (), resourceForkName, stat2)) 709 { 710 mNumForks = 2; 711 ComputeDigestForFile ((char*) resourceForkName.c_str (), mDigest[1], mFileLengths[1], stat2); 712 } 713} 714 715 716 717static const int kReadChunkSize = 4096 * 4; 718 719 720 721static u_int32_t ExtractUInt32 (u_int8_t *&finger) 722{ 723 u_int32_t result = 0; 724 int i; 725 for (i = 0; i < 4; ++i) 726 { 727 result = (result << 8) | *finger++; 728 } 729 730 return result; 731} 732 733 734 735void ManifestFileItem::ComputeDigestForAppleDoubleResourceFork (char* name, SHA1Digest &digest, size_t &fileLength) 736{ 737 secdebug ("manifest", "Creating digest for AppleDouble resource fork %s", name); 738 739 CC_SHA1_CTX digestContext; 740 CC_SHA1_Init (&digestContext); 741 742 // bring the file into memory 743 int fileNo = open (name, O_RDONLY, 0); 744 if (fileNo == -1) 745 { 746 UnixError::throwMe (); 747 } 748 749 // figure out how big the file is. 750 struct stat st; 751 int result = fstat (fileNo, &st); 752 if (result == -1) 753 { 754 UnixError::throwMe (); 755 } 756 757 u_int8_t *buffer = new u_int8_t[st.st_size]; 758 ssize_t bytesRead = read (fileNo, buffer, (size_t)st.st_size); 759 close (fileNo); 760 761 if (bytesRead != st.st_size) 762 { 763 delete buffer; 764 UnixError::throwMe (); 765 } 766 767 // walk the entry table to find the offset to our resource fork 768 u_int8_t *bufPtr = buffer + 24; // size of the header + filler 769 770 // compute the number of entries in the file 771 int numEntries = (((int) bufPtr[0]) << 8) | (int) (bufPtr [1]); 772 bufPtr += 2; 773 774 ssize_t length = 0; 775 ssize_t offset = 0; 776 777 int i; 778 for (i = 0; i < numEntries; ++i) 779 { 780 // bufPtr points to an entry descriptor. Four bytes for the ID, four for the offset, four for the length 781 ssize_t id = ExtractUInt32 (bufPtr); 782 offset = ExtractUInt32 (bufPtr); 783 length = ExtractUInt32 (bufPtr); 784 785 if (id == 2) // is it the resource fork? 786 { 787 break; 788 } 789 } 790 791 if (i >= numEntries) // did we run off the end? This had better not happen 792 { 793 MacOSError::throwMe (errSecManifestNotSupported); 794 } 795 796 fileLength = length; 797 798 // digest the data 799 CC_SHA1_Update (&digestContext, buffer + offset, (CC_LONG)length); 800 801 // compute the SHA1 hash 802 CC_SHA1_Final (digest, &digestContext); 803 804 delete buffer; 805} 806 807 808 809void ManifestFileItem::ComputeDigestForFile (char* name, SHA1Digest &digest, size_t &fileLength, struct stat &st) 810{ 811 secdebug ("manifest", "Creating digest for %s", name); 812 813 // create a context for the digest operation 814 CC_SHA1_CTX digestContext; 815 CC_SHA1_Init (&digestContext); 816 817 818 int fileNo = open (name, O_RDONLY, 0); 819 if (fileNo == -1) 820 { 821 UnixError::throwMe (); 822 } 823 824 fileLength = (size_t)st.st_size; 825 826 if (st.st_size != 0) 827 { 828 // read the file 829 char buffer[kReadChunkSize]; 830 831 ssize_t bytesRead; 832 while ((bytesRead = read (fileNo, buffer, kReadChunkSize)) != 0) 833 { 834 // digest the read data 835 CC_SHA1_Update (&digestContext, buffer, (CC_LONG)bytesRead); 836 } 837 838 // compute the SHA1 hash 839 CC_SHA1_Final (digest, &digestContext); 840 } 841 842 close (fileNo); 843} 844 845 846 847void ManifestFileItem::GetItemRepresentation (int whichFork, void* &itemRep, size_t &size) 848{ 849 itemRep = (void*) &mDigest[whichFork]; 850 size = kSHA1DigestSize; 851} 852 853 854 855void ManifestFileItem::SetItemRepresentation (int whichFork, const void* itemRep, size_t size) 856{ 857 memcpy ((void*) &mDigest[whichFork], itemRep, size); 858} 859 860 861 862void ManifestFileItem::Compare (ManifestItem *manifestItem, bool compareOwnerAndGroup) 863{ 864 FileSystemEntryItem::Compare (manifestItem, compareOwnerAndGroup); 865 866 ManifestFileItem* item = static_cast< ManifestFileItem*>(manifestItem); 867 868 secdebug ("manifest", "Comparing file item %s against %s", GetName (), item->GetName ()); 869 870 // the number of forks should be equal 871 if (mNumForks != item->mNumForks) 872 { 873 MacOSError::throwMe (errSecManifestNotEqual); 874 } 875 876 // compare file lengths 877 int i; 878 for (i = 0; i < mNumForks; ++i) 879 { 880 if (mFileLengths[i] != item->mFileLengths[i]) 881 { 882 MacOSError::throwMe (errSecManifestNotEqual); 883 } 884 885 if (memcmp (&mDigest[i], item->mDigest[i], kSHA1DigestSize) != 0) 886 { 887 MacOSError::throwMe (errSecManifestNotEqual); 888 } 889 } 890} 891 892 893 894//========================== MANIFEST DIRECTORY ITEM ========================== 895 896 897 898ManifestDirectoryItem::ManifestDirectoryItem () 899{ 900} 901 902 903 904ManifestDirectoryItem::~ManifestDirectoryItem () 905{ 906 secdebug ("manifest", "Destroyed directory item %p for path %s", this, mPath.c_str ()); 907} 908 909 910const char* kAppleDoublePrefix = "._"; 911const int kAppleDoublePrefixLength = 2; 912 913static int CompareFilenames (const FTSENT** a, const FTSENT** b) 914{ 915 // ._name is always greater than name 916 // otherwise, ._ is ignored for sorting purposes 917 const char* aa = (*a)->fts_name; 918 const char* bb = (*b)->fts_name; 919 bool aHasPrefix = false; 920 921 if (strncmp (aa, kAppleDoublePrefix, kAppleDoublePrefixLength) == 0) // do we have an appledouble prefix? 922 { 923 aHasPrefix = true; 924 aa += kAppleDoublePrefixLength; 925 } 926 927 if (strncmp (bb, kAppleDoublePrefix, kAppleDoublePrefixLength) == 0) // do we have an appledouble prefix? 928 { 929 bb += kAppleDoublePrefixLength; 930 } 931 932 int compare = strcmp (aa, bb); 933 934 if (compare == 0 && aHasPrefix) 935 { 936 return 1; 937 } 938 939 return compare; 940} 941 942 943 944const u_int8_t kAppleDoubleMagicNumber[] = {0x00, 0x05, 0x16, 0x07}; 945 946 947 948static bool PathIsAppleDoubleFile (const char* path) 949{ 950 // Open the file and check the "magic number". 951 int fRef = open (path, O_RDONLY, 0); 952 953 u_int8_t buffer[4]; 954 955 // read the first four bytes of the file 956 ssize_t bytesRead = read(fRef, buffer, 4); 957 if (bytesRead == -1) 958 { 959 int err = errno; 960 close (fRef); 961 UnixError::throwMe (err); 962 } 963 964 close (fRef); 965 966 if (bytesRead != 4) // did we get enough bytes? 967 { 968 return false; 969 } 970 971 // what we got had better be the proper magic number for this file type 972 int i; 973 for (i = 0; i < 4; ++i) 974 { 975 if (buffer[i] != kAppleDoubleMagicNumber[i]) 976 { 977 return false; 978 } 979 } 980 981 return true; 982} 983 984 985 986void ManifestDirectoryItem::SetPath (char* path, StringSet &exceptions, bool isRoot) 987{ 988 if (isRoot) 989 { 990 mName = "/"; 991 mPath = path; 992 } 993 else 994 { 995 FileSystemEntryItem::SetPath (path); 996 } 997 998 secdebug ("manifest", "Added directory entry for %s with name %s", mPath.c_str (), mName.c_str ()); 999 1000 // enumerate the contents of the directory. 1001 char* path_argv[] = { path, NULL }; 1002 FTS* thisDir = fts_open (path_argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_NOSTAT | FTS_XDEV, CompareFilenames); 1003 if (thisDir == NULL) // huh? The file disappeared or isn't a directory any more 1004 { 1005 UnixError::throwMe (); 1006 } 1007 1008 (void)fts_read(thisDir); 1009 FTSENT* dirEnt = fts_children (thisDir, FTS_NAMEONLY); 1010 1011 while (dirEnt != NULL) 1012 { 1013 // get the next entry 1014 FTSENT* dirEntNext = dirEnt->fts_link; 1015 bool hasAppleDoubleResourceFork = false; 1016 1017 // see if it is an AppleDouble resource fork for this file 1018 if (dirEntNext && 1019 strncmp (dirEntNext->fts_name, kAppleDoublePrefix, kAppleDoublePrefixLength) == 0 && 1020 strcmp (dirEnt->fts_name, dirEntNext->fts_name + kAppleDoublePrefixLength) == 0) 1021 { 1022 if (PathIsAppleDoubleFile ((mPath + "/" + dirEntNext->fts_name).c_str ())) 1023 { 1024 hasAppleDoubleResourceFork = true; 1025 dirEntNext = dirEntNext->fts_link; 1026 } 1027 } 1028 1029 // figure out what this is pointing to. 1030 std::string fileName = mPath + "/" + dirEnt->fts_name; 1031 1032 mDirectoryItems.AddFileSystemObject ((char*) fileName.c_str(), exceptions, false, hasAppleDoubleResourceFork); 1033 1034 dirEnt = dirEntNext; 1035 } 1036 1037 fts_close(thisDir); 1038} 1039 1040 1041 1042ManifestItemType ManifestDirectoryItem::GetItemType () 1043{ 1044 return kManifestDirectoryItemType; 1045} 1046 1047 1048 1049void ManifestDirectoryItem::Compare (ManifestItem* a, bool compareOwnerAndGroup) 1050{ 1051 FileSystemEntryItem::Compare (a, compareOwnerAndGroup); 1052 ManifestDirectoryItem* aa = static_cast<ManifestDirectoryItem*>(a); 1053 secdebug ("manifest", "Comparing directory item %s against %s", GetName (), aa->GetName ()); 1054 mDirectoryItems.Compare (aa->mDirectoryItems, compareOwnerAndGroup); 1055} 1056 1057 1058 1059//========================== MANIFEST SYMLINK ITEM ========================== 1060 1061 1062 1063ManifestSymLinkItem::ManifestSymLinkItem () 1064{ 1065} 1066 1067 1068 1069ManifestSymLinkItem::~ManifestSymLinkItem () 1070{ 1071 secdebug ("manifest", "Destroyed symlink item for %s", mPath.c_str ()); 1072} 1073 1074 1075 1076void ManifestSymLinkItem::ComputeRepresentation () 1077{ 1078 char path [FILENAME_MAX]; 1079 int result = (int)readlink (mPath.c_str (), path, sizeof (path)); 1080 secdebug ("manifest", "Read content %s for %s", path, mPath.c_str ()); 1081 1082 // create a digest context 1083 CC_SHA1_CTX digestContext; 1084 CC_SHA1_Init (&digestContext); 1085 1086 // digest the data 1087 CC_SHA1_Update (&digestContext, path, result); 1088 1089 // compute the result 1090 CC_SHA1_Final (mDigest, &digestContext); 1091 1092 UnixError::check (result); 1093} 1094 1095 1096 1097const SHA1Digest* ManifestSymLinkItem::GetDigest () 1098{ 1099 return &mDigest; 1100} 1101 1102 1103 1104void ManifestSymLinkItem::SetDigest (const SHA1Digest* digest) 1105{ 1106 memcpy (mDigest, digest, sizeof (SHA1Digest)); 1107} 1108 1109 1110 1111ManifestItemType ManifestSymLinkItem::GetItemType () 1112{ 1113 return kManifestSymLinkItemType; 1114} 1115 1116 1117 1118void ManifestSymLinkItem::Compare (ManifestItem *a, bool compareOwnerAndGroup) 1119{ 1120 FileSystemEntryItem::Compare (a, compareOwnerAndGroup); 1121 ManifestSymLinkItem* aa = static_cast<ManifestSymLinkItem*>(a); 1122 secdebug ("manifest", "Comparing symlink item %s against %s", GetName (), aa->GetName ()); 1123 1124 // now compare the data 1125 if (memcmp (&mDigest, &aa->mDigest, kSHA1DigestSize) != 0) 1126 { 1127 MacOSError::throwMe (errSecManifestNotEqual); 1128 } 1129} 1130 1131 1132 1133//========================== MANIFEST OTHER ITEM ========================== 1134 1135 1136 1137ManifestOtherItem::ManifestOtherItem () 1138{ 1139} 1140 1141 1142 1143ManifestOtherItem::~ManifestOtherItem () 1144{ 1145 secdebug ("manifest", "Destroyed other item for path %s", mPath.c_str ()); 1146} 1147 1148 1149 1150ManifestItemType ManifestOtherItem::GetItemType () 1151{ 1152 return kManifestOtherType; 1153} 1154 1155 1156 1157void ManifestOtherItem::Compare (ManifestItem *a, bool compareOwnerAndGroup) 1158{ 1159 FileSystemEntryItem::Compare (a, compareOwnerAndGroup); 1160 secdebug ("manifest", "Comparing other item %s against %s", GetName (), static_cast<FileSystemEntryItem*>(a)->GetName ()); 1161} 1162