]> git.saurik.com Git - apple/libsecurity_codesigning.git/blobdiff - lib/signerutils.cpp
libsecurity_codesigning-55037.15.tar.gz
[apple/libsecurity_codesigning.git] / lib / signerutils.cpp
index e005e82700b6e4d2f6713cfa2e34eea25ede015a..d03d884e5ede3411717bbbd525c894416da899fe 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved.
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
 #include <Security/SecIdentity.h>
 #include <Security/CMSEncoder.h>
 #include "renum.h"
+#include "csutilities.h"
+#include "drmaker.h"
 #include <security_utilities/unix++.h>
 #include <security_utilities/unixchild.h>
 #include <vector>
 
+// for helper validation
+#include "Code.h"
+#include <security_utilities/cfmunge.h>
+#include <sys/codesign.h>
+
+
 namespace Security {
 namespace CodeSigning {
 
@@ -43,24 +51,10 @@ namespace CodeSigning {
 //
 static const char helperName[] = "codesign_allocate";
 static const char helperPath[] = "/usr/bin/codesign_allocate";
+static const char helperOverride[] = "CODESIGN_ALLOCATE";
 static const size_t csAlign = 16;
 
 
-//
-// InternalRequirements
-//
-void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted)
-{
-       if (defaulted) {
-               this->add(defaulted);
-               ::free((void *)defaulted);              // was malloc(3)ed by DiskRep
-       }
-       if (given)
-               this->add(given);
-       mReqs = make();
-}
-
-
 //
 // BlobWriters
 //
@@ -73,8 +67,8 @@ void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
 void DetachedBlobWriter::flush()
 {
        EmbeddedSignatureBlob *blob = this->make();
-       signer.code->detachedSignature(CFTempData(*blob));
-       signer.state.returnDetachedSignature(blob);
+       signer.code->detachedSignature(makeCFData(*blob));
+       signer.state.returnDetachedSignature(blob, signer);
        ::free(blob);
 }
 
@@ -82,14 +76,14 @@ void DetachedBlobWriter::flush()
 //
 // ArchEditor
 //
-ArchEditor::ArchEditor(Universal &code, uint32_t attrs /* = 0 */)
+ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithm hashType, uint32_t attrs)
        : DiskRep::Writer(attrs)
 {
        Universal::Architectures archList;
        code.architectures(archList);
        for (Universal::Architectures::const_iterator it = archList.begin();
                        it != archList.end(); ++it)
-               architecture[*it] = new Arch(*it);
+               architecture[*it] = new Arch(*it, hashType);
 }
 
 
@@ -103,6 +97,11 @@ ArchEditor::~ArchEditor()
 //
 // BlobEditor
 //
+BlobEditor::BlobEditor(Universal &fat, SecCodeSigner::Signer &s)
+       : ArchEditor(fat, s.digestAlgorithm(), 0), signer(s)
+{ }
+
+
 void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
 {
        mGlobal.component(slot, data);
@@ -121,7 +120,7 @@ void BlobEditor::commit()
 
        // finish up the superblob and deliver it
        DetachedSignatureBlob *blob = mMaker.make();
-       signer.state.returnDetachedSignature(blob);
+       signer.state.returnDetachedSignature(blob, signer);
        ::free(blob);
 }
 
@@ -131,10 +130,21 @@ void BlobEditor::commit()
 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
 // After the tool succeeds, we open the new file and are ready to write it.
 //
-MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, std::string srcPath)
-       : ArchEditor(code, w->attributes()), writer(w), sourcePath(srcPath), tempPath(srcPath + ".cstemp"),
-         mNewCode(NULL), mTempMayExist(false)
+MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, CodeDirectory::HashAlgorithm hashType, std::string srcPath)
+       : ArchEditor(code, hashType, w->attributes()),
+         writer(w),
+         sourcePath(srcPath),
+         tempPath(srcPath + ".cstemp"),
+         mNewCode(NULL),
+         mTempMayExist(false)
 {
+       if (const char *path = getenv(helperOverride)) {
+               mHelperPath = path;
+               mHelperOverridden = true;
+       } else {
+               mHelperPath = helperPath;
+               mHelperOverridden = false;
+       }
 }
 
 MachOEditor::~MachOEditor()
@@ -142,6 +152,7 @@ MachOEditor::~MachOEditor()
        delete mNewCode;
        if (mTempMayExist)
                ::remove(tempPath.c_str());             // ignore error (can't do anything about it)
+       this->kill();
 }
 
 
@@ -170,6 +181,29 @@ void MachOEditor::allocate()
        mNewCode = new Universal(mFd);
 }
 
+static const unsigned char appleReq[] = {
+       // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate"
+       0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
+       0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c,
+       0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
+       0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f,
+       0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65,
+};
+
+void MachOEditor::parentAction()
+{
+       if (mHelperOverridden) {
+               CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath, this->pid());
+               // check code identity of an overridden allocation helper
+               SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(mHelperPath));
+               code->validateDirectory();
+               code->validateExecutable();
+               code->validateResources();
+               code->validateRequirement((const Requirement *)appleReq, errSecCSReqFailed);
+       }
+}
+
 void MachOEditor::childAction()
 {
        vector<const char *> arguments;
@@ -180,16 +214,31 @@ void MachOEditor::childAction()
        arguments.push_back(tempPath.c_str());
        
        for (Iterator it = architecture.begin(); it != architecture.end(); ++it) {
-               char *size;                             // we'll leak this (execv is coming soon)
-               asprintf(&size, "%d", LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign));
-               secdebug("machoedit", "preparing %s size=%s", it->first.name(), size);
-               arguments.push_back("-a");
-               arguments.push_back(it->first.name());
-               arguments.push_back(size);
+               size_t size = LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign);
+               char *ssize;                    // we'll leak this (execv is coming soon)
+               asprintf(&ssize, "%zd", size);
+
+               if (const char *arch = it->first.name()) {
+                       CODESIGN_ALLOCATE_ARCH((char*)arch, size);
+                       arguments.push_back("-a");
+                       arguments.push_back(arch);
+               } else {
+                       CODESIGN_ALLOCATE_ARCHN(it->first.cpuType(), it->first.cpuSubtype(), size);
+                       arguments.push_back("-A");
+                       char *anum;
+                       asprintf(&anum, "%d", it->first.cpuType());
+                       arguments.push_back(anum);
+                       asprintf(&anum, "%d", it->first.cpuSubtype());
+                       arguments.push_back(anum);
+               }
+               arguments.push_back(ssize);
        }
        arguments.push_back(NULL);
+       
+       if (mHelperOverridden)
+               ::csops(0, CS_EXEC_SET_KILL, NULL, 0);          // force code integrity
        ::seteuid(0);   // activate privilege if caller has it; ignore error if not
-       execv(helperPath, (char * const *)&arguments[0]);
+       execv(mHelperPath, (char * const *)&arguments[0]);
 }
 
 void MachOEditor::reset(Arch &arch)
@@ -208,13 +257,9 @@ void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
 {
        if (size_t offset = arch.source->signingOffset()) {
                size_t signingLength = arch.source->signingLength();
-               secdebug("codesign", "writing architecture %s at 0x%zx (%zd of %zd)",
-                       arch.architecture.name(), offset, blob->length(), signingLength);
-               if (signingLength < blob->length()) {
-                       secdebug("codesign", "trying to write %zd bytes into %zd area",
-                               blob->length(), signingLength);
-                       MacOSError::throwMe(errSecCSInternalError);
-               }
+               CODESIGN_ALLOCATE_WRITE((char*)arch.architecture.name(), offset, blob->length(), signingLength);
+               if (signingLength < blob->length())
+                       MacOSError::throwMe(errSecCSCMSTooLarge);
                arch.source->seek(offset);
                arch.source->writeAll(*blob);
                ::free(blob);           // done with it
@@ -246,7 +291,14 @@ void MachOEditor::commit()
                UidGuard guard;
                if (!guard.seteuid(0))
                        guard.seteuid(st.st_uid);
+               
+               // copy metadata from original file...
                copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
+               
+               // ... but explicitly update the timestamps since we did change the file
+               char buf;
+               mFd.read(&buf, sizeof(buf), 0);
+               mFd.write(&buf, sizeof(buf), 0);
 
                // move the new file into place
                UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
@@ -256,33 +308,54 @@ void MachOEditor::commit()
 
 
 //
-// Copyfile
+// InternalRequirements
 //
-Copyfile::Copyfile()
+void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context)
 {
-       if (!(mState = copyfile_state_alloc()))
-               UnixError::throwMe();
-}
+       // first add the default internal requirements
+       if (defaulted) {
+               this->add(defaulted);
+               ::free((void *)defaulted);              // was malloc(3)ed by DiskRep
+       }
        
-void Copyfile::set(uint32_t flag, const void *value)
-{
-       check(::copyfile_state_set(mState, flag, value));
-}
+       // now override them with any requirements explicitly given by the signer
+       if (given)
+               this->add(given);
 
-void Copyfile::get(uint32_t flag, void *value)
-{
-       check(::copyfile_state_set(mState, flag, value));
-}
+       // now add the Designated Requirement, if we can make it and it's not been provided
+       if (!this->contains(kSecDesignatedRequirementType)) {
+               DRMaker maker(context);
+               if (Requirement *dr = maker.make()) {
+                       this->add(kSecDesignatedRequirementType, dr);           // takes ownership of dr
+               }
+       }
        
-void Copyfile::operator () (const char *src, const char *dst, copyfile_flags_t flags)
-{
-       check(::copyfile(src, dst, mState, flags));
+       // return the result
+       mReqs = this->make();
 }
 
-void Copyfile::check(int rc)
+
+//
+// Pre-Signing contexts
+//
+PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer)
 {
-       if (rc < 0)
-               UnixError::throwMe();
+       // construct a cert chain
+       if (signer.signingIdentity() != SecIdentityRef(kCFNull)) {
+               CFRef<SecCertificateRef> signingCert;
+               MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref()));
+               CFRef<SecPolicyRef> policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning);
+               CFRef<SecTrustRef> trust;
+               MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert.get()), policy, &trust.aref()));
+               SecTrustResultType result;
+               MacOSError::check(SecTrustEvaluate(trust, &result));
+               CSSM_TP_APPLE_EVIDENCE_INFO *info;
+               MacOSError::check(SecTrustGetResult(trust, &result, &mCerts.aref(), &info));
+               this->certs = mCerts;
+       }
+       
+       // other stuff
+       this->identifier = signer.signingIdentifier();
 }