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