]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
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> | |
427c49bc | 32 | #include "resources.h" |
b1ab9ed8 A |
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(); | |
427c49bc | 70 | signer.code->detachedSignature(CFTempData(*blob)); |
b1ab9ed8 A |
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()) | |
427c49bc | 174 | MacOSError::throwMe(errSecCSHelperFailed); |
b1ab9ed8 A |
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)); | |
427c49bc | 200 | code->staticValidate(kSecCSDefaultFlags, NULL); |
b1ab9ed8 A |
201 | code->validateRequirement((const Requirement *)appleReq, errSecCSReqFailed); |
202 | } | |
203 | } | |
204 | ||
205 | void MachOEditor::childAction() | |
206 | { | |
207 | vector<const char *> arguments; | |
208 | arguments.push_back(helperName); | |
209 | arguments.push_back("-i"); | |
210 | arguments.push_back(sourcePath.c_str()); | |
211 | arguments.push_back("-o"); | |
212 | arguments.push_back(tempPath.c_str()); | |
213 | ||
214 | for (Iterator it = architecture.begin(); it != architecture.end(); ++it) { | |
215 | size_t size = LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign); | |
216 | char *ssize; // we'll leak this (execv is coming soon) | |
217 | asprintf(&ssize, "%zd", size); | |
218 | ||
219 | if (const char *arch = it->first.name()) { | |
427c49bc | 220 | CODESIGN_ALLOCATE_ARCH((char*)arch, (unsigned int)size); |
b1ab9ed8 A |
221 | arguments.push_back("-a"); |
222 | arguments.push_back(arch); | |
223 | } else { | |
427c49bc | 224 | CODESIGN_ALLOCATE_ARCHN(it->first.cpuType(), it->first.cpuSubtype(), (unsigned int)size); |
b1ab9ed8 A |
225 | arguments.push_back("-A"); |
226 | char *anum; | |
227 | asprintf(&anum, "%d", it->first.cpuType()); | |
228 | arguments.push_back(anum); | |
229 | asprintf(&anum, "%d", it->first.cpuSubtype()); | |
230 | arguments.push_back(anum); | |
231 | } | |
232 | arguments.push_back(ssize); | |
233 | } | |
234 | arguments.push_back(NULL); | |
235 | ||
236 | if (mHelperOverridden) | |
237 | ::csops(0, CS_EXEC_SET_KILL, NULL, 0); // force code integrity | |
238 | ::seteuid(0); // activate privilege if caller has it; ignore error if not | |
239 | execv(mHelperPath, (char * const *)&arguments[0]); | |
240 | } | |
241 | ||
242 | void MachOEditor::reset(Arch &arch) | |
243 | { | |
244 | arch.source.reset(mNewCode->architecture(arch.architecture)); | |
245 | arch.cdbuilder.reopen(tempPath, | |
246 | arch.source->offset(), arch.source->signingOffset()); | |
247 | } | |
248 | ||
249 | ||
250 | // | |
251 | // MachOEditor's write() method actually writes the blob into the CODESIGNING section | |
252 | // of the executable image file. | |
253 | // | |
254 | void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob) | |
255 | { | |
256 | if (size_t offset = arch.source->signingOffset()) { | |
257 | size_t signingLength = arch.source->signingLength(); | |
427c49bc | 258 | CODESIGN_ALLOCATE_WRITE((char*)arch.architecture.name(), offset, (unsigned)blob->length(), (unsigned)signingLength); |
b1ab9ed8 A |
259 | if (signingLength < blob->length()) |
260 | MacOSError::throwMe(errSecCSCMSTooLarge); | |
261 | arch.source->seek(offset); | |
262 | arch.source->writeAll(*blob); | |
263 | ::free(blob); // done with it | |
264 | } else { | |
265 | secdebug("signer", "%p cannot find CODESIGNING section", this); | |
266 | MacOSError::throwMe(errSecCSInternalError); | |
267 | } | |
268 | } | |
269 | ||
270 | ||
271 | // | |
272 | // Commit the edit. | |
273 | // This moves the temporary editor copy over the source image file. | |
274 | // Note that the Universal object returned by allocate() is still open | |
275 | // and valid; the caller owns it. | |
276 | // | |
277 | void MachOEditor::commit() | |
278 | { | |
279 | // if the file's owned by someone else *and* we can become root... | |
280 | struct stat st; | |
281 | UnixError::check(::stat(sourcePath.c_str(), &st)); | |
282 | ||
283 | // copy over all the *other* stuff | |
284 | Copyfile copy; | |
285 | int fd = mFd; | |
286 | copy.set(COPYFILE_STATE_DST_FD, &fd); | |
287 | { | |
288 | // perform copy under root or file-owner privileges if available | |
289 | UidGuard guard; | |
290 | if (!guard.seteuid(0)) | |
291 | guard.seteuid(st.st_uid); | |
292 | ||
293 | // copy metadata from original file... | |
294 | copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA); | |
295 | ||
296 | // ... but explicitly update the timestamps since we did change the file | |
297 | char buf; | |
298 | mFd.read(&buf, sizeof(buf), 0); | |
299 | mFd.write(&buf, sizeof(buf), 0); | |
300 | ||
301 | // move the new file into place | |
302 | UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str())); | |
303 | mTempMayExist = false; // we renamed it away | |
304 | } | |
305 | } | |
306 | ||
307 | ||
308 | // | |
309 | // InternalRequirements | |
310 | // | |
311 | void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context) | |
312 | { | |
313 | // first add the default internal requirements | |
314 | if (defaulted) { | |
315 | this->add(defaulted); | |
316 | ::free((void *)defaulted); // was malloc(3)ed by DiskRep | |
317 | } | |
318 | ||
319 | // now override them with any requirements explicitly given by the signer | |
320 | if (given) | |
321 | this->add(given); | |
322 | ||
323 | // now add the Designated Requirement, if we can make it and it's not been provided | |
324 | if (!this->contains(kSecDesignatedRequirementType)) { | |
325 | DRMaker maker(context); | |
326 | if (Requirement *dr = maker.make()) { | |
327 | this->add(kSecDesignatedRequirementType, dr); // takes ownership of dr | |
328 | } | |
329 | } | |
330 | ||
331 | // return the result | |
332 | mReqs = this->make(); | |
333 | } | |
334 | ||
335 | ||
336 | // | |
337 | // Pre-Signing contexts | |
338 | // | |
339 | PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer) | |
340 | { | |
341 | // construct a cert chain | |
342 | if (signer.signingIdentity() != SecIdentityRef(kCFNull)) { | |
343 | CFRef<SecCertificateRef> signingCert; | |
344 | MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref())); | |
345 | CFRef<SecPolicyRef> policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning); | |
346 | CFRef<SecTrustRef> trust; | |
347 | MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert.get()), policy, &trust.aref())); | |
348 | SecTrustResultType result; | |
349 | MacOSError::check(SecTrustEvaluate(trust, &result)); | |
350 | CSSM_TP_APPLE_EVIDENCE_INFO *info; | |
351 | MacOSError::check(SecTrustGetResult(trust, &result, &mCerts.aref(), &info)); | |
352 | this->certs = mCerts; | |
353 | } | |
354 | ||
355 | // other stuff | |
356 | this->identifier = signer.signingIdentifier(); | |
357 | } | |
358 | ||
359 | ||
360 | } // end namespace CodeSigning | |
361 | } // end namespace Security |