-
Notifications
You must be signed in to change notification settings - Fork 37
Store sync metadata keychain info on per-app basis #521
Conversation
9e5ac24
to
5a52b82
Compare
This is ready for review. Apologies for the spam earlier. |
src/impl/apple/keychain_helper.cpp
Outdated
@@ -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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
src/impl/apple/keychain_helper.cpp
Outdated
#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) |
There was a problem hiding this comment.
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
?
src/impl/apple/keychain_helper.cpp
Outdated
{ | ||
const CFStringPtr account = convert_string("metadata"); | ||
CFStringPtr default_service = convert_string("io.realm.sync.keychain"); | ||
auto service = adoptCF(CFBundleGetIdentifier(CFBundleGetMainBundle())); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
src/impl/apple/keychain_helper.cpp
Outdated
|
||
std::vector<char> metadata_realm_encryption_key(bool check_legacy_service) | ||
{ | ||
const CFStringPtr account = convert_string("metadata"); |
There was a problem hiding this comment.
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.
91a6f28
to
da5d3ed
Compare
src/impl/apple/keychain_helper.cpp
Outdated
std::vector<char> metadata_realm_encryption_key(bool check_legacy_service) | ||
{ | ||
CFStringRef account = CFSTR("metadata"); | ||
CFStringRef default_service = CFSTR("io.realm.sync.keychain"); |
There was a problem hiding this comment.
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.
src/impl/apple/keychain_helper.cpp
Outdated
} | ||
|
||
// Try retrieving the key. | ||
CFStringRef service = managed_service.get(); |
There was a problem hiding this comment.
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.
src/impl/apple/keychain_helper.cpp
Outdated
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. |
There was a problem hiding this comment.
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"?
src/impl/apple/keychain_helper.cpp
Outdated
} 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. |
There was a problem hiding this comment.
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.
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. |
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. |
src/impl/apple/keychain_helper.cpp
Outdated
// 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); |
There was a problem hiding this comment.
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.
src/impl/apple/keychain_helper.cpp
Outdated
@@ -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. |
There was a problem hiding this comment.
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;
}
There was a problem hiding this comment.
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.
👍 |
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.