]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_manifest/lib/ManifestInternal.cpp
Security-57740.51.3.tar.gz
[apple/security.git] / OSX / libsecurity_manifest / lib / 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 char buffer[kReadChunkSize];
828
829 ssize_t bytesRead;
830 while ((bytesRead = read (fileNo, buffer, kReadChunkSize)) != 0)
831 {
832 // digest the read data
833 CC_SHA1_Update (&digestContext, buffer, (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 }