+static string trim(string s, char delimiter, string suffix)
+{
+ s = trim(s, delimiter);
+ int preLength = s.length() - suffix.length();
+ if (preLength > 0 && s.substr(preLength) == suffix)
+ s = s.substr(0, preLength);
+ return s;
+}
+
+OSStatus CodeSignatures::matchSignedClientToLegacyACL(Process &process,
+ SecCodeRef code, const OSXVerifier &verifier, const AclValidationContext &context)
+{
+ //
+ // Check whether we seem to be matching a legacy .Mac ACL against a member of the .Mac group
+ //
+ if (SecurityServerAcl::looksLikeLegacyDotMac(context)) {
+ Server::active().longTermActivity();
+ CFRef<SecRequirementRef> dotmac;
+ MacOSError::check(SecRequirementCreateGroup(CFSTR("dot-mac"), NULL, kSecCSDefaultFlags, &dotmac.aref()));
+ if (SecCodeCheckValidity(code, kSecCSDefaultFlags, dotmac) == noErr) {
+ secdebug("codesign", "client is a dot-mac application; update the ACL accordingly");
+
+ // create a suitable AclSubject (this is the above-the-API-line way)
+ CFRef<CFDataRef> reqdata;
+ MacOSError::check(SecRequirementCopyData(dotmac, kSecCSDefaultFlags, &reqdata.aref()));
+ RefPointer<CodeSignatureAclSubject> subject = new CodeSignatureAclSubject(NULL, "group://dot-mac");
+ subject->add((const BlobCore *)CFDataGetBytePtr(reqdata));
+
+ // add it to the ACL and pass the access check (we just quite literally did it above)
+ SecurityServerAcl::addToStandardACL(context, subject);
+ return noErr;
+ }
+ }
+
+ //
+ // Get best names for the ACL (legacy) subject and the (signed) client
+ //
+ CFRef<CFDictionaryRef> info;
+ MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
+ CFStringRef signingIdentity = CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoIdentifier));
+ if (!signingIdentity) // unsigned
+ return errSecCSUnsigned;
+
+ string bundleName; // client
+ if (CFDictionaryRef infoList = CFDictionaryRef(CFDictionaryGetValue(info, kSecCodeInfoPList)))
+ if (CFStringRef name = CFStringRef(CFDictionaryGetValue(infoList, kCFBundleNameKey)))
+ bundleName = trim(cfString(name), '.');
+ if (bundleName.empty()) // fall back to signing identifier
+ bundleName = trim(cfString(signingIdentity), '.');
+
+ string aclName = trim(verifier.path(), '/', ".app"); // ACL
+
+ secdebug("codesign", "matching signed client \"%s\" against legacy ACL \"%s\"",
+ bundleName.c_str(), aclName.c_str());
+
+ //
+ // Check whether we're matching a signed APPLE application against a legacy ACL by the same name
+ //
+ if (bundleName == aclName) {
+ const unsigned char reqData[] = { // "anchor apple", version 1 blob, embedded here
+ 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03
+ };
+ CFRef<SecRequirementRef> apple;
+ MacOSError::check(SecRequirementCreateWithData(CFTempData(reqData, sizeof(reqData)),
+ kSecCSDefaultFlags, &apple.aref()));
+ Server::active().longTermActivity();
+ switch (OSStatus rc = SecCodeCheckValidity(code, kSecCSDefaultFlags, apple)) {
+ case noErr:
+ {
+ secdebug("codesign", "withstands strict scrutiny; quietly adding new ACL");
+ RefPointer<OSXCode> wrap = new OSXCodeWrap(code);
+ RefPointer<AclSubject> subject = new CodeSignatureAclSubject(OSXVerifier(wrap));
+ SecurityServerAcl::addToStandardACL(context, subject);
+ return noErr;
+ }
+ default:
+ secdebug("codesign", "validation fails with rc=%d, rejecting", int32_t(rc));
+ return rc;
+ }
+ secdebug("codesign", "does not withstand strict scrutiny; ask the user");
+ QueryCodeCheck query;
+ query.inferHints(process);
+ if (!query(verifier.path().c_str())) {
+ secdebug("codesign", "user declined equivalence: cancel the access");
+ CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
+ }
+ RefPointer<OSXCode> wrap = new OSXCodeWrap(code);
+ RefPointer<AclSubject> subject = new CodeSignatureAclSubject(OSXVerifier(wrap));
+ SecurityServerAcl::addToStandardACL(context, subject);
+ return noErr;
+ }
+
+ // not close enough to even ask - this can't match
+ return errSecCSReqFailed;
+}
+
+
+//
+// Perform legacy hash verification.
+// This is the pre-Leopard (Tiger, Panther) code path. Here we only have legacy hashes
+// (called, confusingly, "signatures"), which we're matching against suitably computed
+// "signatures" (hashes) on the requesting application. We consult the CodeEquivalenceDatabase
+// in a doomed attempt to track changes made to applications through updates, and issue
+// equivalence dialogs to users if we have a name match (but hash mismatch). That's all
+// there was before Code Signing; and that's what you'll continue to get if the requesting
+// application is unsigned. Until we throw the whole mess out altogether, hopefully by
+// the Next Big Cat After Leopard.
+//
+bool CodeSignatures::verifyLegacy(Process &process, const CssmData &signature, string path)
+{
+ // First of all, if the signature directly matches the client's code, we're obviously fine