]> git.saurik.com Git - apple/security.git/blob - libsecurity_codesigning/lib/signerutils.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_codesigning / lib / signerutils.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 // signerutils - utilities for signature generation
26 //
27 #include "signerutils.h"
28 #include "signer.h"
29 #include "SecCodeSigner.h"
30 #include <Security/SecIdentity.h>
31 #include <Security/CMSEncoder.h>
32 #include "renum.h"
33 #include "csutilities.h"
34 #include "drmaker.h"
35 #include <security_utilities/unix++.h>
36 #include <security_utilities/unixchild.h>
37 #include <vector>
38
39 // for helper validation
40 #include "Code.h"
41 #include <security_utilities/cfmunge.h>
42 #include <sys/codesign.h>
43
44
45 namespace Security {
46 namespace CodeSigning {
47
48
49 //
50 // About the Mach-O allocation helper
51 //
52 static const char helperName[] = "codesign_allocate";
53 static const char helperPath[] = "/usr/bin/codesign_allocate";
54 static const char helperOverride[] = "CODESIGN_ALLOCATE";
55 static const size_t csAlign = 16;
56
57
58 //
59 // BlobWriters
60 //
61 void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
62 {
63 return EmbeddedSignatureBlob::Maker::component(slot, data);
64 }
65
66
67 void DetachedBlobWriter::flush()
68 {
69 EmbeddedSignatureBlob *blob = this->make();
70 signer.code->detachedSignature(makeCFData(*blob));
71 signer.state.returnDetachedSignature(blob, signer);
72 ::free(blob);
73 }
74
75
76 //
77 // ArchEditor
78 //
79 ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithm hashType, uint32_t attrs)
80 : DiskRep::Writer(attrs)
81 {
82 Universal::Architectures archList;
83 code.architectures(archList);
84 for (Universal::Architectures::const_iterator it = archList.begin();
85 it != archList.end(); ++it)
86 architecture[*it] = new Arch(*it, hashType);
87 }
88
89
90 ArchEditor::~ArchEditor()
91 {
92 for (ArchMap::iterator it = begin(); it != end(); ++it)
93 delete it->second;
94 }
95
96
97 //
98 // BlobEditor
99 //
100 BlobEditor::BlobEditor(Universal &fat, SecCodeSigner::Signer &s)
101 : ArchEditor(fat, s.digestAlgorithm(), 0), signer(s)
102 { }
103
104
105 void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
106 {
107 mGlobal.component(slot, data);
108 }
109
110 void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
111 {
112 mMaker.add(arch.architecture.cpuType(), blob);
113 }
114
115
116 void BlobEditor::commit()
117 {
118 // create the architecture-global blob and store it into the superblob
119 mMaker.add(0, mGlobal.make()); // takes ownership of blob
120
121 // finish up the superblob and deliver it
122 DetachedSignatureBlob *blob = mMaker.make();
123 signer.state.returnDetachedSignature(blob, signer);
124 ::free(blob);
125 }
126
127
128 //
129 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to
130 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
131 // After the tool succeeds, we open the new file and are ready to write it.
132 //
133 MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, CodeDirectory::HashAlgorithm hashType, std::string srcPath)
134 : ArchEditor(code, hashType, w->attributes()),
135 writer(w),
136 sourcePath(srcPath),
137 tempPath(srcPath + ".cstemp"),
138 mNewCode(NULL),
139 mTempMayExist(false)
140 {
141 if (const char *path = getenv(helperOverride)) {
142 mHelperPath = path;
143 mHelperOverridden = true;
144 } else {
145 mHelperPath = helperPath;
146 mHelperOverridden = false;
147 }
148 }
149
150 MachOEditor::~MachOEditor()
151 {
152 delete mNewCode;
153 if (mTempMayExist)
154 ::remove(tempPath.c_str()); // ignore error (can't do anything about it)
155 this->kill();
156 }
157
158
159 void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
160 {
161 writer->component(slot, data);
162 }
163
164
165 void MachOEditor::allocate()
166 {
167 // note that we may have a temporary file from now on (for cleanup in the error case)
168 mTempMayExist = true;
169
170 // run codesign_allocate to make room in the executable file
171 fork();
172 wait();
173 if (!Child::succeeded())
174 UnixError::throwMe(ENOEXEC); //@@@ how to signal "it din' work"?
175
176 // open the new (temporary) Universal file
177 {
178 UidGuard guard(0);
179 mFd.open(tempPath, O_RDWR);
180 }
181 mNewCode = new Universal(mFd);
182 }
183
184 static const unsigned char appleReq[] = {
185 // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate"
186 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
187 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c,
188 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00,
189 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
190 0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f,
191 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65,
192 };
193
194 void MachOEditor::parentAction()
195 {
196 if (mHelperOverridden) {
197 CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath, this->pid());
198 // check code identity of an overridden allocation helper
199 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(mHelperPath));
200 code->validateDirectory();
201 code->validateExecutable();
202 code->validateResources();
203 code->validateRequirement((const Requirement *)appleReq, errSecCSReqFailed);
204 }
205 }
206
207 void MachOEditor::childAction()
208 {
209 vector<const char *> arguments;
210 arguments.push_back(helperName);
211 arguments.push_back("-i");
212 arguments.push_back(sourcePath.c_str());
213 arguments.push_back("-o");
214 arguments.push_back(tempPath.c_str());
215
216 for (Iterator it = architecture.begin(); it != architecture.end(); ++it) {
217 size_t size = LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign);
218 char *ssize; // we'll leak this (execv is coming soon)
219 asprintf(&ssize, "%zd", size);
220
221 if (const char *arch = it->first.name()) {
222 CODESIGN_ALLOCATE_ARCH((char*)arch, size);
223 arguments.push_back("-a");
224 arguments.push_back(arch);
225 } else {
226 CODESIGN_ALLOCATE_ARCHN(it->first.cpuType(), it->first.cpuSubtype(), size);
227 arguments.push_back("-A");
228 char *anum;
229 asprintf(&anum, "%d", it->first.cpuType());
230 arguments.push_back(anum);
231 asprintf(&anum, "%d", it->first.cpuSubtype());
232 arguments.push_back(anum);
233 }
234 arguments.push_back(ssize);
235 }
236 arguments.push_back(NULL);
237
238 if (mHelperOverridden)
239 ::csops(0, CS_EXEC_SET_KILL, NULL, 0); // force code integrity
240 ::seteuid(0); // activate privilege if caller has it; ignore error if not
241 execv(mHelperPath, (char * const *)&arguments[0]);
242 }
243
244 void MachOEditor::reset(Arch &arch)
245 {
246 arch.source.reset(mNewCode->architecture(arch.architecture));
247 arch.cdbuilder.reopen(tempPath,
248 arch.source->offset(), arch.source->signingOffset());
249 }
250
251
252 //
253 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
254 // of the executable image file.
255 //
256 void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
257 {
258 if (size_t offset = arch.source->signingOffset()) {
259 size_t signingLength = arch.source->signingLength();
260 CODESIGN_ALLOCATE_WRITE((char*)arch.architecture.name(), offset, blob->length(), signingLength);
261 if (signingLength < blob->length())
262 MacOSError::throwMe(errSecCSCMSTooLarge);
263 arch.source->seek(offset);
264 arch.source->writeAll(*blob);
265 ::free(blob); // done with it
266 } else {
267 secdebug("signer", "%p cannot find CODESIGNING section", this);
268 MacOSError::throwMe(errSecCSInternalError);
269 }
270 }
271
272
273 //
274 // Commit the edit.
275 // This moves the temporary editor copy over the source image file.
276 // Note that the Universal object returned by allocate() is still open
277 // and valid; the caller owns it.
278 //
279 void MachOEditor::commit()
280 {
281 // if the file's owned by someone else *and* we can become root...
282 struct stat st;
283 UnixError::check(::stat(sourcePath.c_str(), &st));
284
285 // copy over all the *other* stuff
286 Copyfile copy;
287 int fd = mFd;
288 copy.set(COPYFILE_STATE_DST_FD, &fd);
289 {
290 // perform copy under root or file-owner privileges if available
291 UidGuard guard;
292 if (!guard.seteuid(0))
293 guard.seteuid(st.st_uid);
294
295 // copy metadata from original file...
296 copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
297
298 // ... but explicitly update the timestamps since we did change the file
299 char buf;
300 mFd.read(&buf, sizeof(buf), 0);
301 mFd.write(&buf, sizeof(buf), 0);
302
303 // move the new file into place
304 UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
305 mTempMayExist = false; // we renamed it away
306 }
307 }
308
309
310 //
311 // InternalRequirements
312 //
313 void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context)
314 {
315 // first add the default internal requirements
316 if (defaulted) {
317 this->add(defaulted);
318 ::free((void *)defaulted); // was malloc(3)ed by DiskRep
319 }
320
321 // now override them with any requirements explicitly given by the signer
322 if (given)
323 this->add(given);
324
325 // now add the Designated Requirement, if we can make it and it's not been provided
326 if (!this->contains(kSecDesignatedRequirementType)) {
327 DRMaker maker(context);
328 if (Requirement *dr = maker.make()) {
329 this->add(kSecDesignatedRequirementType, dr); // takes ownership of dr
330 }
331 }
332
333 // return the result
334 mReqs = this->make();
335 }
336
337
338 //
339 // Pre-Signing contexts
340 //
341 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer)
342 {
343 // construct a cert chain
344 if (signer.signingIdentity() != SecIdentityRef(kCFNull)) {
345 CFRef<SecCertificateRef> signingCert;
346 MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref()));
347 CFRef<SecPolicyRef> policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning);
348 CFRef<SecTrustRef> trust;
349 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert.get()), policy, &trust.aref()));
350 SecTrustResultType result;
351 MacOSError::check(SecTrustEvaluate(trust, &result));
352 CSSM_TP_APPLE_EVIDENCE_INFO *info;
353 MacOSError::check(SecTrustGetResult(trust, &result, &mCerts.aref(), &info));
354 this->certs = mCerts;
355 }
356
357 // other stuff
358 this->identifier = signer.signingIdentifier();
359 }
360
361
362 } // end namespace CodeSigning
363 } // end namespace Security