Skip to content
This repository has been archived by the owner on Nov 8, 2021. It is now read-only.

Store sync metadata keychain info on per-app basis #521

Merged
merged 8 commits into from
Aug 28, 2017

Conversation

austinzheng
Copy link
Contributor

@austinzheng austinzheng commented Aug 21, 2017

It's not clear how to test this change in isolation, since it touches Apple's Keychain stuff. The binding-level integration tests continue to work unhindered.

@austinzheng
Copy link
Contributor Author

This is ready for review. Apologies for the spam earlier.

@@ -50,66 +51,91 @@ CFPtr<CFStringRef> convert_string(const std::string& string)
return result;
}

CFPtr<CFMutableDictionaryRef> build_search_dictionary(const std::string& account, const std::string& service,
CFPtr<CFMutableDictionaryRef> build_search_dictionary(const CFStringPtr& account,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking smart pointers by const reference is a little weird. Typically you'd take a raw pointer to indicate that you're merely borrowing a reference, rather than doing anything with the ownership of the object. For that reason I'd suggest just using CFStringRef for the string arguments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally forgot about this pattern.

#endif
return d;
}

std::vector<char> metadata_realm_encryption_key()
/// Get the encryption key for a given service.
util::Optional<std::vector<char>> try_getting_key(const CFStringPtr& account, const CFStringPtr& service)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this just be get_key?

{
const CFStringPtr account = convert_string("metadata");
CFStringPtr default_service = convert_string("io.realm.sync.keychain");
auto service = adoptCF(CFBundleGetIdentifier(CFBundleGetMainBundle()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want to use something beyond just the bare bundle identifier as the keychain item name to avoid conflicting with anything created by the app itself. Maybe something like "com.example.app - Realm Sync Metadata Key"?

adoptCF isn't appropriate here. CFBundleGetIdentifier does not return an owned object, only a borrowed object. Adopting the returned reference will result in a use after free. You want retainCF instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I need to review the CF ownership conventions before writing any more CF code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CF documentation has a section on ownership that's relatively readable. Many CF APIs describe their return values as using one of the two rules described within (create vs get).


std::vector<char> metadata_realm_encryption_key(bool check_legacy_service)
{
const CFStringPtr account = convert_string("metadata");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These constants should use the CFSTR macro instead, so they end up in the __cfstring section of the binary rather than being allocated on the heap at runtime. There'd be no reason to use CFPtr with the resulting strings, either, as they'd have an eternal lifetime.

@austinzheng austinzheng force-pushed the az/set-custom-keychain-service branch from 91a6f28 to da5d3ed Compare August 24, 2017 00:57
std::vector<char> metadata_realm_encryption_key(bool check_legacy_service)
{
CFStringRef account = CFSTR("metadata");
CFStringRef default_service = CFSTR("io.realm.sync.keychain");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think legacy_service would better reflect how the relationship between this and the check_legacy_service argument.

}

// Try retrieving the key.
CFStringRef service = managed_service.get();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be inclined to ditch this local and just add the .get() calls in the handful of places it is necessary. It's effectively free to call, and would avoid the need for the slightly odd managed_ prefix on the variable.

if (auto existing_key = get_key(account, service)) {
return *existing_key;
} else if (check_legacy_service) {
// See if there's a key in the old shared keychain.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"See if there's a key stored using the legacy shared keychain item"?

} else if (check_legacy_service) {
// See if there's a key in the old shared keychain.
if (auto existing_legacy_key = get_key(account, default_service)) {
// If so, copy it to the per-app keychain before returning it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The keychain isn't per application, the item name is.

@austinzheng
Copy link
Contributor Author

Thank you for the review! I will hold off on merging this until the Cocoa PR has been updated to compile and the integration tests have been run successfully.

@austinzheng austinzheng changed the title Allow the keychain service to be customized when configuring sync met… Store sync metadata keychain info on per-app basis Aug 26, 2017
@austinzheng
Copy link
Contributor Author

austinzheng commented Aug 26, 2017

There was an issue that caused the Cocoa ROS integration tests to fail. I created realm/realm-core#2821 to track a fix and worked around it in my latest change.

// FIXME: Get rid of this manual retain once we fix CFPtr to accept null.
CFRetain(bundle_id);
service = adoptCF(CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ - Realm Sync Metadata Key"), bundle_id));
CFRelease(bundle_id);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no reason to retain / release bundle_id here.

@@ -119,10 +119,8 @@ std::vector<char> metadata_realm_encryption_key(bool check_legacy_service)
service = retainCF(legacy_service);
check_legacy_service = false;
} else {
// FIXME: Get rid of this manual retain once we fix CFPtr to accept null.
CFRetain(bundle_id);
// FIXME: Collapse the null check on bundle_id once CFPtr is fixed to accept null pointers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure what you mean by "collapse the null check" here. We could write this like so, though:

if (CFStringRef bundle_id = CFBundleGetIdentifier(CFBundleGetMainBundle()))
    service = adoptCF(CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ - Realm Sync Metadata Key"), bundle_id));
else {
    service = retainCF(legacy_service);
    check_legacy_service = false;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't actually need to use retainCF for the temporary bundle_id, so your suggestion addresses the problem and there isn't anything we need to do in the future.

@bdash
Copy link
Contributor

bdash commented Aug 28, 2017

👍

@austinzheng austinzheng merged commit 54a98b2 into master Aug 28, 2017
@austinzheng austinzheng deleted the az/set-custom-keychain-service branch August 28, 2017 22:43
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants