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