]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_manifest/lib/ManifestInternal.cpp
Security-57336.1.9.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 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
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 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
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 secdebug ("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 secdebug ("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 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
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 return filePath;
605 }
606
607
608
609 bool 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
627 ManifestFileItem::ManifestFileItem () : mNumForks (1)
628 {
629 }
630
631
632
633 ManifestFileItem::~ManifestFileItem ()
634 {
635 secdebug ("manifest", "Destroyed manifest item %p for path %s", this, mPath.c_str ());
636 }
637
638
639
640 ManifestItemType ManifestFileItem::GetItemType ()
641 {
642 return kManifestFileItemType;
643 }
644
645
646
647 u_int32_t ManifestFileItem::GetNumberOfForks ()
648 {
649 return mNumForks;
650 }
651
652
653
654 void ManifestFileItem::SetNumberOfForks (u_int32_t numForks)
655 {
656 mNumForks = numForks;
657 }
658
659
660
661 bool ManifestFileItem::FileIsMachOBinary (char* path)
662 {
663 return false;
664 }
665
666
667
668 void ManifestFileItem::SetForkLength (int which, size_t length)
669 {
670 mFileLengths[which] = length;
671 }
672
673
674
675 size_t ManifestFileItem::GetForkLength (int which)
676 {
677 return mFileLengths[which];
678 }
679
680
681
682 void 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
717 static const int kReadChunkSize = 4096 * 4;
718
719
720
721 static 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
735 void 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
809 void 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
847 void ManifestFileItem::GetItemRepresentation (int whichFork, void* &itemRep, size_t &size)
848 {
849 itemRep = (void*) &mDigest[whichFork];
850 size = kSHA1DigestSize;
851 }
852
853
854
855 void ManifestFileItem::SetItemRepresentation (int whichFork, const void* itemRep, size_t size)
856 {
857 memcpy ((void*) &mDigest[whichFork], itemRep, size);
858 }
859
860
861
862 void 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
898 ManifestDirectoryItem::ManifestDirectoryItem ()
899 {
900 }
901
902
903
904 ManifestDirectoryItem::~ManifestDirectoryItem ()
905 {
906 secdebug ("manifest", "Destroyed directory item %p for path %s", this, mPath.c_str ());
907 }
908
909
910 const char* kAppleDoublePrefix = "._";
911 const int kAppleDoublePrefixLength = 2;
912
913 static 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
944 const u_int8_t kAppleDoubleMagicNumber[] = {0x00, 0x05, 0x16, 0x07};
945
946
947
948 static 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
986 void 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
1042 ManifestItemType ManifestDirectoryItem::GetItemType ()
1043 {
1044 return kManifestDirectoryItemType;
1045 }
1046
1047
1048
1049 void 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
1063 ManifestSymLinkItem::ManifestSymLinkItem ()
1064 {
1065 }
1066
1067
1068
1069 ManifestSymLinkItem::~ManifestSymLinkItem ()
1070 {
1071 secdebug ("manifest", "Destroyed symlink item for %s", mPath.c_str ());
1072 }
1073
1074
1075
1076 void 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
1097 const SHA1Digest* ManifestSymLinkItem::GetDigest ()
1098 {
1099 return &mDigest;
1100 }
1101
1102
1103
1104 void ManifestSymLinkItem::SetDigest (const SHA1Digest* digest)
1105 {
1106 memcpy (mDigest, digest, sizeof (SHA1Digest));
1107 }
1108
1109
1110
1111 ManifestItemType ManifestSymLinkItem::GetItemType ()
1112 {
1113 return kManifestSymLinkItemType;
1114 }
1115
1116
1117
1118 void 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
1137 ManifestOtherItem::ManifestOtherItem ()
1138 {
1139 }
1140
1141
1142
1143 ManifestOtherItem::~ManifestOtherItem ()
1144 {
1145 secdebug ("manifest", "Destroyed other item for path %s", mPath.c_str ());
1146 }
1147
1148
1149
1150 ManifestItemType ManifestOtherItem::GetItemType ()
1151 {
1152 return kManifestOtherType;
1153 }
1154
1155
1156
1157 void 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 }