X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/866f8763175ff60e4fa455b92b5eb660a12fe6c7..7e6b461318c8a779d91381531435a68ee4e8b6ed:/OSX/libsecurity_codesigning/lib/bundlediskrep.cpp diff --git a/OSX/libsecurity_codesigning/lib/bundlediskrep.cpp b/OSX/libsecurity_codesigning/lib/bundlediskrep.cpp index 8ec8852c..7c490970 100644 --- a/OSX/libsecurity_codesigning/lib/bundlediskrep.cpp +++ b/OSX/libsecurity_codesigning/lib/bundlediskrep.cpp @@ -23,6 +23,7 @@ #include "bundlediskrep.h" #include "filediskrep.h" #include "dirscanner.h" +#include "notarization.h" #include #include #include @@ -47,11 +48,12 @@ static std::string findDistFile(const std::string &directory); // We make a CFBundleRef immediately, but everything else is lazy // BundleDiskRep::BundleDiskRep(const char *path, const Context *ctx) - : mBundle(_CFBundleCreateUnique(NULL, CFTempURL(path))) + : mBundle(_CFBundleCreateUnique(NULL, CFTempURL(path))), forcePlatform(false) { if (!mBundle) MacOSError::throwMe(errSecCSBadBundleFormat); setup(ctx); + forcePlatform = mExecRep->appleInternalForcePlatform(); CODESIGN_DISKREP_CREATE_BUNDLE_PATH(this, (char*)path, (void*)ctx, mExecRep); } @@ -59,6 +61,7 @@ BundleDiskRep::BundleDiskRep(CFBundleRef ref, const Context *ctx) { mBundle = ref; // retains setup(ctx); + forcePlatform = mExecRep->appleInternalForcePlatform(); CODESIGN_DISKREP_CREATE_BUNDLE_REF(this, ref, (void*)ctx, mExecRep); } @@ -87,7 +90,7 @@ void BundleDiskRep::setup(const Context *ctx) mInstallerPackage = false; // default mAppLike = false; // pessimism first bool appDisqualified = false; // found reason to disqualify as app - + // capture the path of the main executable before descending into a specific version CFRef mainExecBefore = CFBundleCopyExecutableURL(mBundle); CFRef infoPlistBefore = _CFBundleCopyInfoPlistURL(mBundle); @@ -360,6 +363,45 @@ CFDataRef BundleDiskRep::component(CodeDirectory::SpecialSlot slot) } } +BundleDiskRep::RawComponentMap BundleDiskRep::createRawComponents() +{ + RawComponentMap map; + + /* Those are the slots known to BundleDiskReps. + * Unlike e.g. MachOReps, we cannot handle unknown slots, + * as we won't know their slot <-> filename mapping. + */ + int const slots[] = { + cdCodeDirectorySlot, cdSignatureSlot, cdResourceDirSlot, + cdTopDirectorySlot, cdEntitlementSlot, cdEntitlementDERSlot, + cdRepSpecificSlot}; + + for (int slot = 0; slot < (int)(sizeof(slots)/sizeof(slots[0])); ++slot) { + /* Here, we only handle metaData slots, i.e. slots that + * are explicit files in the _CodeSignature directory. + * Main executable slots (if the main executable is a + * EditableDiskRep) are handled when editing the + * main executable's rep explicitly. + * There is also an Info.plist slot, which is not a + * real part of the code signature. + */ + CFRef data = metaData(slot); + + if (data) { + map[slot] = data; + } + } + + for (CodeDirectory::Slot slot = cdAlternateCodeDirectorySlots; slot < cdAlternateCodeDirectoryLimit; ++slot) { + CFRef data = metaData(slot); + + if (data) { + map[slot] = data; + } + } + + return map; +} // Check that all components of this BundleDiskRep come from either the main // executable or the _CodeSignature directory (not mix-and-match). @@ -471,6 +513,7 @@ CFArrayRef BundleDiskRep::modifiedFiles() checkModifiedFile(files, cdResourceDirSlot); checkModifiedFile(files, cdTopDirectorySlot); checkModifiedFile(files, cdEntitlementSlot); + checkModifiedFile(files, cdEntitlementDERSlot); checkModifiedFile(files, cdRepSpecificSlot); for (CodeDirectory::Slot slot = cdAlternateCodeDirectorySlots; slot < cdAlternateCodeDirectoryLimit; ++slot) checkModifiedFile(files, slot); @@ -575,41 +618,73 @@ CFDictionaryRef BundleDiskRep::defaultResourceRules(const SigningContext &ctx) "'^.*' = #T" // everything is a resource "'^Info\\.plist$' = {omit=#T,weight=10}" // explicitly exclude this for backward compatibility "}}"); - - // new (V2) executable bundle rules - return cfmake("{" // *** the new (V2) world *** - "rules={" // old (V1; legacy) version - "'^version.plist$' = #T" // include version.plist - "%s = #T" // include Resources - "%s = {optional=#T, weight=1000}" // make localizations optional - "%s = {weight=1010}" // ... except for Base.lproj which really isn't optional at all - "%s = {omit=#T, weight=1100}" // exclude all locversion.plist files - "},rules2={" - "'^.*' = #T" // include everything as a resource, with the following exceptions - "'^[^/]+$' = {nested=#T, weight=10}" // files directly in Contents - "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=10}" // dynamic repositories - "'.*\\.dSYM($|/)' = {weight=11}" // but allow dSYM directories in code locations (parallel to their code) - "'^(.*/)?\\.DS_Store$' = {omit=#T,weight=2000}" // ignore .DS_Store files - "'^Info\\.plist$' = {omit=#T, weight=20}" // excluded automatically now, but old systems need to be told - "'^version\\.plist$' = {weight=20}" // include version.plist as resource - "'^embedded\\.provisionprofile$' = {weight=20}" // include embedded.provisionprofile as resource - "'^PkgInfo$' = {omit=#T, weight=20}" // traditionally not included - "%s = {weight=20}" // Resources override default nested (widgets) - "%s = {optional=#T, weight=1000}" // make localizations optional - "%s = {weight=1010}" // ... except for Base.lproj which really isn't optional at all - "%s = {omit=#T, weight=1100}" // exclude all locversion.plist files - "}}", - - (string("^") + resources).c_str(), - (string("^") + resources + ".*\\.lproj/").c_str(), - (string("^") + resources + "Base\\.lproj/").c_str(), - (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str(), - - (string("^") + resources).c_str(), - (string("^") + resources + ".*\\.lproj/").c_str(), - (string("^") + resources + "Base\\.lproj/").c_str(), - (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str() - ); + + // new (V2) executable bundle rules + if (!resources.empty()) { + return cfmake("{" // *** the new (V2) world *** + "rules={" // old (V1; legacy) version + "'^version.plist$' = #T" // include version.plist + "%s = #T" // include Resources + "%s = {optional=#T, weight=1000}" // make localizations optional + "%s = {weight=1010}" // ... except for Base.lproj which really isn't optional at all + "%s = {omit=#T, weight=1100}" // exclude all locversion.plist files + "},rules2={" + "'^.*' = #T" // include everything as a resource, with the following exceptions + "'^[^/]+$' = {nested=#T, weight=10}" // files directly in Contents + "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=10}" // dynamic repositories + "'.*\\.dSYM($|/)' = {weight=11}" // but allow dSYM directories in code locations (parallel to their code) + "'^(.*/)?\\.DS_Store$' = {omit=#T,weight=2000}" // ignore .DS_Store files + "'^Info\\.plist$' = {omit=#T, weight=20}" // excluded automatically now, but old systems need to be told + "'^version\\.plist$' = {weight=20}" // include version.plist as resource + "'^embedded\\.provisionprofile$' = {weight=20}" // include embedded.provisionprofile as resource + "'^PkgInfo$' = {omit=#T, weight=20}" // traditionally not included + "%s = {weight=20}" // Resources override default nested (widgets) + "%s = {optional=#T, weight=1000}" // make localizations optional + "%s = {weight=1010}" // ... except for Base.lproj which really isn't optional at all + "%s = {omit=#T, weight=1100}" // exclude all locversion.plist files + "}}", + + (string("^") + resources).c_str(), + (string("^") + resources + ".*\\.lproj/").c_str(), + (string("^") + resources + "Base\\.lproj/").c_str(), + (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str(), + + (string("^") + resources).c_str(), + (string("^") + resources + ".*\\.lproj/").c_str(), + (string("^") + resources + "Base\\.lproj/").c_str(), + (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str() + ); + } else { + /* This is a bundle format without a Resources directory, which means we need to omit + * Resources-directory specific rules that would create conflicts. */ + + /* We also declare that flat bundles do not use any nested code rules. + * Embedded, where flat bundles are used, currently does not support them, + * and we have no plans for allowing the replacement of nested code there. + * Should anyone actually intend to use nested code rules in a flat + * bundle, they are free to create their own rules. */ + + return cfmake("{" // *** the new (V2) world *** + "rules={" // old (V1; legacy) version + "'^version.plist$' = #T" // include version.plist + "'^.*' = #T" // include Resources + "'^.*\\.lproj/' = {optional=#T, weight=1000}" // make localizations optional + "'^Base\\.lproj/' = {weight=1010}" // ... except for Base.lproj which really isn't optional at all + "'^.*\\.lproj/locversion.plist$' = {omit=#T, weight=1100}" // exclude all locversion.plist files + "},rules2={" + "'^.*' = #T" // include everything as a resource, with the following exceptions + "'.*\\.dSYM($|/)' = {weight=11}" // but allow dSYM directories in code locations (parallel to their code) + "'^(.*/)?\\.DS_Store$' = {omit=#T,weight=2000}" // ignore .DS_Store files + "'^Info\\.plist$' = {omit=#T, weight=20}" // excluded automatically now, but old systems need to be told + "'^version\\.plist$' = {weight=20}" // include version.plist as resource + "'^embedded\\.provisionprofile$' = {weight=20}" // include embedded.provisionprofile as resource + "'^PkgInfo$' = {omit=#T, weight=20}" // traditionally not included + "'^.*\\.lproj/' = {optional=#T, weight=1000}" // make localizations optional + "'^Base\\.lproj/' = {weight=1010}" // ... except for Base.lproj which really isn't optional at all + "'^.*\\.lproj/locversion.plist$' = {omit=#T, weight=1100}" // exclude all locversion.plist files + "}}" + ); + } } @@ -642,6 +717,14 @@ size_t BundleDiskRep::pageSize(const SigningContext &ctx) // Takes an array of CFNumbers of errors to tolerate. // void BundleDiskRep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags) +{ + strictValidateStructure(cd, tolerated, flags); + + // now strict-check the main executable (which won't be an app-like object) + mExecRep->strictValidate(cd, tolerated, flags & ~kSecCSRestrictToAppLike); +} + +void BundleDiskRep::strictValidateStructure(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags) { // scan our metadirectory (_CodeSignature) for unwanted guests if (!(flags & kSecCSQuickCheck)) @@ -661,9 +744,6 @@ void BundleDiskRep::strictValidate(const CodeDirectory* cd, const ToleratedError if (!mAppLike) if (tolerated.find(kSecCSRestrictToAppLike) == tolerated.end()) MacOSError::throwMe(errSecCSNotAppLike); - - // now strict-check the main executable (which won't be an app-like object) - mExecRep->strictValidate(cd, tolerated, flags & ~kSecCSRestrictToAppLike); } void BundleDiskRep::recordStrictError(OSStatus error) @@ -796,8 +876,48 @@ void BundleDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef if (const char *name = CodeDirectory::canonicalSlotName(slot)) { rep->createMeta(); string path = rep->metaPath(name); + +#if TARGET_OS_OSX + // determine AFSC status if we are told to preserve compression + bool conductCompression = false; + cmpInfo cInfo; + if (this->getPreserveAFSC()) { + struct stat statBuffer; + if (stat(path.c_str(), &statBuffer) == 0) { + if (queryCompressionInfo(path.c_str(), &cInfo) == 0) { + if (cInfo.compressionType != 0 && cInfo.compressedSize > 0) { + conductCompression = true; + } + } + } + } +#endif + AutoFileDesc fd(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); fd.writeAll(CFDataGetBytePtr(data), CFDataGetLength(data)); + fd.close(); + +#if TARGET_OS_OSX + // if the original file was compressed, compress the new file after move + if (conductCompression) { + CFMutableDictionaryRef options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFStringRef val = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), cInfo.compressionType); + CFDictionarySetValue(options, kAFSCCompressionTypes, val); + CFRelease(val); + + CompressionQueueContext compressionQueue = CreateCompressionQueue(NULL, NULL, NULL, NULL, options); + + if (!CompressFile(compressionQueue, path.c_str(), NULL)) { + secinfo("bundlediskrep", "%p Failed to queue compression of file %s", this, path.c_str()); + MacOSError::throwMe(errSecCSInternalError); + } + + FinishCompressionAndCleanUp(compressionQueue); + compressionQueue = NULL; + CFRelease(options); + } +#endif + mWrittenFiles.insert(name); } else MacOSError::throwMe(errSecCSBadBundleFormat); @@ -837,8 +957,7 @@ void BundleDiskRep::Writer::flush() execWriter->flush(); purgeMetaDirectory(); } - - + // purge _CodeSignature of all left-over files from any previous signature void BundleDiskRep::Writer::purgeMetaDirectory() { @@ -855,6 +974,11 @@ void BundleDiskRep::Writer::purgeMetaDirectory() } +void BundleDiskRep::registerStapledTicket() +{ + string root = cfStringRelease(copyCanonicalPath()); + registerStapledTicketInBundle(root); +} } // end namespace CodeSigning } // end namespace Security