[appletls]: Use SecIdentityCreate() to avoid using the Mac keychain. (#4671)
authorMartin Baulig <mabaul@microsoft.com>
Tue, 11 Apr 2017 21:43:51 +0000 (17:43 -0400)
committerGitHub <noreply@github.com>
Tue, 11 Apr 2017 21:43:51 +0000 (17:43 -0400)
* [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).

mcs/class/System/Mono.AppleTls/AppleCertificateHelper.cs
mcs/class/System/Mono.AppleTls/Certificate.cs
mcs/class/System/Mono.AppleTls/Enums.cs
mcs/class/System/Mono.AppleTls/ImportExport.cs
mcs/class/System/Mono.AppleTls/Items.cs
mcs/class/System/System.Net/MacProxy.cs

index d26abe989b415a863bc606a041fb63976513df85..6225abb90ca3fb6c039dac78d06a5bdab886e06f 100644 (file)
@@ -39,11 +39,56 @@ namespace Mono.AppleTls
                                return SecIdentity.Import (certificate2);
 
                        /*
-                        * Otherwise, we require the private key to be in the keychain.
+                        * 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).
                         */
+
+#if MOBILE
                        using (var secCert = new SecCertificate (certificate)) {
                                return SecKeyChain.FindIdentity (secCert, true);
                        }
+#else
+                       return null;
+#endif
                }
 
                public static SecIdentity GetIdentity (X509Certificate certificate, out SecCertificate[] intermediateCerts)
index e78a85f174e3e7e94f27443a37d802b7506f2389..c02c766c5603d8504a4f500f28746bba903063a0 100644 (file)
@@ -31,6 +31,7 @@
 #if SECURITY_DEP && MONO_FEATURE_APPLETLS
 
 using System;
+using System.Collections.Generic;
 using System.Runtime.InteropServices;
 using System.Security.Cryptography.X509Certificates;
 using Mono.Net;
@@ -198,6 +199,8 @@ namespace Mono.AppleTls {
                 
                static readonly CFString ImportExportPassphase;
                static readonly CFString ImportItemIdentity;
+               static readonly CFString ImportExportAccess;
+               static readonly CFString ImportExportKeychain;
                
                static SecIdentity ()
                {
@@ -208,6 +211,8 @@ namespace Mono.AppleTls {
                        try {           
                                ImportExportPassphase = CFObject.GetStringConstant (handle, "kSecImportExportPassphrase");
                                ImportItemIdentity = CFObject.GetStringConstant (handle, "kSecImportItemIdentity");
+                               ImportExportAccess = CFObject.GetStringConstant (handle, "kSecImportExportAccess");
+                               ImportExportKeychain = CFObject.GetStringConstant (handle, "kSecImportExportKeychain");
                        } finally {
                                CFObject.dlclose (handle);
                        }
@@ -240,14 +245,27 @@ namespace Mono.AppleTls {
                        }
                }
 
-               public static SecIdentity Import (byte[] data, string password)
+               static CFDictionary CreateImportOptions (CFString password, SecKeyChain keychain, SecAccess access)
+               {
+                       var items = new List<Tuple<IntPtr, IntPtr>> ();
+                       items.Add (new Tuple<IntPtr, IntPtr> (ImportExportPassphase.Handle, password.Handle));
+
+                       if (keychain != null)
+                               items.Add (new Tuple<IntPtr, IntPtr> (ImportExportKeychain.Handle, keychain.Handle));
+                       if (access != null)
+                               items.Add (new Tuple<IntPtr, IntPtr> (ImportExportAccess.Handle, access.Handle));
+
+                       return CFDictionary.FromKeysAndObjects (items);
+               }
+
+               public static SecIdentity Import (byte[] data, string password, SecKeyChain keychain = null, SecAccess access = null)
                {
                        if (data == null)
                                throw new ArgumentNullException ("data");
                        if (string.IsNullOrEmpty (password)) // SecPKCS12Import() doesn't allow empty passwords.
                                throw new ArgumentException ("password");
                        using (var pwstring = CFString.Create (password))
-                       using (var options = CFDictionary.FromObjectAndKey (pwstring.Handle, ImportExportPassphase.Handle)) {
+                       using (var options = CreateImportOptions (pwstring, keychain, access)) {
                                CFDictionary [] array;
                                SecStatusCode result = SecImportExport.ImportPkcs12 (data, options, out array);
                                if (result != SecStatusCode.Success)
@@ -257,7 +275,7 @@ namespace Mono.AppleTls {
                        }
                }
 
-               public static SecIdentity Import (X509Certificate2 certificate)
+               public static SecIdentity Import (X509Certificate2 certificate, SecKeyChain keychain = null, SecAccess access = null)
                {
                        if (certificate == null)
                                throw new ArgumentNullException ("certificate");
@@ -270,7 +288,7 @@ namespace Mono.AppleTls {
                         */
                        var password = Guid.NewGuid ().ToString ();
                        var pkcs12 = certificate.Export (X509ContentType.Pfx, password);
-                       return Import (pkcs12, password);
+                       return Import (pkcs12, password, keychain, access);
                }
 
                ~SecIdentity ()
@@ -301,6 +319,7 @@ namespace Mono.AppleTls {
 
        partial class SecKey : INativeObject, IDisposable {
                internal IntPtr handle;
+               internal IntPtr owner;
                
                public SecKey (IntPtr handle, bool owns = false)
                {
@@ -309,6 +328,17 @@ namespace Mono.AppleTls {
                                CFObject.CFRetain (handle);
                }
 
+               /*
+                * SecItemImport() returns a SecArrayRef.  We need to free the array, not the items inside it.
+                * 
+                */
+               internal SecKey (IntPtr handle, IntPtr owner)
+               {
+                       this.handle = handle;
+                       this.owner = owner;
+                       CFObject.CFRetain (owner);
+               }
+
                [DllImport (AppleTlsContext.SecurityLibrary, EntryPoint="SecKeyGetTypeID")]
                public extern static IntPtr GetTypeID ();
                
@@ -331,7 +361,67 @@ namespace Mono.AppleTls {
 
                protected virtual void Dispose (bool disposing)
                {
-                       if (handle != IntPtr.Zero){
+                       if (owner != IntPtr.Zero) {
+                               CFObject.CFRelease (owner);
+                               owner = handle = IntPtr.Zero;
+                       } else if (handle != IntPtr.Zero) {
+                               CFObject.CFRelease (handle);
+                               handle = IntPtr.Zero;
+                       }
+               }
+       }
+
+       class SecAccess : INativeObject, IDisposable {
+               internal IntPtr handle;
+
+               public SecAccess (IntPtr handle, bool owns = false)
+               {
+                       this.handle = handle;
+                       if (!owns)
+                               CFObject.CFRetain (handle);
+               }
+
+               ~SecAccess ()
+               {
+                       Dispose (false);
+               }
+
+               public IntPtr Handle {
+                       get {
+                               return handle;
+                       }
+               }
+
+               [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)
+               {
+                       var descriptorHandle = CFString.Create (descriptor);
+                       if (descriptorHandle == null)
+                               throw new InvalidOperationException ();
+
+                       try {
+                               IntPtr accessRef;
+                               var result = SecAccessCreate (descriptorHandle.Handle, IntPtr.Zero, out accessRef);
+                               if (result != SecStatusCode.Success)
+                                       throw new InvalidOperationException (result.ToString ());
+
+                               return new SecAccess (accessRef, true);
+                       } finally {
+                               descriptorHandle.Dispose ();
+                       }
+               }
+
+               public void Dispose ()
+               {
+                       Dispose (true);
+                       GC.SuppressFinalize (this);
+               }
+
+               protected virtual void Dispose (bool disposing)
+               {
+                       if (handle != IntPtr.Zero) {
                                CFObject.CFRelease (handle);
                                handle = IntPtr.Zero;
                        }
index 3ec210767385686ed2e4fd4793c548815cef3e42..e71e94ff1444aced2574cd7e6964cb0961494e46 100644 (file)
@@ -9,8 +9,386 @@ namespace Mono.AppleTls {
        // values are defined in Security.framework/Headers/SecBase.h 
        enum SecStatusCode {
                Success                                                         = 0,
-               DuplicateItem                                           = -25299,
+               Unimplemented                                           = -4,
+               DiskFull                                                        = -34,
+               IO                                                                      = -36,
+               OpWr                                                            = -49,
                Param                                                           = -50,
+               WritePermissions                                        = -61,
+               Allocate                                                        = -108,
+               UserCanceled                                            = -128,
+               BadReq                                                          = -909,
+               InternalComponent                                       = -2070,
+               CoreFoundationUnknown                           = -4960,
+               NotAvailable                                            = -25291,
+               ReadOnly                                                        = -25292,
+               AuthFailed                                                      = -25293,
+               NoSuchKeyChain                                          = -25294,
+               InvalidKeyChain                                         = -25295,
+               DuplicateKeyChain                                       = -25296,
+               DuplicateItem                                           = -25299,
+               ItemNotFound                                            = -25300,
+               InteractionNotAllowed                           = -25308,
+               Decode                                                          = -26275,
+               DuplicateCallback                                       = -25297,
+               InvalidCallback                                         = -25298,
+               BufferTooSmall                                          = -25301,
+               DataTooLarge                                            = -25302,
+               NoSuchAttribute                                         = -25303,
+               InvalidItemRef                                          = -25304,
+               InvalidSearchRef                                        = -25305,
+               NoSuchClass                                                     = -25306,
+               NoDefaultKeychain                                       = -25307,
+               ReadOnlyAttribute                                       = -25309,
+               WrongSecVersion                                         = -25310,
+               KeySizeNotAllowed                                       = -25311,
+               NoStorageModule                                         = -25312,
+               NoCertificateModule                             = -25313,
+               NoPolicyModule                                          = -25314,
+               InteractionRequired                             = -25315,
+               DataNotAvailable                                        = -25316,
+               DataNotModifiable                                       = -25317,
+               CreateChainFailed                                       = -25318,
+               InvalidPrefsDomain                                      = -25319,
+               InDarkWake                                                      = -25320,
+               ACLNotSimple                                            = -25240,
+               PolicyNotFound                                          = -25241,
+               InvalidTrustSetting                                     = -25242,
+               NoAccessForItem                                         = -25243,
+               InvalidOwnerEdit                                        = -25244,
+               TrustNotAvailable                                       = -25245,
+               UnsupportedFormat                                       = -25256,
+               UnknownFormat                                           = -25257,
+               KeyIsSensitive                                          = -25258,
+               MultiplePrivateKeys                             = -25259,
+               PassphraseRequired                                      = -25260,
+               InvalidPasswordRef                                      = -25261,
+               InvalidTrustSettings                            = -25262,
+               NoTrustSettings                                         = -25263,
+               Pkcs12VerifyFailure                             = -25264,
+               NotSigner                                                       = -26267,
+               ServiceNotAvailable                             = -67585,
+               InsufficientClientID                            = -67586,
+               DeviceReset                                             = -67587,
+               DeviceFailed                                            = -67588,
+               AppleAddAppACLSubject                           = -67589,
+               ApplePublicKeyIncomplete                        = -67590,
+               AppleSignatureMismatch                          = -67591,
+               AppleInvalidKeyStartDate                        = -67592,
+               AppleInvalidKeyEndDate                          = -67593,
+               ConversionError                                         = -67594,
+               AppleSSLv2Rollback                                      = -67595,
+               QuotaExceeded                                           = -67596,
+               FileTooBig                                                      = -67597,
+               InvalidDatabaseBlob                             = -67598,
+               InvalidKeyBlob                                          = -67599,
+               IncompatibleDatabaseBlob                        = -67600,
+               IncompatibleKeyBlob                             = -67601,
+               HostNameMismatch                                        = -67602,
+               UnknownCriticalExtensionFlag            = -67603,
+               NoBasicConstraints                                      = -67604,
+               NoBasicConstraintsCA                            = -67605,
+               InvalidAuthorityKeyID                           = -67606,
+               InvalidSubjectKeyID                                     = -67607,       /* The subject key ID is not valid. */
+               InvalidKeyUsageForPolicy                        = -67608,       /* The key usage is not valid for the specified policy. */
+               InvalidExtendedKeyUsage                         = -67609,       /* The extended key usage is not valid. */
+               InvalidIDLinkage                                        = -67610,       /* The ID linkage is not valid. */
+               PathLengthConstraintExceeded            = -67611,       /* The path length constraint was exceeded. */
+               InvalidRoot                                                     = -67612,       /* The root or anchor certificate is not valid. */
+               CRLExpired                                                      = -67613,       /* The CRL has expired. */
+               CRLNotValidYet                                          = -67614,       /* The CRL is not yet valid. */
+               CRLNotFound                                                     = -67615,       /* The CRL was not found. */
+               CRLServerDown                                           = -67616,       /* The CRL server is down. */
+               CRLBadURI                                                       = -67617,       /* The CRL has a bad Uniform Resource Identifier. */
+               UnknownCertExtension                            = -67618,       /* An unknown certificate extension was encountered. */
+               UnknownCRLExtension                                     = -67619,       /* An unknown CRL extension was encountered. */
+               CRLNotTrusted                                           = -67620,       /* The CRL is not trusted. */
+               CRLPolicyFailed                                         = -67621,       /* The CRL policy failed. */
+               IDPFailure                                                      = -67622,       /* The issuing distribution point was not valid. */
+               SMIMEEmailAddressesNotFound                     = -67623,       /* An email address mismatch was encountered. */
+               SMIMEBadExtendedKeyUsage                        = -67624,       /* The appropriate extended key usage for SMIME was not found. */
+               SMIMEBadKeyUsage                                        = -67625,       /* The key usage is not compatible with SMIME. */
+               SMIMEKeyUsageNotCritical                        = -67626,       /* The key usage extension is not marked as critical. */
+               SMIMENoEmailAddress                                     = -67627,       /* No email address was found in the certificate. */
+               SMIMESubjAltNameNotCritical                     = -67628,       /* The subject alternative name extension is not marked as critical. */
+               SSLBadExtendedKeyUsage                          = -67629,       /* The appropriate extended key usage for SSL was not found. */
+               OCSPBadResponse                                         = -67630,       /* The OCSP response was incorrect or could not be parsed. */
+               OCSPBadRequest                                          = -67631,       /* The OCSP request was incorrect or could not be parsed. */
+               OCSPUnavailable                                         = -67632,       /* OCSP service is unavailable. */
+               OCSPStatusUnrecognized                          = -67633,       /* The OCSP server did not recognize this certificate. */
+               EndOfData                                                       = -67634,       /* An end-of-data was detected. */
+               IncompleteCertRevocationCheck           = -67635,       /* An incomplete certificate revocation check occurred. */
+               NetworkFailure                                          = -67636,       /* A network failure occurred. */
+               OCSPNotTrustedToAnchor                          = -67637,       /* The OCSP response was not trusted to a root or anchor certificate. */
+               RecordModified                                          = -67638,       /* The record was modified. */
+               OCSPSignatureError                                      = -67639,       /* The OCSP response had an invalid signature. */
+               OCSPNoSigner                                            = -67640,       /* The OCSP response had no signer. */
+               OCSPResponderMalformedReq                       = -67641,       /* The OCSP responder was given a malformed request. */
+               OCSPResponderInternalError                      = -67642,       /* The OCSP responder encountered an internal error. */
+               OCSPResponderTryLater                           = -67643,       /* The OCSP responder is busy, try again later. */
+               OCSPResponderSignatureRequired          = -67644,       /* The OCSP responder requires a signature. */
+               OCSPResponderUnauthorized                       = -67645,       /* The OCSP responder rejected this request as unauthorized. */
+               OCSPResponseNonceMismatch                       = -67646,       /* The OCSP response nonce did not match the request. */
+               CodeSigningBadCertChainLength           = -67647,       /* Code signing encountered an incorrect certificate chain length. */
+               CodeSigningNoBasicConstraints           = -67648,       /* Code signing found no basic constraints. */
+               CodeSigningBadPathLengthConstraint      = -67649,       /* Code signing encountered an incorrect path length constraint. */
+               CodeSigningNoExtendedKeyUsage           = -67650,       /* Code signing found no extended key usage. */
+               CodeSigningDevelopment                          = -67651,       /* Code signing indicated use of a development-only certificate. */
+               ResourceSignBadCertChainLength          = -67652,       /* Resource signing has encountered an incorrect certificate chain length. */
+               ResourceSignBadExtKeyUsage                      = -67653,       /* Resource signing has encountered an error in the extended key usage. */
+               TrustSettingDeny                                        = -67654,       /* The trust setting for this policy was set to Deny. */
+               InvalidSubjectName                                      = -67655,       /* An invalid certificate subject name was encountered. */
+               UnknownQualifiedCertStatement           = -67656,       /* An unknown qualified certificate statement was encountered. */
+               MobileMeRequestQueued                           = -67657,       /* The MobileMe request will be sent during the next connection. */
+               MobileMeRequestRedirected                       = -67658,       /* The MobileMe request was redirected. */
+               MobileMeServerError                                     = -67659,       /* A MobileMe server error occurred. */
+               MobileMeServerNotAvailable                      = -67660,       /* The MobileMe server is not available. */
+               MobileMeServerAlreadyExists                     = -67661,       /* The MobileMe server reported that the item already exists. */
+               MobileMeServerServiceErr                        = -67662,       /* A MobileMe service error has occurred. */
+               MobileMeRequestAlreadyPending           = -67663,       /* A MobileMe request is already pending. */
+               MobileMeNoRequestPending                        = -67664,       /* MobileMe has no request pending. */
+               MobileMeCSRVerifyFailure                        = -67665,       /* A MobileMe CSR verification failure has occurred. */
+               MobileMeFailedConsistencyCheck          = -67666,       /* MobileMe has found a failed consistency check. */
+               NotInitialized                                          = -67667,       /* A function was called without initializing CSSM. */
+               InvalidHandleUsage                                      = -67668,       /* The CSSM handle does not match with the service type. */
+               PVCReferentNotFound                                     = -67669,       /* A reference to the calling module was not found in the list of authorized callers. */
+               FunctionIntegrityFail                           = -67670,       /* A function address was not within the verified module. */
+               InternalError                                           = -67671,       /* An internal error has occurred. */
+               MemoryError                                                     = -67672,       /* A memory error has occurred. */
+               InvalidData                                                     = -67673,       /* Invalid data was encountered. */
+               MDSError                                                        = -67674,       /* A Module Directory Service error has occurred. */
+               InvalidPointer                                          = -67675,       /* An invalid pointer was encountered. */
+               SelfCheckFailed                                         = -67676,       /* Self-check has failed. */
+               FunctionFailed                                          = -67677,       /* A function has failed. */
+               ModuleManifestVerifyFailed                      = -67678,       /* A module manifest verification failure has occurred. */
+               InvalidGUID                                                     = -67679,       /* An invalid GUID was encountered. */
+               InvalidHandle                                           = -67680,       /* An invalid handle was encountered. */
+               InvalidDBList                                           = -67681,       /* An invalid DB list was encountered. */
+               InvalidPassthroughID                            = -67682,       /* An invalid passthrough ID was encountered. */
+               InvalidNetworkAddress                           = -67683,       /* An invalid network address was encountered. */
+               CRLAlreadySigned                                        = -67684,       /* The certificate revocation list is already signed. */
+               InvalidNumberOfFields                           = -67685,       /* An invalid number of fields were encountered. */
+               VerificationFailure                                     = -67686,       /* A verification failure occurred. */
+               UnknownTag                                                      = -67687,       /* An unknown tag was encountered. */
+               InvalidSignature                                        = -67688,       /* An invalid signature was encountered. */
+               InvalidName                                                     = -67689,       /* An invalid name was encountered. */
+               InvalidCertificateRef                           = -67690,       /* An invalid certificate reference was encountered. */
+               InvalidCertificateGroup                         = -67691,       /* An invalid certificate group was encountered. */
+               TagNotFound                                                     = -67692,       /* The specified tag was not found. */
+               InvalidQuery                                            = -67693,       /* The specified query was not valid. */
+               InvalidValue                                            = -67694,       /* An invalid value was detected. */
+               CallbackFailed                                          = -67695,       /* A callback has failed. */
+               ACLDeleteFailed                                         = -67696,       /* An ACL delete operation has failed. */
+               ACLReplaceFailed                                        = -67697,       /* An ACL replace operation has failed. */
+               ACLAddFailed                                            = -67698,       /* An ACL add operation has failed. */
+               ACLChangeFailed                                         = -67699,       /* An ACL change operation has failed. */
+               InvalidAccessCredentials                        = -67700,       /* Invalid access credentials were encountered. */
+               InvalidRecord                                           = -67701,       /* An invalid record was encountered. */
+               InvalidACL                                                      = -67702,       /* An invalid ACL was encountered. */
+               InvalidSampleValue                                      = -67703,       /* An invalid sample value was encountered. */
+               IncompatibleVersion                                     = -67704,       /* An incompatible version was encountered. */
+               PrivilegeNotGranted                                     = -67705,       /* The privilege was not granted. */
+               InvalidScope                                            = -67706,       /* An invalid scope was encountered. */
+               PVCAlreadyConfigured                            = -67707,       /* The PVC is already configured. */
+               InvalidPVC                                                      = -67708,       /* An invalid PVC was encountered. */
+               EMMLoadFailed                                           = -67709,       /* The EMM load has failed. */
+               EMMUnloadFailed                                         = -67710,       /* The EMM unload has failed. */
+               AddinLoadFailed                                         = -67711,       /* The add-in load operation has failed. */
+               InvalidKeyRef                                           = -67712,       /* An invalid key was encountered. */
+               InvalidKeyHierarchy                                     = -67713,       /* An invalid key hierarchy was encountered. */
+               AddinUnloadFailed                                       = -67714,       /* The add-in unload operation has failed. */
+               LibraryReferenceNotFound                        = -67715,       /* A library reference was not found. */
+               InvalidAddinFunctionTable                       = -67716,       /* An invalid add-in function table was encountered. */
+               InvalidServiceMask                                      = -67717,       /* An invalid service mask was encountered. */
+               ModuleNotLoaded                                         = -67718,       /* A module was not loaded. */
+               InvalidSubServiceID                                     = -67719,       /* An invalid subservice ID was encountered. */
+               AttributeNotInContext                           = -67720,       /* An attribute was not in the context. */
+               ModuleManagerInitializeFailed           = -67721,       /* A module failed to initialize. */
+               ModuleManagerNotFound                           = -67722,       /* A module was not found. */
+               EventNotificationCallbackNotFound       = -67723,       /* An event notification callback was not found. */
+               InputLengthError                                        = -67724,       /* An input length error was encountered. */
+               OutputLengthError                                       = -67725,       /* An output length error was encountered. */
+               PrivilegeNotSupported                           = -67726,       /* The privilege is not supported. */
+               DeviceError                                                     = -67727,       /* A device error was encountered. */
+               AttachHandleBusy                                        = -67728,       /* The CSP handle was busy. */
+               NotLoggedIn                                                     = -67729,       /* You are not logged in. */
+               AlgorithmMismatch                                       = -67730,       /* An algorithm mismatch was encountered. */
+               KeyUsageIncorrect                                       = -67731,       /* The key usage is incorrect. */
+               KeyBlobTypeIncorrect                            = -67732,       /* The key blob type is incorrect. */
+               KeyHeaderInconsistent                           = -67733,       /* The key header is inconsistent. */
+               UnsupportedKeyFormat                            = -67734,       /* The key header format is not supported. */
+               UnsupportedKeySize                                      = -67735,       /* The key size is not supported. */
+               InvalidKeyUsageMask                                     = -67736,       /* The key usage mask is not valid. */
+               UnsupportedKeyUsageMask                         = -67737,       /* The key usage mask is not supported. */
+               InvalidKeyAttributeMask                         = -67738,       /* The key attribute mask is not valid. */
+               UnsupportedKeyAttributeMask                     = -67739,       /* The key attribute mask is not supported. */
+               InvalidKeyLabel                                         = -67740,       /* The key label is not valid. */
+               UnsupportedKeyLabel                                     = -67741,       /* The key label is not supported. */
+               InvalidKeyFormat                                        = -67742,       /* The key format is not valid. */
+               UnsupportedVectorOfBuffers                      = -67743,       /* The vector of buffers is not supported. */
+               InvalidInputVector                                      = -67744,       /* The input vector is not valid. */
+               InvalidOutputVector                                     = -67745,       /* The output vector is not valid. */
+               InvalidContext                                          = -67746,       /* An invalid context was encountered. */
+               InvalidAlgorithm                                        = -67747,       /* An invalid algorithm was encountered. */
+               InvalidAttributeKey                                     = -67748,       /* A key attribute was not valid. */
+               MissingAttributeKey                                     = -67749,       /* A key attribute was missing. */
+               InvalidAttributeInitVector                      = -67750,       /* An init vector attribute was not valid. */
+               MissingAttributeInitVector                      = -67751,       /* An init vector attribute was missing. */
+               InvalidAttributeSalt                            = -67752,       /* A salt attribute was not valid. */
+               MissingAttributeSalt                            = -67753,       /* A salt attribute was missing. */
+               InvalidAttributePadding                         = -67754,       /* A padding attribute was not valid. */
+               MissingAttributePadding                         = -67755,       /* A padding attribute was missing. */
+               InvalidAttributeRandom                          = -67756,       /* A random number attribute was not valid. */
+               MissingAttributeRandom                          = -67757,       /* A random number attribute was missing. */
+               InvalidAttributeSeed                            = -67758,       /* A seed attribute was not valid. */
+               MissingAttributeSeed                            = -67759,       /* A seed attribute was missing. */
+               InvalidAttributePassphrase                      = -67760,       /* A passphrase attribute was not valid. */
+               MissingAttributePassphrase                      = -67761,       /* A passphrase attribute was missing. */
+               InvalidAttributeKeyLength                       = -67762,       /* A key length attribute was not valid. */
+               MissingAttributeKeyLength                       = -67763,       /* A key length attribute was missing. */
+               InvalidAttributeBlockSize                       = -67764,       /* A block size attribute was not valid. */
+               MissingAttributeBlockSize                       = -67765,       /* A block size attribute was missing. */
+               InvalidAttributeOutputSize                      = -67766,       /* An output size attribute was not valid. */
+               MissingAttributeOutputSize                      = -67767,       /* An output size attribute was missing. */
+               InvalidAttributeRounds                          = -67768,       /* The number of rounds attribute was not valid. */
+               MissingAttributeRounds                          = -67769,       /* The number of rounds attribute was missing. */
+               InvalidAlgorithmParms                           = -67770,       /* An algorithm parameters attribute was not valid. */
+               MissingAlgorithmParms                           = -67771,       /* An algorithm parameters attribute was missing. */
+               InvalidAttributeLabel                           = -67772,       /* A label attribute was not valid. */
+               MissingAttributeLabel                           = -67773,       /* A label attribute was missing. */
+               InvalidAttributeKeyType                         = -67774,       /* A key type attribute was not valid. */
+               MissingAttributeKeyType                         = -67775,       /* A key type attribute was missing. */
+               InvalidAttributeMode                            = -67776,       /* A mode attribute was not valid. */
+               MissingAttributeMode                            = -67777,       /* A mode attribute was missing. */
+               InvalidAttributeEffectiveBits           = -67778,       /* An effective bits attribute was not valid. */
+               MissingAttributeEffectiveBits           = -67779,       /* An effective bits attribute was missing. */
+               InvalidAttributeStartDate                       = -67780,       /* A start date attribute was not valid. */
+               MissingAttributeStartDate                       = -67781,       /* A start date attribute was missing. */
+               InvalidAttributeEndDate                         = -67782,       /* An end date attribute was not valid. */
+               MissingAttributeEndDate                         = -67783,       /* An end date attribute was missing. */
+               InvalidAttributeVersion                         = -67784,       /* A version attribute was not valid. */
+               MissingAttributeVersion                         = -67785,       /* A version attribute was missing. */
+               InvalidAttributePrime                           = -67786,       /* A prime attribute was not valid. */
+               MissingAttributePrime                           = -67787,       /* A prime attribute was missing. */
+               InvalidAttributeBase                            = -67788,       /* A base attribute was not valid. */
+               MissingAttributeBase                            = -67789,       /* A base attribute was missing. */
+               InvalidAttributeSubprime                        = -67790,       /* A subprime attribute was not valid. */
+               MissingAttributeSubprime                        = -67791,       /* A subprime attribute was missing. */
+               InvalidAttributeIterationCount          = -67792,       /* An iteration count attribute was not valid. */
+               MissingAttributeIterationCount          = -67793,       /* An iteration count attribute was missing. */
+               InvalidAttributeDLDBHandle                      = -67794,       /* A database handle attribute was not valid. */
+               MissingAttributeDLDBHandle                      = -67795,       /* A database handle attribute was missing. */
+               InvalidAttributeAccessCredentials       = -67796,       /* An access credentials attribute was not valid. */
+               MissingAttributeAccessCredentials       = -67797,       /* An access credentials attribute was missing. */
+               InvalidAttributePublicKeyFormat         = -67798,       /* A public key format attribute was not valid. */
+               MissingAttributePublicKeyFormat         = -67799,       /* A public key format attribute was missing. */
+               InvalidAttributePrivateKeyFormat        = -67800,       /* A private key format attribute was not valid. */
+               MissingAttributePrivateKeyFormat        = -67801,       /* A private key format attribute was missing. */
+               InvalidAttributeSymmetricKeyFormat      = -67802,       /* A symmetric key format attribute was not valid. */
+               MissingAttributeSymmetricKeyFormat      = -67803,       /* A symmetric key format attribute was missing. */
+               InvalidAttributeWrappedKeyFormat        = -67804,       /* A wrapped key format attribute was not valid. */
+               MissingAttributeWrappedKeyFormat        = -67805,       /* A wrapped key format attribute was missing. */
+               StagedOperationInProgress                       = -67806,       /* A staged operation is in progress. */
+               StagedOperationNotStarted                       = -67807,       /* A staged operation was not started. */
+               VerifyFailed                                            = -67808,       /* A cryptographic verification failure has occurred. */
+               QuerySizeUnknown                                        = -67809,       /* The query size is unknown. */
+               BlockSizeMismatch                                       = -67810,       /* A block size mismatch occurred. */
+               PublicKeyInconsistent                           = -67811,       /* The public key was inconsistent. */
+               DeviceVerifyFailed                                      = -67812,       /* A device verification failure has occurred. */
+               InvalidLoginName                                        = -67813,       /* An invalid login name was detected. */
+               AlreadyLoggedIn                                         = -67814,       /* The user is already logged in. */
+               InvalidDigestAlgorithm                          = -67815,       /* An invalid digest algorithm was detected. */
+               InvalidCRLGroup                                         = -67816,       /* An invalid CRL group was detected. */
+               CertificateCannotOperate                        = -67817,       /* The certificate cannot operate. */
+               CertificateExpired                                      = -67818,       /* An expired certificate was detected. */
+               CertificateNotValidYet                          = -67819,       /* The certificate is not yet valid. */
+               CertificateRevoked                                      = -67820,       /* The certificate was revoked. */
+               CertificateSuspended                            = -67821,       /* The certificate was suspended. */
+               InsufficientCredentials                         = -67822,       /* Insufficient credentials were detected. */
+               InvalidAction                                           = -67823,       /* The action was not valid. */
+               InvalidAuthority                                        = -67824,       /* The authority was not valid. */
+               VerifyActionFailed                                      = -67825,       /* A verify action has failed. */
+               InvalidCertAuthority                            = -67826,       /* The certificate authority was not valid. */
+               InvalidCRLAuthority                                     = -67827,       /* The CRL authority was not valid. */
+#if MONOMAC
+               [Obsolete ("Use InvalidCRLAuthority")]
+               InvaldCRLAuthority                                      = InvalidCRLAuthority,
+#endif
+               InvalidCRLEncoding                                      = -67828,       /* The CRL encoding was not valid. */
+               InvalidCRLType                                          = -67829,       /* The CRL type was not valid. */
+               InvalidCRL                                                      = -67830,       /* The CRL was not valid. */
+               InvalidFormType                                         = -67831,       /* The form type was not valid. */
+               InvalidID                                                       = -67832,       /* The ID was not valid. */
+               InvalidIdentifier                                       = -67833,       /* The identifier was not valid. */
+               InvalidIndex                                            = -67834,       /* The index was not valid. */
+               InvalidPolicyIdentifiers                        = -67835,       /* The policy identifiers are not valid. */
+               InvalidTimeString                                       = -67836,       /* The time specified was not valid. */
+               InvalidReason                                           = -67837,       /* The trust policy reason was not valid. */
+               InvalidRequestInputs                            = -67838,       /* The request inputs are not valid. */
+               InvalidResponseVector                           = -67839,       /* The response vector was not valid. */
+               InvalidStopOnPolicy                                     = -67840,       /* The stop-on policy was not valid. */
+               InvalidTuple                                            = -67841,       /* The tuple was not valid. */
+               MultipleValuesUnsupported                       = -67842,       /* Multiple values are not supported. */
+               NotTrusted                                                      = -67843,       /* The trust policy was not trusted. */
+               NoDefaultAuthority                                      = -67844,       /* No default authority was detected. */
+               RejectedForm                                            = -67845,       /* The trust policy had a rejected form. */
+               RequestLost                                                     = -67846,       /* The request was lost. */
+               RequestRejected                                         = -67847,       /* The request was rejected. */
+               UnsupportedAddressType                          = -67848,       /* The address type is not supported. */
+               UnsupportedService                                      = -67849,       /* The service is not supported. */
+               InvalidTupleGroup                                       = -67850,       /* The tuple group was not valid. */
+               InvalidBaseACLs                                         = -67851,       /* The base ACLs are not valid. */
+               InvalidTupleCredentials                         = -67852,       /* The tuple credentials are not valid. */
+#if MONOMAC
+               [Obsolete ("Use InvalidTupleCredentials")]
+               InvalidTupleCredendtials                        = InvalidTupleCredentials,
+#endif
+               InvalidEncoding                                         = -67853,       /* The encoding was not valid. */
+               InvalidValidityPeriod                           = -67854,       /* The validity period was not valid. */
+               InvalidRequestor                                        = -67855,       /* The requestor was not valid. */
+               RequestDescriptor                                       = -67856,       /* The request descriptor was not valid. */
+               InvalidBundleInfo                                       = -67857,       /* The bundle information was not valid. */
+               InvalidCRLIndex                                         = -67858,       /* The CRL index was not valid. */
+               NoFieldValues                                           = -67859,       /* No field values were detected. */
+               UnsupportedFieldFormat                          = -67860,       /* The field format is not supported. */
+               UnsupportedIndexInfo                            = -67861,       /* The index information is not supported. */
+               UnsupportedLocality                                     = -67862,       /* The locality is not supported. */
+               UnsupportedNumAttributes                        = -67863,       /* The number of attributes is not supported. */
+               UnsupportedNumIndexes                           = -67864,       /* The number of indexes is not supported. */
+               UnsupportedNumRecordTypes                       = -67865,       /* The number of record types is not supported. */
+               FieldSpecifiedMultiple                          = -67866,       /* Too many fields were specified. */
+               IncompatibleFieldFormat                         = -67867,       /* The field format was incompatible. */
+               InvalidParsingModule                            = -67868,       /* The parsing module was not valid. */
+               DatabaseLocked                                          = -67869,       /* The database is locked. */
+               DatastoreIsOpen                                         = -67870,       /* The data store is open. */
+               MissingValue                                            = -67871,       /* A missing value was detected. */
+               UnsupportedQueryLimits                          = -67872,       /* The query limits are not supported. */
+               UnsupportedNumSelectionPreds            = -67873,       /* The number of selection predicates is not supported. */
+               UnsupportedOperator                                     = -67874,       /* The operator is not supported. */
+               InvalidDBLocation                                       = -67875,       /* The database location is not valid. */
+               InvalidAccessRequest                            = -67876,       /* The access request is not valid. */
+               InvalidIndexInfo                                        = -67877,       /* The index information is not valid. */
+               InvalidNewOwner                                         = -67878,       /* The new owner is not valid. */
+               InvalidModifyMode                                       = -67879,       /* The modify mode is not valid. */
+               MissingRequiredExtension                        = -67880,       /* A required certificate extension is missing. */
+               ExtendedKeyUsageNotCritical                     = -67881,       /* The extended key usage extension was not marked critical. */
+               TimestampMissing                                        = -67882,       /* A timestamp was expected but was not found. */
+               TimestampInvalid                                        = -67883,       /* The timestamp was not valid. */
+               TimestampNotTrusted                                     = -67884,       /* The timestamp was not trusted. */
+               TimestampServiceNotAvailable            = -67885,       /* The timestamp service is not available. */
+               TimestampBadAlg                                         = -67886,       /* An unrecognized or unsupported Algorithm Identifier in timestamp. */
+               TimestampBadRequest                                     = -67887,       /* The timestamp transaction is not permitted or supported. */
+               TimestampBadDataFormat                          = -67888,       /* The timestamp data submitted has the wrong format. */
+               TimestampTimeNotAvailable                       = -67889,       /* The time source for the Timestamp Authority is not available. */
+               TimestampUnacceptedPolicy                       = -67890,       /* The requested policy is not supported by the Timestamp Authority. */
+               TimestampUnacceptedExtension            = -67891,       /* The requested extension is not supported by the Timestamp Authority. */
+               TimestampAddInfoNotAvailable            = -67892,       /* The additional information requested is not available. */
+               TimestampSystemFailure                          = -67893,       /* The timestamp request cannot be handled due to system failure. */
+               SigningTimeMissing                                      = -67894,       /* A signing time was expected but was not found. */
+               TimestampRejection                                      = -67895,       /* A timestamp transaction was rejected. */
+               TimestampWaiting                    = -67896,   /* A timestamp transaction is waiting. */
+               TimestampRevocationWarning          = -67897,   /* A timestamp authority revocation warning was issued. */
+               TimestampRevocationNotification     = -67898,   /* A timestamp authority revocation notification was issued. */
        }
 
        // typedef uint32_t SecTrustResultType;
index 518f0b13fc25d9a68427b219e33a4f699450ce46..d968f83851475a711589e27ea27c34ea4d14cda0 100644 (file)
@@ -1,4 +1,4 @@
-#if SECURITY_DEP && MONO_FEATURE_APPLETLS
+#if SECURITY_DEP && MONO_FEATURE_APPLETLS
 // 
 // ImportExport.cs
 //
 
 using System;
 using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
 using ObjCRuntime;
 using Mono.Net;
 
+#if MONO_FEATURE_BTLS
+using Mono.Btls;
+#else
+using Mono.Security.Cryptography;
+#endif
+
 namespace Mono.AppleTls {
 
        internal partial class SecImportExport {
@@ -54,9 +62,161 @@ namespace Mono.AppleTls {
                        IntPtr handle;
                        SecStatusCode code = SecPKCS12Import (data.Handle, options.Handle, out handle);
                        array = CFArray.ArrayFromHandle <CFDictionary> (handle, h => new CFDictionary (h, false));
-                       CFObject.CFRelease (handle);
+                       if (handle != IntPtr.Zero)
+                               CFObject.CFRelease (handle);
                        return code;
                }
+
+               [DllImport (AppleTlsContext.SecurityLibrary)]
+               extern static SecStatusCode SecItemImport (
+                       /* CFDataRef */ IntPtr importedData,
+                       /* CFStringRef */ IntPtr fileNameOrExtension, // optional
+                       /* SecExternalFormat* */ ref SecExternalFormat inputFormat, // optional, IN/OUT
+                       /* SecExternalItemType* */ ref SecExternalItemType itemType, // optional, IN/OUT
+                       /* SecItemImportExportFlags */ SecItemImportExportFlags flags,
+                       /* const SecItemImportExportKeyParameters* */ IntPtr keyParams, // optional
+                       /* SecKeychainRef */ IntPtr importKeychain, // optional
+                       /* CFArrayRef* */ out IntPtr outItems);
+
+               static public CFArray ItemImport (byte[] buffer, string password)
+               {
+                       using (var data = CFData.FromData (buffer))
+                       using (var pwstring = CFString.Create (password)) {
+                               SecItemImportExportKeyParameters keyParams = new SecItemImportExportKeyParameters ();
+                               keyParams.passphrase = pwstring.Handle;
+
+                               return ItemImport (data, SecExternalFormat.PKCS12, SecExternalItemType.Aggregate, SecItemImportExportFlags.None, keyParams);
+                       }
+               }
+
+               static CFArray ItemImport (CFData data, SecExternalFormat format, SecExternalItemType itemType,
+                                          SecItemImportExportFlags flags = SecItemImportExportFlags.None,
+                                          SecItemImportExportKeyParameters? keyParams = null)
+               {
+                       return ItemImport (data, ref format, ref itemType, flags, keyParams);
+               }
+
+               static CFArray ItemImport (CFData data, ref SecExternalFormat format, ref SecExternalItemType itemType,
+                                          SecItemImportExportFlags flags = SecItemImportExportFlags.None,
+                                          SecItemImportExportKeyParameters? keyParams = null)
+               {
+                       IntPtr keyParamsPtr = IntPtr.Zero;
+                       if (keyParams != null) {
+                               keyParamsPtr = Marshal.AllocHGlobal (Marshal.SizeOf (keyParams.Value));
+                               if (keyParamsPtr == IntPtr.Zero)
+                                       throw new OutOfMemoryException ();
+                               Marshal.StructureToPtr (keyParams.Value, keyParamsPtr, false);
+                       }
+
+                       IntPtr result;
+                       var status = SecItemImport (data.Handle, IntPtr.Zero, ref format, ref itemType, flags, keyParamsPtr, IntPtr.Zero, out result);
+
+                       if (keyParamsPtr != IntPtr.Zero)
+                               Marshal.FreeHGlobal (keyParamsPtr);
+
+                       if (status != SecStatusCode.Success)
+                               throw new NotSupportedException (status.ToString ());
+
+                       return new CFArray (result, true);
+               }
+
+               [DllImport (AppleTlsContext.SecurityLibrary)]
+               extern static /* SecIdentityRef */ IntPtr SecIdentityCreate (
+                       /* CFAllocatorRef */ IntPtr allocator,
+                       /* SecCertificateRef */ IntPtr certificate,
+                       /* SecKeyRef */ IntPtr privateKey);
+
+               static public SecIdentity ItemImport (X509Certificate2 certificate)
+               {
+                       if (!certificate.HasPrivateKey)
+                               throw new NotSupportedException ();
+
+                       using (var key = ImportPrivateKey (certificate))
+                       using (var cert = new SecCertificate (certificate)) {
+                               var identity = SecIdentityCreate (IntPtr.Zero, cert.Handle, key.Handle);
+                               if (CFType.GetTypeID (identity) != SecIdentity.GetTypeID ())
+                                       throw new InvalidOperationException ();
+
+                               return new SecIdentity (identity, true);
+                       }
+               }
+
+               static byte[] ExportKey (RSA key)
+               {
+#if MONO_FEATURE_BTLS
+                       using (var btlsKey = MonoBtlsKey.CreateFromRSAPrivateKey (key))
+                               return btlsKey.GetBytes (true);
+#else
+                       return PKCS8.PrivateKeyInfo.Encode (key);
+#endif
+               }
+
+               static SecKey ImportPrivateKey (X509Certificate2 certificate)
+               {
+                       if (!certificate.HasPrivateKey)
+                               throw new NotSupportedException ();
+
+                       CFArray items;
+                       using (var data = CFData.FromData (ExportKey ((RSA)certificate.PrivateKey)))
+                               items = ItemImport (data, SecExternalFormat.OpenSSL, SecExternalItemType.PrivateKey);
+
+                       try {
+                               if (items.Count != 1)
+                                       throw new InvalidOperationException ("Private key import failed.");
+
+                               var imported = items[0];
+                               if (CFType.GetTypeID (imported) != SecKey.GetTypeID ())
+                                       throw new InvalidOperationException ("Private key import doesn't return SecKey.");
+
+                               return new SecKey (imported, items.Handle);
+                       } finally {
+                               items.Dispose ();
+                       }
+               }
+
+               const int SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION = 0;
+
+               // Native enum; don't change.
+               enum SecExternalFormat : int {
+                       Unknown = 0,
+                       OpenSSL = 1,
+                       X509Cert = 9,
+                       PEMSequence = 10,
+                       PKCS7 = 11,
+                       PKCS12 = 12
+               }
+
+               // Native enum; don't change.
+               enum SecExternalItemType : int {
+                       Unknown = 0,
+                       PrivateKey = 1,
+                       PublicKey = 2,
+                       SessionKey = 3,
+                       Certificate = 4,
+                       Aggregate = 5
+               }
+
+               // Native enum; don't change
+               enum SecItemImportExportFlags : int {
+                       None,
+                       PemArmour = 0x00000001,   /* exported blob is PEM formatted */
+               }
+
+               // Native struct; don't change
+               [StructLayout (LayoutKind.Sequential)]
+               struct SecItemImportExportKeyParameters {
+                       public int version;            /* SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION */
+                       public int flags;              /* SecKeyImportExportFlags bits */
+                       public IntPtr passphrase;      /* SecExternalFormat.PKCS12 only.  Legal types are CFStringRef and CFDataRef. */
+
+                       IntPtr alertTitle;
+                       IntPtr alertPrompt;
+
+                       public IntPtr accessRef;       /* SecAccessRef */
+
+                       IntPtr keyUsage;
+                       IntPtr keyAttributes;
+               }
        }
 }
 #endif
index 1c9fd0375e6933a88e670ab117d08d23c5829128..3eab7cc36f66681f4c9b7aade514374a30b9e505 100644 (file)
@@ -1,4 +1,4 @@
-#if SECURITY_DEP && MONO_FEATURE_APPLETLS
+#if SECURITY_DEP && MONO_FEATURE_APPLETLS
 // 
 // Items.cs: Implements the KeyChain query access APIs
 //
@@ -40,13 +40,26 @@ using Mono.Net;
 namespace Mono.AppleTls {
 
        enum SecKind {
-               Identity
+               Identity,
+               Certificate
        }
 
-       static class SecKeyChain {
-               static readonly IntPtr MatchLimitAll;
-               static readonly IntPtr MatchLimitOne;
-               static readonly IntPtr MatchLimit;
+       class SecKeyChain : INativeObject, IDisposable {
+               internal static readonly IntPtr MatchLimitAll;
+               internal static readonly IntPtr MatchLimitOne;
+               internal static readonly IntPtr MatchLimit;
+
+               IntPtr handle;
+
+               internal SecKeyChain (IntPtr handle, bool owns = false)
+               {
+                       if (handle == IntPtr.Zero)
+                               throw new ArgumentException ("Invalid handle");
+
+                       this.handle = handle;
+                       if (!owns)
+                               CFObject.CFRetain (handle);
+               }
 
                static SecKeyChain ()
                {
@@ -98,39 +111,47 @@ namespace Mono.AppleTls {
 
                        return null;
                }
-               
-               public static INativeObject[] QueryAsReference (SecRecord query, int max, out SecStatusCode result)
+
+               static INativeObject [] QueryAsReference (SecRecord query, int max, out SecStatusCode result)
                {
-                       if (query == null){
+                       if (query == null) {
                                result = SecStatusCode.Param;
                                return null;
                        }
 
-                       using (var copy = query.queryDict.MutableCopy ()) {
+                       using (var copy = query.QueryDict.MutableCopy ()) {
                                copy.SetValue (CFBoolean.True.Handle, SecItem.ReturnRef);
                                SetLimit (copy, max);
+                               return QueryAsReference (copy, out result);
+                       }
+               }
 
-                               IntPtr ptr;
-                               result = SecItem.SecItemCopyMatching (copy.Handle, out ptr);
-                               if ((result == SecStatusCode.Success) && (ptr != IntPtr.Zero)) {
-                                       var array = CFArray.ArrayFromHandle<INativeObject> (ptr, p => {
-                                               IntPtr cfType = CFType.GetTypeID (p);
-                                               if (cfType == SecCertificate.GetTypeID ())
-                                                       return new SecCertificate (p, true);
-                                               else if (cfType == SecKey.GetTypeID ())
-                                                       return new SecKey (p, true);
-                                               else if (cfType == SecIdentity.GetTypeID ())
-                                                       return new SecIdentity (p, true);
-                                               else
-                                                       throw new Exception (String.Format ("Unexpected type: 0x{0:x}", cfType));
-                                       });
-                                       return array;
-                               }
+               static INativeObject [] QueryAsReference (CFDictionary query, out SecStatusCode result)
+               {
+                       if (query == null) {
+                               result = SecStatusCode.Param;
                                return null;
                        }
+
+                       IntPtr ptr;
+                       result = SecItem.SecItemCopyMatching (query.Handle, out ptr);
+                       if (result == SecStatusCode.Success && ptr != IntPtr.Zero) {
+                               var array = CFArray.ArrayFromHandle<INativeObject> (ptr, p => {
+                                       IntPtr cfType = CFType.GetTypeID (p);
+                                       if (cfType == SecCertificate.GetTypeID ())
+                                               return new SecCertificate (p, true);
+                                       if (cfType == SecKey.GetTypeID ())
+                                               return new SecKey (p, true);
+                                       if (cfType == SecIdentity.GetTypeID ())
+                                               return new SecIdentity (p, true);
+                                       throw new Exception (String.Format ("Unexpected type: 0x{0:x}", cfType));
+                               });
+                               return array;
+                       }
+                       return null;
                }
 
-               static CFNumber SetLimit (CFMutableDictionary dict, int max)
+               internal static CFNumber SetLimit (CFMutableDictionary dict, int max)
                {
                        CFNumber n = null;
                        IntPtr val;
@@ -142,47 +163,110 @@ namespace Mono.AppleTls {
                                n = CFNumber.FromInt32 (max);
                                val = n.Handle;
                        }
-                       
+
                        dict.SetValue (val, SecKeyChain.MatchLimit);
                        return n;
                }
+
+               [DllImport (AppleTlsContext.SecurityLibrary)]
+               extern static /* OSStatus */ SecStatusCode SecKeychainCreate (/* const char * */ IntPtr pathName, uint passwordLength, /* const void * */ IntPtr password,
+                                                                             bool promptUser, /* SecAccessRef */ IntPtr initialAccess,
+                                                                             /* SecKeychainRef  _Nullable * */ out IntPtr keychain);
+
+               internal static SecKeyChain Create (string pathName, string password)
+               {
+                       IntPtr handle;
+                       var pathNamePtr = Marshal.StringToHGlobalAnsi (pathName);
+                       var passwordPtr = Marshal.StringToHGlobalAnsi (password);
+                       var result = SecKeychainCreate (pathNamePtr, (uint)password.Length, passwordPtr, false, IntPtr.Zero, out handle);
+                       if (result != SecStatusCode.Success)
+                               throw new InvalidOperationException (result.ToString ());
+                       return new SecKeyChain (handle, true);
+               }
+
+               [DllImport (AppleTlsContext.SecurityLibrary)]
+               extern static /* OSStatus */ SecStatusCode SecKeychainOpen (/* const char * */ IntPtr pathName, /* SecKeychainRef  _Nullable * */ out IntPtr keychain);
+
+               internal static SecKeyChain Open (string pathName)
+               {
+                       IntPtr handle;
+                       IntPtr pathNamePtr = IntPtr.Zero;
+                       try {
+                               pathNamePtr = Marshal.StringToHGlobalAnsi (pathName);
+                               var result = SecKeychainOpen (pathNamePtr, out handle);
+                               if (result != SecStatusCode.Success)
+                                       throw new InvalidOperationException (result.ToString ());
+                               return new SecKeyChain (handle, true);
+                       } finally {
+                               if (pathNamePtr != IntPtr.Zero)
+                                       Marshal.FreeHGlobal (pathNamePtr);
+                       }
+               }
+
+               internal static SecKeyChain OpenSystemRootCertificates ()
+               {
+                       return Open ("/System/Library/Keychains/SystemRootCertificates.keychain");
+               }
+
+               ~SecKeyChain ()
+               {
+                       Dispose (false);
+               }
+
+               public IntPtr Handle {
+                       get {
+                               return handle;
+                       }
+               }
+
+               public void Dispose ()
+               {
+                       Dispose (true);
+                       GC.SuppressFinalize (this);
+               }
+
+               protected virtual void Dispose (bool disposing)
+               {
+                       if (handle != IntPtr.Zero) {
+                               CFObject.CFRelease (handle);
+                               handle = IntPtr.Zero;
+                       }
+               }
        }
-       
+
        class SecRecord : IDisposable {
 
-               static readonly IntPtr SecClassKey;
+               internal static readonly IntPtr SecClassKey;
                static SecRecord ()
                {
                        var handle = CFObject.dlopen (AppleTlsContext.SecurityLibrary, 0);
                        if (handle == IntPtr.Zero)
                                return;
 
-                       try {           
-                               SecClassKey = CFObject.GetIntPtr (handle, "kSecClassKey");
+                       try {
+                               SecClassKey = CFObject.GetIntPtr (handle, "kSecClass");
                        } finally {
                                CFObject.dlclose (handle);
                        }
                }
 
-               // Fix <= iOS 6 Behaviour - Desk #83099
-               // NSCFDictionary: mutating method sent to immutable object
-               // iOS 6 returns an inmutable NSDictionary handle and when we try to set its values it goes kaboom
-               // By explicitly calling `MutableCopy` we ensure we always have a mutable reference we expect that.
-               CFDictionary _queryDict;
-               internal CFDictionary queryDict 
-               { 
+               CFMutableDictionary _queryDict;
+               internal CFMutableDictionary QueryDict {
                        get {
                                return _queryDict;
                        }
-                       set {
-                               _queryDict = value != null ? value.Copy () : null;
-                       }
+               }
+
+               internal void SetValue (IntPtr key, IntPtr value)
+               {
+                       _queryDict.SetValue (key, value);
                }
 
                public SecRecord (SecKind secKind)
                {
                        var kind = SecClass.FromSecKind (secKind);
-                       queryDict = CFDictionary.FromObjectAndKey (kind, SecClassKey);
+                       _queryDict = CFMutableDictionary.Create ();
+                       _queryDict.SetValue (SecClassKey, kind);
                }
 
                public void Dispose ()
@@ -193,10 +277,10 @@ namespace Mono.AppleTls {
 
                protected virtual void Dispose (bool disposing)
                {
-                       if (queryDict != null){
+                       if (_queryDict != null){
                                if (disposing){
-                                       queryDict.Dispose ();
-                                       queryDict = null;
+                                       _queryDict.Dispose ();
+                                       _queryDict = null;
                                }
                        }
                }
@@ -209,6 +293,7 @@ namespace Mono.AppleTls {
        
        partial class SecItem {
                public static readonly IntPtr ReturnRef;
+               public static readonly IntPtr MatchSearchList;
                
                static SecItem ()
                {
@@ -218,6 +303,7 @@ namespace Mono.AppleTls {
 
                        try {           
                                ReturnRef = CFObject.GetIntPtr (handle, "kSecReturnRef");
+                               MatchSearchList = CFObject.GetIntPtr (handle, "kSecMatchSearchList");
                        } finally {
                                CFObject.dlclose (handle);
                        }
@@ -230,6 +316,7 @@ namespace Mono.AppleTls {
        static partial class SecClass {
        
                public static readonly IntPtr Identity;
+               public static readonly IntPtr Certificate;
                
                static SecClass ()
                {
@@ -237,8 +324,9 @@ namespace Mono.AppleTls {
                        if (handle == IntPtr.Zero)
                                return;
 
-                       try {           
+                       try {
                                Identity = CFObject.GetIntPtr (handle, "kSecClassIdentity");
+                               Certificate = CFObject.GetIntPtr (handle, "kSecClassCertificate");
                        } finally {
                                CFObject.dlclose (handle);
                        }
@@ -249,6 +337,8 @@ namespace Mono.AppleTls {
                        switch (secKind){
                        case SecKind.Identity:
                                return Identity;
+                       case SecKind.Certificate:
+                               return Certificate;
                        default:
                                throw new ArgumentException ("secKind");
                        }
index bd2cdc10b9876881f10ee913c5dac33e576bc6de..7dbb89571587bcbeff55aac49630cac7084296d4 100644 (file)
@@ -469,7 +469,18 @@ namespace Mono.Net
                {
                        return new CFDictionary (CFDictionaryCreate (IntPtr.Zero, new IntPtr[] { key }, new IntPtr [] { obj }, (IntPtr)1, KeyCallbacks, ValueCallbacks), true);
                }
-               
+
+               public static CFDictionary FromKeysAndObjects (IList<Tuple<IntPtr,IntPtr>> items)
+               {
+                       var keys = new IntPtr [items.Count];
+                       var values = new IntPtr [items.Count];
+                       for (int i = 0; i < items.Count; i++) {
+                               keys [i] = items [i].Item1;
+                               values [i] = items [i].Item2;
+                       }
+                       return new CFDictionary (CFDictionaryCreate (IntPtr.Zero, keys, values, (IntPtr)items.Count, KeyCallbacks, ValueCallbacks), true);
+               }
+
                [DllImport (CoreFoundationLibrary)]
                extern static IntPtr CFDictionaryCreate (IntPtr allocator, IntPtr[] keys, IntPtr[] vals, IntPtr len, IntPtr keyCallbacks, IntPtr valCallbacks);
 
@@ -513,8 +524,20 @@ namespace Mono.Net
                        CFDictionarySetValue (Handle, key, val);
                }
 
+               public static CFMutableDictionary Create ()
+               {
+                       var handle = CFDictionaryCreateMutable (IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
+                       if (handle == IntPtr.Zero)
+                               throw new InvalidOperationException ();
+                       return new CFMutableDictionary (handle, true);
+               }
+
                [DllImport (CoreFoundationLibrary)]
                extern static void CFDictionarySetValue (IntPtr handle, IntPtr key, IntPtr val);
+
+               [DllImport (CoreFoundationLibrary)]
+               extern static IntPtr CFDictionaryCreateMutable (IntPtr allocator, IntPtr capacity, IntPtr keyCallback, IntPtr valueCallbacks);
+
        }
 
        internal class CFUrl : CFObject