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