[appletls]: Use SecIdentityCreate() to avoid using the Mac keychain.#4671
[appletls]: Use SecIdentityCreate() to avoid using the Mac keychain.#4671
Conversation
| return SecKeyChain.FindIdentity (secCert, true); | ||
| } | ||
| #else | ||
| return null; |
There was a problem hiding this comment.
Are internal users of this API going to check for null or could this cause NRE?
There was a problem hiding this comment.
Null is a valid return value - the caller is responsible for throwing an exception.
| CFObject.CFRetain (handle); | ||
| } | ||
|
|
||
| /* |
There was a problem hiding this comment.
Should ^ ctor set this.owner explicitly?
There was a problem hiding this comment.
Hmm, we could also make owner the object that's CFRelease()d and set it to IntPtr.Zero if we don't "own" it.
| if (owner != IntPtr.Zero) { | ||
| CFObject.CFRelease (owner); | ||
| owner = handle = IntPtr.Zero; | ||
| } else if (handle != IntPtr.Zero) { |
There was a problem hiding this comment.
It seems a tad weird to have this else in the Dispose. Not necessary wrong, but not expected.
| { | ||
| var descriptorHandle = CFString.Create (descriptor); | ||
| if (descriptorHandle == null) | ||
| throw new InvalidOperationException (); |
There was a problem hiding this comment.
Is this the right exception?
There was a problem hiding this comment.
Maybe OutOfMemoryException()?
There was a problem hiding this comment.
If that method is used (I did not see it used in the diff) then it seems this should be a null check on descriptor to throw an ArgumentNullException
|
|
||
| return new SecAccess (accessRef, true); | ||
| } finally { | ||
| descriptorHandle.Dispose (); |
There was a problem hiding this comment.
Could we use a using block here instead?
There was a problem hiding this comment.
Bad idea, but we're incorrectly doing it in so many other places. We'd have to make all those CFString.Create() etc. functions throw instead of possibly returning null.
There was a problem hiding this comment.
The idea is not to change CFString.Create() but keep the code as it but replace try/finally with using
|
|
||
| IntPtr result = IntPtr.Zero; | ||
| SecStatusCode status = SecStatusCode.Success; | ||
| for (var test = 0; test < 13; test++) { |
There was a problem hiding this comment.
Oops - that entire loop must be removed!
| using (var cert = new SecCertificate (certificate)) { | ||
| var identity = SecIdentityCreate (IntPtr.Zero, cert.Handle, key.Handle); | ||
| if (CFType.GetTypeID (identity) != SecIdentity.GetTypeID ()) | ||
| throw new InvalidOperationException (); |
There was a problem hiding this comment.
It should never happen - do we have some kind of "internal error" exception?
|
|
||
| IntPtr result = IntPtr.Zero; | ||
| SecStatusCode status = SecStatusCode.Success; | ||
| keyParams = new SecItemImportExportKeyParameters (); |
| Marshal.StructureToPtr (keyParams.Value, keyParamsPtr, false); | ||
| } | ||
|
|
||
| IntPtr result = IntPtr.Zero; |
There was a problem hiding this comment.
You don't need to explicitly set there when they are set by SecItemImport
| // Native enum; don't change. | ||
| enum SecExternalFormat : int { | ||
| Unknown = 0, | ||
| OpenSSL, |
There was a problem hiding this comment.
If it's native enum, set OpenSSL value explicitly
|
|
||
| // Native enum; don't change. | ||
| enum SecExternalItemType : int { | ||
| Unknown, |
|
|
||
| IntPtr ptr; | ||
| result = SecItem.SecItemCopyMatching (query.Handle, out ptr); | ||
| if ((result == SecStatusCode.Success) && (ptr != IntPtr.Zero)) { |
| IntPtr ptr; | ||
| result = SecItem.SecItemCopyMatching (query.Handle, out ptr); | ||
| if ((result == SecStatusCode.Success) && (ptr != IntPtr.Zero)) { | ||
| var array = CFArray.ArrayFromHandle<INativeObject> (ptr, p => { |
There was a problem hiding this comment.
why not just return CFArray.ArrayFromHandle...
There was a problem hiding this comment.
Not my code, I only moved it around a little bit. I just made this private as it's only supported as called from FindIdentity().
In some situations, the result array may contain elements of different types.
| IntPtr cfType = CFType.GetTypeID (p); | ||
| if (cfType == SecCertificate.GetTypeID ()) | ||
| return new SecCertificate (p, true); | ||
| else if (cfType == SecKey.GetTypeID ()) |
| [DllImport (AppleTlsContext.SecurityLibrary)] | ||
| extern static /* OSStatus */ SecStatusCode SecAccessCreate (/* CFStringRef */ IntPtr descriptor, /* CFArrayRef */ IntPtr trustedList, /* SecAccessRef _Nullable * */ out IntPtr accessRef); | ||
|
|
||
| public static SecAccess Create (string descriptor) |
There was a problem hiding this comment.
where is that method used ?
There was a problem hiding this comment.
It will be used later on - after we re-enabled the keychain on mobile.
| { | ||
| var descriptorHandle = CFString.Create (descriptor); | ||
| if (descriptorHandle == null) | ||
| throw new InvalidOperationException (); |
There was a problem hiding this comment.
If that method is used (I did not see it used in the diff) then it seems this should be a null check on descriptor to throw an ArgumentNullException
| // values are defined in Security.framework/Headers/SecBase.h | ||
| enum SecStatusCode { | ||
| Success = 0, | ||
| DuplicateItem = -25299, |
There was a problem hiding this comment.
It is also already public in Xamarin.*.dll so we'll end up shipping it twice (lots of string metadata that only grows over time).
I understand it's nice for debugging the internals but it's not exposed. Maybe it could live under a #if XXX ?
There was a problem hiding this comment.
We could probably remove some of the more obscure values.
…hen we have a private key. Reading Certificates from the Mac Keychain ========================================== Reading the private key from the keychain is a new feature introduced with AppleTls on XamMac and iOS. On Desktop Mono, this new feature has several known issues and it also did not received any testing yet. We go back to the old way of doing things, which is to explicitly provide an X509Certificate2 with a private key. Keychain Dialog Popups ====================== When using Xamarin.Mac or Xamarin.iOS, we try to search the keychain for the certificate and private key. On Xamarin.iOS, this is easy because each app has its own keychain. On Xamarin.Mac, the .app package needs to be trusted via code-sign to get permission to access the user's keychain. [FIXME: I still have to research how to actually do that.] Without this, you will get a popup message each time, asking you whether you want to allow the app to access the keychain, but you can make these go away by selecting "Trust always". On Desktop Mono, this is problematic because selecting "Trust always" give the 'mono' binary (and thus everything you'll ever run with Mono) permission to retrieve the private key from the keychain. This code would also trigger constant keychain popup messages, which could only be suppressed by granting full trust. It also makes it impossible to run Mono in headless mode. SecIdentityCreate ================= To avoid these problems, we are currently using an undocumented API called SecIdentityRef() to avoid using the Mac keychain whenever a X509Certificate2 with a private key is used. On iOS and XamMac, you can still provide the X509Certificate without a private key - in this case, a keychain search will be performed (and you may get a popup message on XamMac).
|
I did some cleanups, fixed all the problems you guys addressed and added some documentation. Could you please have another look at this to see whether this can now go in? @marek-safar @chamons |
| internal SecKeyChain (IntPtr handle, bool owns = false) | ||
| { | ||
| if (handle == IntPtr.Zero) | ||
| throw new Exception ("Invalid handle"); |
There was a problem hiding this comment.
Should be ArgumentException
…4671) * [appletls]: Use SecIdentityCreate() to avoid using the Mac keychain when we have a private key. Reading Certificates from the Mac Keychain ========================================== Reading the private key from the keychain is a new feature introduced with AppleTls on XamMac and iOS. On Desktop Mono, this new feature has several known issues and it also did not received any testing yet. We go back to the old way of doing things, which is to explicitly provide an X509Certificate2 with a private key. Keychain Dialog Popups ====================== When using Xamarin.Mac or Xamarin.iOS, we try to search the keychain for the certificate and private key. On Xamarin.iOS, this is easy because each app has its own keychain. On Xamarin.Mac, the .app package needs to be trusted via code-sign to get permission to access the user's keychain. [FIXME: I still have to research how to actually do that.] Without this, you will get a popup message each time, asking you whether you want to allow the app to access the keychain, but you can make these go away by selecting "Trust always". On Desktop Mono, this is problematic because selecting "Trust always" give the 'mono' binary (and thus everything you'll ever run with Mono) permission to retrieve the private key from the keychain. This code would also trigger constant keychain popup messages, which could only be suppressed by granting full trust. It also makes it impossible to run Mono in headless mode. SecIdentityCreate ================= To avoid these problems, we are currently using an undocumented API called SecIdentityRef() to avoid using the Mac keychain whenever a X509Certificate2 with a private key is used. On iOS and XamMac, you can still provide the X509Certificate without a private key - in this case, a keychain search will be performed (and you may get a popup message on XamMac). (cherry picked from commit 1eb27c1)
Temporary PR, preparing the cherry-picking of Mono's PR #4671 (mono/mono#4671) into mono/2017-02.
* [BTLS]: Cleanup certificate store initialization. * Kill unused MonoBtlsX509Store.AddTrustedRoots(). * In server-mode, MonoBtlsProvider.SetupCertificateStore() now only adds certificates explicitly trused via MonoTlsSettings.TrustAnchors. * MonoTlsProvider.ValidateCertificate() - which is called from X509Certificate2.Verify() via X509CertificateImplBtls.Verify() - now uses MonoTlsSettings.DefaultSettings and assumes client-mode. * [appletls]: Actually use the new code that was added in PR #4671. * [btls]: Add new MonoTlsSettings.CertificateValidationTime property. This allows you to set a custom time for certificate expiration checks. * [appletls]: Bind SecTrustSetVerifyDate() and check MonoTlsSettings.CertificateValidationTime.
…4671) (#4677) * [appletls]: Use SecIdentityCreate() to avoid using the Mac keychain. (#4671) Reading Certificates from the Mac Keychain ========================================== Reading the private key from the keychain is a new feature introduced with AppleTls on XamMac and iOS. On Desktop Mono, this new feature has several known issues and it also did not received any testing yet. We go back to the old way of doing things, which is to explicitly provide an X509Certificate2 with a private key. Keychain Dialog Popups ====================== When using Xamarin.Mac or Xamarin.iOS, we try to search the keychain for the certificate and private key. On Xamarin.iOS, this is easy because each app has its own keychain. On Xamarin.Mac, the .app package needs to be trusted via code-sign to get permission to access the user's keychain. [FIXME: I still have to research how to actually do that.] Without this, you will get a popup message each time, asking you whether you want to allow the app to access the keychain, but you can make these go away by selecting "Trust always". On Desktop Mono, this is problematic because selecting "Trust always" give the 'mono' binary (and thus everything you'll ever run with Mono) permission to retrieve the private key from the keychain. This code would also trigger constant keychain popup messages, which could only be suppressed by granting full trust. It also makes it impossible to run Mono in headless mode. SecIdentityCreate ================= To avoid these problems, we are currently using an undocumented API called SecIdentityRef() to avoid using the Mac keychain whenever a X509Certificate2 with a private key is used. On iOS and XamMac, you can still provide the X509Certificate without a private key - in this case, a keychain search will be performed (and you may get a popup message on XamMac).
…4671) (#4677) * [appletls]: Use SecIdentityCreate() to avoid using the Mac keychain. (#4671) Reading Certificates from the Mac Keychain ========================================== Reading the private key from the keychain is a new feature introduced with AppleTls on XamMac and iOS. On Desktop Mono, this new feature has several known issues and it also did not received any testing yet. We go back to the old way of doing things, which is to explicitly provide an X509Certificate2 with a private key. Keychain Dialog Popups ====================== When using Xamarin.Mac or Xamarin.iOS, we try to search the keychain for the certificate and private key. On Xamarin.iOS, this is easy because each app has its own keychain. On Xamarin.Mac, the .app package needs to be trusted via code-sign to get permission to access the user's keychain. [FIXME: I still have to research how to actually do that.] Without this, you will get a popup message each time, asking you whether you want to allow the app to access the keychain, but you can make these go away by selecting "Trust always". On Desktop Mono, this is problematic because selecting "Trust always" give the 'mono' binary (and thus everything you'll ever run with Mono) permission to retrieve the private key from the keychain. This code would also trigger constant keychain popup messages, which could only be suppressed by granting full trust. It also makes it impossible to run Mono in headless mode. SecIdentityCreate ================= To avoid these problems, we are currently using an undocumented API called SecIdentityRef() to avoid using the Mac keychain whenever a X509Certificate2 with a private key is used. On iOS and XamMac, you can still provide the X509Certificate without a private key - in this case, a keychain search will be performed (and you may get a popup message on XamMac). (cherry picked from commit b9b2d23)
…4671) (#4677) (#4689) * [appletls]: Use SecIdentityCreate() to avoid using the Mac keychain. (#4671) Reading Certificates from the Mac Keychain ========================================== Reading the private key from the keychain is a new feature introduced with AppleTls on XamMac and iOS. On Desktop Mono, this new feature has several known issues and it also did not received any testing yet. We go back to the old way of doing things, which is to explicitly provide an X509Certificate2 with a private key. Keychain Dialog Popups ====================== When using Xamarin.Mac or Xamarin.iOS, we try to search the keychain for the certificate and private key. On Xamarin.iOS, this is easy because each app has its own keychain. On Xamarin.Mac, the .app package needs to be trusted via code-sign to get permission to access the user's keychain. [FIXME: I still have to research how to actually do that.] Without this, you will get a popup message each time, asking you whether you want to allow the app to access the keychain, but you can make these go away by selecting "Trust always". On Desktop Mono, this is problematic because selecting "Trust always" give the 'mono' binary (and thus everything you'll ever run with Mono) permission to retrieve the private key from the keychain. This code would also trigger constant keychain popup messages, which could only be suppressed by granting full trust. It also makes it impossible to run Mono in headless mode. SecIdentityCreate ================= To avoid these problems, we are currently using an undocumented API called SecIdentityRef() to avoid using the Mac keychain whenever a X509Certificate2 with a private key is used. On iOS and XamMac, you can still provide the X509Certificate without a private key - in this case, a keychain search will be performed (and you may get a popup message on XamMac). (cherry picked from commit b9b2d23)
Reading Certificates from the Mac Keychain
Reading the private key from the keychain is a new feature introduced with
AppleTls on XamMac and iOS. On Desktop Mono, this new feature has several
known issues and it also did not received any testing yet. We go back to the old
way of doing things, which is to explicitly provide an
X509Certificate2with aprivate key.
Keychain Dialog Popups
When using Xamarin.Mac or Xamarin.iOS, we try to search the keychain
for the certificate and private key.
On Xamarin.iOS, this is easy because each app has its own keychain.
On Xamarin.Mac, the
.apppackage needs to be trusted via code-signto get permission to access the user's keychain. [FIXME: I still have to
research how to actually do that.] Without this, you will get a popup
message each time, asking you whether you want to allow the app to access
the keychain, but you can make these go away by selecting "Trust always".
On Desktop Mono, this is problematic because selecting "Trust always"
give the 'mono' binary (and thus everything you'll ever run with Mono)
permission to retrieve the private key from the keychain.
This code would also trigger constant keychain popup messages,
which could only be suppressed by granting full trust. It also makes it
impossible to run Mono in headless mode.
SecIdentityCreate
To avoid these problems, we are currently using an undocumented API
called
SecIdentityRef()to avoid using the Mac keychain whenever aX509Certificate2with a private key is used.On iOS and XamMac, you can still provide the
X509Certificatewithouta private key - in this case, a keychain search will be performed (and you
may get a popup message on XamMac).