]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_codesigning/lib/CodeSigner.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_codesigning / lib / CodeSigner.cpp
diff --git a/libsecurity_codesigning/lib/CodeSigner.cpp b/libsecurity_codesigning/lib/CodeSigner.cpp
new file mode 100644 (file)
index 0000000..e40620b
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2006-2010 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@
+ */
+
+//
+// CodeSigner - SecCodeSigner API objects
+//
+#include "CodeSigner.h"
+#include "signer.h"
+#include "reqparser.h"
+#include "renum.h"
+#include "csdatabase.h"
+#include "drmaker.h"
+#include "csutilities.h"
+#include <security_utilities/unix++.h>
+#include <security_utilities/unixchild.h>
+#include <Security/SecCertificate.h>
+#include <vector>
+
+namespace Security {
+
+__SEC_CFTYPE(SecIdentity)
+
+namespace CodeSigning {
+
+using namespace UnixPlusPlus;
+
+
+//
+// A helper for parsing out a CFDictionary signing-data specification
+//
+class SecCodeSigner::Parser : CFDictionary {
+public:
+       Parser(SecCodeSigner &signer, CFDictionaryRef parameters);
+       
+       bool getBool(CFStringRef key) const
+       {
+               if (CFBooleanRef flag = get<CFBooleanRef>(key))
+                       return flag == kCFBooleanTrue;
+               else
+                       return false;
+       }
+};
+
+
+//
+// Construct a SecCodeSigner
+//
+SecCodeSigner::SecCodeSigner(SecCSFlags flags)
+       : mOpFlags(flags), mRequirements(NULL), mDigestAlgorithm(kSecCodeSignatureDefaultDigestAlgorithm)
+{
+}
+
+
+//
+// Clean up a SecCodeSigner
+//
+SecCodeSigner::~SecCodeSigner() throw()
+try {
+       ::free((Requirements *)mRequirements);
+} catch (...) {
+       return;
+}
+
+
+//
+// Parse an input parameter dictionary and set ready-to-use parameters
+//
+void SecCodeSigner::parameters(CFDictionaryRef paramDict)
+{
+       Parser(*this, paramDict);
+       if (!valid())
+               MacOSError::throwMe(errSecCSInvalidObjectRef);
+}
+
+
+//
+// Roughly check for validity.
+// This isn't thorough; it just sees if if looks like we've set up the object appropriately.
+//
+bool SecCodeSigner::valid() const
+{
+       if (mOpFlags & kSecCSRemoveSignature)
+               return true;
+       return mSigner;
+}
+
+
+//
+// Sign code
+//
+void SecCodeSigner::sign(SecStaticCode *code, SecCSFlags flags)
+{
+       Signer operation(*this, code);
+       if ((flags | mOpFlags) & kSecCSRemoveSignature) {
+               secdebug("signer", "%p will remove signature from %p", this, code);
+               operation.remove(flags);
+       } else {
+               if (!valid())
+                       MacOSError::throwMe(errSecCSInvalidObjectRef);
+               secdebug("signer", "%p will sign %p (flags 0x%x)", this, code, flags);
+               operation.sign(flags);
+       }
+       code->resetValidity();
+}
+
+
+//
+// ReturnDetachedSignature is called by writers or editors that try to return
+// detached signature data (rather than annotate the target).
+//
+void SecCodeSigner::returnDetachedSignature(BlobCore *blob, Signer &signer)
+{
+       assert(mDetached);
+       if (CFGetTypeID(mDetached) == CFURLGetTypeID()) {
+               // URL to destination file
+               AutoFileDesc fd(cfString(CFURLRef(mDetached.get())), O_WRONLY | O_CREAT | O_TRUNC);
+               fd.writeAll(*blob);
+       } else if (CFGetTypeID(mDetached) == CFDataGetTypeID()) {
+               CFDataAppendBytes(CFMutableDataRef(mDetached.get()),
+                       (const UInt8 *)blob, blob->length());
+       } else if (CFGetTypeID(mDetached) == CFNullGetTypeID()) {
+               signatureDatabaseWriter().storeCode(blob, signer.path().c_str());
+       } else
+               assert(false);
+}
+
+
+//
+// Our DiskRep::signingContext methods communicate with the signing subsystem
+// in terms those callers can easily understand.
+//
+string SecCodeSigner::sdkPath(const std::string &path) const
+{
+       assert(path[0] == '/'); // need absolute path here
+       if (mSDKRoot)
+               return cfString(mSDKRoot) + path;
+       else
+               return path;
+}
+
+bool SecCodeSigner::isAdhoc() const
+{
+       return mSigner == SecIdentityRef(kCFNull);
+}
+
+
+//
+// The actual parsing operation is done in the Parser class.
+//
+// Note that we need to copy or retain all incoming data. The caller has no requirement
+// to keep the parameters dictionary around.
+//
+SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters)
+       : CFDictionary(parameters, errSecCSBadDictionaryFormat)
+{
+       // the signer may be an identity or null
+       state.mSigner = SecIdentityRef(get<CFTypeRef>(kSecCodeSignerIdentity));
+       if (state.mSigner)
+               if (CFGetTypeID(state.mSigner) != SecIdentityGetTypeID() && !CFEqual(state.mSigner, kCFNull))
+                       MacOSError::throwMe(errSecCSInvalidObjectRef);
+
+       // the flags need some augmentation
+       if (CFNumberRef flags = get<CFNumberRef>(kSecCodeSignerFlags)) {
+               state.mCdFlagsGiven = true;
+               state.mCdFlags = cfNumber<uint32_t>(flags);
+       } else
+               state.mCdFlagsGiven = false;
+       
+       // digest algorithms are specified as a numeric code
+       if (CFNumberRef digestAlgorithm = get<CFNumberRef>(kSecCodeSignerDigestAlgorithm))
+               state.mDigestAlgorithm = cfNumber<unsigned int>(digestAlgorithm);
+
+       if (CFNumberRef cmsSize = get<CFNumberRef>(CFSTR("cmssize")))
+               state.mCMSSize = cfNumber<size_t>(cmsSize);
+       else
+               state.mCMSSize = 9000;  // likely big enough
+
+       // signing time can be a CFDateRef or null
+       if (CFTypeRef time = get<CFTypeRef>(kSecCodeSignerSigningTime)) {
+               if (CFGetTypeID(time) == CFDateGetTypeID() || time == kCFNull)
+                       state.mSigningTime = CFDateRef(time);
+               else
+                       MacOSError::throwMe(errSecCSInvalidObjectRef);
+       }
+       
+       if (CFStringRef ident = get<CFStringRef>(kSecCodeSignerIdentifier))
+               state.mIdentifier = cfString(ident);
+       
+       if (CFStringRef prefix = get<CFStringRef>(kSecCodeSignerIdentifierPrefix))
+               state.mIdentifierPrefix = cfString(prefix);
+       
+       // requirements can be binary or string (to be compiled)
+       if (CFTypeRef reqs = get<CFTypeRef>(kSecCodeSignerRequirements)) {
+               if (CFGetTypeID(reqs) == CFDataGetTypeID()) {           // binary form
+                       const Requirements *rp = (const Requirements *)CFDataGetBytePtr(CFDataRef(reqs));
+                       state.mRequirements = rp->clone();
+               } else if (CFGetTypeID(reqs) == CFStringGetTypeID()) { // text form
+                       state.mRequirements = parseRequirements(cfString(CFStringRef(reqs)));
+               } else
+                       MacOSError::throwMe(errSecCSInvalidObjectRef);
+       } else
+               state.mRequirements = NULL;
+       
+       state.mNoMachO = getBool(CFSTR("no-macho"));
+       
+       state.mPageSize = get<CFNumberRef>(kSecCodeSignerPageSize);
+       
+       // detached can be (destination) file URL or (mutable) Data to be appended-to
+       if ((state.mDetached = get<CFTypeRef>(kSecCodeSignerDetached))) {
+               CFTypeID type = CFGetTypeID(state.mDetached);
+               if (type != CFURLGetTypeID() && type != CFDataGetTypeID() && type != CFNullGetTypeID())
+                       MacOSError::throwMe(errSecCSInvalidObjectRef);
+       }
+       
+       state.mDryRun = getBool(kSecCodeSignerDryRun);
+
+       state.mResourceRules = get<CFDictionaryRef>(kSecCodeSignerResourceRules);
+       
+       state.mApplicationData = get<CFDataRef>(kSecCodeSignerApplicationData);
+       state.mEntitlementData = get<CFDataRef>(kSecCodeSignerEntitlements);
+       
+       state.mSDKRoot = get<CFURLRef>(kSecCodeSignerSDKRoot);
+    
+       if (CFBooleanRef timestampRequest = get<CFBooleanRef>(kSecCodeSignerRequireTimestamp)) {
+               state.mWantTimeStamp = timestampRequest == kCFBooleanTrue;
+       } else {        // pick default
+               state.mWantTimeStamp = false;
+               if (state.mSigner && state.mSigner != SecIdentityRef(kCFNull)) {
+                       CFRef<SecCertificateRef> signerCert;
+                       MacOSError::check(SecIdentityCopyCertificate(state.mSigner, &signerCert.aref()));
+                       if (certificateHasField(signerCert, devIdLeafMarkerOID))
+                               state.mWantTimeStamp = true;
+               }
+       }
+       state.mTimestampAuthentication = get<SecIdentityRef>(kSecCodeSignerTimestampAuthentication);
+       state.mTimestampService = get<CFURLRef>(kSecCodeSignerTimestampServer);
+       state.mNoTimeStampCerts = getBool(kSecCodeSignerTimestampOmitCertificates);
+}
+
+
+} // end namespace CodeSigning
+} // end namespace Security