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