Added tests for Task.WhenAll w/ empty list
[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         public delegate bool CertificateValidationCallback(
41                 X509Certificate certificate, 
42                 int[]                   certificateErrors);
43
44         public class ValidationResult {
45                 bool trusted;
46                 bool user_denied;
47                 int error_code;
48
49                 public ValidationResult (bool trusted, bool user_denied, int error_code)
50                 {
51                         this.trusted = trusted;
52                         this.user_denied = user_denied;
53                         this.error_code = error_code;
54                 }
55
56                 public bool Trusted {
57                         get { return trusted; }
58                 }
59
60                 public bool UserDenied {
61                         get { return user_denied; }
62                 }
63
64                 public int ErrorCode {
65                         get { return error_code; }
66                 }
67         }
68
69         public
70         delegate ValidationResult CertificateValidationCallback2 (Mono.Security.X509.X509CertificateCollection collection);
71
72         public delegate X509Certificate CertificateSelectionCallback(
73                 X509CertificateCollection       clientCertificates, 
74                 X509Certificate                         serverCertificate, 
75                 string                                          targetHost, 
76                 X509CertificateCollection       serverRequestedCertificates);
77
78         public delegate AsymmetricAlgorithm PrivateKeySelectionCallback(
79                 X509Certificate certificate, 
80                 string                  targetHost);
81
82         #endregion
83
84         public 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                 internal override IAsyncResult OnBeginNegotiateHandshake(AsyncCallback callback, object state)
255                 {
256                         try
257                         {
258                                 if (this.context.HandshakeState != HandshakeState.None)
259                                 {
260                                         this.context.Clear();
261                                 }
262
263                                 // Obtain supported cipher suites
264                                 this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(this.context.SecurityProtocol);
265
266                                 // Set handshake state
267                                 this.context.HandshakeState = HandshakeState.Started;
268
269                                 // Send client hello
270                                 return this.protocol.BeginSendRecord(HandshakeType.ClientHello, callback, state);
271                         }
272                         catch (TlsException ex)
273                         {
274                                 this.protocol.SendAlert(ex.Alert);
275
276                                 throw new IOException("The authentication or decryption has failed.", ex);
277                         }
278                         catch (Exception ex)
279                         {
280                                 this.protocol.SendAlert(AlertDescription.InternalError);
281
282                                 throw new IOException("The authentication or decryption has failed.", ex);
283                         }
284                 }
285
286                 private void SafeReceiveRecord (Stream s, bool ignoreEmpty = false)
287                 {
288                         byte[] record = this.protocol.ReceiveRecord (s);
289                         if (!ignoreEmpty && ((record == null) || (record.Length == 0))) {
290                                 throw new TlsException (
291                                         AlertDescription.HandshakeFailiure,
292                                         "The server stopped the handshake.");
293                         }
294                 }
295
296                 internal override void OnNegotiateHandshakeCallback(IAsyncResult asyncResult)
297                 {
298                         this.protocol.EndSendRecord(asyncResult);
299
300                         // Read server response
301                         while (this.context.LastHandshakeMsg != HandshakeType.ServerHelloDone) 
302                         {
303                                 // Read next record (skip empty, e.g. warnings alerts)
304                                 SafeReceiveRecord (this.innerStream, true);
305
306                                 // special case for abbreviated handshake where no ServerHelloDone is sent from the server
307                                 if (this.context.AbbreviatedHandshake && (this.context.LastHandshakeMsg == HandshakeType.ServerHello))
308                                         break;
309                         }
310
311                         // the handshake is much easier if we can reuse a previous session settings
312                         if (this.context.AbbreviatedHandshake) 
313                         {
314                                 ClientSessionCache.SetContextFromCache (this.context);
315                                 this.context.Negotiating.Cipher.ComputeKeys ();
316                                 this.context.Negotiating.Cipher.InitializeCipher ();
317
318                                 // Send Cipher Spec protocol
319                                 this.protocol.SendChangeCipherSpec ();
320
321                                 // Read record until server finished is received
322                                 while (this.context.HandshakeState != HandshakeState.Finished) 
323                                 {
324                                         // If all goes well this will process messages:
325                                         //              Change Cipher Spec
326                                         //              Server finished
327                                         SafeReceiveRecord (this.innerStream);
328                                 }
329
330                                 // Send Finished message
331                                 this.protocol.SendRecord (HandshakeType.Finished);
332                         }
333                         else
334                         {
335                                 // Send client certificate if requested
336                                 // even if the server ask for it it _may_ still be optional
337                                 bool clientCertificate = this.context.ServerSettings.CertificateRequest;
338
339                                 // NOTE: sadly SSL3 and TLS1 differs in how they handle this and
340                                 // the current design doesn't allow a very cute way to handle 
341                                 // SSL3 alert warning for NoCertificate (41).
342                                 if (this.context.SecurityProtocol == SecurityProtocolType.Ssl3)
343                                 {
344                                         clientCertificate = ((this.context.ClientSettings.Certificates != null) &&
345                                                 (this.context.ClientSettings.Certificates.Count > 0));
346                                         // this works well with OpenSSL (but only for SSL3)
347                                 }
348
349                                 if (clientCertificate)
350                                 {
351                                         this.protocol.SendRecord(HandshakeType.Certificate);
352                                 }
353
354                                 // Send Client Key Exchange
355                                 this.protocol.SendRecord(HandshakeType.ClientKeyExchange);
356
357                                 // Now initialize session cipher with the generated keys
358                                 this.context.Negotiating.Cipher.InitializeCipher();
359
360                                 // Send certificate verify if requested (optional)
361                                 if (clientCertificate && (this.context.ClientSettings.ClientCertificate != null))
362                                 {
363                                         this.protocol.SendRecord(HandshakeType.CertificateVerify);
364                                 }
365
366                                 // Send Cipher Spec protocol
367                                 this.protocol.SendChangeCipherSpec ();
368
369                                 // Send Finished message
370                                 this.protocol.SendRecord (HandshakeType.Finished);
371
372                                 // Read record until server finished is received
373                                 while (this.context.HandshakeState != HandshakeState.Finished) {
374                                         // If all goes well this will process messages:
375                                         //              Change Cipher Spec
376                                         //              Server finished
377                                         SafeReceiveRecord (this.innerStream);
378                                 }
379                         }
380
381                         // Reset Handshake messages information
382                         this.context.HandshakeMessages.Reset ();
383
384                         // Clear Key Info
385                         this.context.ClearKeyInfo();
386
387                 }
388
389                 #endregion
390
391                 #region Event Methods
392
393                 internal override X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string targetHost, X509CertificateCollection serverRequestedCertificates)
394                 {
395                         if (this.ClientCertSelection != null)
396                         {
397                                 return this.ClientCertSelection(
398                                         clientCertificates,
399                                         serverCertificate,
400                                         targetHost,
401                                         serverRequestedCertificates);
402                         }
403
404                         return null;
405                 }
406
407                 internal override bool HaveRemoteValidation2Callback {
408                         get { return ServerCertValidation2 != null; }
409                 }
410
411                 internal override ValidationResult OnRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
412                 {
413                         CertificateValidationCallback2 cb = ServerCertValidation2;
414                         if (cb != null)
415                                 return cb (collection);
416                         return null;
417                 }
418
419                 internal override bool OnRemoteCertificateValidation(X509Certificate certificate, int[] errors)
420                 {
421                         if (this.ServerCertValidation != null)
422                         {
423                                 return this.ServerCertValidation(certificate, errors);
424                         }
425
426                         return (errors != null && errors.Length == 0);
427                 }
428
429                 internal virtual bool RaiseServerCertificateValidation(
430                         X509Certificate certificate, 
431                         int[]                   certificateErrors)
432                 {
433                         return base.RaiseRemoteCertificateValidation(certificate, certificateErrors);
434                 }
435
436                 internal virtual ValidationResult RaiseServerCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
437                 {
438                         return base.RaiseRemoteCertificateValidation2 (collection);
439                 }
440
441                 internal X509Certificate RaiseClientCertificateSelection(
442                         X509CertificateCollection       clientCertificates, 
443                         X509Certificate                         serverCertificate, 
444                         string                                          targetHost, 
445                         X509CertificateCollection       serverRequestedCertificates)
446                 {
447                         return base.RaiseLocalCertificateSelection(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates);
448                 }
449
450                 internal override AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost)
451                 {
452                         if (this.PrivateKeySelection != null)
453                         {
454                                 return this.PrivateKeySelection(certificate, targetHost);
455                         }
456
457                         return null;
458                 }
459
460                 internal AsymmetricAlgorithm RaisePrivateKeySelection(
461                         X509Certificate certificate,
462                         string targetHost)
463                 {
464                         return base.RaiseLocalPrivateKeySelection(certificate, targetHost);
465                 }
466
467                 #endregion
468         }
469 }