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