]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/signerutils.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / signerutils.cpp
index eb19d2d3e2ea037017433b6a2ed4755c412fba2b..f6da5e936cd7142612990322bafe4659a46568b7 100644 (file)
 //
 // signerutils - utilities for signature generation
 //
+#include "csutilities.h"
+#include "drmaker.h"
+#include "resources.h"
 #include "signerutils.h"
 #include "signer.h"
-#include "SecCodeSigner.h"
+
+#include <Security/SecCmsBase.h>
 #include <Security/SecIdentity.h>
 #include <Security/CMSEncoder.h>
-#include "resources.h"
-#include "csutilities.h"
-#include "drmaker.h"
+
+#include "SecCodeSigner.h"
+
 #include <security_utilities/unix++.h>
+#include <security_utilities/logging.h>
 #include <security_utilities/unixchild.h>
+
 #include <vector>
 
 // for helper validation
@@ -76,14 +82,14 @@ void DetachedBlobWriter::flush()
 //
 // ArchEditor
 //
-ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithm hashType, uint32_t attrs)
+ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithms hashTypes, 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, hashType);
+               architecture[*it] = new Arch(*it, hashTypes);
 }
 
 
@@ -92,13 +98,22 @@ ArchEditor::~ArchEditor()
        for (ArchMap::iterator it = begin(); it != end(); ++it)
                delete it->second;
 }
+       
+       
+ArchEditor::Arch::Arch(const Architecture &arch, CodeDirectory::HashAlgorithms hashTypes)
+       : architecture(arch)
+{
+       blobSize = 0;
+       for (auto type = hashTypes.begin(); type != hashTypes.end(); ++type)
+               cdBuilders.insert(make_pair(*type, new CodeDirectory::Builder(*type)));
+}
 
 
 //
 // BlobEditor
 //
 BlobEditor::BlobEditor(Universal &fat, SecCodeSigner::Signer &s)
-       : ArchEditor(fat, s.digestAlgorithm(), 0), signer(s)
+       : ArchEditor(fat, s.digestAlgorithms(), 0), signer(s)
 { }
 
 
@@ -130,11 +145,12 @@ 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, CodeDirectory::HashAlgorithm hashType, std::string srcPath)
-       : ArchEditor(code, hashType, w->attributes()),
+MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, CodeDirectory::HashAlgorithms hashTypes, std::string srcPath)
+       : ArchEditor(code, hashTypes, w->attributes()),
          writer(w),
          sourcePath(srcPath),
          tempPath(srcPath + ".cstemp"),
+         mHashTypes(hashTypes),
          mNewCode(NULL),
          mTempMayExist(false)
 {
@@ -152,6 +168,7 @@ MachOEditor::~MachOEditor()
        delete mNewCode;
        if (mTempMayExist)
                ::remove(tempPath.c_str());             // ignore error (can't do anything about it)
+
        this->kill();
 }
 
@@ -235,15 +252,23 @@ void MachOEditor::childAction()
        
        if (mHelperOverridden)
                ::csops(0, CS_OPS_MARKKILL, NULL, 0);           // force code integrity
-       ::seteuid(0);   // activate privilege if caller has it; ignore error if not
+       (void)::seteuid(0);     // activate privilege if caller has it; ignore error if not
        execv(mHelperPath, (char * const *)&arguments[0]);
 }
 
 void MachOEditor::reset(Arch &arch)
 {
        arch.source.reset(mNewCode->architecture(arch.architecture));
-       arch.cdbuilder.reopen(tempPath,
-               arch.source->offset(), arch.source->signingOffset());
+
+       for (auto type = mHashTypes.begin(); type != mHashTypes.end(); ++type) {
+               arch.eachDigest(^(CodeDirectory::Builder& builder) {
+                       /* Signature editing may have no need for cd builders, and not
+                        * have opened them, so only reopen them conditionally. */
+                       if (builder.opened()) {
+                               builder.reopen(tempPath, arch.source->offset(), arch.source->signingOffset());
+                       }
+               });
+       }
 }
 
 
@@ -262,7 +287,7 @@ void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
                arch.source->writeAll(*blob);
                ::free(blob);           // done with it
        } else {
-               secdebug("signer", "%p cannot find CODESIGNING section", this);
+               secinfo("signer", "%p cannot find CODESIGNING data in Mach-O", this);
                MacOSError::throwMe(errSecCSInternalError);
        }
 }
@@ -288,11 +313,23 @@ void MachOEditor::commit()
                // perform copy under root or file-owner privileges if available
                UidGuard guard;
                if (!guard.seteuid(0))
-                       guard.seteuid(st.st_uid);
+                       (void)guard.seteuid(st.st_uid);
                
                // copy metadata from original file...
                copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
-               
+
+#if TARGET_OS_OSX
+               // determine AFSC status if we are told to preserve compression
+               bool conductCompression = false;
+               cmpInfo cInfo;
+               if (writer->getPreserveAFSC()) {
+                       if (queryCompressionInfo(sourcePath.c_str(), &cInfo) == 0) {
+                               if (cInfo.compressionType != 0 && cInfo.compressedSize > 0)
+                                       conductCompression = true;
+                       }
+               }
+#endif
+
                // ... but explicitly update the timestamps since we did change the file
                char buf;
                mFd.read(&buf, sizeof(buf), 0);
@@ -301,7 +338,30 @@ void MachOEditor::commit()
                // move the new file into place
                UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
                mTempMayExist = false;          // we renamed it away
+
+#if TARGET_OS_OSX
+               // if the original file was compressed, compress the new file after move
+               if (conductCompression) {
+                       CFMutableDictionaryRef options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+                       CFStringRef val = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), cInfo.compressionType);
+                       CFDictionarySetValue(options, kAFSCCompressionTypes, val);
+                       CFRelease(val);
+
+                       CompressionQueueContext compressionQueue = CreateCompressionQueue(NULL, NULL, NULL, NULL, options);
+
+                       if (!CompressFile(compressionQueue, sourcePath.c_str(), NULL)) {
+                               secinfo("signer", "%p Failed to queue compression of file %s", this, sourcePath.c_str());
+                               MacOSError::throwMe(errSecCSInternalError);
+                       }
+                       FinishCompressionAndCleanUp(compressionQueue);
+
+                       compressionQueue = NULL;
+                       CFRelease(options);
+               }
+#endif
+
        }
+       this->writer->flush();
 }
 
 
@@ -355,6 +415,102 @@ PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer)
        // other stuff
        this->identifier = signer.signingIdentifier();
 }
+       
+       
+//
+// A collector of CodeDirectories for hash-agile construction of signatures.
+//
+CodeDirectorySet::~CodeDirectorySet()
+{
+       for (auto it = begin(); it != end(); ++it)
+               ::free(const_cast<CodeDirectory*>(it->second));
+}
+       
+       
+void CodeDirectorySet::add(const Security::CodeSigning::CodeDirectory *cd)
+{
+       insert(make_pair(cd->hashType, cd));
+       if (cd->hashType == kSecCodeSignatureHashSHA1)
+               mPrimary = cd;
+}
+       
+       
+void CodeDirectorySet::populate(DiskRep::Writer *writer) const
+{
+       assert(!empty());
+       
+       if (mPrimary == NULL)   // didn't add SHA-1; pick another occupant for this slot
+               mPrimary = begin()->second;
+       
+       // reserve slot zero for a SHA-1 digest if present; else pick something else
+       CodeDirectory::SpecialSlot nextAlternate = cdAlternateCodeDirectorySlots;
+       for (auto it = begin(); it != end(); ++it) {
+               if (it->second == mPrimary) {
+                       writer->codeDirectory(it->second, cdCodeDirectorySlot);
+               } else {
+                       writer->codeDirectory(it->second, nextAlternate++);
+               }
+       }
+}
+       
+
+const CodeDirectory* CodeDirectorySet::primary() const
+{
+       if (mPrimary == NULL)
+               mPrimary = begin()->second;
+       return mPrimary;
+}
+
+CFArrayRef CodeDirectorySet::hashList() const
+{
+       CFRef<CFMutableArrayRef> hashList = makeCFMutableArray(0);
+       for (auto it = begin(); it != end(); ++it) {
+               CFRef<CFDataRef> cdhash = it->second->cdhash(true);
+               CFArrayAppendValue(hashList, cdhash);
+       }
+       return hashList.yield();
+}
+
+CFDictionaryRef CodeDirectorySet::hashDict() const
+{
+       CFRef<CFMutableDictionaryRef> hashDict = makeCFMutableDictionary();
+
+       for (auto it = begin(); it != end(); ++it) {
+               SECOidTag tag = CodeDirectorySet::SECOidTagForAlgorithm(it->first);
+
+               if (tag == SEC_OID_UNKNOWN) {
+                       MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm);
+               }
+
+               CFRef<CFNumberRef> hashType = makeCFNumber(int(tag));
+               CFRef<CFDataRef> fullCdhash = it->second->cdhash(false); // Full-length cdhash!
+               CFDictionarySetValue(hashDict, hashType, fullCdhash);
+       }
+
+       return hashDict.yield();
+}
+
+SECOidTag CodeDirectorySet::SECOidTagForAlgorithm(CodeDirectory::HashAlgorithm algorithm) {
+       SECOidTag tag;
+
+       switch (algorithm) {
+               case kSecCodeSignatureHashSHA1:
+                       tag = SEC_OID_SHA1;
+                       break;
+               case kSecCodeSignatureHashSHA256:
+               case kSecCodeSignatureHashSHA256Truncated: // truncated *page* hashes, not cdhash
+                       tag = SEC_OID_SHA256;
+                       break;
+               case kSecCodeSignatureHashSHA384:
+                       tag = SEC_OID_SHA384;
+                       break;
+               default:
+                       tag = SEC_OID_UNKNOWN;
+       }
+
+       return tag;
+}
+
 
 
 } // end namespace CodeSigning