]>
git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/signerutils.cpp
c270ed22cb7327c029b57c2af1de6d3aeebb5f41
2 * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 // signerutils - utilities for signature generation
27 #include "signerutils.h"
29 #include "SecCodeSigner.h"
30 #include <Security/SecIdentity.h>
31 #include <Security/CMSEncoder.h>
33 #include <security_utilities/unix++.h>
34 #include <security_utilities/unixchild.h>
37 // for helper validation
40 #include <sys/codesign.h>
44 namespace CodeSigning
{
48 // About the Mach-O allocation helper
50 static const char helperName
[] = "codesign_allocate";
51 static const char helperPath
[] = "/usr/bin/codesign_allocate";
52 static const char helperOverride
[] = "CODESIGN_ALLOCATE";
53 static const size_t csAlign
= 16;
57 // InternalRequirements
59 void InternalRequirements::operator () (const Requirements
*given
, const Requirements
*defaulted
)
63 ::free((void *)defaulted
); // was malloc(3)ed by DiskRep
74 void BlobWriter::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
76 return EmbeddedSignatureBlob::Maker::component(slot
, data
);
80 void DetachedBlobWriter::flush()
82 EmbeddedSignatureBlob
*blob
= this->make();
83 signer
.code
->detachedSignature(CFTempData(*blob
));
84 signer
.state
.returnDetachedSignature(blob
);
92 ArchEditor::ArchEditor(Universal
&code
, uint32_t attrs
/* = 0 */)
93 : DiskRep::Writer(attrs
)
95 Universal::Architectures archList
;
96 code
.architectures(archList
);
97 for (Universal::Architectures::const_iterator it
= archList
.begin();
98 it
!= archList
.end(); ++it
)
99 architecture
[*it
] = new Arch(*it
);
103 ArchEditor::~ArchEditor()
105 for (ArchMap::iterator it
= begin(); it
!= end(); ++it
)
113 void BlobEditor::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
115 mGlobal
.component(slot
, data
);
118 void BlobEditor::write(Arch
&arch
, EmbeddedSignatureBlob
*blob
)
120 mMaker
.add(arch
.architecture
.cpuType(), blob
);
124 void BlobEditor::commit()
126 // create the architecture-global blob and store it into the superblob
127 mMaker
.add(0, mGlobal
.make()); // takes ownership of blob
129 // finish up the superblob and deliver it
130 DetachedSignatureBlob
*blob
= mMaker
.make();
131 signer
.state
.returnDetachedSignature(blob
);
137 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to
138 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
139 // After the tool succeeds, we open the new file and are ready to write it.
141 MachOEditor::MachOEditor(DiskRep::Writer
*w
, Universal
&code
, std::string srcPath
)
142 : ArchEditor(code
, w
->attributes()), writer(w
), sourcePath(srcPath
), tempPath(srcPath
+ ".cstemp"),
143 mNewCode(NULL
), mTempMayExist(false)
145 if (const char *path
= getenv(helperOverride
)) {
147 mHelperOverridden
= true;
149 mHelperPath
= helperPath
;
150 mHelperOverridden
= false;
154 MachOEditor::~MachOEditor()
158 ::remove(tempPath
.c_str()); // ignore error (can't do anything about it)
160 //@@@ this code should be in UnixChild::kill() -- migrate it there
161 if (state() == alive
) {
162 this->kill(SIGTERM
); // shoot it once
163 checkChildren(); // check for quick death
164 if (state() == alive
) {
165 usleep(500000); // give it some grace
166 if (state() == alive
) { // could have been reaped by another thread
167 checkChildren(); // check again
168 if (state() == alive
) { // it... just... won't... die...
169 this->kill(SIGKILL
); // take THAT!
171 if (state() == alive
) // stuck zombie
172 abandon(); // leave the body behind
180 void MachOEditor::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
182 writer
->component(slot
, data
);
186 void MachOEditor::allocate()
188 // note that we may have a temporary file from now on (for cleanup in the error case)
189 mTempMayExist
= true;
191 // run codesign_allocate to make room in the executable file
194 if (!Child::succeeded())
195 UnixError::throwMe(ENOEXEC
); //@@@ how to signal "it din' work"?
197 // open the new (temporary) Universal file
200 mFd
.open(tempPath
, O_RDWR
);
202 mNewCode
= new Universal(mFd
);
205 static const unsigned char appleReq
[] = { // anchor apple
206 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03,
209 void MachOEditor::parentAction()
211 if (mHelperOverridden
) {
212 secdebug("machoedit", "validating alternate codesign_allocate at %s (pid=%d)", mHelperPath
, this->pid());
213 // check code identity of an overridden allocation helper
214 SecPointer
<SecStaticCode
> code
= new SecStaticCode(DiskRep::bestGuess(mHelperPath
));
215 code
->validateDirectory();
216 code
->validateExecutable();
217 code
->validateResources();
218 code
->validateRequirements((const Requirement
*)appleReq
, errSecCSReqFailed
);
222 void MachOEditor::childAction()
224 vector
<const char *> arguments
;
225 arguments
.push_back(helperName
);
226 arguments
.push_back("-i");
227 arguments
.push_back(sourcePath
.c_str());
228 arguments
.push_back("-o");
229 arguments
.push_back(tempPath
.c_str());
231 for (Iterator it
= architecture
.begin(); it
!= architecture
.end(); ++it
) {
232 char *size
; // we'll leak this (execv is coming soon)
233 asprintf(&size
, "%d", LowLevelMemoryUtilities::alignUp(it
->second
->blobSize
, csAlign
));
234 secdebug("machoedit", "preparing %s size=%s", it
->first
.name(), size
);
236 if (const char *arch
= it
->first
.name()) {
237 arguments
.push_back("-a");
238 arguments
.push_back(arch
);
240 arguments
.push_back("-A");
242 asprintf(&anum
, "%d", it
->first
.cpuType());
243 arguments
.push_back(anum
);
244 asprintf(&anum
, "%d", it
->first
.cpuSubtype());
245 arguments
.push_back(anum
);
247 arguments
.push_back(size
);
249 arguments
.push_back(NULL
);
251 if (mHelperOverridden
)
252 ::csops(0, CS_EXEC_SET_KILL
, NULL
, 0); // force code integrity
253 ::seteuid(0); // activate privilege if caller has it; ignore error if not
254 execv(mHelperPath
, (char * const *)&arguments
[0]);
257 void MachOEditor::reset(Arch
&arch
)
259 arch
.source
.reset(mNewCode
->architecture(arch
.architecture
));
260 arch
.cdbuilder
.reopen(tempPath
,
261 arch
.source
->offset(), arch
.source
->signingOffset());
266 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
267 // of the executable image file.
269 void MachOEditor::write(Arch
&arch
, EmbeddedSignatureBlob
*blob
)
271 if (size_t offset
= arch
.source
->signingOffset()) {
272 size_t signingLength
= arch
.source
->signingLength();
273 secdebug("codesign", "writing architecture %s at 0x%zx (%zd of %zd)",
274 arch
.architecture
.name(), offset
, blob
->length(), signingLength
);
275 if (signingLength
< blob
->length()) {
276 secdebug("codesign", "trying to write %zd bytes into %zd area",
277 blob
->length(), signingLength
);
278 MacOSError::throwMe(errSecCSInternalError
);
280 arch
.source
->seek(offset
);
281 arch
.source
->writeAll(*blob
);
282 ::free(blob
); // done with it
284 secdebug("signer", "%p cannot find CODESIGNING section", this);
285 MacOSError::throwMe(errSecCSInternalError
);
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.
296 void MachOEditor::commit()
298 // if the file's owned by someone else *and* we can become root...
300 UnixError::check(::stat(sourcePath
.c_str(), &st
));
302 // copy over all the *other* stuff
305 copy
.set(COPYFILE_STATE_DST_FD
, &fd
);
307 // perform copy under root or file-owner privileges if available
309 if (!guard
.seteuid(0))
310 guard
.seteuid(st
.st_uid
);
311 copy(sourcePath
.c_str(), NULL
, COPYFILE_SECURITY
| COPYFILE_METADATA
);
313 // move the new file into place
314 UnixError::check(::rename(tempPath
.c_str(), sourcePath
.c_str()));
315 mTempMayExist
= false; // we renamed it away
325 if (!(mState
= copyfile_state_alloc()))
326 UnixError::throwMe();
329 void Copyfile::set(uint32_t flag
, const void *value
)
331 check(::copyfile_state_set(mState
, flag
, value
));
334 void Copyfile::get(uint32_t flag
, void *value
)
336 check(::copyfile_state_set(mState
, flag
, value
));
339 void Copyfile::operator () (const char *src
, const char *dst
, copyfile_flags_t flags
)
341 check(::copyfile(src
, dst
, mState
, flags
));
344 void Copyfile::check(int rc
)
347 UnixError::throwMe();
351 } // end namespace CodeSigning
352 } // end namespace Security