]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/signerutils.cpp
d543433ac7cf3e903caf6376f0d4674f461aae8e
[apple/security.git] / OSX / libsecurity_codesigning / lib / signerutils.cpp
1 /*
2 * Copyright (c) 2006-2013 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 // signerutils - utilities for signature generation
26 //
27 #include "csutilities.h"
28 #include "drmaker.h"
29 #include "resources.h"
30 #include "signerutils.h"
31 #include "signer.h"
32
33 #include <Security/SecCmsBase.h>
34 #include <Security/SecIdentity.h>
35 #include <Security/CMSEncoder.h>
36
37 #include "SecCodeSigner.h"
38
39 #include <security_utilities/unix++.h>
40 #include <security_utilities/logging.h>
41 #include <security_utilities/unixchild.h>
42
43 #include <vector>
44
45 // for helper validation
46 #include "Code.h"
47 #include <security_utilities/cfmunge.h>
48 #include <sys/codesign.h>
49
50
51 namespace Security {
52 namespace CodeSigning {
53
54
55 //
56 // About the Mach-O allocation helper
57 //
58 static const char helperName[] = "codesign_allocate";
59 static const char helperPath[] = "/usr/bin/codesign_allocate";
60 static const char helperOverride[] = "CODESIGN_ALLOCATE";
61 static const size_t csAlign = 16;
62
63
64 //
65 // BlobWriters
66 //
67 void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
68 {
69 return EmbeddedSignatureBlob::Maker::component(slot, data);
70 }
71
72
73 void DetachedBlobWriter::flush()
74 {
75 EmbeddedSignatureBlob *blob = this->make();
76 signer.code->detachedSignature(CFTempData(*blob));
77 signer.state.returnDetachedSignature(blob, signer);
78 ::free(blob);
79 }
80
81
82 //
83 // ArchEditor
84 //
85 ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithms hashTypes, uint32_t attrs)
86 : DiskRep::Writer(attrs)
87 {
88 Universal::Architectures archList;
89 code.architectures(archList);
90 for (Universal::Architectures::const_iterator it = archList.begin();
91 it != archList.end(); ++it)
92 architecture[*it] = new Arch(*it, hashTypes);
93 }
94
95
96 ArchEditor::~ArchEditor()
97 {
98 for (ArchMap::iterator it = begin(); it != end(); ++it)
99 delete it->second;
100 }
101
102
103 ArchEditor::Arch::Arch(const Architecture &arch, CodeDirectory::HashAlgorithms hashTypes)
104 : architecture(arch)
105 {
106 for (auto type = hashTypes.begin(); type != hashTypes.end(); ++type)
107 cdBuilders.insert(make_pair(*type, new CodeDirectory::Builder(*type)));
108 }
109
110
111 //
112 // BlobEditor
113 //
114 BlobEditor::BlobEditor(Universal &fat, SecCodeSigner::Signer &s)
115 : ArchEditor(fat, s.digestAlgorithms(), 0), signer(s)
116 { }
117
118
119 void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
120 {
121 mGlobal.component(slot, data);
122 }
123
124 void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
125 {
126 mMaker.add(arch.architecture.cpuType(), blob);
127 }
128
129
130 void BlobEditor::commit()
131 {
132 // create the architecture-global blob and store it into the superblob
133 mMaker.add(0, mGlobal.make()); // takes ownership of blob
134
135 // finish up the superblob and deliver it
136 DetachedSignatureBlob *blob = mMaker.make();
137 signer.state.returnDetachedSignature(blob, signer);
138 ::free(blob);
139 }
140
141
142 //
143 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to
144 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
145 // After the tool succeeds, we open the new file and are ready to write it.
146 //
147 MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, CodeDirectory::HashAlgorithms hashTypes, std::string srcPath)
148 : ArchEditor(code, hashTypes, w->attributes()),
149 writer(w),
150 sourcePath(srcPath),
151 tempPath(srcPath + ".cstemp"),
152 mHashTypes(hashTypes),
153 mNewCode(NULL),
154 mTempMayExist(false)
155 {
156 if (const char *path = getenv(helperOverride)) {
157 mHelperPath = path;
158 mHelperOverridden = true;
159 } else {
160 mHelperPath = helperPath;
161 mHelperOverridden = false;
162 }
163 }
164
165 MachOEditor::~MachOEditor()
166 {
167 delete mNewCode;
168 if (mTempMayExist)
169 ::remove(tempPath.c_str()); // ignore error (can't do anything about it)
170 this->kill();
171 }
172
173
174 void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
175 {
176 writer->component(slot, data);
177 }
178
179
180 void MachOEditor::allocate()
181 {
182 // note that we may have a temporary file from now on (for cleanup in the error case)
183 mTempMayExist = true;
184
185 // run codesign_allocate to make room in the executable file
186 fork();
187 wait();
188 if (!Child::succeeded())
189 MacOSError::throwMe(errSecCSHelperFailed);
190
191 // open the new (temporary) Universal file
192 {
193 UidGuard guard(0);
194 mFd.open(tempPath, O_RDWR);
195 }
196 mNewCode = new Universal(mFd);
197 }
198
199 static const unsigned char appleReq[] = {
200 // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate"
201 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
202 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c,
203 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00,
204 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
205 0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f,
206 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65,
207 };
208
209 void MachOEditor::parentAction()
210 {
211 if (mHelperOverridden) {
212 CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath, this->pid());
213 // check code identity of an overridden allocation helper
214 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(mHelperPath));
215 code->staticValidate(kSecCSDefaultFlags, NULL);
216 code->validateRequirement((const Requirement *)appleReq, errSecCSReqFailed);
217 }
218 }
219
220 void MachOEditor::childAction()
221 {
222 vector<const char *> arguments;
223 arguments.push_back(helperName);
224 arguments.push_back("-i");
225 arguments.push_back(sourcePath.c_str());
226 arguments.push_back("-o");
227 arguments.push_back(tempPath.c_str());
228
229 for (Iterator it = architecture.begin(); it != architecture.end(); ++it) {
230 size_t size = LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign);
231 char *ssize; // we'll leak this (execv is coming soon)
232 asprintf(&ssize, "%zd", size);
233
234 if (const char *arch = it->first.name()) {
235 CODESIGN_ALLOCATE_ARCH((char*)arch, (unsigned int)size);
236 arguments.push_back("-a");
237 arguments.push_back(arch);
238 } else {
239 CODESIGN_ALLOCATE_ARCHN(it->first.cpuType(), it->first.cpuSubtype(), (unsigned int)size);
240 arguments.push_back("-A");
241 char *anum;
242 asprintf(&anum, "%d", it->first.cpuType());
243 arguments.push_back(anum);
244 asprintf(&anum, "%d", it->first.cpuSubtype());
245 arguments.push_back(anum);
246 }
247 arguments.push_back(ssize);
248 }
249 arguments.push_back(NULL);
250
251 if (mHelperOverridden)
252 ::csops(0, CS_OPS_MARKKILL, NULL, 0); // force code integrity
253 (void)::seteuid(0); // activate privilege if caller has it; ignore error if not
254 execv(mHelperPath, (char * const *)&arguments[0]);
255 }
256
257 void MachOEditor::reset(Arch &arch)
258 {
259 arch.source.reset(mNewCode->architecture(arch.architecture));
260
261 for (auto type = mHashTypes.begin(); type != mHashTypes.end(); ++type) {
262 arch.eachDigest(^(CodeDirectory::Builder& builder) {
263 builder.reopen(tempPath, arch.source->offset(), arch.source->signingOffset());
264 });
265 }
266 }
267
268
269 //
270 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
271 // of the executable image file.
272 //
273 void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
274 {
275 if (size_t offset = arch.source->signingOffset()) {
276 size_t signingLength = arch.source->signingLength();
277 CODESIGN_ALLOCATE_WRITE((char*)arch.architecture.name(), offset, (unsigned)blob->length(), (unsigned)signingLength);
278 if (signingLength < blob->length())
279 MacOSError::throwMe(errSecCSCMSTooLarge);
280 arch.source->seek(offset);
281 arch.source->writeAll(*blob);
282 ::free(blob); // done with it
283 } else {
284 secinfo("signer", "%p cannot find CODESIGNING data in Mach-O", this);
285 MacOSError::throwMe(errSecCSInternalError);
286 }
287 }
288
289
290 //
291 // Commit the edit.
292 // This moves the temporary editor copy over the source image file.
293 // Note that the Universal object returned by allocate() is still open
294 // and valid; the caller owns it.
295 //
296 void MachOEditor::commit()
297 {
298 // if the file's owned by someone else *and* we can become root...
299 struct stat st;
300 UnixError::check(::stat(sourcePath.c_str(), &st));
301
302 // copy over all the *other* stuff
303 Copyfile copy;
304 int fd = mFd;
305 copy.set(COPYFILE_STATE_DST_FD, &fd);
306 {
307 // perform copy under root or file-owner privileges if available
308 UidGuard guard;
309 if (!guard.seteuid(0))
310 (void)guard.seteuid(st.st_uid);
311
312 // copy metadata from original file...
313 copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
314
315 // ... but explicitly update the timestamps since we did change the file
316 char buf;
317 mFd.read(&buf, sizeof(buf), 0);
318 mFd.write(&buf, sizeof(buf), 0);
319
320 // move the new file into place
321 UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
322 mTempMayExist = false; // we renamed it away
323 }
324 this->writer->flush();
325 }
326
327
328 //
329 // InternalRequirements
330 //
331 void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context)
332 {
333 // first add the default internal requirements
334 if (defaulted) {
335 this->add(defaulted);
336 ::free((void *)defaulted); // was malloc(3)ed by DiskRep
337 }
338
339 // now override them with any requirements explicitly given by the signer
340 if (given)
341 this->add(given);
342
343 // now add the Designated Requirement, if we can make it and it's not been provided
344 if (!this->contains(kSecDesignatedRequirementType)) {
345 DRMaker maker(context);
346 if (Requirement *dr = maker.make()) {
347 this->add(kSecDesignatedRequirementType, dr); // takes ownership of dr
348 }
349 }
350
351 // return the result
352 mReqs = this->make();
353 }
354
355
356 //
357 // Pre-Signing contexts
358 //
359 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer)
360 {
361 // construct a cert chain
362 if (signer.signingIdentity() != SecIdentityRef(kCFNull)) {
363 CFRef<SecCertificateRef> signingCert;
364 MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref()));
365 CFRef<SecPolicyRef> policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning);
366 CFRef<SecTrustRef> trust;
367 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert.get()), policy, &trust.aref()));
368 SecTrustResultType result;
369 MacOSError::check(SecTrustEvaluate(trust, &result));
370 CSSM_TP_APPLE_EVIDENCE_INFO *info;
371 MacOSError::check(SecTrustGetResult(trust, &result, &mCerts.aref(), &info));
372 this->certs = mCerts;
373 }
374
375 // other stuff
376 this->identifier = signer.signingIdentifier();
377 }
378
379
380 //
381 // A collector of CodeDirectories for hash-agile construction of signatures.
382 //
383 CodeDirectorySet::~CodeDirectorySet()
384 {
385 for (auto it = begin(); it != end(); ++it)
386 ::free(const_cast<CodeDirectory*>(it->second));
387 }
388
389
390 void CodeDirectorySet::add(const Security::CodeSigning::CodeDirectory *cd)
391 {
392 insert(make_pair(cd->hashType, cd));
393 if (cd->hashType == kSecCodeSignatureHashSHA1)
394 mPrimary = cd;
395 }
396
397
398 void CodeDirectorySet::populate(DiskRep::Writer *writer) const
399 {
400 assert(!empty());
401
402 if (mPrimary == NULL) // didn't add SHA-1; pick another occupant for this slot
403 mPrimary = begin()->second;
404
405 // reserve slot zero for a SHA-1 digest if present; else pick something else
406 CodeDirectory::SpecialSlot nextAlternate = cdAlternateCodeDirectorySlots;
407 for (auto it = begin(); it != end(); ++it) {
408 if (it->second == mPrimary) {
409 writer->codeDirectory(it->second, cdCodeDirectorySlot);
410 } else {
411 writer->codeDirectory(it->second, nextAlternate++);
412 }
413 }
414 }
415
416
417 const CodeDirectory* CodeDirectorySet::primary() const
418 {
419 if (mPrimary == NULL)
420 mPrimary = begin()->second;
421 return mPrimary;
422 }
423
424 CFArrayRef CodeDirectorySet::hashList() const
425 {
426 CFRef<CFMutableArrayRef> hashList = makeCFMutableArray(0);
427 for (auto it = begin(); it != end(); ++it) {
428 CFRef<CFDataRef> cdhash = it->second->cdhash(true);
429 CFArrayAppendValue(hashList, cdhash);
430 }
431 return hashList.yield();
432 }
433
434 CFDictionaryRef CodeDirectorySet::hashDict() const
435 {
436 CFRef<CFMutableDictionaryRef> hashDict = makeCFMutableDictionary();
437
438 for (auto it = begin(); it != end(); ++it) {
439 SECOidTag tag = CodeDirectorySet::SECOidTagForAlgorithm(it->first);
440
441 if (tag == SEC_OID_UNKNOWN) {
442 MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm);
443 }
444
445 CFRef<CFNumberRef> hashType = makeCFNumber(int(tag));
446 CFRef<CFDataRef> fullCdhash = it->second->cdhash(false); // Full-length cdhash!
447 CFDictionarySetValue(hashDict, hashType, fullCdhash);
448 }
449
450 return hashDict.yield();
451 }
452
453 SECOidTag CodeDirectorySet::SECOidTagForAlgorithm(CodeDirectory::HashAlgorithm algorithm) {
454 SECOidTag tag;
455
456 switch (algorithm) {
457 case kSecCodeSignatureHashSHA1:
458 tag = SEC_OID_SHA1;
459 break;
460 case kSecCodeSignatureHashSHA256:
461 case kSecCodeSignatureHashSHA256Truncated: // truncated *page* hashes, not cdhash
462 tag = SEC_OID_SHA256;
463 break;
464 case kSecCodeSignatureHashSHA384:
465 tag = SEC_OID_SHA384;
466 break;
467 default:
468 tag = SEC_OID_UNKNOWN;
469 }
470
471 return tag;
472 }
473
474
475
476 } // end namespace CodeSigning
477 } // end namespace Security