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 [System.Diagnostics.Conditional ("APPLE_TLS_DEBUG")]
99 protected new void Debug (string message, params object[] args)
101 Console.Error.WriteLine ("MobileTlsStream({0}): {1}", Parent.ID, string.Format (message, args));
104 void CheckStatusAndThrow (SslStatus status, params SslStatus[] acceptable)
106 var last = Interlocked.Exchange (ref lastException, null);
110 if (status == SslStatus.Success || Array.IndexOf (acceptable, status) > -1)
114 case SslStatus.ClosedAbort:
115 throw new IOException ("Connection closed.");
117 case SslStatus.BadCert:
118 throw new TlsException (AlertDescription.BadCertificate);
120 case SslStatus.UnknownRootCert:
121 case SslStatus.NoRootCert:
122 case SslStatus.XCertChainInvalid:
123 throw new TlsException (AlertDescription.CertificateUnknown, status.ToString ());
125 case SslStatus.CertExpired:
126 case SslStatus.CertNotYetValid:
127 throw new TlsException (AlertDescription.CertificateExpired);
129 case SslStatus.Protocol:
130 throw new TlsException (AlertDescription.ProtocolVersion);
133 throw new TlsException (AlertDescription.InternalError, "Unknown Secure Transport error `{0}'.", status);
139 public override bool IsAuthenticated {
140 get { return isAuthenticated; }
143 public override void StartHandshake ()
145 Debug ("StartHandshake: {0}", IsServer);
147 if (Interlocked.CompareExchange (ref handshakeStarted, 1, 1) != 0)
148 throw new InvalidOperationException ();
150 InitializeConnection ();
152 SetSessionOption (SslSessionOption.BreakOnCertRequested, true);
153 SetSessionOption (SslSessionOption.BreakOnClientAuth, true);
154 SetSessionOption (SslSessionOption.BreakOnServerAuth, true);
157 SecCertificate[] intermediateCerts;
158 serverIdentity = AppleCertificateHelper.GetIdentity (LocalServerCertificate, out intermediateCerts);
159 if (serverIdentity == null)
160 throw new SSA.AuthenticationException ("Unable to get server certificate from keychain.");
162 SetCertificate (serverIdentity, intermediateCerts);
163 for (int i = 0; i < intermediateCerts.Length; i++)
164 intermediateCerts [i].Dispose ();
168 public override void FinishHandshake ()
170 InitializeSession ();
172 isAuthenticated = true;
175 public override void Flush ()
179 public override bool ProcessHandshake ()
181 if (handshakeFinished)
182 throw new NotSupportedException ("Handshake already finished.");
185 lastException = null;
186 var status = SSLHandshake (Handle);
187 Debug ("Handshake: {0} - {0:x}", status);
189 CheckStatusAndThrow (status, SslStatus.WouldBlock, SslStatus.PeerAuthCompleted, SslStatus.PeerClientCertRequested);
191 if (status == SslStatus.PeerAuthCompleted) {
193 } else if (status == SslStatus.PeerClientCertRequested) {
195 if (remoteCertificate == null)
196 throw new TlsException (AlertDescription.InternalError, "Cannot request client certificate before receiving one from the server.");
197 localClientCertificate = SelectClientCertificate (remoteCertificate, null);
198 if (localClientCertificate == null)
200 clientIdentity = AppleCertificateHelper.GetIdentity (localClientCertificate);
201 if (clientIdentity == null)
202 throw new TlsException (AlertDescription.CertificateUnknown);
203 SetCertificate (clientIdentity, new SecCertificate [0]);
204 } else if (status == SslStatus.WouldBlock) {
206 } else if (status == SslStatus.Success) {
207 handshakeFinished = true;
213 void RequirePeerTrust ()
215 if (!havePeerTrust) {
217 havePeerTrust = true;
221 void EvaluateTrust ()
223 InitializeSession ();
226 * We're using .NET's SslStream semantics here.
228 * A server must always provide a valid certificate.
230 * However, in server mode, "ask for client certificate" means that
231 * we ask the client to provide a certificate, then invoke the client
232 * certificate validator - passing 'null' if the client didn't provide
237 var trust = GetPeerTrust (!IsServer);
238 X509CertificateCollection certificates;
240 if (trust == null || trust.Count == 0) {
241 remoteCertificate = null;
243 throw new TlsException (AlertDescription.CertificateUnknown);
247 Debug ("WARNING: Got multiple certificates in SecTrust!");
249 certificates = new X509CertificateCollection ();
250 for (int i = 0; i < trust.Count; i++)
251 certificates.Add (trust [(IntPtr)i].ToX509Certificate ());
253 remoteCertificate = certificates [0];
254 Debug ("Got peer trust: {0}", remoteCertificate);
259 ok = ValidateCertificate (certificates);
260 } catch (Exception ex) {
261 Debug ("Certificate validation failed: {0}", ex);
262 throw new TlsException (AlertDescription.CertificateUnknown, "Certificate validation threw exception.");
266 throw new TlsException (AlertDescription.CertificateUnknown);
269 void InitializeConnection ()
271 context = SSLCreateContext (IntPtr.Zero, IsServer ? SslProtocolSide.Server : SslProtocolSide.Client, SslConnectionType.Stream);
273 var result = SSLSetIOFuncs (Handle, readFunc, writeFunc);
274 CheckStatusAndThrow (result);
276 result = SSLSetConnection (Handle, GCHandle.ToIntPtr (handle));
277 CheckStatusAndThrow (result);
279 if ((EnabledProtocols & SSA.SslProtocols.Tls) != 0)
280 MinProtocol = SslProtocol.Tls_1_0;
281 else if ((EnabledProtocols & SSA.SslProtocols.Tls11) != 0)
282 MinProtocol = SslProtocol.Tls_1_1;
284 MinProtocol = SslProtocol.Tls_1_2;
286 if ((EnabledProtocols & SSA.SslProtocols.Tls12) != 0)
287 MaxProtocol = SslProtocol.Tls_1_2;
288 else if ((EnabledProtocols & SSA.SslProtocols.Tls11) != 0)
289 MaxProtocol = SslProtocol.Tls_1_1;
291 MaxProtocol = SslProtocol.Tls_1_0;
294 foreach (var c in GetSupportedCiphers ())
295 Debug (" {0} SslCipherSuite.{1} {2:x} {3}", IsServer ? "Server" : "Client", c, (int)c, (CipherSuiteCode)c);
298 if (Settings != null && Settings.EnabledCiphers != null) {
299 SslCipherSuite [] ciphers = new SslCipherSuite [Settings.EnabledCiphers.Length];
300 for (int i = 0 ; i < Settings.EnabledCiphers.Length; ++i)
301 ciphers [i] = (SslCipherSuite)Settings.EnabledCiphers[i];
302 SetEnabledCiphers (ciphers);
305 if (AskForClientCertificate)
306 SetClientSideAuthenticate (SslAuthenticate.Try);
309 if (!IsServer && !string.IsNullOrEmpty (TargetHost) &&
310 !IPAddress.TryParse (TargetHost, out address)) {
311 PeerDomainName = ServerName;
315 void InitializeSession ()
317 if (connectionInfo != null)
320 var cipher = NegotiatedCipher;
321 var protocol = GetNegotiatedProtocolVersion ();
322 Debug ("GET CONNECTION INFO: {0:x}:{0} {1:x}:{1} {2}", cipher, protocol, (TlsProtocolCode)protocol);
324 connectionInfo = new MonoTlsConnectionInfo {
325 CipherSuiteCode = (CipherSuiteCode)cipher,
326 ProtocolVersion = GetProtocol (protocol),
327 PeerDomainName = PeerDomainName
331 static TlsProtocols GetProtocol (SslProtocol protocol)
334 case SslProtocol.Tls_1_0:
335 return TlsProtocols.Tls10;
336 case SslProtocol.Tls_1_1:
337 return TlsProtocols.Tls11;
338 case SslProtocol.Tls_1_2:
339 return TlsProtocols.Tls12;
341 throw new NotSupportedException ();
345 public override MonoTlsConnectionInfo ConnectionInfo {
346 get { return connectionInfo; }
349 internal override bool IsRemoteCertificateAvailable {
350 get { return remoteCertificate != null; }
353 internal override X509Certificate LocalClientCertificate {
354 get { return localClientCertificate; }
357 public override X509Certificate RemoteCertificate {
358 get { return remoteCertificate; }
361 public override TlsProtocols NegotiatedProtocol {
362 get { return connectionInfo.ProtocolVersion; }
367 #region General P/Invokes
369 [DllImport (SecurityLibrary )]
370 extern static /* OSStatus */ SslStatus SSLGetProtocolVersionMax (/* SSLContextRef */ IntPtr context, out SslProtocol maxVersion);
372 [DllImport (SecurityLibrary)]
373 extern static /* OSStatus */ SslStatus SSLSetProtocolVersionMax (/* SSLContextRef */ IntPtr context, SslProtocol maxVersion);
375 public SslProtocol MaxProtocol {
378 var result = SSLGetProtocolVersionMax (Handle, out value);
379 CheckStatusAndThrow (result);
383 var result = SSLSetProtocolVersionMax (Handle, value);
384 CheckStatusAndThrow (result);
388 [DllImport (SecurityLibrary)]
389 extern static /* OSStatus */ SslStatus SSLGetProtocolVersionMin (/* SSLContextRef */ IntPtr context, out SslProtocol minVersion);
391 [DllImport (SecurityLibrary)]
392 extern static /* OSStatus */ SslStatus SSLSetProtocolVersionMin (/* SSLContextRef */ IntPtr context, SslProtocol minVersion);
394 public SslProtocol MinProtocol {
397 var result = SSLGetProtocolVersionMin (Handle, out value);
398 CheckStatusAndThrow (result);
402 var result = SSLSetProtocolVersionMin (Handle, value);
403 CheckStatusAndThrow (result);
407 [DllImport (SecurityLibrary)]
408 extern static /* OSStatus */ SslStatus SSLGetNegotiatedProtocolVersion (/* SSLContextRef */ IntPtr context, out SslProtocol protocol);
410 public SslProtocol GetNegotiatedProtocolVersion ()
413 var result = SSLGetNegotiatedProtocolVersion (Handle, out value);
414 CheckStatusAndThrow (result);
418 [DllImport (SecurityLibrary)]
419 extern static /* OSStatus */ SslStatus SSLGetSessionOption (/* SSLContextRef */ IntPtr context, SslSessionOption option, out bool value);
421 public bool GetSessionOption (SslSessionOption option)
424 var result = SSLGetSessionOption (Handle, option, out value);
425 CheckStatusAndThrow (result);
429 [DllImport (SecurityLibrary)]
430 extern static /* OSStatus */ SslStatus SSLSetSessionOption (/* SSLContextRef */ IntPtr context, SslSessionOption option, bool value);
432 public void SetSessionOption (SslSessionOption option, bool value)
434 var result = SSLSetSessionOption (Handle, option, value);
435 CheckStatusAndThrow (result);
438 [DllImport (SecurityLibrary)]
439 extern static /* OSStatus */ SslStatus SSLSetClientSideAuthenticate (/* SSLContextRef */ IntPtr context, SslAuthenticate auth);
441 public void SetClientSideAuthenticate (SslAuthenticate auth)
443 var result = SSLSetClientSideAuthenticate (Handle, auth);
444 CheckStatusAndThrow (result);
447 [DllImport (SecurityLibrary)]
448 extern static /* OSStatus */ SslStatus SSLHandshake (/* SSLContextRef */ IntPtr context);
450 [DllImport (SecurityLibrary)]
451 extern static /* OSStatus */ SslStatus SSLGetSessionState (/* SSLContextRef */ IntPtr context, ref SslSessionState state);
453 public SslSessionState SessionState {
455 var value = SslSessionState.Invalid;
456 var result = SSLGetSessionState (Handle, ref value);
457 CheckStatusAndThrow (result);
462 [DllImport (SecurityLibrary)]
463 extern unsafe static /* OSStatus */ SslStatus SSLGetPeerID (/* SSLContextRef */ IntPtr context, /* const void** */ out IntPtr peerID, /* size_t* */ out IntPtr peerIDLen);
465 [DllImport (SecurityLibrary)]
466 extern unsafe static /* OSStatus */ SslStatus SSLSetPeerID (/* SSLContextRef */ IntPtr context, /* const void* */ byte* peerID, /* size_t */ IntPtr peerIDLen);
468 public unsafe byte[] PeerId {
472 var result = SSLGetPeerID (Handle, out id, out length);
473 CheckStatusAndThrow (result);
474 if ((result != SslStatus.Success) || ((int)length == 0))
476 var data = new byte [(int)length];
477 Marshal.Copy (id, data, 0, (int) length);
482 IntPtr length = (value == null) ? IntPtr.Zero : (IntPtr)value.Length;
483 fixed (byte *p = value) {
484 result = SSLSetPeerID (Handle, p, length);
486 CheckStatusAndThrow (result);
490 [DllImport (SecurityLibrary)]
491 extern unsafe static /* OSStatus */ SslStatus SSLGetBufferedReadSize (/* SSLContextRef */ IntPtr context, /* size_t* */ out IntPtr bufSize);
493 public IntPtr BufferedReadSize {
496 var result = SSLGetBufferedReadSize (Handle, out value);
497 CheckStatusAndThrow (result);
502 [DllImport (SecurityLibrary)]
503 extern unsafe static /* OSStatus */ SslStatus SSLGetNumberSupportedCiphers (/* SSLContextRef */ IntPtr context, /* size_t* */ out IntPtr numCiphers);
505 [DllImport (SecurityLibrary)]
506 extern unsafe static /* OSStatus */ SslStatus SSLGetSupportedCiphers (/* SSLContextRef */ IntPtr context, SslCipherSuite *ciphers, /* size_t* */ ref IntPtr numCiphers);
508 public unsafe IList<SslCipherSuite> GetSupportedCiphers ()
511 var result = SSLGetNumberSupportedCiphers (Handle, out n);
512 CheckStatusAndThrow (result);
513 if ((result != SslStatus.Success) || ((int)n <= 0))
516 var ciphers = new SslCipherSuite [(int)n];
517 fixed (SslCipherSuite *p = ciphers) {
518 result = SSLGetSupportedCiphers (Handle, p, ref n);
520 CheckStatusAndThrow (result);
524 [DllImport (SecurityLibrary)]
525 extern unsafe static /* OSStatus */ SslStatus SSLGetNumberEnabledCiphers (/* SSLContextRef */ IntPtr context, /* size_t* */ out IntPtr numCiphers);
527 [DllImport (SecurityLibrary)]
528 extern unsafe static /* OSStatus */ SslStatus SSLGetEnabledCiphers (/* SSLContextRef */ IntPtr context, SslCipherSuite *ciphers, /* size_t* */ ref IntPtr numCiphers);
530 public unsafe IList<SslCipherSuite> GetEnabledCiphers ()
533 var result = SSLGetNumberEnabledCiphers (Handle, out n);
534 CheckStatusAndThrow (result);
535 if ((result != SslStatus.Success) || ((int)n <= 0))
538 var ciphers = new SslCipherSuite [(int)n];
539 fixed (SslCipherSuite *p = ciphers) {
540 result = SSLGetEnabledCiphers (Handle, p, ref n);
542 CheckStatusAndThrow (result);
546 [DllImport (SecurityLibrary)]
547 extern unsafe static /* OSStatus */ SslStatus SSLSetEnabledCiphers (/* SSLContextRef */ IntPtr context, SslCipherSuite *ciphers, /* size_t */ IntPtr numCiphers);
549 public unsafe void SetEnabledCiphers (SslCipherSuite [] ciphers)
552 throw new ArgumentNullException ("ciphers");
556 fixed (SslCipherSuite *p = ciphers)
557 result = SSLSetEnabledCiphers (Handle, p, (IntPtr)ciphers.Length);
558 CheckStatusAndThrow (result);
561 [DllImport (SecurityLibrary)]
562 extern unsafe static /* OSStatus */ SslStatus SSLGetNegotiatedCipher (/* SSLContextRef */ IntPtr context, /* SslCipherSuite* */ out SslCipherSuite cipherSuite);
564 public SslCipherSuite NegotiatedCipher {
566 SslCipherSuite value;
567 var result = SSLGetNegotiatedCipher (Handle, out value);
568 CheckStatusAndThrow (result);
573 [DllImport (SecurityLibrary)]
574 extern unsafe static /* OSStatus */ SslStatus SSLGetPeerDomainNameLength (/* SSLContextRef */ IntPtr context, /* size_t* */ out IntPtr peerNameLen);
576 [DllImport (SecurityLibrary)]
577 extern unsafe static /* OSStatus */ SslStatus SSLGetPeerDomainName (/* SSLContextRef */ IntPtr context, /* char* */ byte[] peerName, /* size_t */ ref IntPtr peerNameLen);
579 [DllImport (SecurityLibrary)]
580 extern unsafe static /* OSStatus */ SslStatus SSLSetPeerDomainName (/* SSLContextRef */ IntPtr context, /* char* */ byte[] peerName, /* size_t */ IntPtr peerNameLen);
582 public string PeerDomainName {
585 var result = SSLGetPeerDomainNameLength (Handle, out length);
586 CheckStatusAndThrow (result);
587 if (result != SslStatus.Success || (int)length == 0)
589 var bytes = new byte [(int)length];
590 result = SSLGetPeerDomainName (Handle, bytes, ref length);
591 CheckStatusAndThrow (result);
593 int peerDomainLength = (int)length;
595 if (result != SslStatus.Success)
597 if (peerDomainLength > 0 && bytes [peerDomainLength-1] == 0)
598 peerDomainLength = peerDomainLength - 1;
599 return Encoding.UTF8.GetString (bytes, 0, peerDomainLength);
604 result = SSLSetPeerDomainName (Handle, null, (IntPtr)0);
606 var bytes = Encoding.UTF8.GetBytes (value);
607 result = SSLSetPeerDomainName (Handle, bytes, (IntPtr)bytes.Length);
609 CheckStatusAndThrow (result);
613 [DllImport (SecurityLibrary)]
614 extern unsafe static /* OSStatus */ SslStatus SSLSetCertificate (/* SSLContextRef */ IntPtr context, /* CFArrayRef */ IntPtr certRefs);
616 CFArray Bundle (SecIdentity identity, IEnumerable<SecCertificate> certificates)
618 if (identity == null)
619 throw new ArgumentNullException ("identity");
623 if (certificates != null) {
624 foreach (var obj in certificates)
628 var ptrs = new IntPtr [n + 1];
629 ptrs [0] = identity.Handle;
630 foreach (var certificate in certificates)
631 ptrs [++i] = certificate.Handle;
632 return CFArray.CreateArray (ptrs);
635 public void SetCertificate (SecIdentity identify, IEnumerable<SecCertificate> certificates)
637 using (var array = Bundle (identify, certificates)) {
638 var result = SSLSetCertificate (Handle, array.Handle);
639 CheckStatusAndThrow (result);
643 [DllImport (SecurityLibrary)]
644 extern unsafe static /* OSStatus */ SslStatus SSLGetClientCertificateState (/* SSLContextRef */ IntPtr context, out SslClientCertificateState clientState);
646 public SslClientCertificateState ClientCertificateState {
648 SslClientCertificateState value;
649 var result = SSLGetClientCertificateState (Handle, out value);
650 CheckStatusAndThrow (result);
655 [DllImport (SecurityLibrary)]
656 extern unsafe static /* OSStatus */ SslStatus SSLCopyPeerTrust (/* SSLContextRef */ IntPtr context, /* SecTrustRef */ out IntPtr trust);
658 public SecTrust GetPeerTrust (bool requireTrust)
661 var result = SSLCopyPeerTrust (Handle, out value);
663 CheckStatusAndThrow (result);
664 if (value == IntPtr.Zero)
665 throw new TlsException (AlertDescription.CertificateUnknown);
667 return (value == IntPtr.Zero) ? null : new SecTrust (value, true);
674 [DllImport (SecurityLibrary)]
675 extern static /* SSLContextRef */ IntPtr SSLCreateContext (/* CFAllocatorRef */ IntPtr alloc, SslProtocolSide protocolSide, SslConnectionType connectionType);
677 [DllImport (SecurityLibrary)]
678 extern static /* OSStatus */ SslStatus SSLSetConnection (/* SSLContextRef */ IntPtr context, /* SSLConnectionRef */ IntPtr connection);
680 [DllImport (SecurityLibrary)]
681 extern static /* OSStatus */ SslStatus SSLSetIOFuncs (/* SSLContextRef */ IntPtr context, /* SSLReadFunc */ SslReadFunc readFunc, /* SSLWriteFunc */ SslWriteFunc writeFunc);
683 [Mono.Util.MonoPInvokeCallback (typeof (SslReadFunc))]
684 static SslStatus NativeReadCallback (IntPtr ptr, IntPtr data, ref IntPtr dataLength)
686 AppleTlsContext context = null;
688 var weakHandle = GCHandle.FromIntPtr (ptr);
689 if (!weakHandle.IsAllocated)
690 return SslStatus.Internal;
692 context = (AppleTlsContext) weakHandle.Target;
693 if (context == null || context.disposed)
694 return SslStatus.ClosedAbort;
696 return context.NativeReadCallback (data, ref dataLength);
697 } catch (Exception ex) {
698 if (context != null && 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 AppleTlsContext context = null;
709 var weakHandle = GCHandle.FromIntPtr (ptr);
710 if (!weakHandle.IsAllocated)
711 return SslStatus.Internal;
713 context = (AppleTlsContext) weakHandle.Target;
714 if (context == null || context.disposed)
715 return SslStatus.ClosedAbort;
717 return context.NativeWriteCallback (data, ref dataLength);
718 } catch (Exception ex) {
719 if (context != null && context.lastException == null)
720 context.lastException = ex;
721 return SslStatus.Internal;
725 SslStatus NativeReadCallback (IntPtr data, ref IntPtr dataLength)
727 if (closed || disposed || Parent == null)
728 return SslStatus.ClosedAbort;
730 var len = (int)dataLength;
731 var readBuffer = new byte [len];
733 Debug ("NativeReadCallback: {0} {1}", dataLength, len);
736 var ret = Parent.InternalRead (readBuffer, 0, len, out wantMore);
737 dataLength = (IntPtr)ret;
739 Debug ("NativeReadCallback #1: {0} - {1} {2}", len, ret, wantMore);
742 return SslStatus.ClosedAbort;
744 Marshal.Copy (readBuffer, 0, data, ret);
747 return SslStatus.Success;
749 return SslStatus.WouldBlock;
751 closedGraceful = true;
752 return SslStatus.ClosedGraceful;
754 return SslStatus.Success;
758 SslStatus NativeWriteCallback (IntPtr data, ref IntPtr dataLength)
760 if (closed || disposed || Parent == null)
761 return SslStatus.ClosedAbort;
763 var len = (int)dataLength;
764 var writeBuffer = new byte [len];
766 Marshal.Copy (data, writeBuffer, 0, len);
768 Debug ("NativeWriteCallback: {0}", len);
770 var ok = Parent.InternalWrite (writeBuffer, 0, len);
772 Debug ("NativeWriteCallback done: {0} {1}", len, ok);
774 return ok ? SslStatus.Success : SslStatus.ClosedAbort;
777 [DllImport (SecurityLibrary)]
778 extern unsafe static /* OSStatus */ SslStatus SSLRead (/* SSLContextRef */ IntPtr context, /* const void* */ byte* data, /* size_t */ IntPtr dataLength, /* size_t* */ out IntPtr processed);
780 public override unsafe int Read (byte[] buffer, int offset, int count, out bool wantMore)
782 if (Interlocked.Exchange (ref pendingIO, 1) == 1)
783 throw new InvalidOperationException ();
785 Debug ("Read: {0},{1}", offset, count);
787 lastException = null;
793 fixed (byte *d = &buffer [offset])
794 status = SSLRead (Handle, d, (IntPtr)count, out processed);
796 Debug ("Read done: {0} {1} {2}", status, count, processed);
798 if (closedGraceful && (status == SslStatus.ClosedAbort || status == SslStatus.ClosedGraceful)) {
800 * This is really ugly, but unfortunately SSLRead() also returns 'SslStatus.ClosedAbort'
801 * when the first inner Read() returns 0. MobileAuthenticatedStream.InnerRead() attempts
802 * to distinguish between a graceful close and abnormal termination of connection.
808 CheckStatusAndThrow (status, SslStatus.WouldBlock, SslStatus.ClosedGraceful);
809 wantMore = status == SslStatus.WouldBlock;
810 return (int)processed;
811 } catch (Exception ex) {
812 Debug ("Read error: {0}", ex);
819 [DllImport (SecurityLibrary)]
820 extern unsafe static /* OSStatus */ SslStatus SSLWrite (/* SSLContextRef */ IntPtr context, /* const void* */ byte* data, /* size_t */ IntPtr dataLength, /* size_t* */ out IntPtr processed);
822 public override unsafe int Write (byte[] buffer, int offset, int count, out bool wantMore)
824 if (Interlocked.Exchange (ref pendingIO, 1) == 1)
825 throw new InvalidOperationException ();
827 Debug ("Write: {0},{1}", offset, count);
829 lastException = null;
832 SslStatus status = SslStatus.ClosedAbort;
833 IntPtr processed = (IntPtr)(-1);
835 fixed (byte *d = &buffer [offset])
836 status = SSLWrite (Handle, d, (IntPtr)count, out processed);
838 Debug ("Write done: {0} {1}", status, processed);
840 CheckStatusAndThrow (status, SslStatus.WouldBlock);
842 wantMore = status == SslStatus.WouldBlock;
843 return (int)processed;
849 [DllImport (SecurityLibrary)]
850 extern static /* OSStatus */ SslStatus SSLClose (/* SSLContextRef */ IntPtr context);
852 public override void Shutdown ()
854 if (Interlocked.Exchange (ref pendingIO, 1) == 1)
855 throw new InvalidOperationException ();
859 lastException = null;
862 if (closed || disposed)
865 var status = SSLClose (Handle);
866 Debug ("Shutdown done: {0}", status);
867 CheckStatusAndThrow (status);
876 protected override void Dispose (bool disposing)
883 if (serverIdentity != null) {
884 serverIdentity.Dispose ();
885 serverIdentity = null;
887 if (clientIdentity != null) {
888 clientIdentity.Dispose ();
889 clientIdentity = null;
891 if (remoteCertificate != null) {
892 remoteCertificate.Dispose ();
893 remoteCertificate = null;
898 if (context != IntPtr.Zero) {
899 CFObject.CFRelease (context);
900 context = IntPtr.Zero;
902 base.Dispose (disposing);