StaticCode.h   [plain text]


/*
 * Copyright (c) 2006-2012 Apple Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

//
// StaticCode - SecStaticCode API objects
//
#ifndef _H_STATICCODE
#define _H_STATICCODE

#include "cs.h"
#include "Requirements.h"
#include "requirement.h"
#include "diskrep.h"
#include "codedirectory.h"
#include <Security/SecTrust.h>
#include <CoreFoundation/CFData.h>

namespace Security {
namespace CodeSigning {


class SecCode;


//
// A SecStaticCode object represents the file system version of some code.
// There's a lot of pieces to this, and we'll bring them all into
// memory here (lazily) and let you fondle them with ease.
//
// Note that concrete knowledge of where stuff is stored resides in the DiskRep
// object we hold. DiskReps allocate, retrieve, and return data to us. We are
// responsible for interpreting, caching, and validating them. (In other words,
// DiskReps know where stuff is and how it is stored, but we know what it means.)
//
// Data accessors (returning CFDataRef, CFDictionaryRef, various pointers, etc.)
// cache those values internally and return unretained(!) references ("Get" style)
// that are valid as long as the SecStaticCode object's lifetime, or until
// resetValidity() is called, whichever is sooner. If you need to keep them,
// retain or copy them as needed.
//
class SecStaticCode : public SecCFObject {
	NOCOPY(SecStaticCode)
	
protected:
	//
	// A context for resource validation operations, to tailor error response.
	// The base class throws an exception immediately and ignores detail data.
	// 
	class ValidationContext {
	public:
		virtual ~ValidationContext();
		virtual void reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value);
	};
	
	//
	// A CollectingContext collects all error details and throws an annotated final error.
	//
	class CollectingContext : public ValidationContext {
	public:
		CollectingContext(SecStaticCode &c) : code(c), mStatus(noErr) { }
		void reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value);
		
		OSStatus osStatus()		{ return mStatus; }
		operator OSStatus () const		{ return mStatus; }
		void throwMe() __attribute__((noreturn));
		
		SecStaticCode &code;

	private:
		CFRef<CFMutableDictionaryRef> mCollection;
		OSStatus mStatus;
	};
	
public:
	SECCFFUNCTIONS(SecStaticCode, SecStaticCodeRef,
		errSecCSInvalidObjectRef, gCFObjects().StaticCode)
	
	// implicitly convert SecCodeRefs to their SecStaticCodeRefs
	static SecStaticCode *requiredStatic(SecStaticCodeRef ref);	// convert SecCodeRef
	static SecCode *optionalDynamic(SecStaticCodeRef ref); // extract SecCodeRef or NULL

	SecStaticCode(DiskRep *rep);
    virtual ~SecStaticCode() throw();
	
    bool equal(SecCFObject &other);
    CFHashCode hash();
	
	void detachedSignature(CFDataRef sig);		// attach an explicitly given detached signature
	void checkForSystemSignature();				// check for and attach system-supplied detached signature

	const CodeDirectory *codeDirectory(bool check = true);
	CFDataRef cdHash();
	CFDataRef signature();
	CFAbsoluteTime signingTime();
	CFAbsoluteTime signingTimestamp();
	bool isSigned() { return codeDirectory(false) != NULL; }
	DiskRep *diskRep() { return mRep; }
	std::string mainExecutablePath() { return mRep->mainExecutablePath(); }
	CFURLRef canonicalPath() const { return mRep->canonicalPath(); }
	std::string identifier() { return codeDirectory()->identifier(); }
	std::string format() const { return mRep->format(); }
	std::string signatureSource();
	CFDataRef component(CodeDirectory::SpecialSlot slot, OSStatus fail = errSecCSSignatureFailed);
	CFDictionaryRef infoDictionary();
	CFDictionaryRef entitlements();

	CFDictionaryRef resourceDictionary();
	CFURLRef resourceBase();
	CFDataRef resource(std::string path);
	CFDataRef resource(std::string path, ValidationContext &ctx);
	void validateResource(std::string path, ValidationContext &ctx);
	
	bool flag(uint32_t tested);
	
	void resetValidity();						// clear validation caches (if something may have changed)
	
	bool validated() const	{ return mValidated; }
	bool valid() const
		{ assert(validated()); return mValidated && (mValidationResult == noErr); }
	bool validatedExecutable() const	{ return mExecutableValidated; }
	bool validatedResources() const	{ return mResourcesValidated; }

	void validateDirectory();
	void validateComponent(CodeDirectory::SpecialSlot slot, OSStatus fail = errSecCSSignatureFailed);
	void validateNonResourceComponents();
	void validateResources();
	void validateExecutable();
	
	const Requirements *internalRequirements();
	const Requirement *internalRequirement(SecRequirementType type);
	const Requirement *designatedRequirement();
	const Requirement *defaultDesignatedRequirement();		// newly allocated (caller owns)
	
	void validateRequirements(SecRequirementType type, SecStaticCode *target,
		OSStatus nullError = noErr);										// target against my [type], throws
	void validateRequirement(const Requirement *req, OSStatus failure);		// me against [req], throws
	bool satisfiesRequirement(const Requirement *req, OSStatus failure);	// me against [req], returns on clean miss
	
	// certificates are available after signature validation (they are stored in the CMS signature)
	SecCertificateRef cert(int ix);		// get a cert from the cert chain
	CFArrayRef certificates();			// get the entire certificate chain
	
	CFDictionaryRef signingInformation(SecCSFlags flags); // omnibus information-gathering API (creates new dictionary)
	
public:
	class AllArchitectures;
	
protected:
	CFDictionaryRef getDictionary(CodeDirectory::SpecialSlot slot, OSStatus fail); // component value as a dictionary
	bool verifySignature();
	CFTypeRef verificationPolicy(SecCSFlags flags);

	static void checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context);

private:
	RefPointer<DiskRep> mRep;			// on-disk representation
	
	// master validation state
	bool mValidated;					// core validation was attempted
	OSStatus mValidationResult;			// outcome of core validation
	bool mValidationExpired;			// outcome had expired certificates
	
	// static executable validation state (nested within mValidated/mValid)
	bool mExecutableValidated;			// tried to validate executable file
	OSStatus mExecutableValidResult;		// outcome if mExecutableValidated

	// static resource validation state (nested within mValidated/mValid)
	bool mResourcesValidated;			// tried to validate resources
	OSStatus mResourcesValidResult;			// outcome if mResourceValidated or..
	CollectingContext *mResourcesValidContext;	// other outcome

	// cached contents
	CFRef<CFDataRef> mDir;				// code directory data
	CFRef<CFDataRef> mSignature;		// CMS signature data
	CFAbsoluteTime mSigningTime;		// (signed) signing time
	CFAbsoluteTime mSigningTimestamp;		// Timestamp time (from timestamping authority)
	CFRef<CFDataRef> mCache[cdSlotCount]; // NULL => not tried, kCFNull => absent, other => present
	
	// alternative cache forms (storage may depend on cached contents above)
	CFRef<CFDictionaryRef> mInfoDict;	// derived from mCache slot
	CFRef<CFDictionaryRef> mEntitlements; // derived from mCache slot
	CFRef<CFDictionaryRef> mResourceDict; // derived from mCache slot
	const Requirement *mDesignatedReq;	// cached designated req if we made one up
	CFRef<CFDataRef> mCDHash;			// hash of CodeDirectory
	
	bool mGotResourceBase;				// asked mRep for resourceBasePath
	CFRef<CFURLRef> mResourceBase;		// URL form of resource base directory
	
	// signature verification outcome (mTrust == NULL => not done yet)
	CFRef<SecTrustRef> mTrust;			// outcome of crypto validation (valid or not)
	CFRef<CFArrayRef> mCertChain;
	CSSM_TP_APPLE_EVIDENCE_INFO *mEvalDetails;
};


//
// Given a SecStaticCode, create an iterator that produces SecStaticCodes
// for all architectures encompassed by this static code reference.
//
class SecStaticCode::AllArchitectures : public SecPointer<SecStaticCode> {
public:
	AllArchitectures(SecStaticCode *code);
	
	SecStaticCode *operator () ();
	
private:
	SecPointer<SecStaticCode> mBase;
	enum { fatBinary, firstNonFat, atEnd } mState;
	Universal::Architectures mArchitectures;
	Universal::Architectures::const_iterator mCurrent;
};


} // end namespace CodeSigning
} // end namespace Security

#endif // !_H_STATICCODE