//
// 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
//
// 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);
}
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)
{ }
// "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)
{
delete mNewCode;
if (mTempMayExist)
::remove(tempPath.c_str()); // ignore error (can't do anything about it)
+
this->kill();
}
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());
+ }
+ });
+ }
}
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);
}
}
// 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);
// 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();
}
// 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