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);
77 connectionId = GCHandle.ToIntPtr (handle);
78 readFunc = NativeReadCallback;
79 writeFunc = NativeWriteCallback;
82 if (serverCertificate == null)
83 throw new ArgumentNullException ("serverCertificate");
87 public IntPtr Handle {
90 throw new ObjectDisposedException ("AppleTlsContext");
95 public override bool HasContext {
96 get { return !disposed && context != IntPtr.Zero; }
99 [System.Diagnostics.Conditional ("APPLE_TLS_DEBUG")]
100 protected new void Debug (string message, params object[] args)
102 Console.Error.WriteLine ("MobileTlsStream({0}): {1}", Parent.ID, string.Format (message, args));
105 void CheckStatusAndThrow (SslStatus status, params SslStatus[] acceptable)
107 var last = Interlocked.Exchange (ref lastException, null);
111 if (status == SslStatus.Success || Array.IndexOf (acceptable, status) > -1)
115 case SslStatus.ClosedAbort:
116 throw new IOException ("Connection closed.");
118 case SslStatus.BadCert:
119 throw new TlsException (AlertDescription.BadCertificate);
121 case SslStatus.UnknownRootCert:
122 case SslStatus.NoRootCert:
123 case SslStatus.XCertChainInvalid:
124 throw new TlsException (AlertDescription.CertificateUnknown, status.ToString ());
126 case SslStatus.CertExpired:
127 case SslStatus.CertNotYetValid:
128 throw new TlsException (AlertDescription.CertificateExpired);
130 case SslStatus.Protocol:
131 throw new TlsException (AlertDescription.ProtocolVersion);
134 throw new TlsException (AlertDescription.InternalError, "Unknown Secure Transport error `{0}'.", status);
140 public override bool IsAuthenticated {
141 get { return isAuthenticated; }
144 public override void StartHandshake ()
146 Debug ("StartHandshake: {0}", IsServer);
148 if (Interlocked.CompareExchange (ref handshakeStarted, 1, 1) != 0)
149 throw new InvalidOperationException ();
151 InitializeConnection ();
153 SetSessionOption (SslSessionOption.BreakOnCertRequested, true);
154 SetSessionOption (SslSessionOption.BreakOnClientAuth, true);
155 SetSessionOption (SslSessionOption.BreakOnServerAuth, true);
158 SecCertificate[] intermediateCerts;
159 serverIdentity = AppleCertificateHelper.GetIdentity (LocalServerCertificate, out intermediateCerts);
160 if (serverIdentity == null)
161 throw new SSA.AuthenticationException ("Unable to get server certificate from keychain.");
163 SetCertificate (serverIdentity, intermediateCerts);
164 for (int i = 0; i < intermediateCerts.Length; i++)
165 intermediateCerts [i].Dispose ();
169 public override void FinishHandshake ()
171 InitializeSession ();
173 isAuthenticated = true;
176 public override void Flush ()
180 public override bool ProcessHandshake ()
182 if (handshakeFinished)
183 throw new NotSupportedException ("Handshake already finished.");
186 lastException = null;
187 var status = SSLHandshake (Handle);
188 Debug ("Handshake: {0} - {0:x}", status);
190 CheckStatusAndThrow (status, SslStatus.WouldBlock, SslStatus.PeerAuthCompleted, SslStatus.PeerClientCertRequested);
192 if (status == SslStatus.PeerAuthCompleted) {
194 } else if (status == SslStatus.PeerClientCertRequested) {
196 if (remoteCertificate == null)
197 throw new TlsException (AlertDescription.InternalError, "Cannot request client certificate before receiving one from the server.");
198 localClientCertificate = SelectClientCertificate (remoteCertificate, null);
199 if (localClientCertificate == null)
201 clientIdentity = AppleCertificateHelper.GetIdentity (localClientCertificate);
202 if (clientIdentity == null)
203 throw new TlsException (AlertDescription.CertificateUnknown);
204 SetCertificate (clientIdentity, new SecCertificate [0]);
205 } else if (status == SslStatus.WouldBlock) {
207 } else if (status == SslStatus.Success) {
208 handshakeFinished = true;
214 void RequirePeerTrust ()
216 if (!havePeerTrust) {
218 havePeerTrust = true;
222 void EvaluateTrust ()
224 InitializeSession ();
227 * We're using .NET's SslStream semantics here.
229 * A server must always provide a valid certificate.
231 * However, in server mode, "ask for client certificate" means that
232 * we ask the client to provide a certificate, then invoke the client
233 * certificate validator - passing 'null' if the client didn't provide
238 var trust = GetPeerTrust (!IsServer);
239 X509CertificateCollection certificates;
241 if (trust == null || trust.Count == 0) {
242 remoteCertificate = null;
244 throw new TlsException (AlertDescription.CertificateUnknown);
248 Debug ("WARNING: Got multiple certificates in SecTrust!");
250 certificates = new X509CertificateCollection ();
251 for (int i = 0; i < trust.Count; i++)
252 certificates.Add (trust [(IntPtr)i].ToX509Certificate ());
254 remoteCertificate = certificates [0];
255 Debug ("Got peer trust: {0}", remoteCertificate);
260 ok = ValidateCertificate (certificates);
261 } catch (Exception ex) {
262 Debug ("Certificate validation failed: {0}", ex);
263 throw new TlsException (AlertDescription.CertificateUnknown, "Certificate validation threw exception.");
267 throw new TlsException (AlertDescription.CertificateUnknown);
270 void InitializeConnection ()
272 context = SSLCreateContext (IntPtr.Zero, IsServer ? SslProtocolSide.Server : SslProtocolSide.Client, SslConnectionType.Stream);
274 var result = SSLSetIOFuncs (Handle, readFunc, writeFunc);
275 CheckStatusAndThrow (result);
277 result = SSLSetConnection (Handle, connectionId);
278 CheckStatusAndThrow (result);
280 if ((EnabledProtocols & SSA.SslProtocols.Tls) != 0)
281 MinProtocol = SslProtocol.Tls_1_0;
282 else if ((EnabledProtocols & SSA.SslProtocols.Tls11) != 0)
283 MinProtocol = SslProtocol.Tls_1_1;
285 MinProtocol = SslProtocol.Tls_1_2;
287 if ((EnabledProtocols & SSA.SslProtocols.Tls12) != 0)
288 MaxProtocol = SslProtocol.Tls_1_2;
289 else if ((EnabledProtocols & SSA.SslProtocols.Tls11) != 0)
290 MaxProtocol = SslProtocol.Tls_1_1;
292 MaxProtocol = SslProtocol.Tls_1_0;
295 foreach (var c in GetSupportedCiphers ())
296 Debug (" {0} SslCipherSuite.{1} {2:x} {3}", IsServer ? "Server" : "Client", c, (int)c, (CipherSuiteCode)c);
299 if (Settings != null && Settings.EnabledCiphers != null) {
300 SslCipherSuite [] ciphers = new SslCipherSuite [Settings.EnabledCiphers.Length];
301 for (int i = 0 ; i < Settings.EnabledCiphers.Length; ++i)
302 ciphers [i] = (SslCipherSuite)Settings.EnabledCiphers[i];
303 SetEnabledCiphers (ciphers);
306 if (AskForClientCertificate)
307 SetClientSideAuthenticate (SslAuthenticate.Try);
310 if (!IsServer && !string.IsNullOrEmpty (TargetHost) &&
311 !IPAddress.TryParse (TargetHost, out address)) {
312 PeerDomainName = ServerName;
316 void InitializeSession ()
318 if (connectionInfo != null)
321 var cipher = NegotiatedCipher;
322 var protocol = GetNegotiatedProtocolVersion ();
323 Debug ("GET CONNECTION INFO: {0:x}:{0} {1:x}:{1} {2}", cipher, protocol, (TlsProtocolCode)protocol);
325 connectionInfo = new MonoTlsConnectionInfo {
326 CipherSuiteCode = (CipherSuiteCode)cipher,
327 ProtocolVersion = GetProtocol (protocol),
328 PeerDomainName = PeerDomainName
332 static TlsProtocols GetProtocol (SslProtocol protocol)
335 case SslProtocol.Tls_1_0:
336 return TlsProtocols.Tls10;
337 case SslProtocol.Tls_1_1:
338 return TlsProtocols.Tls11;
339 case SslProtocol.Tls_1_2:
340 return TlsProtocols.Tls12;
342 throw new NotSupportedException ();
346 public override MonoTlsConnectionInfo ConnectionInfo {
347 get { return connectionInfo; }
350 internal override bool IsRemoteCertificateAvailable {
351 get { return remoteCertificate != null; }
354 internal override X509Certificate LocalClientCertificate {
355 get { return localClientCertificate; }
358 public override X509Certificate RemoteCertificate {
359 get { return remoteCertificate; }
362 public override TlsProtocols NegotiatedProtocol {
363 get { return connectionInfo.ProtocolVersion; }
368 #region General P/Invokes
370 [DllImport (SecurityLibrary )]
371 extern static /* OSStatus */ SslStatus SSLGetProtocolVersionMax (/* SSLContextRef */ IntPtr context, out SslProtocol maxVersion);
373 [DllImport (SecurityLibrary)]
374 extern static /* OSStatus */ SslStatus SSLSetProtocolVersionMax (/* SSLContextRef */ IntPtr context, SslProtocol maxVersion);
376 public SslProtocol MaxProtocol {
379 var result = SSLGetProtocolVersionMax (Handle, out value);
380 CheckStatusAndThrow (result);
384 var result = SSLSetProtocolVersionMax (Handle, value);
385 CheckStatusAndThrow (result);
389 [DllImport (SecurityLibrary)]
390 extern static /* OSStatus */ SslStatus SSLGetProtocolVersionMin (/* SSLContextRef */ IntPtr context, out SslProtocol minVersion);
392 [DllImport (SecurityLibrary)]
393 extern static /* OSStatus */ SslStatus SSLSetProtocolVersionMin (/* SSLContextRef */ IntPtr context, SslProtocol minVersion);
395 public SslProtocol MinProtocol {
398 var result = SSLGetProtocolVersionMin (Handle, out value);
399 CheckStatusAndThrow (result);
403 var result = SSLSetProtocolVersionMin (Handle, value);
404 CheckStatusAndThrow (result);
408 [DllImport (SecurityLibrary)]
409 extern static /* OSStatus */ SslStatus SSLGetNegotiatedProtocolVersion (/* SSLContextRef */ IntPtr context, out SslProtocol protocol);
411 public SslProtocol GetNegotiatedProtocolVersion ()
414 var result = SSLGetNegotiatedProtocolVersion (Handle, out value);
415 CheckStatusAndThrow (result);
419 [DllImport (SecurityLibrary)]
420 extern static /* OSStatus */ SslStatus SSLGetSessionOption (/* SSLContextRef */ IntPtr context, SslSessionOption option, out bool value);
422 public bool GetSessionOption (SslSessionOption option)
425 var result = SSLGetSessionOption (Handle, option, out value);
426 CheckStatusAndThrow (result);
430 [DllImport (SecurityLibrary)]
431 extern static /* OSStatus */ SslStatus SSLSetSessionOption (/* SSLContextRef */ IntPtr context, SslSessionOption option, bool value);
433 public void SetSessionOption (SslSessionOption option, bool value)
435 var result = SSLSetSessionOption (Handle, option, value);
436 CheckStatusAndThrow (result);
439 [DllImport (SecurityLibrary)]
440 extern static /* OSStatus */ SslStatus SSLSetClientSideAuthenticate (/* SSLContextRef */ IntPtr context, SslAuthenticate auth);
442 public void SetClientSideAuthenticate (SslAuthenticate auth)
444 var result = SSLSetClientSideAuthenticate (Handle, auth);
445 CheckStatusAndThrow (result);
448 [DllImport (SecurityLibrary)]
449 extern static /* OSStatus */ SslStatus SSLHandshake (/* SSLContextRef */ IntPtr context);
451 [DllImport (SecurityLibrary)]
452 extern static /* OSStatus */ SslStatus SSLGetSessionState (/* SSLContextRef */ IntPtr context, ref SslSessionState state);
454 public SslSessionState SessionState {
456 var value = SslSessionState.Invalid;
457 var result = SSLGetSessionState (Handle, ref value);
458 CheckStatusAndThrow (result);
463 [DllImport (SecurityLibrary)]
464 extern unsafe static /* OSStatus */ SslStatus SSLGetPeerID (/* SSLContextRef */ IntPtr context, /* const void** */ out IntPtr peerID, /* size_t* */ out IntPtr peerIDLen);
466 [DllImport (SecurityLibrary)]
467 extern unsafe static /* OSStatus */ SslStatus SSLSetPeerID (/* SSLContextRef */ IntPtr context, /* const void* */ byte* peerID, /* size_t */ IntPtr peerIDLen);
469 public unsafe byte[] PeerId {
473 var result = SSLGetPeerID (Handle, out id, out length);
474 CheckStatusAndThrow (result);
475 if ((result != SslStatus.Success) || ((int)length == 0))
477 var data = new byte [(int)length];
478 Marshal.Copy (id, data, 0, (int) length);
483 IntPtr length = (value == null) ? IntPtr.Zero : (IntPtr)value.Length;
484 fixed (byte *p = value) {
485 result = SSLSetPeerID (Handle, p, length);
487 CheckStatusAndThrow (result);
491 [DllImport (SecurityLibrary)]
492 extern unsafe static /* OSStatus */ SslStatus SSLGetBufferedReadSize (/* SSLContextRef */ IntPtr context, /* size_t* */ out IntPtr bufSize);
494 public IntPtr BufferedReadSize {
497 var result = SSLGetBufferedReadSize (Handle, out value);
498 CheckStatusAndThrow (result);
503 [DllImport (SecurityLibrary)]
504 extern unsafe static /* OSStatus */ SslStatus SSLGetNumberSupportedCiphers (/* SSLContextRef */ IntPtr context, /* size_t* */ out IntPtr numCiphers);
506 [DllImport (SecurityLibrary)]
507 extern unsafe static /* OSStatus */ SslStatus SSLGetSupportedCiphers (/* SSLContextRef */ IntPtr context, SslCipherSuite *ciphers, /* size_t* */ ref IntPtr numCiphers);
509 public unsafe IList<SslCipherSuite> GetSupportedCiphers ()
512 var result = SSLGetNumberSupportedCiphers (Handle, out n);
513 CheckStatusAndThrow (result);
514 if ((result != SslStatus.Success) || ((int)n <= 0))
517 var ciphers = new SslCipherSuite [(int)n];
518 fixed (SslCipherSuite *p = ciphers) {
519 result = SSLGetSupportedCiphers (Handle, p, ref n);
521 CheckStatusAndThrow (result);
525 [DllImport (SecurityLibrary)]
526 extern unsafe static /* OSStatus */ SslStatus SSLGetNumberEnabledCiphers (/* SSLContextRef */ IntPtr context, /* size_t* */ out IntPtr numCiphers);
528 [DllImport (SecurityLibrary)]
529 extern unsafe static /* OSStatus */ SslStatus SSLGetEnabledCiphers (/* SSLContextRef */ IntPtr context, SslCipherSuite *ciphers, /* size_t* */ ref IntPtr numCiphers);
531 public unsafe IList<SslCipherSuite> GetEnabledCiphers ()
534 var result = SSLGetNumberEnabledCiphers (Handle, out n);
535 CheckStatusAndThrow (result);
536 if ((result != SslStatus.Success) || ((int)n <= 0))
539 var ciphers = new SslCipherSuite [(int)n];
540 fixed (SslCipherSuite *p = ciphers) {
541 result = SSLGetEnabledCiphers (Handle, p, ref n);
543 CheckStatusAndThrow (result);
547 [DllImport (SecurityLibrary)]
548 extern unsafe static /* OSStatus */ SslStatus SSLSetEnabledCiphers (/* SSLContextRef */ IntPtr context, SslCipherSuite *ciphers, /* size_t */ IntPtr numCiphers);
550 public unsafe void SetEnabledCiphers (SslCipherSuite [] ciphers)
553 throw new ArgumentNullException ("ciphers");
557 fixed (SslCipherSuite *p = ciphers)
558 result = SSLSetEnabledCiphers (Handle, p, (IntPtr)ciphers.Length);
559 CheckStatusAndThrow (result);
562 [DllImport (SecurityLibrary)]
563 extern unsafe static /* OSStatus */ SslStatus SSLGetNegotiatedCipher (/* SSLContextRef */ IntPtr context, /* SslCipherSuite* */ out SslCipherSuite cipherSuite);
565 public SslCipherSuite NegotiatedCipher {
567 SslCipherSuite value;
568 var result = SSLGetNegotiatedCipher (Handle, out value);
569 CheckStatusAndThrow (result);
574 [DllImport (SecurityLibrary)]
575 extern unsafe static /* OSStatus */ SslStatus SSLGetPeerDomainNameLength (/* SSLContextRef */ IntPtr context, /* size_t* */ out IntPtr peerNameLen);
577 [DllImport (SecurityLibrary)]
578 extern unsafe static /* OSStatus */ SslStatus SSLGetPeerDomainName (/* SSLContextRef */ IntPtr context, /* char* */ byte[] peerName, /* size_t */ ref IntPtr peerNameLen);
580 [DllImport (SecurityLibrary)]
581 extern unsafe static /* OSStatus */ SslStatus SSLSetPeerDomainName (/* SSLContextRef */ IntPtr context, /* char* */ byte[] peerName, /* size_t */ IntPtr peerNameLen);
583 public string PeerDomainName {
586 var result = SSLGetPeerDomainNameLength (Handle, out length);
587 CheckStatusAndThrow (result);
588 if (result != SslStatus.Success || (int)length == 0)
590 var bytes = new byte [(int)length];
591 result = SSLGetPeerDomainName (Handle, bytes, ref length);
592 CheckStatusAndThrow (result);
594 int peerDomainLength = (int)length;
596 if (result != SslStatus.Success)
598 if (peerDomainLength > 0 && bytes [peerDomainLength-1] == 0)
599 peerDomainLength = peerDomainLength - 1;
600 return Encoding.UTF8.GetString (bytes, 0, peerDomainLength);
605 result = SSLSetPeerDomainName (Handle, null, (IntPtr)0);
607 var bytes = Encoding.UTF8.GetBytes (value);
608 result = SSLSetPeerDomainName (Handle, bytes, (IntPtr)bytes.Length);
610 CheckStatusAndThrow (result);
614 [DllImport (SecurityLibrary)]
615 extern unsafe static /* OSStatus */ SslStatus SSLSetCertificate (/* SSLContextRef */ IntPtr context, /* CFArrayRef */ IntPtr certRefs);
617 CFArray Bundle (SecIdentity identity, IEnumerable<SecCertificate> certificates)
619 if (identity == null)
620 throw new ArgumentNullException ("identity");
624 if (certificates != null) {
625 foreach (var obj in certificates)
629 var ptrs = new IntPtr [n + 1];
630 ptrs [0] = identity.Handle;
631 foreach (var certificate in certificates)
632 ptrs [++i] = certificate.Handle;
633 return CFArray.CreateArray (ptrs);
636 public void SetCertificate (SecIdentity identify, IEnumerable<SecCertificate> certificates)
638 using (var array = Bundle (identify, certificates)) {
639 var result = SSLSetCertificate (Handle, array.Handle);
640 CheckStatusAndThrow (result);
644 [DllImport (SecurityLibrary)]
645 extern unsafe static /* OSStatus */ SslStatus SSLGetClientCertificateState (/* SSLContextRef */ IntPtr context, out SslClientCertificateState clientState);
647 public SslClientCertificateState ClientCertificateState {
649 SslClientCertificateState value;
650 var result = SSLGetClientCertificateState (Handle, out value);
651 CheckStatusAndThrow (result);
656 [DllImport (SecurityLibrary)]
657 extern unsafe static /* OSStatus */ SslStatus SSLCopyPeerTrust (/* SSLContextRef */ IntPtr context, /* SecTrustRef */ out IntPtr trust);
659 public SecTrust GetPeerTrust (bool requireTrust)
662 var result = SSLCopyPeerTrust (Handle, out value);
664 CheckStatusAndThrow (result);
665 if (value == IntPtr.Zero)
666 throw new TlsException (AlertDescription.CertificateUnknown);
668 return (value == IntPtr.Zero) ? null : new SecTrust (value);
675 [DllImport (SecurityLibrary)]
676 extern static /* SSLContextRef */ IntPtr SSLCreateContext (/* CFAllocatorRef */ IntPtr alloc, SslProtocolSide protocolSide, SslConnectionType connectionType);
678 [DllImport (SecurityLibrary)]
679 extern static /* OSStatus */ SslStatus SSLSetConnection (/* SSLContextRef */ IntPtr context, /* SSLConnectionRef */ IntPtr connection);
681 [DllImport (SecurityLibrary)]
682 extern static /* OSStatus */ SslStatus SSLSetIOFuncs (/* SSLContextRef */ IntPtr context, /* SSLReadFunc */ SslReadFunc readFunc, /* SSLWriteFunc */ SslWriteFunc writeFunc);
684 [Mono.Util.MonoPInvokeCallback (typeof (SslReadFunc))]
685 static SslStatus NativeReadCallback (IntPtr ptr, IntPtr data, ref IntPtr dataLength)
687 var handle = GCHandle.FromIntPtr (ptr);
688 if (!handle.IsAllocated)
689 return SslStatus.Internal;
691 var context = (AppleTlsContext) handle.Target;
692 if (context.disposed)
693 return SslStatus.ClosedAbort;
696 return context.NativeReadCallback (data, ref dataLength);
697 } catch (Exception ex) {
698 if (context.lastException == null)
699 context.lastException = ex;
700 return SslStatus.Internal;
704 [Mono.Util.MonoPInvokeCallback (typeof (SslWriteFunc))]
705 static SslStatus NativeWriteCallback (IntPtr ptr, IntPtr data, ref IntPtr dataLength)
707 var handle = GCHandle.FromIntPtr (ptr);
708 if (!handle.IsAllocated)
709 return SslStatus.Internal;
711 var context = (AppleTlsContext) handle.Target;
712 if (context.disposed)
713 return SslStatus.ClosedAbort;
716 return context.NativeWriteCallback (data, ref dataLength);
717 } catch (Exception ex) {
718 if (context.lastException == null)
719 context.lastException = ex;
720 return SslStatus.Internal;
724 SslStatus NativeReadCallback (IntPtr data, ref IntPtr dataLength)
726 if (closed || disposed || Parent == null)
727 return SslStatus.ClosedAbort;
729 var len = (int)dataLength;
730 var readBuffer = new byte [len];
732 Debug ("NativeReadCallback: {0} {1}", dataLength, len);
735 var ret = Parent.InternalRead (readBuffer, 0, len, out wantMore);
736 dataLength = (IntPtr)ret;
738 Debug ("NativeReadCallback #1: {0} - {1} {2}", len, ret, wantMore);
741 return SslStatus.ClosedAbort;
743 Marshal.Copy (readBuffer, 0, data, ret);
746 return SslStatus.Success;
748 return SslStatus.WouldBlock;
750 closedGraceful = true;
751 return SslStatus.ClosedGraceful;
753 return SslStatus.Success;
757 SslStatus NativeWriteCallback (IntPtr data, ref IntPtr dataLength)
759 if (closed || disposed || Parent == null)
760 return SslStatus.ClosedAbort;
762 var len = (int)dataLength;
763 var writeBuffer = new byte [len];
765 Marshal.Copy (data, writeBuffer, 0, len);
767 Debug ("NativeWriteCallback: {0}", len);
769 var ok = Parent.InternalWrite (writeBuffer, 0, len);
771 Debug ("NativeWriteCallback done: {0} {1}", len, ok);
773 return ok ? SslStatus.Success : SslStatus.ClosedAbort;
776 [DllImport (SecurityLibrary)]
777 extern unsafe static /* OSStatus */ SslStatus SSLRead (/* SSLContextRef */ IntPtr context, /* const void* */ byte* data, /* size_t */ IntPtr dataLength, /* size_t* */ out IntPtr processed);
779 public override unsafe int Read (byte[] buffer, int offset, int count, out bool wantMore)
781 if (Interlocked.Exchange (ref pendingIO, 1) == 1)
782 throw new InvalidOperationException ();
784 Debug ("Read: {0},{1}", offset, count);
786 lastException = null;
792 fixed (byte *d = &buffer [offset])
793 status = SSLRead (Handle, d, (IntPtr)count, out processed);
795 Debug ("Read done: {0} {1} {2}", status, count, processed);
797 if (closedGraceful && (status == SslStatus.ClosedAbort || status == SslStatus.ClosedGraceful)) {
799 * This is really ugly, but unfortunately SSLRead() also returns 'SslStatus.ClosedAbort'
800 * when the first inner Read() returns 0. MobileAuthenticatedStream.InnerRead() attempts
801 * to distinguish between a graceful close and abnormal termination of connection.
807 CheckStatusAndThrow (status, SslStatus.WouldBlock, SslStatus.ClosedGraceful);
808 wantMore = status == SslStatus.WouldBlock;
809 return (int)processed;
810 } catch (Exception ex) {
811 Debug ("Read error: {0}", ex);
818 [DllImport (SecurityLibrary)]
819 extern unsafe static /* OSStatus */ SslStatus SSLWrite (/* SSLContextRef */ IntPtr context, /* const void* */ byte* data, /* size_t */ IntPtr dataLength, /* size_t* */ out IntPtr processed);
821 public override unsafe int Write (byte[] buffer, int offset, int count, out bool wantMore)
823 if (Interlocked.Exchange (ref pendingIO, 1) == 1)
824 throw new InvalidOperationException ();
826 Debug ("Write: {0},{1}", offset, count);
828 lastException = null;
831 SslStatus status = SslStatus.ClosedAbort;
832 IntPtr processed = (IntPtr)(-1);
834 fixed (byte *d = &buffer [offset])
835 status = SSLWrite (Handle, d, (IntPtr)count, out processed);
837 Debug ("Write done: {0} {1}", status, processed);
839 CheckStatusAndThrow (status, SslStatus.WouldBlock);
841 wantMore = status == SslStatus.WouldBlock;
842 return (int)processed;
848 [DllImport (SecurityLibrary)]
849 extern static /* OSStatus */ SslStatus SSLClose (/* SSLContextRef */ IntPtr context);
851 public override void Shutdown ()
853 if (Interlocked.Exchange (ref pendingIO, 1) == 1)
854 throw new InvalidOperationException ();
858 lastException = null;
861 if (closed || disposed)
864 var status = SSLClose (Handle);
865 Debug ("Shutdown done: {0}", status);
866 CheckStatusAndThrow (status);
875 protected override void Dispose (bool disposing)
882 if (serverIdentity != null) {
883 serverIdentity.Dispose ();
884 serverIdentity = null;
886 if (clientIdentity != null) {
887 clientIdentity.Dispose ();
888 clientIdentity = null;
890 if (remoteCertificate != null) {
891 remoteCertificate.Dispose ();
892 remoteCertificate = null;
897 if (context != IntPtr.Zero) {
898 CFObject.CFRelease (context);
899 context = IntPtr.Zero;
901 base.Dispose (disposing);