1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
3 // Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com)
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 
25 using System;
26 using System.Collections;
27 using System.IO;
28 using System.Net;
29 using System.Net.Sockets;
30 using System.Security.Cryptography;
31 using System.Security.Cryptography.X509Certificates;
32 using System.Threading;
33 
34 namespace Mono.Security.Protocol.Tls
35 {
36 	public abstract class SslStreamBase: Stream, IDisposable
37 	{
AsyncHandshakeDelegate(InternalAsyncResult asyncResult, bool fromWrite)38 		private delegate void AsyncHandshakeDelegate(InternalAsyncResult asyncResult, bool fromWrite);
39 
40 		#region Fields
41 
42 		static ManualResetEvent record_processing = new ManualResetEvent (true);
43 
44 		private const int WaitTimeOut = 5 * 60 * 1000;
45 
46 		internal Stream innerStream;
47 		internal MemoryStream inputBuffer;
48 		internal Context context;
49 		internal RecordProtocol protocol;
50 		internal bool ownsStream;
51 		private volatile bool disposed;
52 		private bool checkCertRevocationStatus;
53 		private object negotiate;
54 		private object read;
55 		private object write;
56 		private ManualResetEvent negotiationComplete;
57 
58 		#endregion
59 
60 
61 		#region Constructors
62 
SslStreamBase( Stream stream, bool ownsStream)63 		protected SslStreamBase(
64 			Stream stream,
65 			bool ownsStream)
66 		{
67 			if (stream == null)
68 			{
69 				throw new ArgumentNullException("stream is null.");
70 			}
71 			if (!stream.CanRead || !stream.CanWrite)
72 			{
73 				throw new ArgumentNullException("stream is not both readable and writable.");
74 			}
75 
76 			this.inputBuffer = new MemoryStream();
77 			this.innerStream = stream;
78 			this.ownsStream = ownsStream;
79 			this.negotiate = new object();
80 			this.read = new object();
81 			this.write = new object();
82 			this.negotiationComplete = new ManualResetEvent(false);
83 		}
84 
85 		#endregion
86 
87 		#region Handshakes
AsyncHandshakeCallback(IAsyncResult asyncResult)88 		private void AsyncHandshakeCallback(IAsyncResult asyncResult)
89 		{
90 			InternalAsyncResult internalResult = asyncResult.AsyncState as InternalAsyncResult;
91 
92 			try
93 			{
94 				try
95 				{
96 					this.OnNegotiateHandshakeCallback(asyncResult);
97 				}
98 				catch (TlsException ex)
99 				{
100 					this.protocol.SendAlert(ex.Alert);
101 
102 					throw new IOException("The authentication or decryption has failed.", ex);
103 				}
104 				catch (Exception ex)
105 				{
106 					this.protocol.SendAlert(AlertDescription.InternalError);
107 
108 					throw new IOException("The authentication or decryption has failed.", ex);
109 				}
110 
111 				if (internalResult.ProceedAfterHandshake)
112 				{
113 					//kick off the read or write process (whichever called us) after the handshake is complete
114 					if (internalResult.FromWrite)
115 					{
116 						InternalBeginWrite(internalResult);
117 					}
118 					else
119 					{
120 						InternalBeginRead(internalResult);
121 					}
122 					negotiationComplete.Set();
123 				}
124 				else
125 				{
126 					negotiationComplete.Set();
127 					internalResult.SetComplete();
128 				}
129 
130 			}
131 			catch (Exception ex)
132 			{
133 				negotiationComplete.Set();
134 				internalResult.SetComplete(ex);
135 			}
136 		}
137 
138 		internal bool MightNeedHandshake
139 		{
140 			get
141 			{
142 				if (this.context.HandshakeState == HandshakeState.Finished)
143 				{
144 					return false;
145 				}
146 				else
147 				{
148 					lock (this.negotiate)
149 					{
150 						return (this.context.HandshakeState != HandshakeState.Finished);
151 					}
152 				}
153 			}
154 		}
155 
NegotiateHandshake()156 		internal void NegotiateHandshake()
157 		{
158 			if (this.MightNeedHandshake)
159 			{
160 				InternalAsyncResult ar = new InternalAsyncResult(null, null, null, 0, 0, false, false);
161 
162 				//if something already started negotiation, wait for it.
163 				//otherwise end it ourselves.
164 				if (!BeginNegotiateHandshake(ar))
165 				{
166 					this.negotiationComplete.WaitOne();
167 				}
168 				else
169 				{
170 					this.EndNegotiateHandshake(ar);
171 				}
172 			}
173 		}
174 
175 		#endregion
176 
177 		#region Abstracts/Virtuals
178 
OnBeginNegotiateHandshake(AsyncCallback callback, object state)179 		internal abstract IAsyncResult OnBeginNegotiateHandshake(AsyncCallback callback, object state);
OnNegotiateHandshakeCallback(IAsyncResult asyncResult)180 		internal abstract void OnNegotiateHandshakeCallback(IAsyncResult asyncResult);
181 
OnLocalCertificateSelection(X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string targetHost, X509CertificateCollection serverRequestedCertificates)182 		internal abstract X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates,
183 															X509Certificate serverCertificate,
184 															string targetHost,
185 															X509CertificateCollection serverRequestedCertificates);
186 
OnRemoteCertificateValidation(X509Certificate certificate, int[] errors)187 		internal abstract bool OnRemoteCertificateValidation(X509Certificate certificate, int[] errors);
OnRemoteCertificateValidation2(Mono.Security.X509.X509CertificateCollection collection)188 		internal abstract ValidationResult OnRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection);
189 		internal abstract bool HaveRemoteValidation2Callback { get; }
190 
OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost)191 		internal abstract AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost);
192 
193 		#endregion
194 
195 		#region Event Methods
196 
RaiseLocalCertificateSelection(X509CertificateCollection certificates, X509Certificate remoteCertificate, string targetHost, X509CertificateCollection requestedCertificates)197 		internal X509Certificate RaiseLocalCertificateSelection(X509CertificateCollection certificates,
198 															X509Certificate remoteCertificate,
199 															string targetHost,
200 															X509CertificateCollection requestedCertificates)
201 		{
202 			return OnLocalCertificateSelection(certificates, remoteCertificate, targetHost, requestedCertificates);
203 		}
204 
RaiseRemoteCertificateValidation(X509Certificate certificate, int[] errors)205 		internal bool RaiseRemoteCertificateValidation(X509Certificate certificate, int[] errors)
206 		{
207 			return OnRemoteCertificateValidation(certificate, errors);
208 		}
209 
RaiseRemoteCertificateValidation2(Mono.Security.X509.X509CertificateCollection collection)210 		internal ValidationResult RaiseRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
211 		{
212 			return OnRemoteCertificateValidation2 (collection);
213 		}
214 
RaiseLocalPrivateKeySelection( X509Certificate certificate, string targetHost)215 		internal AsymmetricAlgorithm RaiseLocalPrivateKeySelection(
216 			X509Certificate certificate,
217 			string targetHost)
218 		{
219 			return OnLocalPrivateKeySelection(certificate, targetHost);
220 		}
221 		#endregion
222 
223 		#region Security Properties
224 
225 		public bool CheckCertRevocationStatus
226 		{
227 			get { return this.checkCertRevocationStatus; }
228 			set { this.checkCertRevocationStatus = value; }
229 		}
230 
231 		public CipherAlgorithmType CipherAlgorithm
232 		{
233 			get
234 			{
235 				if (this.context.HandshakeState == HandshakeState.Finished)
236 				{
237 					return this.context.Current.Cipher.CipherAlgorithmType;
238 				}
239 
240 				return CipherAlgorithmType.None;
241 			}
242 		}
243 
244 		public int CipherStrength
245 		{
246 			get
247 			{
248 				if (this.context.HandshakeState == HandshakeState.Finished)
249 				{
250 					return this.context.Current.Cipher.EffectiveKeyBits;
251 				}
252 
253 				return 0;
254 			}
255 		}
256 
257 		public HashAlgorithmType HashAlgorithm
258 		{
259 			get
260 			{
261 				if (this.context.HandshakeState == HandshakeState.Finished)
262 				{
263 					return this.context.Current.Cipher.HashAlgorithmType;
264 				}
265 
266 				return HashAlgorithmType.None;
267 			}
268 		}
269 
270 		public int HashStrength
271 		{
272 			get
273 			{
274 				if (this.context.HandshakeState == HandshakeState.Finished)
275 				{
276 					return this.context.Current.Cipher.HashSize * 8;
277 				}
278 
279 				return 0;
280 			}
281 		}
282 
283 		public int KeyExchangeStrength
284 		{
285 			get
286 			{
287 				if (this.context.HandshakeState == HandshakeState.Finished)
288 				{
289 					return this.context.ServerSettings.Certificates[0].RSA.KeySize;
290 				}
291 
292 				return 0;
293 			}
294 		}
295 
296 		public ExchangeAlgorithmType KeyExchangeAlgorithm
297 		{
298 			get
299 			{
300 				if (this.context.HandshakeState == HandshakeState.Finished)
301 				{
302 					return this.context.Current.Cipher.ExchangeAlgorithmType;
303 				}
304 
305 				return ExchangeAlgorithmType.None;
306 			}
307 		}
308 
309 		public SecurityProtocolType SecurityProtocol
310 		{
311 			get
312 			{
313 				if (this.context.HandshakeState == HandshakeState.Finished)
314 				{
315 					return this.context.SecurityProtocol;
316 				}
317 
318 				return 0;
319 			}
320 		}
321 
322 		public X509Certificate ServerCertificate
323 		{
324 			get
325 			{
326 				if (this.context.HandshakeState == HandshakeState.Finished)
327 				{
328 					if (this.context.ServerSettings.Certificates != null &&
329 						this.context.ServerSettings.Certificates.Count > 0)
330 					{
331 						return new X509Certificate(this.context.ServerSettings.Certificates[0].RawData);
332 					}
333 				}
334 
335 				return null;
336 			}
337 		}
338 
339 		// this is used by Mono's certmgr tool to download certificates
340 		internal Mono.Security.X509.X509CertificateCollection ServerCertificates
341 		{
342 			get { return context.ServerSettings.Certificates; }
343 		}
344 
345 		#endregion
346 
347 		#region Internal Async Result/State Class
348 
349 		private class InternalAsyncResult : IAsyncResult
350 		{
351 			private object locker = new object ();
352 			private AsyncCallback _userCallback;
353 			private object _userState;
354 			private Exception _asyncException;
355 			private ManualResetEvent handle;
356 			private bool completed;
357 			private int _bytesRead;
358 			private bool _fromWrite;
359 			private bool _proceedAfterHandshake;
360 
361 			private byte[] _buffer;
362 			private int _offset;
363 			private int _count;
364 
InternalAsyncResult(AsyncCallback userCallback, object userState, byte[] buffer, int offset, int count, bool fromWrite, bool proceedAfterHandshake)365 			public InternalAsyncResult(AsyncCallback userCallback, object userState, byte[] buffer, int offset, int count, bool fromWrite, bool proceedAfterHandshake)
366 			{
367 				_userCallback = userCallback;
368 				_userState = userState;
369 				_buffer = buffer;
370 				_offset = offset;
371 				_count = count;
372 				_fromWrite = fromWrite;
373 				_proceedAfterHandshake = proceedAfterHandshake;
374 			}
375 
376 			public bool ProceedAfterHandshake
377 			{
378 				get { return _proceedAfterHandshake; }
379 			}
380 
381 			public bool FromWrite
382 			{
383 				get { return _fromWrite; }
384 			}
385 
386 			public byte[] Buffer
387 			{
388 				get { return _buffer; }
389 			}
390 
391 			public int Offset
392 			{
393 				get { return _offset; }
394 			}
395 
396 			public int Count
397 			{
398 				get { return _count; }
399 			}
400 
401 			public int BytesRead
402 			{
403 				get { return _bytesRead; }
404 			}
405 
406 			public object AsyncState
407 			{
408 				get { return _userState; }
409 			}
410 
411 			public Exception AsyncException
412 			{
413 				get { return _asyncException; }
414 			}
415 
416 			public bool CompletedWithError
417 			{
418 				get {
419 					if (IsCompleted == false)
420 						return false;
421 					return null != _asyncException;
422 				}
423 			}
424 
425 			public WaitHandle AsyncWaitHandle
426 			{
427 				get {
428 					lock (locker) {
429 						if (handle == null)
430 							handle = new ManualResetEvent (completed);
431 					}
432 					return handle;
433 				}
434 			}
435 
436 			public bool CompletedSynchronously
437 			{
438 				get { return false; }
439 			}
440 
441 			public bool IsCompleted
442 			{
443 				get {
444 					lock (locker)
445 						return completed;
446 				}
447 			}
448 
SetComplete(Exception ex, int bytesRead)449 			private void SetComplete(Exception ex, int bytesRead)
450 			{
451 				lock (locker) {
452 					if (completed)
453 						return;
454 
455 					completed = true;
456 					_asyncException = ex;
457 					_bytesRead = bytesRead;
458 					if (handle != null)
459 						handle.Set ();
460 				}
461 				if (_userCallback != null)
462 					_userCallback.BeginInvoke (this, null, null);
463 			}
464 
SetComplete(Exception ex)465 			public void SetComplete(Exception ex)
466 			{
467 				SetComplete(ex, 0);
468 			}
469 
SetComplete(int bytesRead)470 			public void SetComplete(int bytesRead)
471 			{
472 				SetComplete(null, bytesRead);
473 			}
474 
SetComplete()475 			public void SetComplete()
476 			{
477 				SetComplete(null, 0);
478 			}
479 		}
480 		#endregion
481 
482 		#region Stream Overrides and Async Stream Operations
483 
BeginNegotiateHandshake(InternalAsyncResult asyncResult)484 		private bool BeginNegotiateHandshake(InternalAsyncResult asyncResult)
485 		{
486 			try
487 			{
488 				lock (this.negotiate)
489 				{
490 					if (this.context.HandshakeState == HandshakeState.None)
491 					{
492 						this.OnBeginNegotiateHandshake(new AsyncCallback(AsyncHandshakeCallback), asyncResult);
493 
494 						return true;
495 					}
496 					else
497 					{
498 						return false;
499 					}
500 				}
501 			}
502 			catch (TlsException ex)
503 			{
504 				this.negotiationComplete.Set();
505 				this.protocol.SendAlert(ex.Alert);
506 
507 				throw new IOException("The authentication or decryption has failed.", ex);
508 			}
509 			catch (Exception ex)
510 			{
511 				this.negotiationComplete.Set();
512 				this.protocol.SendAlert(AlertDescription.InternalError);
513 
514 				throw new IOException("The authentication or decryption has failed.", ex);
515 			}
516 		}
517 
EndNegotiateHandshake(InternalAsyncResult asyncResult)518 		private void EndNegotiateHandshake(InternalAsyncResult asyncResult)
519 		{
520 			if (asyncResult.IsCompleted == false)
521 				asyncResult.AsyncWaitHandle.WaitOne();
522 
523 			if (asyncResult.CompletedWithError)
524 			{
525 				throw asyncResult.AsyncException;
526 			}
527 		}
528 
BeginRead( byte[] buffer, int offset, int count, AsyncCallback callback, object state)529 		public override IAsyncResult BeginRead(
530 			byte[] buffer,
531 			int offset,
532 			int count,
533 			AsyncCallback callback,
534 			object state)
535 		{
536 			this.checkDisposed();
537 
538 			if (buffer == null)
539 			{
540 				throw new ArgumentNullException("buffer is a null reference.");
541 			}
542 			if (offset < 0)
543 			{
544 				throw new ArgumentOutOfRangeException("offset is less than 0.");
545 			}
546 			if (offset > buffer.Length)
547 			{
548 				throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
549 			}
550 			if (count < 0)
551 			{
552 				throw new ArgumentOutOfRangeException("count is less than 0.");
553 			}
554 			if (count > (buffer.Length - offset))
555 			{
556 				throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
557 			}
558 
559 			InternalAsyncResult asyncResult = new InternalAsyncResult(callback, state, buffer, offset, count, false, true);
560 
561 			if (this.MightNeedHandshake)
562 			{
563 				if (! BeginNegotiateHandshake(asyncResult))
564 				{
565 					//we made it down here so the handshake was not started.
566 					//another thread must have started it in the mean time.
567 					//wait for it to complete and then perform our original operation
568 					this.negotiationComplete.WaitOne();
569 
570 					InternalBeginRead(asyncResult);
571 				}
572 			}
573 			else
574 			{
575 				InternalBeginRead(asyncResult);
576 			}
577 
578 			return asyncResult;
579 		}
580 
581 		// bigger than max record length for SSL/TLS
582 		private byte[] recbuf = new byte[16384];
583 
InternalBeginRead(InternalAsyncResult asyncResult)584 		private void InternalBeginRead(InternalAsyncResult asyncResult)
585 		{
586 			try
587 			{
588 				int preReadSize = 0;
589 
590 				lock (this.read)
591 				{
592 					// If actual buffer is fully read, reset it
593 					bool shouldReset = this.inputBuffer.Position == this.inputBuffer.Length && this.inputBuffer.Length > 0;
594 
595 					// If the buffer isn't fully read, but does have data, we need to immediately
596 					// read the info from the buffer and let the user know that they have more data.
597 					bool shouldReadImmediately = (this.inputBuffer.Length > 0) && (asyncResult.Count > 0);
598 
599 					if (shouldReset)
600 					{
601 						this.resetBuffer();
602 					}
603 					else if (shouldReadImmediately)
604 					{
605 						preReadSize = this.inputBuffer.Read(asyncResult.Buffer, asyncResult.Offset, asyncResult.Count);
606 					}
607 				}
608 
609 				// This is explicitly done outside the synclock to avoid
610 				// any potential deadlocks in the delegate call.
611 				if (0 < preReadSize)
612 				{
613 					asyncResult.SetComplete(preReadSize);
614 				}
615 				else if (!this.context.ReceivedConnectionEnd)
616 				{
617 					// this will read data from the network until we have (at least) one
618 					// record to send back to the caller
619 					this.innerStream.BeginRead(recbuf, 0, recbuf.Length,
620 						new AsyncCallback(InternalReadCallback), new object[] { recbuf, asyncResult });
621 				}
622 				else
623 				{
624 					// We're done with the connection so we need to let the caller know with 0 bytes read
625 					asyncResult.SetComplete(0);
626 				}
627 			}
628 			catch (TlsException ex)
629 			{
630 				this.protocol.SendAlert(ex.Alert);
631 
632 				throw new IOException("The authentication or decryption has failed.", ex);
633 			}
634 			catch (Exception ex)
635 			{
636 				throw new IOException("IO exception during read.", ex);
637 			}
638 		}
639 
640 
641 		private MemoryStream recordStream = new MemoryStream();
642 
643 		// read encrypted data until we have enough to decrypt (at least) one
644 		// record and return are the records (may be more than one) we have
InternalReadCallback(IAsyncResult result)645 		private void InternalReadCallback(IAsyncResult result)
646 		{
647 			if (this.disposed)
648 				return;
649 
650 			object[] state = (object[])result.AsyncState;
651 			byte[] recbuf = (byte[])state[0];
652 			InternalAsyncResult internalResult = (InternalAsyncResult)state[1];
653 
654 			try
655 			{
656 				int n = innerStream.EndRead(result);
657 				if (n > 0)
658 				{
659 					// Add the just received data to the waiting data
660 					recordStream.Write(recbuf, 0, n);
661 				}
662 				else
663 				{
664 					// 0 length data means this read operation is done (lost connection in the case of a network stream).
665 					internalResult.SetComplete(0);
666 					return;
667 				}
668 
669 				bool dataToReturn = false;
670 				long pos = recordStream.Position;
671 
672 				recordStream.Position = 0;
673 				byte[] record = null;
674 
675 				// don't try to decode record unless we have at least 5 bytes
676 				// i.e. type (1), protocol (2) and length (2)
677 				if (recordStream.Length >= 5)
678 				{
679 					record = this.protocol.ReceiveRecord(recordStream);
680 				}
681 
682 				// a record of 0 length is valid (and there may be more record after it)
683 				while (record != null)
684 				{
685 					// we probably received more stuff after the record, and we must keep it!
686 					long remainder = recordStream.Length - recordStream.Position;
687 					byte[] outofrecord = null;
688 					if (remainder > 0)
689 					{
690 						outofrecord = new byte[remainder];
691 						recordStream.Read(outofrecord, 0, outofrecord.Length);
692 					}
693 
694 					lock (this.read)
695 					{
696 						long position = this.inputBuffer.Position;
697 
698 						if (record.Length > 0)
699 						{
700 							// Write new data to the inputBuffer
701 							this.inputBuffer.Seek(0, SeekOrigin.End);
702 							this.inputBuffer.Write(record, 0, record.Length);
703 
704 							// Restore buffer position
705 							this.inputBuffer.Seek(position, SeekOrigin.Begin);
706 							dataToReturn = true;
707 						}
708 					}
709 
710 					recordStream.SetLength(0);
711 					record = null;
712 
713 					if (remainder > 0)
714 					{
715 						recordStream.Write(outofrecord, 0, outofrecord.Length);
716 						// type (1), protocol (2) and length (2)
717 						if (recordStream.Length >= 5)
718 						{
719 							// try to see if another record is available
720 							recordStream.Position = 0;
721 							record = this.protocol.ReceiveRecord(recordStream);
722 							if (record == null)
723 								pos = recordStream.Length;
724 						}
725 						else
726 							pos = remainder;
727 					}
728 					else
729 						pos = 0;
730 				}
731 
732 				if (!dataToReturn && (n > 0))
733 				{
734 					if (context.ReceivedConnectionEnd) {
735 						internalResult.SetComplete (0);
736 					} else {
737 						// there is no record to return to caller and (possibly) more data waiting
738 						// so continue reading from network (and appending to stream)
739 						recordStream.Position = recordStream.Length;
740 						this.innerStream.BeginRead(recbuf, 0, recbuf.Length,
741 							new AsyncCallback(InternalReadCallback), state);
742 					}
743 				}
744 				else
745 				{
746 					// we have record(s) to return -or- no more available to read from network
747 					// reset position for further reading
748 					recordStream.Position = pos;
749 
750 					int bytesRead = 0;
751 					lock (this.read)
752 					{
753 						bytesRead = this.inputBuffer.Read(internalResult.Buffer, internalResult.Offset, internalResult.Count);
754 					}
755 
756 					internalResult.SetComplete(bytesRead);
757 				}
758 			}
759 			catch (Exception ex)
760 			{
761 				internalResult.SetComplete(ex);
762 			}
763 
764 		}
765 
InternalBeginWrite(InternalAsyncResult asyncResult)766 		private void InternalBeginWrite(InternalAsyncResult asyncResult)
767 		{
768 			try
769 			{
770 				// Send the buffer as a TLS record
771 
772 				lock (this.write)
773 				{
774 					byte[] record = this.protocol.EncodeRecord(
775 						ContentType.ApplicationData, asyncResult.Buffer, asyncResult.Offset, asyncResult.Count);
776 
777 					this.innerStream.BeginWrite(
778 						record, 0, record.Length, new AsyncCallback(InternalWriteCallback), asyncResult);
779 				}
780 			}
781 			catch (TlsException ex)
782 			{
783 				this.protocol.SendAlert(ex.Alert);
784 				this.Close();
785 
786 				throw new IOException("The authentication or decryption has failed.", ex);
787 			}
788 			catch (Exception ex)
789 			{
790 				throw new IOException("IO exception during Write.", ex);
791 			}
792 		}
793 
InternalWriteCallback(IAsyncResult ar)794 		private void InternalWriteCallback(IAsyncResult ar)
795 		{
796 			if (this.disposed)
797 				return;
798 
799 			InternalAsyncResult internalResult = (InternalAsyncResult)ar.AsyncState;
800 
801 			try
802 			{
803 				this.innerStream.EndWrite(ar);
804 				internalResult.SetComplete();
805 			}
806 			catch (Exception ex)
807 			{
808 				internalResult.SetComplete(ex);
809 			}
810 		}
811 
BeginWrite( byte[] buffer, int offset, int count, AsyncCallback callback, object state)812 		public override IAsyncResult BeginWrite(
813 			byte[] buffer,
814 			int offset,
815 			int count,
816 			AsyncCallback callback,
817 			object state)
818 		{
819 			this.checkDisposed();
820 
821 			if (buffer == null)
822 			{
823 				throw new ArgumentNullException("buffer is a null reference.");
824 			}
825 			if (offset < 0)
826 			{
827 				throw new ArgumentOutOfRangeException("offset is less than 0.");
828 			}
829 			if (offset > buffer.Length)
830 			{
831 				throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
832 			}
833 			if (count < 0)
834 			{
835 				throw new ArgumentOutOfRangeException("count is less than 0.");
836 			}
837 			if (count > (buffer.Length - offset))
838 			{
839 				throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
840 			}
841 
842 
843 			InternalAsyncResult asyncResult = new InternalAsyncResult(callback, state, buffer, offset, count, true, true);
844 
845 			if (this.MightNeedHandshake)
846 			{
847 				if (! BeginNegotiateHandshake(asyncResult))
848 				{
849 					//we made it down here so the handshake was not started.
850 					//another thread must have started it in the mean time.
851 					//wait for it to complete and then perform our original operation
852 					this.negotiationComplete.WaitOne();
853 
854 					InternalBeginWrite(asyncResult);
855 				}
856 			}
857 			else
858 			{
859 				InternalBeginWrite(asyncResult);
860 			}
861 
862 			return asyncResult;
863 		}
864 
EndRead(IAsyncResult asyncResult)865 		public override int EndRead(IAsyncResult asyncResult)
866 		{
867 			this.checkDisposed();
868 
869 			InternalAsyncResult internalResult = asyncResult as InternalAsyncResult;
870 			if (internalResult == null)
871 			{
872 				throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
873 			}
874 
875 			// Always wait until the read is complete
876 			if (!asyncResult.IsCompleted)
877 			{
878 				if (!asyncResult.AsyncWaitHandle.WaitOne (WaitTimeOut, false))
879 					throw new TlsException (AlertDescription.InternalError, "Couldn't complete EndRead");
880 			}
881 
882 			if (internalResult.CompletedWithError)
883 			{
884 				throw internalResult.AsyncException;
885 			}
886 
887 			return internalResult.BytesRead;
888 		}
889 
EndWrite(IAsyncResult asyncResult)890 		public override void EndWrite(IAsyncResult asyncResult)
891 		{
892 			this.checkDisposed();
893 
894 			InternalAsyncResult internalResult = asyncResult as InternalAsyncResult;
895 			if (internalResult == null)
896 			{
897 				throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginWrite.");
898 			}
899 
900 
901 			if (!asyncResult.IsCompleted)
902 			{
903 				if (!internalResult.AsyncWaitHandle.WaitOne (WaitTimeOut, false))
904 					throw new TlsException (AlertDescription.InternalError, "Couldn't complete EndWrite");
905 			}
906 
907 			if (internalResult.CompletedWithError)
908 			{
909 				throw internalResult.AsyncException;
910 			}
911 		}
912 
Close()913 		public override void Close()
914 		{
915 			base.Close ();
916 		}
917 
Flush()918 		public override void Flush()
919 		{
920 			this.checkDisposed();
921 
922 			this.innerStream.Flush();
923 		}
924 
Read(byte[] buffer)925 		public int Read(byte[] buffer)
926 		{
927 			return this.Read(buffer, 0, buffer.Length);
928 		}
929 
Read(byte[] buffer, int offset, int count)930 		public override int Read(byte[] buffer, int offset, int count)
931 		{
932 			this.checkDisposed ();
933 
934 			if (buffer == null)
935 			{
936 				throw new ArgumentNullException ("buffer");
937 			}
938 			if (offset < 0)
939 			{
940 				throw new ArgumentOutOfRangeException("offset is less than 0.");
941 			}
942 			if (offset > buffer.Length)
943 			{
944 				throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
945 			}
946 			if (count < 0)
947 			{
948 				throw new ArgumentOutOfRangeException("count is less than 0.");
949 			}
950 			if (count > (buffer.Length - offset))
951 			{
952 				throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
953 			}
954 
955 			if (this.context.HandshakeState != HandshakeState.Finished)
956 			{
957 				this.NegotiateHandshake (); // Handshake negotiation
958 			}
959 
960 			lock (this.read) {
961 				try {
962 					record_processing.Reset ();
963 					// do we already have some decrypted data ?
964 					if (this.inputBuffer.Position > 0) {
965 						// or maybe we used all the buffer before ?
966 						if (this.inputBuffer.Position == this.inputBuffer.Length) {
967 							this.inputBuffer.SetLength (0);
968 						} else {
969 							int n = this.inputBuffer.Read (buffer, offset, count);
970 							if (n > 0) {
971 								record_processing.Set ();
972 								return n;
973 							}
974 						}
975 					}
976 
977 					bool needMoreData = false;
978 					while (true) {
979 						// we first try to process the read with the data we already have
980 						if ((recordStream.Position == 0) || needMoreData) {
981 							needMoreData = false;
982 							// if we loop, then it either means we need more data
983 							byte[] recbuf = new byte[16384];
984 							int n = 0;
985 							if (count == 1) {
986 								int value = innerStream.ReadByte ();
987 								if (value >= 0) {
988 									recbuf[0] = (byte) value;
989 									n = 1;
990 								}
991 							} else {
992 								n = innerStream.Read (recbuf, 0, recbuf.Length);
993 							}
994 							if (n > 0) {
995 								// Add the new received data to the waiting data
996 								if ((recordStream.Length > 0) && (recordStream.Position != recordStream.Length))
997 									recordStream.Seek (0, SeekOrigin.End);
998 								recordStream.Write (recbuf, 0, n);
999 							} else {
1000 								// or that the read operation is done (lost connection in the case of a network stream).
1001 								record_processing.Set ();
1002 								return 0;
1003 							}
1004 						}
1005 
1006 						bool dataToReturn = false;
1007 
1008 						recordStream.Position = 0;
1009 						byte[] record = null;
1010 
1011 						// don't try to decode record unless we have at least 5 bytes
1012 						// i.e. type (1), protocol (2) and length (2)
1013 						if (recordStream.Length >= 5) {
1014 							record = this.protocol.ReceiveRecord (recordStream);
1015 							needMoreData = (record == null);
1016 						}
1017 
1018 						// a record of 0 length is valid (and there may be more record after it)
1019 						while (record != null) {
1020 							// we probably received more stuff after the record, and we must keep it!
1021 							long remainder = recordStream.Length - recordStream.Position;
1022 							byte[] outofrecord = null;
1023 							if (remainder > 0) {
1024 								outofrecord = new byte[remainder];
1025 								recordStream.Read (outofrecord, 0, outofrecord.Length);
1026 							}
1027 
1028 							long position = this.inputBuffer.Position;
1029 
1030 							if (record.Length > 0) {
1031 								// Write new data to the inputBuffer
1032 								this.inputBuffer.Seek (0, SeekOrigin.End);
1033 								this.inputBuffer.Write (record, 0, record.Length);
1034 
1035 								// Restore buffer position
1036 								this.inputBuffer.Seek (position, SeekOrigin.Begin);
1037 								dataToReturn = true;
1038 							}
1039 
1040 							recordStream.SetLength (0);
1041 							record = null;
1042 
1043 							if (remainder > 0) {
1044 								recordStream.Write (outofrecord, 0, outofrecord.Length);
1045 							}
1046 
1047 							if (dataToReturn) {
1048 								// we have record(s) to return -or- no more available to read from network
1049 								// reset position for further reading
1050 								int i = inputBuffer.Read (buffer, offset, count);
1051 								record_processing.Set ();
1052 								return i;
1053 							}
1054 						}
1055 					}
1056 				}
1057 				catch (TlsException ex)
1058 				{
1059 					throw new IOException("The authentication or decryption has failed.", ex);
1060 				}
1061 				catch (Exception ex)
1062 				{
1063 					throw new IOException("IO exception during read.", ex);
1064 				}
1065 			}
1066 		}
1067 
Seek(long offset, SeekOrigin origin)1068 		public override long Seek(long offset, SeekOrigin origin)
1069 		{
1070 			throw new NotSupportedException();
1071 		}
1072 
SetLength(long value)1073 		public override void SetLength(long value)
1074 		{
1075 			throw new NotSupportedException();
1076 		}
1077 
Write(byte[] buffer)1078 		public void Write(byte[] buffer)
1079 		{
1080 			this.Write(buffer, 0, buffer.Length);
1081 		}
1082 
Write(byte[] buffer, int offset, int count)1083 		public override void Write(byte[] buffer, int offset, int count)
1084 		{
1085 			this.checkDisposed ();
1086 
1087 			if (buffer == null)
1088 			{
1089 				throw new ArgumentNullException ("buffer");
1090 			}
1091 			if (offset < 0)
1092 			{
1093 				throw new ArgumentOutOfRangeException("offset is less than 0.");
1094 			}
1095 			if (offset > buffer.Length)
1096 			{
1097 				throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
1098 			}
1099 			if (count < 0)
1100 			{
1101 				throw new ArgumentOutOfRangeException("count is less than 0.");
1102 			}
1103 			if (count > (buffer.Length - offset))
1104 			{
1105 				throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
1106 			}
1107 
1108 			if (this.context.HandshakeState != HandshakeState.Finished)
1109 			{
1110 				this.NegotiateHandshake ();
1111 			}
1112 
1113 			lock (this.write)
1114 			{
1115 				try
1116 				{
1117 					// Send the buffer as a TLS record
1118 					byte[] record = this.protocol.EncodeRecord (ContentType.ApplicationData, buffer, offset, count);
1119 					this.innerStream.Write (record, 0, record.Length);
1120 				}
1121 				catch (TlsException ex)
1122 				{
1123 					this.protocol.SendAlert(ex.Alert);
1124 					this.Close();
1125 					throw new IOException("The authentication or decryption has failed.", ex);
1126 				}
1127 				catch (Exception ex)
1128 				{
1129 					throw new IOException("IO exception during Write.", ex);
1130 				}
1131 			}
1132 		}
1133 
1134 		public override bool CanRead
1135 		{
1136 			get { return this.innerStream.CanRead; }
1137 		}
1138 
1139 		public override bool CanSeek
1140 		{
1141 			get { return false; }
1142 		}
1143 
1144 		public override bool CanWrite
1145 		{
1146 			get { return this.innerStream.CanWrite; }
1147 		}
1148 
1149 		public override long Length
1150 		{
1151 			get { throw new NotSupportedException(); }
1152 		}
1153 
1154 		public override long Position
1155 		{
1156 			get
1157 			{
1158 				throw new NotSupportedException();
1159 			}
1160 			set
1161 			{
1162 				throw new NotSupportedException();
1163 			}
1164 		}
1165 		#endregion
1166 
1167 		#region IDisposable Members and Finalizer
1168 
~SslStreamBase()1169 		~SslStreamBase()
1170 		{
1171 			this.Dispose(false);
1172 		}
1173 
Dispose(bool disposing)1174 		protected override void Dispose (bool disposing)
1175 		{
1176 			if (!this.disposed)
1177 			{
1178 				if (disposing)
1179 				{
1180 					if (this.innerStream != null)
1181 					{
1182 						if (this.context.HandshakeState == HandshakeState.Finished &&
1183 							!this.context.SentConnectionEnd)
1184 						{
1185 							// Write close notify
1186 							try {
1187 								this.protocol.SendAlert(AlertDescription.CloseNotify);
1188 							} catch {
1189 							}
1190 						}
1191 
1192 						if (this.ownsStream)
1193 						{
1194 							// Close inner stream
1195 							this.innerStream.Close();
1196 						}
1197 					}
1198 					this.ownsStream = false;
1199 					this.innerStream = null;
1200 				}
1201 
1202 				this.disposed = true;
1203 				base.Dispose (disposing);
1204 			}
1205 		}
1206 
1207 		#endregion
1208 
1209 		#region Misc Methods
1210 
resetBuffer()1211 		private void resetBuffer()
1212 		{
1213 			this.inputBuffer.SetLength(0);
1214 			this.inputBuffer.Position = 0;
1215 		}
1216 
checkDisposed()1217 		internal void checkDisposed()
1218 		{
1219 			if (this.disposed)
1220 			{
1221 				throw new ObjectDisposedException("The Stream is closed.");
1222 			}
1223 		}
1224 
1225 		#endregion
1226 
1227 	}
1228 }
1229