SOSRingConcordanceTrust.c [plain text]
#include <AssertMacros.h>
#include "keychain/SecureObjectSync/SOSInternal.h"
#include "keychain/SecureObjectSync/SOSPeer.h"
#include "keychain/SecureObjectSync/SOSPeerInfoInternal.h"
#include "keychain/SecureObjectSync/SOSPeerInfoCollections.h"
#include "keychain/SecureObjectSync/SOSCircle.h"
#include <Security/SecFramework.h>
#include <Security/SecKey.h>
#include <Security/SecKeyPriv.h>
#include <CoreFoundation/CoreFoundation.h>
#include <utilities/SecCFWrappers.h>
#include <corecrypto/ccder.h>
#include <corecrypto/ccdigest.h>
#include <corecrypto/ccsha2.h>
#include <utilities/der_plist.h>
#include <utilities/der_plist_internal.h>
#include <corecrypto/ccder.h>
#include <utilities/der_date.h>
#include <stdlib.h>
#include <assert.h>
#include "SOSRing.h"
#include "SOSRingUtils.h"
static inline CFDictionaryRef SOSPeerInfoDictionaryCreate(CFSetRef peers) {
size_t n = CFSetGetCount(peers);
SOSPeerInfoRef peerInfos[n];
CFStringRef peerIDs[n];
CFSetGetValues(peers, (const void **) peerInfos);
for(size_t i = 0; i < n; i++) peerIDs[i] = SOSPeerInfoGetPeerID(peerInfos[i]);
return CFDictionaryCreate(NULL, (const void **)peerIDs, (const void **)peerInfos, n, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
static inline SOSConcordanceStatus CheckPeerStatus(CFStringRef peerID, SOSPeerInfoRef peer, SOSRingRef ring, SecKeyRef userPub, CFErrorRef *error) {
SOSConcordanceStatus result = kSOSConcordanceNoPeer;
SecKeyRef pubKey = NULL;
require_action_quiet(peer, exit, result = kSOSConcordanceNoPeer);
pubKey = SOSPeerInfoCopyPubKey(peer, error);
require_quiet(pubKey, exit);
require_action_quiet(SOSRingHasPeerID(ring, peerID), exit, result = kSOSConcordanceNoPeer);
require_action_quiet(SOSPeerInfoApplicationVerify(peer, userPub, NULL), exit, result = kSOSConcordanceNoPeer);
require_action_quiet(SOSRingVerifySignatureExists(ring, pubKey, error), exit, result = kSOSConcordanceNoPeerSig);
require_action_quiet(SOSRingVerify(ring, pubKey, error), exit, result = kSOSConcordanceBadPeerSig);
result = kSOSConcordanceTrusted;
exit:
CFReleaseNull(pubKey);
return result;
}
static inline SOSConcordanceStatus CombineStatus(SOSConcordanceStatus status1, SOSConcordanceStatus status2)
{
if (status1 == kSOSConcordanceTrusted || status2 == kSOSConcordanceTrusted)
return kSOSConcordanceTrusted;
if (status1 == kSOSConcordanceBadPeerSig || status2 == kSOSConcordanceBadPeerSig)
return kSOSConcordanceBadPeerSig;
if (status1 == kSOSConcordanceNoPeerSig || status2 == kSOSConcordanceNoPeerSig)
return kSOSConcordanceNoPeerSig;
return status1;
}
SOSConcordanceStatus GetSignersStatus(CFSetRef peers, SOSRingRef signersRing, SOSRingRef statusRing,
SecKeyRef userPubkey, CFStringRef excludePeerID, CFErrorRef *error) {
CFDictionaryRef ringPeerInfos = SOSPeerInfoDictionaryCreate(peers);
__block SOSConcordanceStatus status = kSOSConcordanceNoPeer;
SOSRingForEachPeerID(signersRing, ^(CFStringRef peerID) {
SOSPeerInfoRef pi = (SOSPeerInfoRef) CFDictionaryGetValue(ringPeerInfos, peerID);
SOSConcordanceStatus peerStatus = CheckPeerStatus(peerID, pi, statusRing, userPubkey, error);
secnotice("ring", "concordance-signer-status: %@ -> %d", peerID, peerStatus);
if (peerStatus == kSOSConcordanceNoPeerSig &&
(CFEqualSafe(SOSPeerInfoGetPeerID(pi), excludePeerID) || SOSPeerInfoIsCloudIdentity(pi)))
peerStatus = kSOSConcordanceNoPeer;
status = CombineStatus(status, peerStatus);
});
return status;
}
SOSConcordanceStatus GetSignersStatus_Transitive(CFSetRef peers, SOSRingRef signersRing, SOSRingRef statusRing,
SecKeyRef userPubkey, CFStringRef excludePeerID, CFErrorRef *error) {
__block SOSConcordanceStatus status = kSOSConcordanceNoPeer;
CFSetForEach(peers, ^(const void *value) {
SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
if(SOSRingHasPeerWithID(statusRing, peerID, NULL)) {
SOSConcordanceStatus peerStatus = CheckPeerStatus(peerID, pi, statusRing, userPubkey, error);
if (peerStatus == kSOSConcordanceNoPeerSig &&
(CFEqualSafe(SOSPeerInfoGetPeerID(pi), excludePeerID) || SOSPeerInfoIsCloudIdentity(pi)))
peerStatus = kSOSConcordanceNoPeer;
status = CombineStatus(status, peerStatus);
}
});
return status;
}
SOSConcordanceStatus SOSRingUserKeyConcordanceTrust(SOSFullPeerInfoRef me, CFSetRef peers, SOSRingRef knownRing, SOSRingRef proposedRing,
SecKeyRef knownPubkey, SecKeyRef userPubkey,
CFStringRef excludePeerID, CFErrorRef *error) {
if(userPubkey == NULL) {
SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Concordance with no public key"), NULL, error);
return kSOSConcordanceNoUserKey;
}
if (SOSRingIsEmpty_Internal(proposedRing)) {
return kSOSConcordanceTrusted;
}
if(!SOSRingVerifySignatureExists(proposedRing, userPubkey, error)) {
SOSCreateError(kSOSErrorBadSignature, CFSTR("No public signature"), (error != NULL) ? *error : NULL, error);
return kSOSConcordanceNoUserSig;
}
if(!SOSRingVerify(proposedRing, userPubkey, error)) {
SOSCreateError(kSOSErrorBadSignature, CFSTR("Bad public signature"), (error != NULL) ? *error : NULL, error);
return kSOSConcordanceBadUserSig;
}
if (SOSRingIsEmpty_Internal(knownRing) || SOSRingIsOffering_Internal(proposedRing)) {
return GetSignersStatus(peers, proposedRing, proposedRing, userPubkey, NULL, error);
}
if(SOSRingIsOlderGeneration(proposedRing, knownRing)) {
SOSCreateError(kSOSErrorReplay, CFSTR("Bad generation"), NULL, error);
return kSOSConcordanceGenOld;
}
if(knownPubkey == NULL) knownPubkey = userPubkey;
if(!SOSRingVerify(knownRing, knownPubkey, error)) knownPubkey = userPubkey;
return GetSignersStatus(peers, knownRing, proposedRing, knownPubkey, CFSTR("novalue"), error);
}
SOSConcordanceStatus SOSRingPeerKeyConcordanceTrust(SOSFullPeerInfoRef me, CFSetRef peers, SOSRingRef knownRing, SOSRingRef proposedRing,
__unused SecKeyRef knownPubkey, SecKeyRef userPubkey,
CFStringRef excludePeerID, CFErrorRef *error) {
if(userPubkey == NULL) {
SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Concordance with no public key - need to validate application"), NULL, error);
return kSOSConcordanceNoUserKey;
}
if (SOSRingIsEmpty_Internal(proposedRing)) {
secnotice("ring", "ring empty -> trusted");
return kSOSConcordanceTrusted;
}
if (SOSRingIsEmpty_Internal(knownRing) || SOSRingIsOffering_Internal(proposedRing)) {
return GetSignersStatus(peers, proposedRing, proposedRing, userPubkey, NULL, error);
}
if(SOSRingIsOlderGeneration(proposedRing, knownRing)) {
SOSCreateError(kSOSErrorReplay, CFSTR("Bad generation"), NULL, error);
return kSOSConcordanceGenOld;
}
return GetSignersStatus(peers, knownRing, proposedRing, userPubkey, excludePeerID, error);
}