]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/reqinterp.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / reqinterp.cpp
index 3215df481b0a4229c590fecdb008a8753b006387..f39706502ea2b258c8a465510fb041046541bbb8 100644 (file)
@@ -24,6 +24,7 @@
 //
 // reqinterp - Requirement language (exprOp) interpreter
 //
 //
 // reqinterp - Requirement language (exprOp) interpreter
 //
+
 #include "reqinterp.h"
 #include "codesigning_dtrace.h"
 #include <Security/SecTrustSettingsPriv.h>
 #include "reqinterp.h"
 #include "codesigning_dtrace.h"
 #include <Security/SecTrustSettingsPriv.h>
 #include <sys/csr.h>
 #include <IOKit/IOKitLib.h>
 #include <IOKit/IOCFUnserialize.h>
 #include <sys/csr.h>
 #include <IOKit/IOKitLib.h>
 #include <IOKit/IOCFUnserialize.h>
+#include <libDER/oids.h>
 #include "csutilities.h"
 #include "csutilities.h"
+#include "notarization.h"
+#include "legacydevid.h"
+
+#define WAITING_FOR_LIB_AMFI_INTERFACE 1
+
+#if WAITING_FOR_LIB_AMFI_INTERFACE
+#define __mac_syscall __sandbox_ms
+#include <security/mac.h>
+
+#define AMFI_INTF_CD_HASH_LEN 20
+#endif
 
 namespace Security {
 namespace CodeSigning {
 
 namespace Security {
 namespace CodeSigning {
@@ -149,6 +162,7 @@ bool Requirement::Interpreter::eval(int depth)
                        Match match(*this);
                        return certFieldValue(key, match, cert);
                }
                        Match match(*this);
                        return certFieldValue(key, match, cert);
                }
+#if TARGET_OS_OSX
        case opCertGeneric:
                {
                        SecCertificateRef cert = mContext->cert(get<int32_t>());
        case opCertGeneric:
                {
                        SecCertificateRef cert = mContext->cert(get<int32_t>());
@@ -156,6 +170,13 @@ bool Requirement::Interpreter::eval(int depth)
                        Match match(*this);
                        return certFieldGeneric(key, match, cert);
                }
                        Match match(*this);
                        return certFieldGeneric(key, match, cert);
                }
+       case opCertFieldDate:
+               {
+                       SecCertificateRef cert = mContext->cert(get<int32_t>());
+                       string key = getString();
+                       Match match(*this);
+                       return certFieldDate(key, match, cert);
+               }
        case opCertPolicy:
                {
                        SecCertificateRef cert = mContext->cert(get<int32_t>());
        case opCertPolicy:
                {
                        SecCertificateRef cert = mContext->cert(get<int32_t>());
@@ -163,6 +184,7 @@ bool Requirement::Interpreter::eval(int depth)
                        Match match(*this);
                        return certFieldPolicy(key, match, cert);
                }
                        Match match(*this);
                        return certFieldPolicy(key, match, cert);
                }
+#endif
        case opTrustedCert:
                return trustedCert(get<int32_t>());
        case opTrustedCerts:
        case opTrustedCert:
                return trustedCert(get<int32_t>());
        case opTrustedCerts:
@@ -176,6 +198,14 @@ bool Requirement::Interpreter::eval(int depth)
                        int32_t targetPlatform = get<int32_t>();
                        return mContext->directory && mContext->directory->platform == targetPlatform;
                }
                        int32_t targetPlatform = get<int32_t>();
                        return mContext->directory && mContext->directory->platform == targetPlatform;
                }
+       case opNotarized:
+               {
+                       return isNotarized(mContext);
+               }
+       case opLegacyDevID:
+               {
+                       return meetsDeveloperIDLegacyAllowedPolicy(mContext);
+               }
        default:
                // opcode not recognized - handle generically if possible, fail otherwise
                if (op & (opGenericFalse | opGenericSkip)) {
        default:
                // opcode not recognized - handle generically if possible, fail otherwise
                if (op & (opGenericFalse | opGenericSkip)) {
@@ -204,7 +234,7 @@ bool Requirement::Interpreter::infoKeyValue(const string &key, const Match &matc
        if (mContext->info)             // we have an Info.plist
                if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key)))
                        return match(value);
        if (mContext->info)             // we have an Info.plist
                if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key)))
                        return match(value);
-       return false;
+       return match(kCFNull);
 }
 
 
 }
 
 
@@ -216,12 +246,13 @@ bool Requirement::Interpreter::entitlementValue(const string &key, const Match &
        if (mContext->entitlements)             // we have an Info.plist
                if (CFTypeRef value = CFDictionaryGetValue(mContext->entitlements, CFTempString(key)))
                        return match(value);
        if (mContext->entitlements)             // we have an Info.plist
                if (CFTypeRef value = CFDictionaryGetValue(mContext->entitlements, CFTempString(key)))
                        return match(value);
-       return false;
+       return match(kCFNull);
 }
 
 
 bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert)
 {
 }
 
 
 bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert)
 {
+#if TARGET_OS_OSX
        // no cert, no chance
        if (cert == NULL)
                return false;
        // no cert, no chance
        if (cert == NULL)
                return false;
@@ -229,32 +260,31 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma
        // a table of recognized keys for the "certificate[foo]" syntax
        static const struct CertField {
                const char *name;
        // a table of recognized keys for the "certificate[foo]" syntax
        static const struct CertField {
                const char *name;
-               const CSSM_OID *oid;
+               const DERItem *oid;
        } certFields[] = {
        } certFields[] = {
-               { "subject.C", &CSSMOID_CountryName },
-               { "subject.CN", &CSSMOID_CommonName },
-               { "subject.D", &CSSMOID_Description },
-               { "subject.L", &CSSMOID_LocalityName },
+               { "subject.C", &oidCountryName},
+               { "subject.CN", &oidCommonName },
+               { "subject.D", &oidDescription },
+               { "subject.L", &oidLocalityName },
 //             { "subject.C-L", &CSSMOID_CollectiveLocalityName },     // missing from Security.framework headers
 //             { "subject.C-L", &CSSMOID_CollectiveLocalityName },     // missing from Security.framework headers
-               { "subject.O", &CSSMOID_OrganizationName },
-               { "subject.C-O", &CSSMOID_CollectiveOrganizationName },
-               { "subject.OU", &CSSMOID_OrganizationalUnitName },
-               { "subject.C-OU", &CSSMOID_CollectiveOrganizationalUnitName },
-               { "subject.ST", &CSSMOID_StateProvinceName },
-               { "subject.C-ST", &CSSMOID_CollectiveStateProvinceName },
-               { "subject.STREET", &CSSMOID_StreetAddress },
-               { "subject.C-STREET", &CSSMOID_CollectiveStreetAddress },
-               { "subject.UID", &CSSMOID_UserID },
+               { "subject.O", &oidOrganizationName },
+               { "subject.C-O", &oidCollectiveOrganizationName },
+               { "subject.OU", &oidOrganizationalUnitName},
+               { "subject.C-OU", &oidCollectiveOrganizationalUnitName},
+               { "subject.ST", &oidStateOrProvinceName},
+               { "subject.C-ST", &oidCollectiveStateOrProvinceName },
+               { "subject.STREET", &oidStreetAddress },
+               { "subject.C-STREET", &oidCollectiveStreetAddress },
+               { "subject.UID", &oidUserId },
                { NULL, NULL }
        };
 
        // DN-component single-value match
        for (const CertField *cf = certFields; cf->name; cf++)
                if (cf->name == key) {
                { NULL, NULL }
        };
 
        // DN-component single-value match
        for (const CertField *cf = certFields; cf->name; cf++)
                if (cf->name == key) {
-                       CFRef<CFStringRef> value;
-            OSStatus rc = SecCertificateCopySubjectComponent(cert, cf->oid, &value.aref());
-                       if (rc) {
-                               secinfo("csinterp", "cert %p lookup for DN.%s failed rc=%d", cert, key.c_str(), (int)rc);
+                       CFRef<CFStringRef> value(SecCertificateCopySubjectAttributeValue(cert, (DERItem *)cf->oid));
+                       if (!value.get()) {
+                               secinfo("csinterp", "cert %p lookup for DN.%s failed", cert, key.c_str());
                                return false;
                        }
                        return match(value);
                                return false;
                        }
                        return match(value);
@@ -273,10 +303,11 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma
 
        // unrecognized key. Fail but do not abort to promote backward compatibility down the road
        secinfo("csinterp", "cert field notation \"%s\" not understood", key.c_str());
 
        // unrecognized key. Fail but do not abort to promote backward compatibility down the road
        secinfo("csinterp", "cert field notation \"%s\" not understood", key.c_str());
+#endif
        return false;
 }
 
        return false;
 }
 
-       
+#if TARGET_OS_OSX
 bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match &match, SecCertificateRef cert)
 {
        // the key is actually a (binary) OID value
 bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match &match, SecCertificateRef cert)
 {
        // the key is actually a (binary) OID value
@@ -286,7 +317,26 @@ bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match &
 
 bool Requirement::Interpreter::certFieldGeneric(const CssmOid &oid, const Match &match, SecCertificateRef cert)
 {
 
 bool Requirement::Interpreter::certFieldGeneric(const CssmOid &oid, const Match &match, SecCertificateRef cert)
 {
-       return cert && certificateHasField(cert, oid) && match(kCFBooleanTrue);
+       return cert && match(certificateHasField(cert, oid) ? (CFTypeRef)kCFBooleanTrue : (CFTypeRef)kCFNull);
+}
+
+bool Requirement::Interpreter::certFieldDate(const string &key, const Match &match, SecCertificateRef cert)
+{
+       // the key is actually a (binary) OID value
+       CssmOid oid((char *)key.data(), key.length());
+       return certFieldDate(oid, match, cert);
+}
+       
+bool Requirement::Interpreter::certFieldDate(const CssmOid &oid, const Match &match, SecCertificateRef cert)
+{
+       CFTypeRef value = cert != NULL ? certificateCopyFieldDate(cert, oid) : NULL;
+       bool matching = match(value != NULL ? value : kCFNull);
+       
+       if (value) {
+               CFRelease(value);
+       }
+       
+       return matching;
 }
 
 bool Requirement::Interpreter::certFieldPolicy(const string &key, const Match &match, SecCertificateRef cert)
 }
 
 bool Requirement::Interpreter::certFieldPolicy(const string &key, const Match &match, SecCertificateRef cert)
@@ -298,9 +348,9 @@ bool Requirement::Interpreter::certFieldPolicy(const string &key, const Match &m
 
 bool Requirement::Interpreter::certFieldPolicy(const CssmOid &oid, const Match &match, SecCertificateRef cert)
 {
 
 bool Requirement::Interpreter::certFieldPolicy(const CssmOid &oid, const Match &match, SecCertificateRef cert)
 {
-       return cert && certificateHasPolicy(cert, oid) && match(kCFBooleanTrue);
+       return cert && match(certificateHasPolicy(cert, oid) ? (CFTypeRef)kCFBooleanTrue : (CFTypeRef)kCFNull);
 }
 }
-
+#endif
 
 //
 // Check the Apple-signed condition
 
 //
 // Check the Apple-signed condition
@@ -361,10 +411,15 @@ CFArrayRef Requirement::Interpreter::getAdditionalTrustedAnchors()
 
 bool Requirement::Interpreter::appleLocalAnchored()
 {
 
 bool Requirement::Interpreter::appleLocalAnchored()
 {
-    static CFArrayRef additionalTrustedCertificates = NULL;
+       static CFArrayRef additionalTrustedCertificates = NULL;
 
 
-    if (csr_check(CSR_ALLOW_APPLE_INTERNAL))
+    if (csr_check(CSR_ALLOW_APPLE_INTERNAL)) {
         return false;
         return false;
+    }
+
+    if (mContext->forcePlatform) {
+        return true;
+    }
 
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
 
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
@@ -384,18 +439,76 @@ bool Requirement::Interpreter::appleLocalAnchored()
     return false;
 }
 
     return false;
 }
 
+#if WAITING_FOR_LIB_AMFI_INTERFACE
+// These bits are here until we get get a new build alias for libamfi-interface.
+
+#define MAC_AMFI_POLICY_NAME "AMFI"
+
+#define AMFI_SYSCALL_CDHASH_IN_TRUSTCACHE  95
+
+typedef struct amfi_cdhash_in_trustcache_ {
+    uint8_t cdhash[20];
+    uint64_t result;
+} amfi_cdhash_in_trustcache_t;
+
+static int
+__amfi_interface_cdhash_in_trustcache(const uint8_t cdhash[], uint64_t* trustcache_result)
+{
+    amfi_cdhash_in_trustcache_t args;
+    static_assert(AMFI_INTF_CD_HASH_LEN == sizeof(args.cdhash), "Error: cdhash length mismatch");
+    int err;
+    memcpy(args.cdhash, cdhash, sizeof(args.cdhash));
+    args.result = 0;
+    err = __mac_syscall(MAC_AMFI_POLICY_NAME, AMFI_SYSCALL_CDHASH_IN_TRUSTCACHE, &args);
+    if (err) {
+        err = errno;
+    }
+    *trustcache_result = args.result;
+    return err;
+}
+
+static int
+amfi_interface_cdhash_in_trustcache(const uint8_t cdhash[], size_t cdhash_len, uint64_t* trustcache_result)
+{
+    int err = EINVAL;
+
+    if (cdhash == nullptr || cdhash_len != AMFI_INTF_CD_HASH_LEN || trustcache_result == nullptr) {
+        goto lb_end;
+    }
+    *trustcache_result = 0;
+
+    err = __amfi_interface_cdhash_in_trustcache(cdhash, trustcache_result);
+
+lb_end:
+    return err;
+}
+#endif
+
+bool Requirement::Interpreter::inTrustCache()
+{
+    uint64_t result = 0;
+    CFRef<CFDataRef> cdhashRef = mContext->directory->cdhash(true);
+    const uint8_t *cdhash = CFDataGetBytePtr(cdhashRef);
+    size_t cdhash_len = CFDataGetLength(cdhashRef);
+    int err = amfi_interface_cdhash_in_trustcache(cdhash, cdhash_len, &result);
+    return (err == 0) && (result != 0);
+}
+
 bool Requirement::Interpreter::appleSigned()
 {
 bool Requirement::Interpreter::appleSigned()
 {
-    if (appleAnchored()) {
-               if (SecCertificateRef intermed = mContext->cert(-2))    // first intermediate
-                       // first intermediate common name match (exact)
-                       if (certFieldValue("subject.CN", Match(appleIntermediateCN, matchEqual), intermed)
-                                       && certFieldValue("subject.O", Match(appleIntermediateO, matchEqual), intermed))
-                               return true;
+    if (inTrustCache()) {
+        return true;
+    }
+    else if (appleAnchored()) {
+        if (SecCertificateRef intermed = mContext->cert(-2))   // first intermediate
+            // first intermediate common name match (exact)
+            if (certFieldValue("subject.CN", Match(appleIntermediateCN, matchEqual), intermed)
+                && certFieldValue("subject.O", Match(appleIntermediateO, matchEqual), intermed))
+                return true;
     } else if (appleLocalAnchored()) {
         return true;
     }
     } else if (appleLocalAnchored()) {
         return true;
     }
-       return false;
+    return false;
 }
 
 
 }
 
 
@@ -406,12 +519,8 @@ bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert, const unsign
 {
        // get certificate bytes
        if (cert) {
 {
        // get certificate bytes
        if (cert) {
-               CSSM_DATA certData;
-               MacOSError::check(SecCertificateGetData(cert, &certData));
-               
-               // verify hash
-               SHA1 hasher;
-               hasher(certData.Data, certData.Length);
+        SHA1 hasher;
+        hasher(SecCertificateGetBytePtr(cert), SecCertificateGetLength(cert));
                return hasher.verify(digest);
        }
        return false;
                return hasher.verify(digest);
        }
        return false;
@@ -469,6 +578,8 @@ bool Requirement::Interpreter::trustedCert(int slot)
 //
 SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor)
 {
 //
 SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor)
 {
+    // XXX: Not supported on embedded yet due to lack of supporting API
+#if TARGET_OS_OSX
        // the SPI input is the uppercase hex form of the SHA-1 of the certificate...
        assert(cert);
        SHA1::Digest digest;
        // the SPI input is the uppercase hex form of the SHA-1 of the certificate...
        assert(cert);
        SHA1::Digest digest;
@@ -506,6 +617,9 @@ SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef
                ::free(errors);
                MacOSError::throwMe(rc);
        }
                ::free(errors);
                MacOSError::throwMe(rc);
        }
+#else
+    return kSecTrustSettingsResultUnspecified;
+#endif
 }
 
 
 }
 
 
@@ -515,6 +629,7 @@ SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef
 Requirement::Interpreter::Match::Match(Interpreter &interp)
 {
        switch (mOp = interp.get<MatchOperation>()) {
 Requirement::Interpreter::Match::Match(Interpreter &interp)
 {
        switch (mOp = interp.get<MatchOperation>()) {
+       case matchAbsent:
        case matchExists:
                break;
        case matchEqual:
        case matchExists:
                break;
        case matchEqual:
@@ -527,6 +642,14 @@ Requirement::Interpreter::Match::Match(Interpreter &interp)
        case matchGreaterEqual:
                mValue.take(makeCFString(interp.getString()));
                break;
        case matchGreaterEqual:
                mValue.take(makeCFString(interp.getString()));
                break;
+       case matchOn:
+       case matchBefore:
+       case matchAfter:
+       case matchOnOrBefore:
+       case matchOnOrAfter: {
+               mValue.take(CFDateCreate(NULL, interp.getAbsoluteTime()));
+               break;
+       }
        default:
                // Assume this (unknown) match type has a single data argument.
                // This gives us a chance to keep the instruction stream aligned.
        default:
                // Assume this (unknown) match type has a single data argument.
                // This gives us a chance to keep the instruction stream aligned.
@@ -545,6 +668,10 @@ bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const
        if (!candidate)
                return false;
 
        if (!candidate)
                return false;
 
+       if (candidate == kCFNull) {
+               return mOp == matchAbsent; // only 'absent' matches
+       }
+       
        // interpret an array as matching alternatives (any one succeeds)
        if (CFGetTypeID(candidate) == CFArrayGetTypeID()) {
                CFArrayRef array = CFArrayRef(candidate);
        // interpret an array as matching alternatives (any one succeeds)
        if (CFGetTypeID(candidate) == CFArrayGetTypeID()) {
                CFArrayRef array = CFArrayRef(candidate);
@@ -555,31 +682,33 @@ bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const
        }
 
        switch (mOp) {
        }
 
        switch (mOp) {
+       case matchAbsent:
+               return false;           // it exists, so it cannot be absent
        case matchExists:               // anything but NULL and boolean false "exists"
                return !CFEqual(candidate, kCFBooleanFalse);
        case matchEqual:                // equality works for all CF types
                return CFEqual(candidate, mValue);
        case matchContains:
        case matchExists:               // anything but NULL and boolean false "exists"
                return !CFEqual(candidate, kCFBooleanFalse);
        case matchEqual:                // equality works for all CF types
                return CFEqual(candidate, mValue);
        case matchContains:
-               if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
+               if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) {
                        CFStringRef value = CFStringRef(candidate);
                        CFStringRef value = CFStringRef(candidate);
-                       if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(value)), 0, NULL))
+                       if (CFStringFindWithOptions(value, cfStringValue(), CFRangeMake(0, CFStringGetLength(value)), 0, NULL))
                                return true;
                }
                return false;
        case matchBeginsWith:
                                return true;
                }
                return false;
        case matchBeginsWith:
-               if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
+               if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) {
                        CFStringRef value = CFStringRef(candidate);
                        CFStringRef value = CFStringRef(candidate);
-                       if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(mValue)), 0, NULL))
+                       if (CFStringFindWithOptions(value, cfStringValue(), CFRangeMake(0, CFStringGetLength(cfStringValue())), 0, NULL))
                                return true;
                }
                return false;
        case matchEndsWith:
                                return true;
                }
                return false;
        case matchEndsWith:
-               if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
+               if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) {
                        CFStringRef value = CFStringRef(candidate);
                        CFStringRef value = CFStringRef(candidate);
-                       CFIndex matchLength = CFStringGetLength(mValue);
+                       CFIndex matchLength = CFStringGetLength(cfStringValue());
                        CFIndex start = CFStringGetLength(value) - matchLength;
                        if (start >= 0)
                        CFIndex start = CFStringGetLength(value) - matchLength;
                        if (start >= 0)
-                               if (CFStringFindWithOptions(value, mValue, CFRangeMake(start, matchLength), 0, NULL))
+                               if (CFStringFindWithOptions(value, cfStringValue(), CFRangeMake(start, matchLength), 0, NULL))
                                        return true;
                }
                return false;
                                        return true;
                }
                return false;
@@ -591,6 +720,26 @@ bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const
                return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, false);
        case matchGreaterEqual:
                return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, false);
                return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, false);
        case matchGreaterEqual:
                return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, false);
+       case matchOn:
+       case matchBefore:
+       case matchAfter:
+       case matchOnOrBefore:
+       case matchOnOrAfter: {
+               if (!isDateValue() || CFGetTypeID(candidate) != CFDateGetTypeID()) {
+                       return false;
+               }
+               
+               CFComparisonResult res = CFDateCompare((CFDateRef)candidate, cfDateValue(), NULL);
+
+               switch (mOp) {
+                       case matchOn: return res == 0;
+                       case matchBefore: return res < 0;
+                       case matchAfter: return res > 0;
+                       case matchOnOrBefore: return res <= 0;
+                       case matchOnOrAfter: return res >= 0;
+                       default: abort();
+               }
+       }
        default:
                // unrecognized match types can never match
                return false;
        default:
                // unrecognized match types can never match
                return false;
@@ -601,9 +750,9 @@ bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const
 bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate, CFStringCompareFlags flags,
        CFComparisonResult outcome, bool negate) const
 {
 bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate, CFStringCompareFlags flags,
        CFComparisonResult outcome, bool negate) const
 {
-       if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
+       if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) {
                CFStringRef value = CFStringRef(candidate);
                CFStringRef value = CFStringRef(candidate);
-               if ((CFStringCompare(value, mValue, flags) == outcome) == negate)
+               if ((CFStringCompare(value, cfStringValue(), flags) == outcome) == negate)
                        return true;
        }
        return false;
                        return true;
        }
        return false;