#include "cs.h"
#include "StaticCode.h"
#include <security_utilities/cfmunge.h>
#include <fcntl.h>
#include <dirent.h>
using namespace CodeSigning;
CFTypeID SecStaticCodeGetTypeID(void)
{
BEGIN_CSAPI
return gCFObjects().StaticCode.typeID;
END_CSAPI1(_kCFRuntimeNotATypeID)
}
OSStatus SecStaticCodeCreateWithPath(CFURLRef path, SecCSFlags flags, SecStaticCodeRef *staticCodeRef)
{
BEGIN_CSAPI
checkFlags(flags);
CodeSigning::Required(staticCodeRef) = (new SecStaticCode(DiskRep::bestGuess(cfString(path).c_str())))->handle();
END_CSAPI
}
const CFStringRef kSecCodeAttributeArchitecture = CFSTR("architecture");
const CFStringRef kSecCodeAttributeSubarchitecture =CFSTR("subarchitecture");
const CFStringRef kSecCodeAttributeBundleVersion = CFSTR("bundleversion");
OSStatus SecStaticCodeCreateWithPathAndAttributes(CFURLRef path, SecCSFlags flags, CFDictionaryRef attributes,
SecStaticCodeRef *staticCodeRef)
{
BEGIN_CSAPI
checkFlags(flags);
DiskRep::Context ctx;
std::string version; if (attributes) {
std::string archName;
int archNumber, subarchNumber;
if (cfscan(attributes, "{%O=%s}", kSecCodeAttributeArchitecture, &archName)) {
ctx.arch = Architecture(archName.c_str());
} else if (cfscan(attributes, "{%O=%d,%O=%d}",
kSecCodeAttributeArchitecture, &archNumber, kSecCodeAttributeSubarchitecture, &subarchNumber))
ctx.arch = Architecture(archNumber, subarchNumber);
else if (cfscan(attributes, "{%O=%d}", kSecCodeAttributeArchitecture, &archNumber))
ctx.arch = Architecture(archNumber);
if (cfscan(attributes, "{%O=%s}", kSecCodeAttributeBundleVersion, &version))
ctx.version = version.c_str();
}
CodeSigning::Required(staticCodeRef) = (new SecStaticCode(DiskRep::bestGuess(cfString(path).c_str(), &ctx)))->handle();
END_CSAPI
}
static void validate(SecStaticCode *code, const SecRequirement *req, SecCSFlags flags);
static void validateNested(string location, const SecRequirement *req, SecCSFlags flags, string exclude = "/");
static void validate(SecStaticCode *code, const SecRequirement *req, SecCSFlags flags)
{
try {
code->validateNonResourceComponents(); if (!(flags & kSecCSDoNotValidateExecutable))
code->validateExecutable();
if (!(flags & kSecCSDoNotValidateResources))
code->validateResources();
if (req)
code->validateRequirement(req->requirement(), errSecCSReqFailed);
if (flags & kSecCSCheckNestedCode)
if (CFURLRef baseUrl = code->resourceBase()) {
string base = cfString(baseUrl) + "/";
validateNested(base + "Frameworks", req, flags);
validateNested(base + "SharedFrameworks", req, flags);
validateNested(base + "PlugIns", req, flags);
validateNested(base + "Plug-ins", req, flags);
validateNested(base + "XPCServices", req, flags);
validateNested(base + "MacOS", req, flags, code->mainExecutablePath()); }
} catch (CSError &err) {
if (Universal *fat = code->diskRep()->mainExecutableImage()) if (MachO *mach = fat->architecture()) {
err.augment(kSecCFErrorArchitecture, CFTempString(mach->architecture().displayName()));
delete mach;
}
throw;
} catch (const MacOSError &err) {
if (Universal *fat = code->diskRep()->mainExecutableImage())
if (MachO *mach = fat->architecture()) {
CFTempString arch(mach->architecture().displayName());
delete mach;
CSError::throwMe(err.error, kSecCFErrorArchitecture, arch);
}
throw;
}
}
static void validateNested(string location, const SecRequirement *req, SecCSFlags flags, string exclude)
{
DIR *dir = opendir(location.c_str());
if (dir == 0) {
if (errno == ENOENT) return;
UnixError::throwMe();
}
while (struct dirent *dp = readdir(dir)) {
switch (dp->d_type) {
case DT_REG:
case DT_LNK:
case DT_DIR:
break;
default:
continue;
}
if (dp->d_name[0] == '.')
continue;
string path = location + "/" + dp->d_name;
if (path == exclude) continue;
try {
SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(path));
validate(code, req, flags);
} catch (CSError &err) {
err.augment(kSecCFErrorPath, CFTempURL(path));
throw;
}
}
closedir(dir);
}
OSStatus SecStaticCodeCheckValidity(SecStaticCodeRef staticCodeRef, SecCSFlags flags,
SecRequirementRef requirementRef)
{
return SecStaticCodeCheckValidityWithErrors(staticCodeRef, flags, requirementRef, NULL);
}
OSStatus SecStaticCodeCheckValidityWithErrors(SecStaticCodeRef staticCodeRef, SecCSFlags flags,
SecRequirementRef requirementRef, CFErrorRef *errors)
{
BEGIN_CSAPI
checkFlags(flags,
kSecCSCheckAllArchitectures
| kSecCSDoNotValidateExecutable
| kSecCSDoNotValidateResources
| kSecCSConsiderExpiration
| kSecCSEnforceRevocationChecks
| kSecCSCheckNestedCode);
SecPointer<SecStaticCode> code = SecStaticCode::requiredStatic(staticCodeRef);
const SecRequirement *req = SecRequirement::optional(requirementRef);
DTRACK(CODESIGN_EVAL_STATIC, code, (char*)code->mainExecutablePath().c_str());
if (flags & kSecCSCheckAllArchitectures) {
SecStaticCode::AllArchitectures archs(code);
while (SecPointer<SecStaticCode> scode = archs())
validate(scode, req, flags);
} else
validate(code, req, flags);
END_CSAPI_ERRORS
}
OSStatus SecCodeCopyPath(SecStaticCodeRef staticCodeRef, SecCSFlags flags, CFURLRef *path)
{
BEGIN_CSAPI
checkFlags(flags);
SecPointer<SecStaticCode> staticCode = SecStaticCode::requiredStatic(staticCodeRef);
CodeSigning::Required(path) = staticCode->canonicalPath();
END_CSAPI
}
OSStatus SecCodeCopyDesignatedRequirement(SecStaticCodeRef staticCodeRef, SecCSFlags flags,
SecRequirementRef *requirementRef)
{
BEGIN_CSAPI
checkFlags(flags);
const Requirement *req =
SecStaticCode::requiredStatic(staticCodeRef)->designatedRequirement();
CodeSigning::Required(requirementRef) = (new SecRequirement(req))->handle();
END_CSAPI
}
OSStatus SecCodeCopyInternalRequirement(SecStaticCodeRef staticCodeRef, SecRequirementType type,
SecCSFlags flags, SecRequirementRef *requirementRef)
{
BEGIN_CSAPI
checkFlags(flags);
const Requirement *req =
SecStaticCode::requiredStatic(staticCodeRef)->internalRequirement(type);
CodeSigning::Required(requirementRef) = req ? (new SecRequirement(req))->handle() : NULL;
END_CSAPI
}
OSStatus SecCodeSetDetachedSignature(SecStaticCodeRef codeRef, CFDataRef signature,
SecCSFlags flags)
{
BEGIN_CSAPI
checkFlags(flags);
SecPointer<SecStaticCode> code = SecStaticCode::requiredStatic(codeRef);
if (signature)
CFRetain(signature); code->detachedSignature(signature); code->resetValidity();
END_CSAPI
}
OSStatus SecCodeMapMemory(SecStaticCodeRef codeRef, SecCSFlags flags)
{
BEGIN_CSAPI
checkFlags(flags);
SecPointer<SecStaticCode> code = SecStaticCode::requiredStatic(codeRef);
if (const CodeDirectory *cd = code->codeDirectory(false)) {
fsignatures args = { code->diskRep()->signingBase(), (void *)cd, cd->length() };
UnixError::check(::fcntl(code->diskRep()->fd(), F_ADDSIGS, &args));
} else
MacOSError::throwMe(errSecCSUnsigned);
END_CSAPI
}