Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / SslClientStream.cs
1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
3
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 using Mono.Security.Protocol.Tls.Handshake;
35
36 namespace Mono.Security.Protocol.Tls
37 {
38         #region Delegates
39
40 #if INSIDE_SYSTEM
41         internal
42 #else
43         public
44 #endif
45         delegate bool CertificateValidationCallback(
46                 X509Certificate certificate, 
47                 int[]                   certificateErrors);
48
49 #if INSIDE_SYSTEM
50         internal
51 #else
52         public
53 #endif
54         class ValidationResult {
55                 bool trusted;
56                 bool user_denied;
57                 int error_code;
58
59                 public ValidationResult (bool trusted, bool user_denied, int error_code)
60                 {
61                         this.trusted = trusted;
62                         this.user_denied = user_denied;
63                         this.error_code = error_code;
64                 }
65
66                 public bool Trusted {
67                         get { return trusted; }
68                 }
69
70                 public bool UserDenied {
71                         get { return user_denied; }
72                 }
73
74                 public int ErrorCode {
75                         get { return error_code; }
76                 }
77         }
78
79 #if INSIDE_SYSTEM
80         internal
81 #else
82         public
83 #endif
84         delegate ValidationResult CertificateValidationCallback2 (Mono.Security.X509.X509CertificateCollection collection);
85
86 #if INSIDE_SYSTEM
87         internal
88 #else
89         public
90 #endif
91         delegate X509Certificate CertificateSelectionCallback(
92                 X509CertificateCollection       clientCertificates, 
93                 X509Certificate                         serverCertificate, 
94                 string                                          targetHost, 
95                 X509CertificateCollection       serverRequestedCertificates);
96
97 #if INSIDE_SYSTEM
98         internal
99 #else
100         public
101 #endif
102         delegate AsymmetricAlgorithm PrivateKeySelectionCallback(
103                 X509Certificate certificate, 
104                 string                  targetHost);
105
106         #endregion
107
108 #if INSIDE_SYSTEM
109         internal
110 #else
111         public
112 #endif
113         class SslClientStream : SslStreamBase
114         {
115                 #region Internal Events
116                 
117                 internal event CertificateValidationCallback    ServerCertValidation;
118                 internal event CertificateSelectionCallback             ClientCertSelection;
119                 internal event PrivateKeySelectionCallback              PrivateKeySelection;
120                 
121                 #endregion
122
123                 #region Properties
124
125                 // required by HttpsClientStream for proxy support
126                 internal Stream InputBuffer 
127                 {
128                         get { return base.inputBuffer; }
129                 }
130
131                 public X509CertificateCollection ClientCertificates
132                 {
133                         get { return this.context.ClientSettings.Certificates; }
134                 }
135
136                 public X509Certificate SelectedClientCertificate
137                 {
138                         get { return this.context.ClientSettings.ClientCertificate; }
139                 }
140
141                 #endregion
142
143                 #region Callback Properties
144
145                 public CertificateValidationCallback ServerCertValidationDelegate
146                 {
147                         get { return this.ServerCertValidation; }
148                         set { this.ServerCertValidation = value; }                      
149                 }
150
151                 public CertificateSelectionCallback ClientCertSelectionDelegate 
152                 {
153                         get { return this.ClientCertSelection; }
154                         set { this.ClientCertSelection = value; }
155                 }
156
157                 public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate
158                 {
159                         get { return this.PrivateKeySelection; }
160                         set { this.PrivateKeySelection = value; }
161                 }
162                 
163                 #endregion
164
165                 public event CertificateValidationCallback2 ServerCertValidation2;
166
167                 #region Constructors
168                 
169                 public SslClientStream(
170                         Stream  stream, 
171                         string  targetHost, 
172                         bool    ownsStream) 
173                         : this(
174                                 stream, targetHost, ownsStream, 
175                                 SecurityProtocolType.Default, null)
176                 {
177                 }
178                 
179                 public SslClientStream(
180                         Stream                          stream, 
181                         string                          targetHost, 
182                         X509Certificate         clientCertificate) 
183                         : this(
184                                 stream, targetHost, false, SecurityProtocolType.Default, 
185                                 new X509CertificateCollection(new X509Certificate[]{clientCertificate}))
186                 {
187                 }
188
189                 public SslClientStream(
190                         Stream                                          stream,
191                         string                                          targetHost, 
192                         X509CertificateCollection clientCertificates) : 
193                         this(
194                                 stream, targetHost, false, SecurityProtocolType.Default, 
195                                 clientCertificates)
196                 {
197                 }
198
199                 public SslClientStream(
200                         Stream                                  stream,
201                         string                                  targetHost,
202                         bool                                    ownsStream,
203                         SecurityProtocolType    securityProtocolType) 
204                         : this(
205                                 stream, targetHost, ownsStream, securityProtocolType,
206                                 new X509CertificateCollection())
207                 {
208                 }
209
210                 public SslClientStream(
211                         Stream                                          stream,
212                         string                                          targetHost,
213                         bool                                            ownsStream,
214                         SecurityProtocolType            securityProtocolType,
215                         X509CertificateCollection       clientCertificates):
216                         base(stream, ownsStream)
217                 {
218                         if (targetHost == null || targetHost.Length == 0)
219                         {
220                                 throw new ArgumentNullException("targetHost is null or an empty string.");
221                         }
222
223                         this.context = new ClientContext(
224                                 this,
225                                 securityProtocolType, 
226                                 targetHost, 
227                                 clientCertificates);
228
229                         this.protocol = new ClientRecordProtocol(innerStream, (ClientContext)this.context);
230                 }
231
232                 #endregion
233
234                 #region Finalizer
235
236                 ~SslClientStream()
237                 {
238                         base.Dispose(false);
239                 }
240
241                 #endregion
242
243                 #region IDisposable Methods
244
245                 protected override void Dispose(bool disposing)
246                 {
247                         base.Dispose(disposing);
248
249                         if (disposing)
250                         {
251                                 this.ServerCertValidation = null;
252                                 this.ClientCertSelection = null;
253                                 this.PrivateKeySelection = null;
254                                 this.ServerCertValidation2 = null;
255                         }
256                 }
257
258                 #endregion
259
260                 #region Handshake Methods
261
262                 /*
263                         Client                                                                                  Server
264
265                         ClientHello                 -------->
266                                                                                                                         ServerHello
267                                                                                                                         Certificate*
268                                                                                                                         ServerKeyExchange*
269                                                                                                                         CertificateRequest*
270                                                                                 <--------                       ServerHelloDone
271                         Certificate*
272                         ClientKeyExchange
273                         CertificateVerify*
274                         [ChangeCipherSpec]
275                         Finished                    -------->
276                                                                                                                         [ChangeCipherSpec]
277                                                                                 <--------           Finished
278                         Application Data            <------->                   Application Data
279
280                                         Fig. 1 - Message flow for a full handshake              
281                 */
282
283                 private void SafeEndReceiveRecord (IAsyncResult ar, bool ignoreEmpty = false)
284                 {
285                         byte[] record = this.protocol.EndReceiveRecord (ar);
286                         if (!ignoreEmpty && ((record == null) || (record.Length == 0))) {
287                                 throw new TlsException (
288                                         AlertDescription.HandshakeFailiure,
289                                         "The server stopped the handshake.");
290                         }
291                 }
292
293                 private enum NegotiateState
294                 {
295                         SentClientHello,
296                         ReceiveClientHelloResponse,
297                         SentCipherSpec,
298                         ReceiveCipherSpecResponse,
299                         SentKeyExchange,
300                         ReceiveFinishResponse,
301                         SentFinished,
302                 };
303
304                 private class NegotiateAsyncResult : IAsyncResult
305                 {
306                         private object locker = new object ();
307                         private AsyncCallback _userCallback;
308                         private object _userState;
309                         private Exception _asyncException;
310                         private ManualResetEvent handle;
311                         private NegotiateState _state;
312                         private bool completed;
313
314                         public NegotiateAsyncResult(AsyncCallback userCallback, object userState, NegotiateState state)
315                         {
316                                 _userCallback = userCallback;
317                                 _userState = userState;
318                                 _state = state;
319                         }
320
321                         public NegotiateState State
322                         {
323                                 get { return _state; }
324                                 set { _state = value; }
325                         }
326
327                         public object AsyncState
328                         {
329                                 get { return _userState; }
330                         }
331
332                         public Exception AsyncException
333                         {
334                                 get { return _asyncException; }
335                         }
336
337                         public bool CompletedWithError
338                         {
339                                 get {
340                                         if (!IsCompleted)
341                                                 return false; // Perhaps throw InvalidOperationExcetion?
342
343                                         return null != _asyncException;
344                                 }
345                         }
346
347                         public WaitHandle AsyncWaitHandle
348                         {
349                                 get {
350                                         lock (locker) {
351                                                 if (handle == null)
352                                                         handle = new ManualResetEvent (completed);
353                                         }
354                                         return handle;
355                                 }
356
357                         }
358
359                         public bool CompletedSynchronously
360                         {
361                                 get { return false; }
362                         }
363
364                         public bool IsCompleted
365                         {
366                                 get {
367                                         lock (locker) {
368                                                 return completed;
369                                         }
370                                 }
371                         }
372
373                         public void SetComplete(Exception ex)
374                         {
375                                 lock (locker) {
376                                         if (completed)
377                                                 return;
378
379                                         completed = true;
380                                         if (handle != null)
381                                                 handle.Set ();
382
383                                         if (_userCallback != null)
384                                                 _userCallback.BeginInvoke (this, null, null);
385
386                                         _asyncException = ex;
387                                 }
388                         }
389
390                         public void SetComplete()
391                         {
392                                 SetComplete(null);
393                         }
394                 }
395
396                 internal override IAsyncResult BeginNegotiateHandshake(AsyncCallback callback, object state)
397                 {
398                         if (this.context.HandshakeState != HandshakeState.None) {
399                                 this.context.Clear ();
400                         }
401
402                         // Obtain supported cipher suites
403                         this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers (this.context.SecurityProtocol);
404
405                         // Set handshake state
406                         this.context.HandshakeState = HandshakeState.Started;
407
408                         NegotiateAsyncResult result = new NegotiateAsyncResult (callback, state, NegotiateState.SentClientHello);
409
410                         // Begin sending the client hello
411                         this.protocol.BeginSendRecord (HandshakeType.ClientHello, NegotiateAsyncWorker, result);
412
413                         return result;
414                 }
415
416                 internal override void EndNegotiateHandshake (IAsyncResult result)
417                 {
418                         NegotiateAsyncResult negotiate = result as NegotiateAsyncResult;
419
420                         if (negotiate == null)
421                                 throw new ArgumentNullException ();
422                         if (!negotiate.IsCompleted)
423                                 negotiate.AsyncWaitHandle.WaitOne();
424                         if (negotiate.CompletedWithError)
425                                 throw negotiate.AsyncException;
426                 }
427
428                 private void NegotiateAsyncWorker (IAsyncResult result)
429                 {
430                         NegotiateAsyncResult negotiate = result.AsyncState as NegotiateAsyncResult;
431
432                         try
433                         {
434                                 switch (negotiate.State)
435                                 {
436                                 case NegotiateState.SentClientHello:
437                                         this.protocol.EndSendRecord (result);
438
439                                         // we are now ready to ready the receive the hello response.
440                                         negotiate.State = NegotiateState.ReceiveClientHelloResponse;
441
442                                         // Start reading the client hello response
443                                         this.protocol.BeginReceiveRecord (this.innerStream, NegotiateAsyncWorker, negotiate);
444                                         break;
445
446                                 case NegotiateState.ReceiveClientHelloResponse:
447                                         this.SafeEndReceiveRecord (result, true);
448
449                                         if (this.context.LastHandshakeMsg != HandshakeType.ServerHelloDone &&
450                                                 (!this.context.AbbreviatedHandshake || this.context.LastHandshakeMsg != HandshakeType.ServerHello)) {
451                                                 // Read next record (skip empty, e.g. warnings alerts)
452                                                 this.protocol.BeginReceiveRecord (this.innerStream, NegotiateAsyncWorker, negotiate);
453                                                 break;
454                                         }
455
456                                         // special case for abbreviated handshake where no ServerHelloDone is sent from the server
457                                         if (this.context.AbbreviatedHandshake) {
458                                                 ClientSessionCache.SetContextFromCache (this.context);
459                                                 this.context.Negotiating.Cipher.ComputeKeys ();
460                                                 this.context.Negotiating.Cipher.InitializeCipher ();
461
462                                                 negotiate.State = NegotiateState.SentCipherSpec;
463
464                                                 // Send Change Cipher Spec message with the current cipher
465                                                 // or as plain text if this is the initial negotiation
466                                                 this.protocol.BeginSendChangeCipherSpec(NegotiateAsyncWorker, negotiate);
467                                         } else {
468                                                 // Send client certificate if requested
469                                                 // even if the server ask for it it _may_ still be optional
470                                                 bool clientCertificate = this.context.ServerSettings.CertificateRequest;
471
472                                                 using (var memstream = new MemoryStream())
473                                                 {
474                                                         // NOTE: sadly SSL3 and TLS1 differs in how they handle this and
475                                                         // the current design doesn't allow a very cute way to handle 
476                                                         // SSL3 alert warning for NoCertificate (41).
477                                                         if (this.context.SecurityProtocol == SecurityProtocolType.Ssl3)
478                                                         {
479                                                                 clientCertificate = ((this.context.ClientSettings.Certificates != null) &&
480                                                                         (this.context.ClientSettings.Certificates.Count > 0));
481                                                                 // this works well with OpenSSL (but only for SSL3)
482                                                         }
483
484                                                         byte[] record = null;
485
486                                                         if (clientCertificate)
487                                                         {
488                                                                 record = this.protocol.EncodeHandshakeRecord(HandshakeType.Certificate);
489                                                                 memstream.Write(record, 0, record.Length);
490                                                         }
491
492                                                         // Send Client Key Exchange
493                                                         record = this.protocol.EncodeHandshakeRecord(HandshakeType.ClientKeyExchange);
494                                                         memstream.Write(record, 0, record.Length);
495
496                                                         // Now initialize session cipher with the generated keys
497                                                         this.context.Negotiating.Cipher.InitializeCipher();
498
499                                                         // Send certificate verify if requested (optional)
500                                                         if (clientCertificate && (this.context.ClientSettings.ClientCertificate != null))
501                                                         {
502                                                                 record = this.protocol.EncodeHandshakeRecord(HandshakeType.CertificateVerify);
503                                                                 memstream.Write(record, 0, record.Length);
504                                                         }
505
506                                                         // send the chnage cipher spec.
507                                                         this.protocol.SendChangeCipherSpec(memstream);
508
509                                                         // Send Finished message
510                                                         record = this.protocol.EncodeHandshakeRecord(HandshakeType.Finished);
511                                                         memstream.Write(record, 0, record.Length);
512
513                                                         negotiate.State = NegotiateState.SentKeyExchange;
514
515                                                         // send all the records.
516                                                         this.innerStream.BeginWrite (memstream.GetBuffer (), 0, (int)memstream.Length, NegotiateAsyncWorker, negotiate);
517                                                 }
518                                         }
519                                         break;
520
521                                 case NegotiateState.SentKeyExchange:
522                                         this.innerStream.EndWrite (result);
523
524                                         negotiate.State = NegotiateState.ReceiveFinishResponse;
525
526                                         this.protocol.BeginReceiveRecord (this.innerStream, NegotiateAsyncWorker, negotiate);
527
528                                         break;
529
530                                 case NegotiateState.ReceiveFinishResponse:
531                                         this.SafeEndReceiveRecord (result);
532
533                                         // Read record until server finished is received
534                                         if (this.context.HandshakeState != HandshakeState.Finished) {
535                                                 // If all goes well this will process messages:
536                                                 //              Change Cipher Spec
537                                                 //              Server finished
538                                                 this.protocol.BeginReceiveRecord (this.innerStream, NegotiateAsyncWorker, negotiate);
539                                         }
540                                         else {
541                                                 // Reset Handshake messages information
542                                                 this.context.HandshakeMessages.Reset ();
543
544                                                 // Clear Key Info
545                                                 this.context.ClearKeyInfo();
546
547                                                 negotiate.SetComplete ();
548                                         }
549                                         break;
550
551
552                                 case NegotiateState.SentCipherSpec:
553                                         this.protocol.EndSendChangeCipherSpec (result);
554
555                                         negotiate.State = NegotiateState.ReceiveCipherSpecResponse;
556
557                                         // Start reading the cipher spec response
558                                         this.protocol.BeginReceiveRecord (this.innerStream, NegotiateAsyncWorker, negotiate);
559                                         break;
560
561                                 case NegotiateState.ReceiveCipherSpecResponse:
562                                         this.SafeEndReceiveRecord (result, true);
563
564                                         if (this.context.HandshakeState != HandshakeState.Finished)
565                                         {
566                                                 this.protocol.BeginReceiveRecord (this.innerStream, NegotiateAsyncWorker, negotiate);
567                                         }
568                                         else
569                                         {
570                                                 negotiate.State = NegotiateState.SentFinished;
571                                                 this.protocol.BeginSendRecord(HandshakeType.Finished, NegotiateAsyncWorker, negotiate);
572                                         }
573                                         break;
574
575                                 case NegotiateState.SentFinished:
576                                         this.protocol.EndSendRecord (result);
577
578                                         // Reset Handshake messages information
579                                         this.context.HandshakeMessages.Reset ();
580
581                                         // Clear Key Info
582                                         this.context.ClearKeyInfo();
583
584                                         negotiate.SetComplete ();
585
586                                         break;
587                                 }
588                         }
589                         catch (TlsException ex)
590                         {
591                                 // FIXME: should the send alert also be done asynchronously here and below?
592                                 this.protocol.SendAlert(ex.Alert);
593                                 negotiate.SetComplete (new IOException("The authentication or decryption has failed.", ex));
594                         }
595                         catch (Exception ex)
596                         {
597                                 this.protocol.SendAlert(AlertDescription.InternalError);
598                                 negotiate.SetComplete (new IOException("The authentication or decryption has failed.", ex));
599                         }
600                 }
601
602                 #endregion
603
604                 #region Event Methods
605
606                 internal override X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string targetHost, X509CertificateCollection serverRequestedCertificates)
607                 {
608                         if (this.ClientCertSelection != null)
609                         {
610                                 return this.ClientCertSelection(
611                                         clientCertificates,
612                                         serverCertificate,
613                                         targetHost,
614                                         serverRequestedCertificates);
615                         }
616
617                         return null;
618                 }
619
620                 internal override bool HaveRemoteValidation2Callback {
621                         get { return ServerCertValidation2 != null; }
622                 }
623
624                 internal override ValidationResult OnRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
625                 {
626                         CertificateValidationCallback2 cb = ServerCertValidation2;
627                         if (cb != null)
628                                 return cb (collection);
629                         return null;
630                 }
631
632                 internal override bool OnRemoteCertificateValidation(X509Certificate certificate, int[] errors)
633                 {
634                         if (this.ServerCertValidation != null)
635                         {
636                                 return this.ServerCertValidation(certificate, errors);
637                         }
638
639                         return (errors != null && errors.Length == 0);
640                 }
641
642                 internal virtual bool RaiseServerCertificateValidation(
643                         X509Certificate certificate, 
644                         int[]                   certificateErrors)
645                 {
646                         return base.RaiseRemoteCertificateValidation(certificate, certificateErrors);
647                 }
648
649                 internal virtual ValidationResult RaiseServerCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
650                 {
651                         return base.RaiseRemoteCertificateValidation2 (collection);
652                 }
653
654                 internal X509Certificate RaiseClientCertificateSelection(
655                         X509CertificateCollection       clientCertificates, 
656                         X509Certificate                         serverCertificate, 
657                         string                                          targetHost, 
658                         X509CertificateCollection       serverRequestedCertificates)
659                 {
660                         return base.RaiseLocalCertificateSelection(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates);
661                 }
662
663                 internal override AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost)
664                 {
665                         if (this.PrivateKeySelection != null)
666                         {
667                                 return this.PrivateKeySelection(certificate, targetHost);
668                         }
669
670                         return null;
671                 }
672
673                 internal AsymmetricAlgorithm RaisePrivateKeySelection(
674                         X509Certificate certificate,
675                         string targetHost)
676                 {
677                         return base.RaiseLocalPrivateKeySelection(certificate, targetHost);
678                 }
679
680                 #endregion
681         }
682 }