1 #if SECURITY_DEP && MONO_FEATURE_APPLETLS
6 // Martin Baulig <martin.baulig@xamarin.com>
8 // Copyright (c) 2015 Xamarin, Inc.
11 #if MONO_SECURITY_ALIAS
12 extern alias MonoSecurity;
19 using System.Globalization;
20 using System.Collections;
21 using System.Collections.Generic;
22 using System.Threading;
23 using System.Threading.Tasks;
24 using System.Runtime.InteropServices;
25 using SSA = System.Security.Authentication;
26 using System.Security.Cryptography.X509Certificates;
28 #if MONO_SECURITY_ALIAS
29 using MonoSecurity::Mono.Security.Interface;
31 using Mono.Security.Interface;
35 using Mono.Net.Security;
37 using ObjCRuntimeInternal;
39 namespace Mono.AppleTls
41 class AppleTlsContext : MobileTlsContext
43 public const string SecurityLibrary = "/System/Library/Frameworks/Security.framework/Security";
49 SslWriteFunc writeFunc;
51 SecIdentity serverIdentity;
52 SecIdentity clientIdentity;
54 X509Certificate remoteCertificate;
55 X509Certificate localClientCertificate;
56 MonoTlsConnectionInfo connectionInfo;
59 bool handshakeFinished;
67 Exception lastException;
69 public AppleTlsContext (
70 MobileAuthenticatedStream parent, bool serverMode, string targetHost,
71 SSA.SslProtocols enabledProtocols, X509Certificate serverCertificate,
72 X509CertificateCollection clientCertificates, bool askForClientCert)
73 : base (parent, serverMode, targetHost, enabledProtocols,
74 serverCertificate, clientCertificates, askForClientCert)
76 handle = GCHandle.Alloc (this, GCHandleType.Weak);
77 readFunc = NativeReadCallback;
78 writeFunc = NativeWriteCallback;
81 if (serverCertificate == null)
82 throw new ArgumentNullException ("serverCertificate");
86 public IntPtr Handle {
89 throw new ObjectDisposedException ("AppleTlsContext");
94 public override bool HasContext {
95 get { return !disposed && context != IntPtr.Zero; }
98 void CheckStatusAndThrow (SslStatus status, params SslStatus[] acceptable)
100 var last = Interlocked.Exchange (ref lastException, null);
104 if (status == SslStatus.Success || Array.IndexOf (acceptable, status) > -1)
108 case SslStatus.ClosedAbort:
109 throw new IOException ("Connection closed.");
111 case SslStatus.BadCert:
112 throw new TlsException (AlertDescription.BadCertificate);
114 case SslStatus.UnknownRootCert:
115 case SslStatus.NoRootCert:
116 case SslStatus.XCertChainInvalid:
117 throw new TlsException (AlertDescription.CertificateUnknown, status.ToString ());
119 case SslStatus.CertExpired:
120 case SslStatus.CertNotYetValid:
121 throw new TlsException (AlertDescription.CertificateExpired);
123 case SslStatus.Protocol:
124 throw new TlsException (AlertDescription.ProtocolVersion);
127 throw new TlsException (AlertDescription.InternalError, "Unknown Secure Transport error `{0}'.", status);
133 public override bool IsAuthenticated {
134 get { return isAuthenticated; }
137 public override void StartHandshake ()
139 Debug ("StartHandshake: {0}", IsServer);
141 if (Interlocked.CompareExchange (ref handshakeStarted, 1, 1) != 0)
142 throw new InvalidOperationException ();
144 InitializeConnection ();
146 SetSessionOption (SslSessionOption.BreakOnCertRequested, true);
147 SetSessionOption (SslSessionOption.BreakOnClientAuth, true);
148 SetSessionOption (SslSessionOption.BreakOnServerAuth, true);
151 SecCertificate[] intermediateCerts;
152 serverIdentity = AppleCertificateHelper.GetIdentity (LocalServerCertificate, out intermediateCerts);
153 if (serverIdentity == null)
154 throw new SSA.AuthenticationException ("Unable to get server certificate from keychain.");
156 SetCertificate (serverIdentity, intermediateCerts);
157 for (int i = 0; i < intermediateCerts.Length; i++)
158 intermediateCerts [i].Dispose ();
162 public override void FinishHandshake ()
164 InitializeSession ();
166 isAuthenticated = true;
169 public override void Flush ()
173 public override bool ProcessHandshake ()
175 if (handshakeFinished)
176 throw new NotSupportedException ("Handshake already finished.");
179 lastException = null;
180 var status = SSLHandshake (Handle);
181 Debug ("Handshake: {0} - {0:x}", status);
183 CheckStatusAndThrow (status, SslStatus.WouldBlock, SslStatus.PeerAuthCompleted, SslStatus.PeerClientCertRequested);
185 if (status == SslStatus.PeerAuthCompleted) {
187 } else if (status == SslStatus.PeerClientCertRequested) {
189 if (remoteCertificate == null)
190 throw new TlsException (AlertDescription.InternalError, "Cannot request client certificate before receiving one from the server.");
191 localClientCertificate = SelectClientCertificate (remoteCertificate, null);
192 if (localClientCertificate == null)
194 clientIdentity = AppleCertificateHelper.GetIdentity (localClientCertificate);
195 if (clientIdentity == null)
196 throw new TlsException (AlertDescription.CertificateUnknown);
197 SetCertificate (clientIdentity, new SecCertificate [0]);
198 } else if (status == SslStatus.WouldBlock) {
200 } else if (status == SslStatus.Success) {
201 handshakeFinished = true;
207 void RequirePeerTrust ()
209 if (!havePeerTrust) {
211 havePeerTrust = true;
215 void EvaluateTrust ()
217 InitializeSession ();
220 * We're using .NET's SslStream semantics here.
222 * A server must always provide a valid certificate.
224 * However, in server mode, "ask for client certificate" means that
225 * we ask the client to provide a certificate, then invoke the client
226 * certificate validator - passing 'null' if the client didn't provide
232 SecTrust trust = null;
233 X509CertificateCollection certificates = null;
236 trust = GetPeerTrust (!IsServer);
238 if (trust == null || trust.Count == 0) {
239 remoteCertificate = null;
241 throw new TlsException (AlertDescription.CertificateUnknown);
245 Debug ("WARNING: Got multiple certificates in SecTrust!");
247 certificates = new X509CertificateCollection ();
248 for (int i = 0; i < trust.Count; i++)
249 certificates.Add (trust.GetCertificate (i));
251 remoteCertificate = new X509Certificate (certificates [0]);
252 Debug ("Got peer trust: {0}", remoteCertificate);
255 ok = ValidateCertificate (certificates);
256 } catch (Exception ex) {
257 Debug ("Certificate validation failed: {0}", ex);
258 throw new TlsException (AlertDescription.CertificateUnknown, "Certificate validation threw exception.");
262 if (certificates != null) {
263 for (int i = 0; i < certificates.Count; i++)
264 certificates [i].Dispose ();
269 throw new TlsException (AlertDescription.CertificateUnknown);
272 void InitializeConnection ()
274 context = SSLCreateContext (IntPtr.Zero, IsServer ? SslProtocolSide.Server : SslProtocolSide.Client, SslConnectionType.Stream);
276 var result = SSLSetIOFuncs (Handle, readFunc, writeFunc);
277 CheckStatusAndThrow (result);
279 result = SSLSetConnection (Handle, GCHandle.ToIntPtr (handle));
280 CheckStatusAndThrow (result);
282 if ((EnabledProtocols & SSA.SslProtocols.Tls) != 0)
283 MinProtocol = SslProtocol.Tls_1_0;
284 else if ((EnabledProtocols & SSA.SslProtocols.Tls11) != 0)
285 MinProtocol = SslProtocol.Tls_1_1;
287 MinProtocol = SslProtocol.Tls_1_2;
289 if ((EnabledProtocols & SSA.SslProtocols.Tls12) != 0)
290 MaxProtocol = SslProtocol.Tls_1_2;
291 else if ((EnabledProtocols & SSA.SslProtocols.Tls11) != 0)
292 MaxProtocol = SslProtocol.Tls_1_1;
294 MaxProtocol = SslProtocol.Tls_1_0;
296 if (Settings != null && Settings.EnabledCiphers != null) {
297 SslCipherSuite [] ciphers = new SslCipherSuite [Settings.EnabledCiphers.Length];
298 for (int i = 0 ; i < Settings.EnabledCiphers.Length; ++i)
299 ciphers [i] = (SslCipherSuite)Settings.EnabledCiphers[i];
300 SetEnabledCiphers (ciphers);
303 if (AskForClientCertificate)
304 SetClientSideAuthenticate (SslAuthenticate.Try);
307 if (!IsServer && !string.IsNullOrEmpty (TargetHost) &&
308 !IPAddress.TryParse (TargetHost, out address)) {
309 PeerDomainName = ServerName;
313 void InitializeSession ()
315 if (connectionInfo != null)
318 var cipher = NegotiatedCipher;
319 var protocol = GetNegotiatedProtocolVersion ();
320 Debug ("GET CONNECTION INFO: {0:x}:{0} {1:x}:{1} {2}", cipher, protocol, (TlsProtocolCode)protocol);
322 connectionInfo = new MonoTlsConnectionInfo {
323 CipherSuiteCode = (CipherSuiteCode)cipher,
324 ProtocolVersion = GetProtocol (protocol),
325 PeerDomainName = PeerDomainName
329 static TlsProtocols GetProtocol (SslProtocol protocol)
332 case SslProtocol.Tls_1_0:
333 return TlsProtocols.Tls10;
334 case SslProtocol.Tls_1_1:
335 return TlsProtocols.Tls11;
336 case SslProtocol.Tls_1_2:
337 return TlsProtocols.Tls12;
339 throw new NotSupportedException ();
343 public override MonoTlsConnectionInfo ConnectionInfo {
344 get { return connectionInfo; }
347 internal override bool IsRemoteCertificateAvailable {
348 get { return remoteCertificate != null; }
351 internal override X509Certificate LocalClientCertificate {
352 get { return localClientCertificate; }
355 public override X509Certificate RemoteCertificate {
356 get { return remoteCertificate; }
359 public override TlsProtocols NegotiatedProtocol {
360 get { return connectionInfo.ProtocolVersion; }
365 #region General P/Invokes
367 [DllImport (SecurityLibrary )]
368 extern static /* OSStatus */ SslStatus SSLGetProtocolVersionMax (/* SSLContextRef */ IntPtr context, out SslProtocol maxVersion);
370 [DllImport (SecurityLibrary)]
371 extern static /* OSStatus */ SslStatus SSLSetProtocolVersionMax (/* SSLContextRef */ IntPtr context, SslProtocol maxVersion);
373 public SslProtocol MaxProtocol {
376 var result = SSLGetProtocolVersionMax (Handle, out value);
377 CheckStatusAndThrow (result);
381 var result = SSLSetProtocolVersionMax (Handle, value);
382 CheckStatusAndThrow (result);
386 [DllImport (SecurityLibrary)]
387 extern static /* OSStatus */ SslStatus SSLGetProtocolVersionMin (/* SSLContextRef */ IntPtr context, out SslProtocol minVersion);
389 [DllImport (SecurityLibrary)]
390 extern static /* OSStatus */ SslStatus SSLSetProtocolVersionMin (/* SSLContextRef */ IntPtr context, SslProtocol minVersion);
392 public SslProtocol MinProtocol {
395 var result = SSLGetProtocolVersionMin (Handle, out value);
396 CheckStatusAndThrow (result);
400 var result = SSLSetProtocolVersionMin (Handle, value);
401 CheckStatusAndThrow (result);
405 [DllImport (SecurityLibrary)]
406 extern static /* OSStatus */ SslStatus SSLGetNegotiatedProtocolVersion (/* SSLContextRef */ IntPtr context, out SslProtocol protocol);
408 public SslProtocol GetNegotiatedProtocolVersion ()
411 var result = SSLGetNegotiatedProtocolVersion (Handle, out value);
412 CheckStatusAndThrow (result);
416 [DllImport (SecurityLibrary)]
417 extern static /* OSStatus */ SslStatus SSLGetSessionOption (/* SSLContextRef */ IntPtr context, SslSessionOption option, out bool value);
419 public bool GetSessionOption (SslSessionOption option)
422 var result = SSLGetSessionOption (Handle, option, out value);
423 CheckStatusAndThrow (result);
427 [DllImport (SecurityLibrary)]
428 extern static /* OSStatus */ SslStatus SSLSetSessionOption (/* SSLContextRef */ IntPtr context, SslSessionOption option, bool value);
430 public void SetSessionOption (SslSessionOption option, bool value)
432 var result = SSLSetSessionOption (Handle, option, value);
433 CheckStatusAndThrow (result);
436 [DllImport (SecurityLibrary)]
437 extern static /* OSStatus */ SslStatus SSLSetClientSideAuthenticate (/* SSLContextRef */ IntPtr context, SslAuthenticate auth);
439 public void SetClientSideAuthenticate (SslAuthenticate auth)
441 var result = SSLSetClientSideAuthenticate (Handle, auth);
442 CheckStatusAndThrow (result);
445 [DllImport (SecurityLibrary)]
446 extern static /* OSStatus */ SslStatus SSLHandshake (/* SSLContextRef */ IntPtr context);
448 [DllImport (SecurityLibrary)]
449 extern static /* OSStatus */ SslStatus SSLGetSessionState (/* SSLContextRef */ IntPtr context, ref SslSessionState state);
451 public SslSessionState SessionState {
453 var value = SslSessionState.Invalid;
454 var result = SSLGetSessionState (Handle, ref value);
455 CheckStatusAndThrow (result);
460 [DllImport (SecurityLibrary)]
461 extern unsafe static /* OSStatus */ SslStatus SSLGetPeerID (/* SSLContextRef */ IntPtr context, /* const void** */ out IntPtr peerID, /* size_t* */ out IntPtr peerIDLen);
463 [DllImport (SecurityLibrary)]
464 extern unsafe static /* OSStatus */ SslStatus SSLSetPeerID (/* SSLContextRef */ IntPtr context, /* const void* */ byte* peerID, /* size_t */ IntPtr peerIDLen);
466 public unsafe byte[] PeerId {
470 var result = SSLGetPeerID (Handle, out id, out length);
471 CheckStatusAndThrow (result);
472 if ((result != SslStatus.Success) || ((int)length == 0))
474 var data = new byte [(int)length];
475 Marshal.Copy (id, data, 0, (int) length);
480 IntPtr length = (value == null) ? IntPtr.Zero : (IntPtr)value.Length;
481 fixed (byte *p = value) {
482 result = SSLSetPeerID (Handle, p, length);
484 CheckStatusAndThrow (result);
488 [DllImport (SecurityLibrary)]
489 extern unsafe static /* OSStatus */ SslStatus SSLGetBufferedReadSize (/* SSLContextRef */ IntPtr context, /* size_t* */ out IntPtr bufSize);
491 public IntPtr BufferedReadSize {
494 var result = SSLGetBufferedReadSize (Handle, out value);
495 CheckStatusAndThrow (result);
500 [DllImport (SecurityLibrary)]
501 extern unsafe static /* OSStatus */ SslStatus SSLGetNumberSupportedCiphers (/* SSLContextRef */ IntPtr context, /* size_t* */ out IntPtr numCiphers);
503 [DllImport (SecurityLibrary)]
504 extern unsafe static /* OSStatus */ SslStatus SSLGetSupportedCiphers (/* SSLContextRef */ IntPtr context, SslCipherSuite *ciphers, /* size_t* */ ref IntPtr numCiphers);
506 public unsafe IList<SslCipherSuite> GetSupportedCiphers ()
509 var result = SSLGetNumberSupportedCiphers (Handle, out n);
510 CheckStatusAndThrow (result);
511 if ((result != SslStatus.Success) || ((int)n <= 0))
514 var ciphers = new SslCipherSuite [(int)n];
515 fixed (SslCipherSuite *p = ciphers) {
516 result = SSLGetSupportedCiphers (Handle, p, ref n);
518 CheckStatusAndThrow (result);
522 [DllImport (SecurityLibrary)]
523 extern unsafe static /* OSStatus */ SslStatus SSLGetNumberEnabledCiphers (/* SSLContextRef */ IntPtr context, /* size_t* */ out IntPtr numCiphers);
525 [DllImport (SecurityLibrary)]
526 extern unsafe static /* OSStatus */ SslStatus SSLGetEnabledCiphers (/* SSLContextRef */ IntPtr context, SslCipherSuite *ciphers, /* size_t* */ ref IntPtr numCiphers);
528 public unsafe IList<SslCipherSuite> GetEnabledCiphers ()
531 var result = SSLGetNumberEnabledCiphers (Handle, out n);
532 CheckStatusAndThrow (result);
533 if ((result != SslStatus.Success) || ((int)n <= 0))
536 var ciphers = new SslCipherSuite [(int)n];
537 fixed (SslCipherSuite *p = ciphers) {
538 result = SSLGetEnabledCiphers (Handle, p, ref n);
540 CheckStatusAndThrow (result);
544 [DllImport (SecurityLibrary)]
545 extern unsafe static /* OSStatus */ SslStatus SSLSetEnabledCiphers (/* SSLContextRef */ IntPtr context, SslCipherSuite *ciphers, /* size_t */ IntPtr numCiphers);
547 public unsafe void SetEnabledCiphers (SslCipherSuite [] ciphers)
550 throw new ArgumentNullException ("ciphers");
554 fixed (SslCipherSuite *p = ciphers)
555 result = SSLSetEnabledCiphers (Handle, p, (IntPtr)ciphers.Length);
556 CheckStatusAndThrow (result);
559 [DllImport (SecurityLibrary)]
560 extern unsafe static /* OSStatus */ SslStatus SSLGetNegotiatedCipher (/* SSLContextRef */ IntPtr context, /* SslCipherSuite* */ out SslCipherSuite cipherSuite);
562 public SslCipherSuite NegotiatedCipher {
564 SslCipherSuite value;
565 var result = SSLGetNegotiatedCipher (Handle, out value);
566 CheckStatusAndThrow (result);
571 [DllImport (SecurityLibrary)]
572 extern unsafe static /* OSStatus */ SslStatus SSLGetPeerDomainNameLength (/* SSLContextRef */ IntPtr context, /* size_t* */ out IntPtr peerNameLen);
574 [DllImport (SecurityLibrary)]
575 extern unsafe static /* OSStatus */ SslStatus SSLGetPeerDomainName (/* SSLContextRef */ IntPtr context, /* char* */ byte[] peerName, /* size_t */ ref IntPtr peerNameLen);
577 [DllImport (SecurityLibrary)]
578 extern unsafe static /* OSStatus */ SslStatus SSLSetPeerDomainName (/* SSLContextRef */ IntPtr context, /* char* */ byte[] peerName, /* size_t */ IntPtr peerNameLen);
580 public string PeerDomainName {
583 var result = SSLGetPeerDomainNameLength (Handle, out length);
584 CheckStatusAndThrow (result);
585 if (result != SslStatus.Success || (int)length == 0)
587 var bytes = new byte [(int)length];
588 result = SSLGetPeerDomainName (Handle, bytes, ref length);
589 CheckStatusAndThrow (result);
591 int peerDomainLength = (int)length;
593 if (result != SslStatus.Success)
595 if (peerDomainLength > 0 && bytes [peerDomainLength-1] == 0)
596 peerDomainLength = peerDomainLength - 1;
597 return Encoding.UTF8.GetString (bytes, 0, peerDomainLength);
602 result = SSLSetPeerDomainName (Handle, null, (IntPtr)0);
604 var bytes = Encoding.UTF8.GetBytes (value);
605 result = SSLSetPeerDomainName (Handle, bytes, (IntPtr)bytes.Length);
607 CheckStatusAndThrow (result);
611 [DllImport (SecurityLibrary)]
612 extern unsafe static /* OSStatus */ SslStatus SSLSetCertificate (/* SSLContextRef */ IntPtr context, /* CFArrayRef */ IntPtr certRefs);
614 CFArray Bundle (SecIdentity identity, IEnumerable<SecCertificate> certificates)
616 if (identity == null)
617 throw new ArgumentNullException ("identity");
621 if (certificates != null) {
622 foreach (var obj in certificates)
626 var ptrs = new IntPtr [n + 1];
627 ptrs [0] = identity.Handle;
628 foreach (var certificate in certificates)
629 ptrs [++i] = certificate.Handle;
630 return CFArray.CreateArray (ptrs);
633 public void SetCertificate (SecIdentity identify, IEnumerable<SecCertificate> certificates)
635 using (var array = Bundle (identify, certificates)) {
636 var result = SSLSetCertificate (Handle, array.Handle);
637 CheckStatusAndThrow (result);
641 [DllImport (SecurityLibrary)]
642 extern unsafe static /* OSStatus */ SslStatus SSLGetClientCertificateState (/* SSLContextRef */ IntPtr context, out SslClientCertificateState clientState);
644 public SslClientCertificateState ClientCertificateState {
646 SslClientCertificateState value;
647 var result = SSLGetClientCertificateState (Handle, out value);
648 CheckStatusAndThrow (result);
653 [DllImport (SecurityLibrary)]
654 extern unsafe static /* OSStatus */ SslStatus SSLCopyPeerTrust (/* SSLContextRef */ IntPtr context, /* SecTrustRef */ out IntPtr trust);
656 public SecTrust GetPeerTrust (bool requireTrust)
659 var result = SSLCopyPeerTrust (Handle, out value);
661 CheckStatusAndThrow (result);
662 if (value == IntPtr.Zero)
663 throw new TlsException (AlertDescription.CertificateUnknown);
665 return (value == IntPtr.Zero) ? null : new SecTrust (value, true);
672 [DllImport (SecurityLibrary)]
673 extern static /* SSLContextRef */ IntPtr SSLCreateContext (/* CFAllocatorRef */ IntPtr alloc, SslProtocolSide protocolSide, SslConnectionType connectionType);
675 [DllImport (SecurityLibrary)]
676 extern static /* OSStatus */ SslStatus SSLSetConnection (/* SSLContextRef */ IntPtr context, /* SSLConnectionRef */ IntPtr connection);
678 [DllImport (SecurityLibrary)]
679 extern static /* OSStatus */ SslStatus SSLSetIOFuncs (/* SSLContextRef */ IntPtr context, /* SSLReadFunc */ SslReadFunc readFunc, /* SSLWriteFunc */ SslWriteFunc writeFunc);
681 [Mono.Util.MonoPInvokeCallback (typeof (SslReadFunc))]
682 static SslStatus NativeReadCallback (IntPtr ptr, IntPtr data, ref IntPtr dataLength)
684 AppleTlsContext context = null;
686 var weakHandle = GCHandle.FromIntPtr (ptr);
687 if (!weakHandle.IsAllocated)
688 return SslStatus.Internal;
690 context = (AppleTlsContext) weakHandle.Target;
691 if (context == null || context.disposed)
692 return SslStatus.ClosedAbort;
694 return context.NativeReadCallback (data, ref dataLength);
695 } catch (Exception ex) {
696 if (context != null && context.lastException == null)
697 context.lastException = ex;
698 return SslStatus.Internal;
702 [Mono.Util.MonoPInvokeCallback (typeof (SslWriteFunc))]
703 static SslStatus NativeWriteCallback (IntPtr ptr, IntPtr data, ref IntPtr dataLength)
705 AppleTlsContext context = null;
707 var weakHandle = GCHandle.FromIntPtr (ptr);
708 if (!weakHandle.IsAllocated)
709 return SslStatus.Internal;
711 context = (AppleTlsContext) weakHandle.Target;
712 if (context == null || context.disposed)
713 return SslStatus.ClosedAbort;
715 return context.NativeWriteCallback (data, ref dataLength);
716 } catch (Exception ex) {
717 if (context != null && context.lastException == null)
718 context.lastException = ex;
719 return SslStatus.Internal;
723 SslStatus NativeReadCallback (IntPtr data, ref IntPtr dataLength)
725 if (closed || disposed || Parent == null)
726 return SslStatus.ClosedAbort;
728 var len = (int)dataLength;
729 var readBuffer = new byte [len];
731 Debug ("NativeReadCallback: {0} {1}", dataLength, len);
734 var ret = Parent.InternalRead (readBuffer, 0, len, out wantMore);
735 dataLength = (IntPtr)ret;
737 Debug ("NativeReadCallback #1: {0} - {1} {2}", len, ret, wantMore);
740 return SslStatus.ClosedAbort;
742 Marshal.Copy (readBuffer, 0, data, ret);
745 return SslStatus.Success;
747 return SslStatus.WouldBlock;
749 closedGraceful = true;
750 return SslStatus.ClosedGraceful;
752 return SslStatus.Success;
756 SslStatus NativeWriteCallback (IntPtr data, ref IntPtr dataLength)
758 if (closed || disposed || Parent == null)
759 return SslStatus.ClosedAbort;
761 var len = (int)dataLength;
762 var writeBuffer = new byte [len];
764 Marshal.Copy (data, writeBuffer, 0, len);
766 Debug ("NativeWriteCallback: {0}", len);
768 var ok = Parent.InternalWrite (writeBuffer, 0, len);
770 Debug ("NativeWriteCallback done: {0} {1}", len, ok);
772 return ok ? SslStatus.Success : SslStatus.ClosedAbort;
775 [DllImport (SecurityLibrary)]
776 extern unsafe static /* OSStatus */ SslStatus SSLRead (/* SSLContextRef */ IntPtr context, /* const void* */ byte* data, /* size_t */ IntPtr dataLength, /* size_t* */ out IntPtr processed);
778 public override unsafe (int ret, bool wantMore) Read (byte[] buffer, int offset, int count)
780 if (Interlocked.Exchange (ref pendingIO, 1) == 1)
781 throw new InvalidOperationException ();
783 Debug ("Read: {0},{1}", offset, count);
785 lastException = null;
791 fixed (byte *d = &buffer [offset])
792 status = SSLRead (Handle, d, (IntPtr)count, out processed);
794 Debug ("Read done: {0} {1} {2}", status, count, processed);
796 if (closedGraceful && (status == SslStatus.ClosedAbort || status == SslStatus.ClosedGraceful)) {
798 * This is really ugly, but unfortunately SSLRead() also returns 'SslStatus.ClosedAbort'
799 * when the first inner Read() returns 0. MobileAuthenticatedStream.InnerRead() attempts
800 * to distinguish between a graceful close and abnormal termination of connection.
805 CheckStatusAndThrow (status, SslStatus.WouldBlock, SslStatus.ClosedGraceful);
806 var wantMore = status == SslStatus.WouldBlock;
807 return ((int)processed, wantMore);
808 } catch (Exception ex) {
809 Debug ("Read error: {0}", ex);
816 [DllImport (SecurityLibrary)]
817 extern unsafe static /* OSStatus */ SslStatus SSLWrite (/* SSLContextRef */ IntPtr context, /* const void* */ byte* data, /* size_t */ IntPtr dataLength, /* size_t* */ out IntPtr processed);
819 public override unsafe (int ret, bool wantMore) Write (byte[] buffer, int offset, int count)
821 if (Interlocked.Exchange (ref pendingIO, 1) == 1)
822 throw new InvalidOperationException ();
824 Debug ("Write: {0},{1}", offset, count);
826 lastException = null;
829 SslStatus status = SslStatus.ClosedAbort;
830 IntPtr processed = (IntPtr)(-1);
832 fixed (byte *d = &buffer [offset])
833 status = SSLWrite (Handle, d, (IntPtr)count, out processed);
835 Debug ("Write done: {0} {1}", status, processed);
837 CheckStatusAndThrow (status, SslStatus.WouldBlock);
839 var wantMore = status == SslStatus.WouldBlock;
840 return ((int)processed, wantMore);
846 [DllImport (SecurityLibrary)]
847 extern static /* OSStatus */ SslStatus SSLClose (/* SSLContextRef */ IntPtr context);
849 public override void Shutdown ()
856 protected override void Dispose (bool disposing)
863 if (serverIdentity != null) {
864 serverIdentity.Dispose ();
865 serverIdentity = null;
867 if (clientIdentity != null) {
868 clientIdentity.Dispose ();
869 clientIdentity = null;
871 if (remoteCertificate != null) {
872 remoteCertificate.Dispose ();
873 remoteCertificate = null;
878 if (context != IntPtr.Zero) {
879 CFObject.CFRelease (context);
880 context = IntPtr.Zero;
882 base.Dispose (disposing);