1/* 2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24// 25// signer - Signing operation supervisor and controller 26// 27#include "signer.h" 28#include "resources.h" 29#include "signerutils.h" 30#include "SecCodeSigner.h" 31#include <Security/SecIdentity.h> 32#include <Security/CMSEncoder.h> 33#include <Security/CMSPrivate.h> 34#include <Security/CSCommonPriv.h> 35#include <CoreFoundation/CFBundlePriv.h> 36#include "resources.h" 37#include "machorep.h" 38#include "reqparser.h" 39#include "reqdumper.h" 40#include "csutilities.h" 41#include <security_utilities/unix++.h> 42#include <security_utilities/unixchild.h> 43#include <security_utilities/cfmunge.h> 44 45namespace Security { 46namespace CodeSigning { 47 48 49// 50// Sign some code. 51// 52void SecCodeSigner::Signer::sign(SecCSFlags flags) 53{ 54 rep = code->diskRep()->base(); 55 this->prepare(flags); 56 57 PreSigningContext context(*this); 58 59 /* If an explicit teamID was passed in it must be 60 the same as what came from the cert */ 61 std::string teamIDFromCert = state.getTeamIDFromSigner(context.certs); 62 63 if (state.mPreserveMetadata & kSecCodeSignerPreserveTeamIdentifier) { 64 /* If preserving the team identifier, teamID is set previously when the 65 code object is still available */ 66 if (!teamIDFromCert.empty() && teamID != teamIDFromCert) 67 MacOSError::throwMe(errSecCSInvalidFlags); 68 } else { 69 if (teamIDFromCert.empty()) { 70 /* state.mTeamID is an explicitly passed teamID */ 71 teamID = state.mTeamID; 72 } else if (state.mTeamID.empty() || (state.mTeamID == teamIDFromCert)) { 73 /* If there was no explicit team ID set, or the explicit team ID matches 74 what is in the cert, use the team ID from the certificate */ 75 teamID = teamIDFromCert; 76 } else { 77 /* The caller passed in an explicit team ID that does not match what is 78 in the signing cert, which is an invalid usage */ 79 MacOSError::throwMe(errSecCSInvalidFlags); 80 } 81 } 82 83 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) { 84 signMachO(fat, context); 85 } else { 86 signArchitectureAgnostic(context); 87 } 88} 89 90 91// 92// Remove any existing code signature from code 93// 94void SecCodeSigner::Signer::remove(SecCSFlags flags) 95{ 96 // can't remove a detached signature 97 if (state.mDetached) 98 MacOSError::throwMe(errSecCSNotSupported); 99 100 rep = code->diskRep(); 101 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) { 102 // architecture-sensitive removal 103 MachOEditor editor(rep->writer(), *fat, kSecCodeSignatureNoHash, rep->mainExecutablePath()); 104 editor.allocate(); // create copy 105 editor.commit(); // commit change 106 } else { 107 // architecture-agnostic removal 108 RefPointer<DiskRep::Writer> writer = rep->writer(); 109 writer->remove(); 110 writer->flush(); 111 } 112} 113 114 115// 116// Contemplate the object-to-be-signed and set up the Signer state accordingly. 117// 118void SecCodeSigner::Signer::prepare(SecCSFlags flags) 119{ 120 // make sure the rep passes strict validation 121 if (strict) 122 rep->strictValidate(MacOSErrorSet()); 123 124 // initialize progress/cancellation state 125 code->prepareProgress(0); // totally fake workload - we don't know how many files we'll encounter 126 127 // get the Info.plist out of the rep for some creative defaulting 128 CFRef<CFDictionaryRef> infoDict; 129 if (CFRef<CFDataRef> infoData = rep->component(cdInfoSlot)) 130 infoDict.take(makeCFDictionaryFrom(infoData)); 131 132 uint32_t inherit = code->isSigned() ? state.mPreserveMetadata : 0; 133 134 // work out the canonical identifier 135 identifier = state.mIdentifier; 136 if (identifier.empty() && (inherit & kSecCodeSignerPreserveIdentifier)) 137 identifier = code->identifier(); 138 if (identifier.empty()) { 139 identifier = rep->recommendedIdentifier(state); 140 if (identifier.find('.') == string::npos) 141 identifier = state.mIdentifierPrefix + identifier; 142 if (identifier.find('.') == string::npos && state.isAdhoc()) 143 identifier = identifier + "-" + uniqueName(); 144 secdebug("signer", "using default identifier=%s", identifier.c_str()); 145 } else 146 secdebug("signer", "using explicit identifier=%s", identifier.c_str()); 147 148 teamID = state.mTeamID; 149 if (teamID.empty() && (inherit & kSecCodeSignerPreserveTeamIdentifier)) { 150 const char *c_id = code->teamID(); 151 if (c_id) 152 teamID = c_id; 153 } 154 155 entitlements = state.mEntitlementData; 156 if (!entitlements && (inherit & kSecCodeSignerPreserveEntitlements)) 157 entitlements = code->component(cdEntitlementSlot); 158 159 // work out the CodeDirectory flags word 160 bool haveCdFlags = false; 161 if (!haveCdFlags && state.mCdFlagsGiven) { 162 cdFlags = state.mCdFlags; 163 secdebug("signer", "using explicit cdFlags=0x%x", cdFlags); 164 haveCdFlags = true; 165 } 166 if (!haveCdFlags) { 167 cdFlags = 0; 168 if (infoDict) 169 if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags"))) { 170 if (CFGetTypeID(csflags) == CFNumberGetTypeID()) { 171 cdFlags = cfNumber<uint32_t>(CFNumberRef(csflags)); 172 secdebug("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags); 173 } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) { 174 cdFlags = cdTextFlags(cfString(CFStringRef(csflags))); 175 secdebug("signer", "using text cdFlags=0x%x from Info.plist", cdFlags); 176 } else 177 MacOSError::throwMe(errSecCSBadDictionaryFormat); 178 haveCdFlags = true; 179 } 180 } 181 if (!haveCdFlags && (inherit & kSecCodeSignerPreserveFlags)) { 182 cdFlags = code->codeDirectory(false)->flags & ~kSecCodeSignatureAdhoc; 183 secdebug("signer", "using inherited cdFlags=0x%x", cdFlags); 184 haveCdFlags = true; 185 } 186 if (!haveCdFlags) 187 cdFlags = 0; 188 if (state.mSigner == SecIdentityRef(kCFNull)) // ad-hoc signing requested... 189 cdFlags |= kSecCodeSignatureAdhoc; // ... so note that 190 191 // prepare the internal requirements input 192 if (state.mRequirements) { 193 if (CFGetTypeID(state.mRequirements) == CFDataGetTypeID()) { // binary form 194 const Requirements *rp = (const Requirements *)CFDataGetBytePtr(state.mRequirements.as<CFDataRef>()); 195 if (!rp->validateBlob()) 196 MacOSError::throwMe(errSecCSReqInvalid); 197 requirements = rp->clone(); 198 } else if (CFGetTypeID(state.mRequirements) == CFStringGetTypeID()) { // text form 199 CFRef<CFMutableStringRef> reqText = CFStringCreateMutableCopy(NULL, 0, state.mRequirements.as<CFStringRef>()); 200 // substitute $ variable tokens 201 CFRange range = { 0, CFStringGetLength(reqText) }; 202 CFStringFindAndReplace(reqText, CFSTR("$self.identifier"), CFTempString(identifier), range, 0); 203 requirements = parseRequirements(cfString(reqText)); 204 } else 205 MacOSError::throwMe(errSecCSInvalidObjectRef); 206 } else if (inherit & kSecCodeSignerPreserveRequirements) 207 if (const Requirements *rp = code->internalRequirements()) 208 requirements = rp->clone(); 209 210 // prepare the resource directory, if any 211 string rpath = rep->resourcesRootPath(); 212 if (!rpath.empty()) { 213 // explicitly given resource rules always win 214 CFCopyRef<CFDictionaryRef> resourceRules = state.mResourceRules; 215 216 // inherited rules come next (overriding embedded ones!) 217 if (!resourceRules && (inherit & kSecCodeSignerPreserveResourceRules)) 218 if (CFDictionaryRef oldRules = code->resourceDictionary(false)) 219 resourceRules = oldRules; 220 221 // embedded resource rules come next 222 if (!resourceRules && infoDict) 223 if (CFTypeRef spec = CFDictionaryGetValue(infoDict, _kCFBundleResourceSpecificationKey)) { 224 if (CFGetTypeID(spec) == CFStringGetTypeID()) 225 if (CFRef<CFDataRef> data = cfLoadFile(rpath + "/" + cfString(CFStringRef(spec)))) 226 if (CFDictionaryRef dict = makeCFDictionaryFrom(data)) 227 resourceRules.take(dict); 228 if (!resourceRules) // embedded rules present but unacceptable 229 MacOSError::throwMe(errSecCSResourceRulesInvalid); 230 } 231 232 // if we got one from anywhere (but the defaults), sanity-check it 233 if (resourceRules) { 234 CFTypeRef rules = CFDictionaryGetValue(resourceRules, CFSTR("rules")); 235 if (!rules || CFGetTypeID(rules) != CFDictionaryGetTypeID()) 236 MacOSError::throwMe(errSecCSResourceRulesInvalid); 237 } 238 239 // finally, ask the DiskRep for its default 240 if (!resourceRules) 241 resourceRules.take(rep->defaultResourceRules(state)); 242 243 // resource root can optionally be the canonical bundle path, 244 // but sealed resource paths are always relative to rpath 245 string root = rpath; 246 if (state.signingFlags() & kSecCSSignBundleRoot) 247 root = cfStringRelease(rep->copyCanonicalPath()); 248 249 // build the resource directory 250 buildResources(root, rpath, resourceRules); 251 } 252 253 // screen and set the signing time 254 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); 255 if (state.mSigningTime == CFDateRef(kCFNull)) { 256 signingTime = 0; // no time at all 257 } else if (!state.mSigningTime) { 258 signingTime = now; // default 259 } else { 260 CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime); 261 if (time > now) // not allowed to post-date a signature 262 MacOSError::throwMe(errSecCSBadDictionaryFormat); 263 signingTime = time; 264 } 265 266 pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize(state); 267 268 // Timestamping setup 269 CFRef<SecIdentityRef> mTSAuth; // identity for client-side authentication to the Timestamp server 270} 271 272 273// 274// Collect the resource seal for a program. 275// This includes both sealed resources and information about nested code. 276// 277void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase, CFDictionaryRef rulesDict) 278{ 279 typedef ResourceBuilder::Rule Rule; 280 281 secdebug("codesign", "start building resource directory"); 282 __block CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary(); 283 284 CFDictionaryRef rules = cfget<CFDictionaryRef>(rulesDict, "rules"); 285 assert(rules); 286 287 CFDictionaryRef files2 = NULL; 288 if (!(state.signingFlags() & kSecCSSignV1)) { 289 CFCopyRef<CFDictionaryRef> rules2 = cfget<CFDictionaryRef>(rulesDict, "rules2"); 290 if (!rules2) { 291 // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules). 292 // V1 rules typically do not cover these places so we'll prevail, but if they do, we defer to them. 293 rules2 = cfmake<CFDictionaryRef>("{+%O" 294 "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories 295 "}", rules); 296 } 297 // build the modern (V2) resource seal 298 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary(); 299 ResourceBuilder resourceBuilder(root, relBase, rules2, digestAlgorithm(), strict, MacOSErrorSet()); 300 ResourceBuilder &resources = resourceBuilder; // (into block) 301 rep->adjustResources(resources); 302 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const char *relpath, Rule *rule) { 303 CFRef<CFMutableDictionaryRef> seal; 304 if (ruleFlags & ResourceBuilder::nested) { 305 seal.take(signNested(ent, relpath)); 306 } else if (ent->fts_info == FTS_SL) { 307 char target[PATH_MAX]; 308 ssize_t len = ::readlink(ent->fts_accpath, target, sizeof(target)-1); 309 if (len < 0) 310 UnixError::check(-1); 311 target[len] = '\0'; 312 seal.take(cfmake<CFMutableDictionaryRef>("{symlink=%s}", target)); 313 } else { 314 seal.take(cfmake<CFMutableDictionaryRef>("{hash=%O}", 315 CFRef<CFDataRef>(resources.hashFile(ent->fts_accpath)).get())); 316 } 317 if (ruleFlags & ResourceBuilder::optional) 318 CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue); 319 CFTypeRef hash; 320 if ((hash = CFDictionaryGetValue(seal, CFSTR("hash"))) && CFDictionaryGetCount(seal) == 1) // simple form 321 CFDictionaryAddValue(files, CFTempString(relpath).get(), hash); 322 else 323 CFDictionaryAddValue(files, CFTempString(relpath).get(), seal.get()); 324 code->reportProgress(); 325 }); 326 CFDictionaryAddValue(result, CFSTR("rules2"), resourceBuilder.rules()); 327 files2 = files; 328 CFDictionaryAddValue(result, CFSTR("files2"), files2); 329 } 330 331 CFDictionaryAddValue(result, CFSTR("rules"), rules); // preserve V1 rules in any case 332 if (!(state.signingFlags() & kSecCSSignNoV1)) { 333 // build the legacy (V1) resource seal 334 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary(); 335 ResourceBuilder resourceBuilder(root, relBase, rules, digestAlgorithm(), strict, MacOSErrorSet()); 336 ResourceBuilder &resources = resourceBuilder; 337 rep->adjustResources(resources); // DiskRep-specific adjustments 338 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const char *relpath, Rule *rule) { 339 if (ent->fts_info == FTS_F) { 340 CFRef<CFDataRef> hash; 341 if (files2) // try to get the hash from a previously-made version 342 if (CFTypeRef seal = CFDictionaryGetValue(files2, CFTempString(relpath))) { 343 if (CFGetTypeID(seal) == CFDataGetTypeID()) 344 hash = CFDataRef(seal); 345 else 346 hash = CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal), CFSTR("hash"))); 347 } 348 if (!hash) 349 hash.take(resources.hashFile(ent->fts_accpath)); 350 if (ruleFlags == 0) { // default case - plain hash 351 cfadd(files, "{%s=%O}", relpath, hash.get()); 352 secdebug("csresource", "%s added simple (rule %p)", relpath, rule); 353 } else { // more complicated - use a sub-dictionary 354 cfadd(files, "{%s={hash=%O,optional=%B}}", 355 relpath, hash.get(), ruleFlags & ResourceBuilder::optional); 356 secdebug("csresource", "%s added complex (rule %p)", relpath, rule); 357 } 358 } 359 }); 360 CFDictionaryAddValue(result, CFSTR("files"), files.get()); 361 } 362 363 resourceDirectory = result.get(); 364 resourceDictData.take(makeCFData(resourceDirectory.get())); 365} 366 367 368// 369// Deal with one piece of nested code 370// 371CFMutableDictionaryRef SecCodeSigner::Signer::signNested(FTSENT *ent, const char *relpath) 372{ 373 // sign nested code and collect nesting information 374 try { 375 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(ent->fts_path)); 376 if (state.signingFlags() & kSecCSSignNestedCode) 377 this->state.sign(code, state.signingFlags()); 378 std::string dr = Dumper::dump(code->designatedRequirement()); 379 return cfmake<CFMutableDictionaryRef>("{requirement=%s,cdhash=%O}", 380 Dumper::dump(code->designatedRequirement()).c_str(), 381 code->cdHash()); 382 } catch (const CommonError &err) { 383 CSError::throwMe(err.osStatus(), kSecCFErrorPath, CFTempURL(relpath, false, this->code->resourceBase())); 384 } 385} 386 387 388// 389// Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce. 390// Note that this will deal just fine with non-fat Mach-O binaries, but it will 391// treat them as architectural binaries containing (only) one architecture - that 392// interpretation is courtesy of the Universal/MachO support classes. 393// 394void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context) 395{ 396 // Mach-O executable at the core - perform multi-architecture signing 397 auto_ptr<ArchEditor> editor(state.mDetached 398 ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this)) 399 : new MachOEditor(rep->writer(), *fat, this->digestAlgorithm(), rep->mainExecutablePath())); 400 assert(editor->count() > 0); 401 if (!editor->attribute(writerNoGlobal)) // can store architecture-common components 402 populate(*editor); 403 404 // pass 1: prepare signature blobs and calculate sizes 405 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) { 406 MachOEditor::Arch &arch = *it->second; 407 arch.source.reset(fat->architecture(it->first)); 408 409 // library validation is not compatible with i386 410 if (arch.architecture.cpuType() == CPU_TYPE_I386) { 411 if (cdFlags & kSecCodeSignatureLibraryValidation) { 412 MacOSError::throwMe(errSecCSBadLVArch); 413 } 414 } 415 416 arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, state), context); 417 if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch 418 populate(arch); 419 populate(arch.cdbuilder, arch, arch.ireqs, 420 arch.source->offset(), arch.source->signingExtent()); 421 422 // add identification blob (made from this architecture) only if we're making a detached signature 423 if (state.mDetached) { 424 CFRef<CFDataRef> identification = MachORep::identificationFor(arch.source.get()); 425 arch.add(cdIdentificationSlot, BlobWrapper::alloc( 426 CFDataGetBytePtr(identification), CFDataGetLength(identification))); 427 } 428 429 // prepare SuperBlob size estimate 430 size_t cdSize = arch.cdbuilder.size(CodeDirectory::currentVersion); 431 arch.blobSize = arch.size(cdSize, state.mCMSSize, 0); 432 } 433 434 editor->allocate(); 435 436 // pass 2: Finish and generate signatures, and write them 437 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) { 438 MachOEditor::Arch &arch = *it->second; 439 editor->reset(arch); 440 441 // finish CodeDirectory (off new binary) and sign it 442 CodeDirectory *cd = arch.cdbuilder.build(); 443 CFRef<CFDataRef> signature = signCodeDirectory(cd); 444 445 // complete the SuperBlob 446 arch.add(cdCodeDirectorySlot, cd); // takes ownership 447 arch.add(cdSignatureSlot, BlobWrapper::alloc( 448 CFDataGetBytePtr(signature), CFDataGetLength(signature))); 449 if (!state.mDryRun) { 450 EmbeddedSignatureBlob *blob = arch.make(); 451 editor->write(arch, blob); // takes ownership of blob 452 } 453 } 454 455 // done: write edit copy back over the original 456 if (!state.mDryRun) 457 editor->commit(); 458} 459 460 461// 462// Sign a binary that has no notion of architecture. 463// That currently means anything that isn't Mach-O format. 464// 465void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context &context) 466{ 467 // non-Mach-O executable - single-instance signing 468 RefPointer<DiskRep::Writer> writer = state.mDetached ? 469 (new DetachedBlobWriter(*this)) : rep->writer(); 470 CodeDirectory::Builder builder(state.mDigestAlgorithm); 471 InternalRequirements ireqs; 472 ireqs(requirements, rep->defaultRequirements(NULL, state), context); 473 populate(*writer); 474 populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit()); 475 476 // add identification blob (made from this architecture) only if we're making a detached signature 477 if (state.mDetached) { 478 CFRef<CFDataRef> identification = rep->identification(); 479 writer->component(cdIdentificationSlot, identification); 480 } 481 482 CodeDirectory *cd = builder.build(); 483 CFRef<CFDataRef> signature = signCodeDirectory(cd); 484 if (!state.mDryRun) { 485 writer->codeDirectory(cd); 486 writer->signature(signature); 487 writer->flush(); 488 } 489 ::free(cd); 490} 491 492 493// 494// Global populate - send components to destination buffers ONCE 495// 496void SecCodeSigner::Signer::populate(DiskRep::Writer &writer) 497{ 498 if (resourceDirectory && !state.mDryRun) 499 writer.component(cdResourceDirSlot, resourceDictData); 500} 501 502 503// 504// Per-architecture populate - send components to per-architecture buffers 505// and populate the CodeDirectory for an architecture. In architecture-agnostic 506// signing operations, the non-architectural binary is considered one (arbitrary) architecture 507// for the purposes of this call. 508// 509void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer, 510 InternalRequirements &ireqs, size_t offset /* = 0 */, size_t length /* = 0 */) 511{ 512 // fill the CodeDirectory 513 builder.executable(rep->mainExecutablePath(), pagesize, offset, length); 514 builder.flags(cdFlags); 515 builder.identifier(identifier); 516 builder.teamID(teamID); 517 518 if (CFRef<CFDataRef> data = rep->component(cdInfoSlot)) 519 builder.specialSlot(cdInfoSlot, data); 520 if (ireqs) { 521 CFRef<CFDataRef> data = makeCFData(*ireqs); 522 writer.component(cdRequirementsSlot, data); 523 builder.specialSlot(cdRequirementsSlot, data); 524 } 525 if (resourceDirectory) 526 builder.specialSlot(cdResourceDirSlot, resourceDictData); 527#if NOT_YET 528 if (state.mApplicationData) 529 builder.specialSlot(cdApplicationSlot, state.mApplicationData); 530#endif 531 if (entitlements) { 532 writer.component(cdEntitlementSlot, entitlements); 533 builder.specialSlot(cdEntitlementSlot, entitlements); 534 } 535 536 writer.addDiscretionary(builder); 537} 538 539#include <security_smime/tsaSupport.h> 540 541// 542// Generate the CMS signature for a (finished) CodeDirectory. 543// 544CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd) 545{ 546 assert(state.mSigner); 547 CFRef<CFMutableDictionaryRef> defaultTSContext = NULL; 548 549 // a null signer generates a null signature blob 550 if (state.mSigner == SecIdentityRef(kCFNull)) 551 return CFDataCreate(NULL, NULL, 0); 552 553 // generate CMS signature 554 CFRef<CMSEncoderRef> cms; 555 MacOSError::check(CMSEncoderCreate(&cms.aref())); 556 MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot)); 557 CMSEncoderAddSigners(cms, state.mSigner); 558 MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true)); 559 560 if (signingTime) { 561 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime)); 562 MacOSError::check(CMSEncoderSetSigningTime(cms, signingTime)); 563 } 564 565 MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length())); 566 567 // Set up to call Timestamp server if requested 568 569 if (state.mWantTimeStamp) 570 { 571 CFRef<CFErrorRef> error = NULL; 572 defaultTSContext = SecCmsTSAGetDefaultContext(&error.aref()); 573 if (error) 574 MacOSError::throwMe(errSecDataNotAvailable); 575 576 if (state.mNoTimeStampCerts || state.mTimestampService) { 577 if (state.mTimestampService) 578 CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService); 579 if (state.mNoTimeStampCerts) 580 CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue); 581 } 582 583 CmsMessageSetTSAContext(cms, defaultTSContext); 584 } 585 586 CFDataRef signature; 587 MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature)); 588 589 return signature; 590} 591 592 593// 594// Parse a text of the form 595// flag,...,flag 596// where each flag is the canonical name of a signable CodeDirectory flag. 597// No abbreviations are allowed, and internally set flags are not accepted. 598// 599uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text) 600{ 601 uint32_t flags = 0; 602 for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) { 603 string word = (comma == string::npos) ? text : text.substr(0, comma); 604 const SecCodeDirectoryFlagTable *item; 605 for (item = kSecCodeDirectoryFlagTable; item->name; item++) 606 if (item->signable && word == item->name) { 607 flags |= item->value; 608 break; 609 } 610 if (!item->name) // not found 611 MacOSError::throwMe(errSecCSInvalidFlags); 612 if (comma == string::npos) // last word 613 break; 614 } 615 return flags; 616} 617 618 619// 620// Generate a unique string from our underlying DiskRep. 621// We could get 90%+ of the uniquing benefit by just generating 622// a random string here. Instead, we pick the (hex string encoding of) 623// the source rep's unique identifier blob. For universal binaries, 624// this is the canonical local architecture, which is a bit arbitrary. 625// This provides us with a consistent unique string for all architectures 626// of a fat binary, *and* (unlike a random string) is reproducible 627// for identical inputs, even upon resigning. 628// 629std::string SecCodeSigner::Signer::uniqueName() const 630{ 631 CFRef<CFDataRef> identification = rep->identification(); 632 const UInt8 *ident = CFDataGetBytePtr(identification); 633 const CFIndex length = CFDataGetLength(identification); 634 string result; 635 for (CFIndex n = 0; n < length; n++) { 636 char hex[3]; 637 snprintf(hex, sizeof(hex), "%02x", ident[n]); 638 result += hex; 639 } 640 return result; 641} 642 643 644} // end namespace CodeSigning 645} // end namespace Security 646