SOSAccountRings.c   [plain text]


//
//  SOSAccountRings.c
//  sec
//

#include "SOSAccountPriv.h"
#include <Security/SecureObjectSync/SOSTransport.h>
#include <Security/SecureObjectSync/SOSRingUtils.h>

//
// MARK: Ring management
//

const CFStringRef kSOSRingCircleV2              = CFSTR("Ring-CircleV2");
const CFStringRef kSOSRingKeychainV0            = CFSTR("Ring-KeychainV0");
const CFStringRef kSOSRingPCSHyperion           = CFSTR("Ring-PCS-Photos");
const CFStringRef kSOSRingPCSBladerunner        = CFSTR("Ring-PCS-iCloudDrive");
const CFStringRef kSOSRingPCSLiverpool          = CFSTR("Ring-PCS-CloudKit");
const CFStringRef kSOSRingPCSEscrow             = CFSTR("Ring-PCS-Escrow");
const CFStringRef kSOSRingPCSPianoMover         = CFSTR("Ring-PCS-Maildrop");
const CFStringRef kSOSRingPCSNotes              = CFSTR("Ring-PCS-Notes");
const CFStringRef kSOSRingPCSFeldspar           = CFSTR("Ring-PCS-Feldspar");
const CFStringRef kSOSRingAppleTV               = CFSTR("Ring-AppleTV");
const CFStringRef kSOSRingHomeKit               = CFSTR("Ring-HomeKit");
const CFStringRef kSOSRingWifi                  = CFSTR("Ring-WiFi");
const CFStringRef kSOSRingPasswords             = CFSTR("Ring-Passwords");
const CFStringRef kSOSRingCreditCards           = CFSTR("Ring-CreditCards");
const CFStringRef kSOSRingiCloudIdentity        = CFSTR("Ring-iCloudIdentity");
const CFStringRef kSOSRingOtherSyncable         = CFSTR("Ring-OtherSyncable");


static CFSetRef allCurrentRings(void) {
    static dispatch_once_t dot;
    static CFMutableSetRef allRings = NULL;
    dispatch_once(&dot, ^{
        allRings = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
        CFSetAddValue(allRings, kSOSRingCircleV2);
        CFSetAddValue(allRings, kSOSRingKeychainV0);
        CFSetAddValue(allRings, kSOSRingPCSHyperion);
        CFSetAddValue(allRings, kSOSRingPCSBladerunner);
        CFSetAddValue(allRings, kSOSRingPCSLiverpool);
        CFSetAddValue(allRings, kSOSRingPCSEscrow);
        CFSetAddValue(allRings, kSOSRingPCSPianoMover);
        CFSetAddValue(allRings, kSOSRingPCSNotes);
        CFSetAddValue(allRings, kSOSRingPCSFeldspar);
        CFSetAddValue(allRings, kSOSRingAppleTV);
        CFSetAddValue(allRings, kSOSRingHomeKit);
        CFSetAddValue(allRings, kSOSRingWifi);
        CFSetAddValue(allRings, kSOSRingPasswords);
        CFSetAddValue(allRings, kSOSRingCreditCards);
        CFSetAddValue(allRings, kSOSRingiCloudIdentity);
        CFSetAddValue(allRings, kSOSRingOtherSyncable);
    });
    return allRings;
}

typedef struct ringDef_t {
    CFStringRef name;
    SOSRingType ringType;
    bool dropWhenLeaving;
} ringDef, *ringDefPtr;

static ringDefPtr getRingDef(CFStringRef ringName) {
    static ringDef retval;

    // Defaults
    retval.name = ringName;
    retval.dropWhenLeaving = true;
    retval.ringType = kSOSRingEntropyKeyed;


    if(CFEqual(ringName, kSOSRingKeychainV0) == 0) {
    } else if(CFEqual(ringName, kSOSRingPCSHyperion) == 0) {
    } else if(CFEqual(ringName, kSOSRingPCSBladerunner) == 0) {
    } else if(CFEqual(ringName, kSOSRingKeychainV0) == 0) {
    } else if(CFEqual(ringName, kSOSRingKeychainV0) == 0) {
    } else if(CFEqual(ringName, kSOSRingKeychainV0) == 0) {
    } else if(CFEqual(ringName, kSOSRingKeychainV0) == 0) {
    } else if(CFEqual(ringName, kSOSRingCircleV2) == 0) {
        retval.ringType = kSOSRingBase;
        retval.dropWhenLeaving = false;
    } else return NULL;
    return &retval;
}

#if 0
static bool isRingKnown(CFStringRef ringname) {
    if(getRingDef(ringname) != NULL) return true;
    secnotice("rings","Not a known ring");
    return false;
}
#endif

static inline void SOSAccountRingForEach(void (^action)(CFStringRef ringname)) {
    CFSetRef allRings = allCurrentRings();
    CFSetForEach(allRings, ^(const void *value) {
        CFStringRef ringName = (CFStringRef) value;
            action(ringName);
    });
}


__unused static inline void SOSAccountRingForEachRingMatching(SOSAccountRef a, void (^action)(SOSRingRef ring), bool (^condition)(SOSRingRef ring)) {
    CFSetRef allRings = allCurrentRings();
    CFSetForEach(allRings, ^(const void *value) {
        CFStringRef ringName = (CFStringRef) value;
        SOSRingRef ring = SOSAccountGetRing(a, ringName, NULL);
        if (condition(ring))
            action(ring);
    });
}

CFMutableDictionaryRef SOSAccountGetRings(SOSAccountRef a, CFErrorRef *error){
    return a->trusted_rings;
}

CFMutableDictionaryRef SOSAccountGetBackups(SOSAccountRef a, CFErrorRef *error){
    return a->backups;
}

SOSRingRef SOSAccountGetRing(SOSAccountRef a, CFStringRef ringName, CFErrorRef *error) {
    CFTypeRef entry = CFDictionaryGetValue(a->trusted_rings, ringName);
    require_action_quiet(entry, fail,
                         SOSCreateError(kSOSErrorNoRing, CFSTR("No Ring found"), NULL, error));
    return (SOSRingRef) entry;

fail:
    return NULL;
}

CFStringRef SOSAccountGetMyPeerID(SOSAccountRef a) {
    SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(a);
    require_quiet(fpi, errOut);
    SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
    require_quiet(pi, errOut);
    return SOSPeerInfoGetPeerID(pi);
errOut:
    return NULL;
}

SOSRingRef SOSAccountRingCreateForName(SOSAccountRef a, CFStringRef ringName, CFErrorRef *error) {
    ringDefPtr rdef = getRingDef(ringName);
    if(!rdef) return NULL;
    SOSRingRef retval = SOSRingCreate(rdef->name, SOSAccountGetMyPeerID(a), rdef->ringType, error);
    return retval;
}

bool SOSAccountCheckForRings(SOSAccountRef a, CFErrorRef *error) {
    bool retval = isDictionary(a->trusted_rings);
    if(!retval) SOSCreateError(kSOSErrorNotReady, CFSTR("Rings not present"), NULL, error);
    return retval;
}

bool SOSAccountEnsureRings(SOSAccountRef a, CFErrorRef *error) {
    bool status = false;

    if(!a->trusted_rings) {
        a->trusted_rings = CFDictionaryCreateMutableForCFTypes(NULL);
    }

    require_quiet(SOSAccountEnsureFullPeerAvailable(a, error), errOut);

    SOSAccountRingForEach(^(CFStringRef ringname) {
        SOSRingRef ring = SOSAccountGetRing(a, ringname, NULL);
        if(!ring) {
            ring = SOSAccountRingCreateForName(a, ringname, error);
            if(ring) {
                CFDictionaryAddValue(a->trusted_rings, ringname, ring);
                SOSUpdateKeyInterest();
            }
            CFReleaseNull(ring);
        }
    });

    status = true;
errOut:
    return status;
}

bool SOSAccountUpdateRingFromRemote(SOSAccountRef account, SOSRingRef newRing, CFErrorRef *error) {
    return SOSAccountHandleUpdateRing(account, newRing, false, error);
}

bool SOSAccountUpdateRing(SOSAccountRef account, SOSRingRef newRing, CFErrorRef *error) {
    return SOSAccountHandleUpdateRing(account, newRing, true, error);
}

bool SOSAccountModifyRing(SOSAccountRef account, CFStringRef ringName, CFErrorRef* error, bool (^action)(SOSRingRef ring)) {
    bool success = false;

    SOSRingRef ring = SOSAccountGetRing(account, ringName, error);
    require_action_quiet(ring, fail, SOSErrorCreate(kSOSErrorNoRing, error, NULL, CFSTR("No Ring to get peer key from")));

    ring = SOSRingCopyRing(ring, error);
    require_quiet(ring, fail);

    success = true;
    require_quiet(action(ring), fail);

    success = SOSAccountUpdateRing(account, ring, error);

fail:
    CFReleaseSafe(ring);
    return success;
}

CFDataRef SOSAccountRingGetPayload(SOSAccountRef account, CFStringRef ringName, CFErrorRef *error) {
    SOSRingRef ring = SOSAccountGetRing(account, ringName, error);
    return SOSRingGetPayload(ring, error);
}

SOSRingRef SOSAccountRingCopyWithPayload(SOSAccountRef account, CFStringRef ringName, CFDataRef payload, CFErrorRef *error) {
    SOSRingRef ring = SOSAccountGetRing(account, ringName, error);
    require_quiet(ring, errOut);
    SOSRingRef new = SOSRingCopyRing(ring, error);
    require_quiet(new, errOut);
    CFDataRef oldpayload = SOSRingGetPayload(ring, error);
    require_quiet(!CFEqualSafe(oldpayload, payload), errOut);
    require_quiet(SOSRingSetPayload(new, NULL, payload, account->my_identity, error), errOut);

errOut:
    return NULL;
}