[sgen] We only use workers when starting the concurrent collection
[mono.git] / mcs / class / System / Mono.Net.Security / LegacySslStream.cs
1 //
2 // System.Net.Security.SslStream.cs
3 //
4 // Authors:
5 //      Tim Coleman (tim@timcoleman.com)
6 //      Atsushi Enomoto (atsushi@ximian.com)
7 //      Marek Safar (marek.safar@gmail.com)
8 //
9 // Copyright (C) Tim Coleman, 2004
10 // (c) 2004,2007 Novell, Inc. (http://www.novell.com)
11 // Copyright 2011 Xamarin Inc.
12 //
13
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 #if SECURITY_DEP
36
37 #if MONO_X509_ALIAS
38 extern alias PrebuiltSystem;
39 #endif
40 #if MONO_SECURITY_ALIAS
41 extern alias MonoSecurity;
42 #endif
43
44 #if MONO_SECURITY_ALIAS
45 using MonoCipherAlgorithmType = MonoSecurity::Mono.Security.Protocol.Tls.CipherAlgorithmType;
46 using MonoHashAlgorithmType = MonoSecurity::Mono.Security.Protocol.Tls.HashAlgorithmType;
47 using MonoExchangeAlgorithmType = MonoSecurity::Mono.Security.Protocol.Tls.ExchangeAlgorithmType;
48 using MonoSecurityProtocolType = MonoSecurity::Mono.Security.Protocol.Tls.SecurityProtocolType;
49 using MonoSecurity::Mono.Security.Protocol.Tls;
50 using MonoSecurity::Mono.Security.Interface;
51 #else
52 using MonoCipherAlgorithmType = Mono.Security.Protocol.Tls.CipherAlgorithmType;
53 using MonoHashAlgorithmType = Mono.Security.Protocol.Tls.HashAlgorithmType;
54 using MonoExchangeAlgorithmType = Mono.Security.Protocol.Tls.ExchangeAlgorithmType;
55 using MonoSecurityProtocolType = Mono.Security.Protocol.Tls.SecurityProtocolType;
56 using Mono.Security.Protocol.Tls;
57 using Mono.Security.Interface;
58 #endif
59 #if MONO_X509_ALIAS
60 using X509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
61 #endif
62
63 using CipherAlgorithmType = System.Security.Authentication.CipherAlgorithmType;
64 using HashAlgorithmType = System.Security.Authentication.HashAlgorithmType;
65 using ExchangeAlgorithmType = System.Security.Authentication.ExchangeAlgorithmType;
66
67 using System;
68 using System.IO;
69 using System.Net;
70 using System.Net.Security;
71 using System.Security.Authentication;
72 using System.Security.Cryptography.X509Certificates;
73 using System.Security.Principal;
74 using System.Security.Cryptography;
75
76 using System.Threading.Tasks;
77
78 namespace Mono.Net.Security 
79 {
80         [MonoTODO ("Non-X509Certificate2 certificate is not supported")]
81         internal class LegacySslStream : AuthenticatedStream, IMonoSslStream
82         {
83                 #region Fields
84
85                 SslStreamBase ssl_stream;
86                 MonoTlsSettings settings;
87                 ICertificateValidator certificateValidator;
88
89                 #endregion // Fields
90
91                 #region Constructors
92
93                 public LegacySslStream (Stream innerStream)
94                         : this (innerStream, false)
95                 {
96                 }
97
98                 public LegacySslStream (Stream innerStream, bool leaveInnerStreamOpen)
99                         : base (innerStream, leaveInnerStreamOpen)
100                 {
101                 }
102
103                 public LegacySslStream (Stream innerStream, bool leaveInnerStreamOpen, MonoTlsSettings settings)
104                         : base (innerStream, leaveInnerStreamOpen)
105                 {
106                         this.settings = settings;
107                         this.certificateValidator = settings.CertificateValidator;
108                 }
109                 #endregion // Constructors
110
111                 #region Properties
112
113                 public override bool CanRead {
114                         get { return InnerStream.CanRead; }
115                 }
116
117                 public override bool CanSeek {
118                         get { return InnerStream.CanSeek; }
119                 }
120
121                 public override bool CanTimeout {
122                         get { return InnerStream.CanTimeout; }
123                 }
124
125                 public override bool CanWrite {
126                         get { return InnerStream.CanWrite; }
127                 }
128
129                 public override long Length {
130                         get { return InnerStream.Length; }
131                 }
132
133                 public override long Position {
134                         get { return InnerStream.Position; }
135                         set {
136                                 throw new NotSupportedException ("This stream does not support seek operations");
137                         }
138                 }
139
140                 // AuthenticatedStream overrides
141
142                 public override bool IsAuthenticated { 
143                         get { return ssl_stream != null; }
144                 }
145
146                 public override bool IsEncrypted { 
147                         get { return IsAuthenticated; }
148                 }
149
150                 public override bool IsMutuallyAuthenticated { 
151                         get { return IsAuthenticated && (IsServer ? RemoteCertificate != null : LocalCertificate != null); }
152                 }
153
154                 public override bool IsServer { 
155                         get { return ssl_stream is SslServerStream; }
156                 }
157
158                 public override bool IsSigned { 
159                         get { return IsAuthenticated; }
160                 }
161
162                 public override int ReadTimeout {
163                         get { return InnerStream.ReadTimeout; }
164                         set { InnerStream.ReadTimeout = value; }
165                 }
166
167                 public override int WriteTimeout {
168                         get { return InnerStream.WriteTimeout; }
169                         set { InnerStream.WriteTimeout = value; }
170                 }
171
172                 // SslStream
173
174                 public virtual bool CheckCertRevocationStatus {
175                         get {
176                                 if (!IsAuthenticated)
177                                         return false;
178
179                                 return ssl_stream.CheckCertRevocationStatus;
180                         }
181                 }
182
183                 public virtual CipherAlgorithmType CipherAlgorithm  {
184                         get {
185                                 CheckConnectionAuthenticated ();
186
187                                 switch (ssl_stream.CipherAlgorithm) {
188                                 case MonoCipherAlgorithmType.Des:
189                                         return CipherAlgorithmType.Des;
190                                 case MonoCipherAlgorithmType.None:
191                                         return CipherAlgorithmType.None;
192                                 case MonoCipherAlgorithmType.Rc2:
193                                         return CipherAlgorithmType.Rc2;
194                                 case MonoCipherAlgorithmType.Rc4:
195                                         return CipherAlgorithmType.Rc4;
196                                 case MonoCipherAlgorithmType.SkipJack:
197                                         break;
198                                 case MonoCipherAlgorithmType.TripleDes:
199                                         return CipherAlgorithmType.TripleDes;
200                                 case MonoCipherAlgorithmType.Rijndael:
201                                         switch (ssl_stream.CipherStrength) {
202                                         case 128:
203                                                 return CipherAlgorithmType.Aes128;
204                                         case 192:
205                                                 return CipherAlgorithmType.Aes192;
206                                         case 256:
207                                                 return CipherAlgorithmType.Aes256;
208                                         }
209                                         break;
210                                 }
211
212                                 throw new InvalidOperationException ("Not supported cipher algorithm is in use. It is likely a bug in SslStream.");
213                         }
214                 }
215
216                 public virtual int CipherStrength  {
217                         get {
218                                 CheckConnectionAuthenticated ();
219
220                                 return ssl_stream.CipherStrength;
221                         }
222                 }
223
224                 public virtual HashAlgorithmType HashAlgorithm  {
225                         get {
226                                 CheckConnectionAuthenticated ();
227
228                                 switch (ssl_stream.HashAlgorithm) {
229                                 case MonoHashAlgorithmType.Md5:
230                                         return HashAlgorithmType.Md5;
231                                 case MonoHashAlgorithmType.None:
232                                         return HashAlgorithmType.None;
233                                 case MonoHashAlgorithmType.Sha1:
234                                         return HashAlgorithmType.Sha1;
235                                 }
236
237                                 throw new InvalidOperationException ("Not supported hash algorithm is in use. It is likely a bug in SslStream.");
238                         }
239                 }
240
241                 public virtual int HashStrength  {
242                         get {
243                                 CheckConnectionAuthenticated ();
244
245                                 return ssl_stream.HashStrength;
246                         }
247                 }
248
249                 public virtual ExchangeAlgorithmType KeyExchangeAlgorithm { 
250                         get {
251                                 CheckConnectionAuthenticated ();
252
253                                 switch (ssl_stream.KeyExchangeAlgorithm) {
254                                 case MonoExchangeAlgorithmType.DiffieHellman:
255                                         return ExchangeAlgorithmType.DiffieHellman;
256                                 case MonoExchangeAlgorithmType.Fortezza:
257                                         break;
258                                 case MonoExchangeAlgorithmType.None:
259                                         return ExchangeAlgorithmType.None;
260                                 case MonoExchangeAlgorithmType.RsaKeyX:
261                                         return ExchangeAlgorithmType.RsaKeyX;
262                                 case MonoExchangeAlgorithmType.RsaSign:
263                                         return ExchangeAlgorithmType.RsaSign;
264                                 }
265
266                                 throw new InvalidOperationException ("Not supported exchange algorithm is in use. It is likely a bug in SslStream.");
267                         }
268                 }
269
270                 public virtual int KeyExchangeStrength { 
271                         get {
272                                 CheckConnectionAuthenticated ();
273
274                                 return ssl_stream.KeyExchangeStrength;
275                         }
276                 }
277
278                 X509Certificate IMonoSslStream.InternalLocalCertificate {
279                         get {
280                                 return IsServer ? ssl_stream.ServerCertificate : ((SslClientStream) ssl_stream).SelectedClientCertificate;
281                         }
282                 }
283
284                 public virtual X509Certificate LocalCertificate {
285                         get {
286                                 CheckConnectionAuthenticated ();
287
288                                 return IsServer ? ssl_stream.ServerCertificate : ((SslClientStream) ssl_stream).SelectedClientCertificate;
289                         }
290                 }
291
292                 public virtual X509Certificate RemoteCertificate {
293                         get {
294                                 CheckConnectionAuthenticated ();
295                                 return !IsServer ? ssl_stream.ServerCertificate : ((SslServerStream) ssl_stream).ClientCertificate;
296                         }
297                 }
298
299                 public virtual SslProtocols SslProtocol {
300                         get {
301                                 CheckConnectionAuthenticated ();
302
303                                 switch (ssl_stream.SecurityProtocol) {
304                                 case MonoSecurityProtocolType.Default:
305                                         return SslProtocols.Default;
306                                 case MonoSecurityProtocolType.Ssl2:
307                                         return SslProtocols.Ssl2;
308                                 case MonoSecurityProtocolType.Ssl3:
309                                         return SslProtocols.Ssl3;
310                                 case MonoSecurityProtocolType.Tls:
311                                         return SslProtocols.Tls;
312                                 }
313
314                                 throw new InvalidOperationException ("Not supported SSL/TLS protocol is in use. It is likely a bug in SslStream.");
315                         }
316                 }
317
318                 #endregion // Properties
319
320                 #region Methods
321
322 /*
323                 AsymmetricAlgorithm GetPrivateKey (X509Certificate cert, string targetHost)
324                 {
325                         // FIXME: what can I do for non-X509Certificate2 ?
326                         X509Certificate2 cert2 = cert as X509Certificate2;
327                         return cert2 != null ? cert2.PrivateKey : null;
328                 }
329 */
330                 X509Certificate OnCertificateSelection (X509CertificateCollection clientCerts, X509Certificate serverCert, string targetHost, X509CertificateCollection serverRequestedCerts)
331                 {
332                         string [] acceptableIssuers = new string [serverRequestedCerts != null ? serverRequestedCerts.Count : 0];
333                         for (int i = 0; i < acceptableIssuers.Length; i++)
334                                 acceptableIssuers [i] = serverRequestedCerts [i].GetIssuerName ();
335                         return certificateValidator.SelectClientCertificate (targetHost, clientCerts, serverCert, acceptableIssuers);
336                 }
337
338                 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, AsyncCallback asyncCallback, object asyncState)
339                 {
340                         return BeginAuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false, asyncCallback, asyncState);
341                 }
342
343                 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
344                 {
345                         if (IsAuthenticated)
346                                 throw new InvalidOperationException ("This SslStream is already authenticated");
347
348                         SslClientStream s = new SslClientStream (InnerStream, targetHost, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols), clientCertificates);
349                         s.CheckCertRevocationStatus = checkCertificateRevocation;
350
351                         // Due to the Mono.Security internal, it cannot reuse
352                         // the delegated argument, as Mono.Security creates 
353                         // another instance of X509Certificate which lacks 
354                         // private key but is filled the private key via this
355                         // delegate.
356                         s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string host) {
357                                 string hash = cert.GetCertHashString ();
358                                 // ... so, we cannot use the delegate argument.
359                                 foreach (X509Certificate cc in clientCertificates) {
360                                         if (cc.GetCertHashString () != hash)
361                                                 continue;
362                                         X509Certificate2 cert2 = cc as X509Certificate2;
363                                         cert2 = cert2 ?? new X509Certificate2 (cc);
364                                         return cert2.PrivateKey;
365                                 }
366                                 return null;
367                         };
368
369                         // Even if validation_callback is null this allows us to verify requests where the user
370                         // does not provide a verification callback but attempts to authenticate with the website
371                         // as a client (see https://bugzilla.xamarin.com/show_bug.cgi?id=18962 for an example)
372                         s.ServerCertValidation2 += (certs) => ((ChainValidationHelper)certificateValidator).ValidateChain (targetHost, certs);
373                         s.ClientCertSelectionDelegate = OnCertificateSelection;
374
375                         ssl_stream = s;
376
377                         return BeginWrite (new byte [0], 0, 0, asyncCallback, asyncState);
378                 }
379
380                 public override IAsyncResult BeginRead (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
381                 {
382                         CheckConnectionAuthenticated ();
383
384                         return ssl_stream.BeginRead (buffer, offset, count, asyncCallback, asyncState);
385                 }
386
387                 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, AsyncCallback asyncCallback, object asyncState)
388                 {
389                         return BeginAuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false, asyncCallback, asyncState);
390                 }
391
392                 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
393                 {
394                         if (IsAuthenticated)
395                                 throw new InvalidOperationException ("This SslStream is already authenticated");
396
397                         SslServerStream s = new SslServerStream (InnerStream, serverCertificate, clientCertificateRequired, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols));
398                         s.CheckCertRevocationStatus = checkCertificateRevocation;
399                         // Due to the Mono.Security internal, it cannot reuse
400                         // the delegated argument, as Mono.Security creates 
401                         // another instance of X509Certificate which lacks 
402                         // private key but is filled the private key via this
403                         // delegate.
404                         s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string targetHost) {
405                                 // ... so, we cannot use the delegate argument.
406                                 X509Certificate2 cert2 = serverCertificate as X509Certificate2 ?? new X509Certificate2 (serverCertificate);
407                                 return cert2 != null ? cert2.PrivateKey : null;
408                         };
409
410                         s.ClientCertValidationDelegate = delegate (X509Certificate cert, int[] certErrors) {
411                                 var errors = certErrors.Length > 0 ? MonoSslPolicyErrors.RemoteCertificateChainErrors : MonoSslPolicyErrors.None;
412                                 return ((ChainValidationHelper)certificateValidator).ValidateClientCertificate (cert, errors);
413                         };
414
415                         ssl_stream = s;
416
417                         return BeginWrite (new byte[0], 0, 0, asyncCallback, asyncState);
418                 }
419
420                 MonoSecurityProtocolType GetMonoSslProtocol (SslProtocols ms)
421                 {
422                         switch (ms) {
423                         case SslProtocols.Ssl2:
424                                 return MonoSecurityProtocolType.Ssl2;
425                         case SslProtocols.Ssl3:
426                                 return MonoSecurityProtocolType.Ssl3;
427                         case SslProtocols.Tls:
428                                 return MonoSecurityProtocolType.Tls;
429                         default:
430                                 return MonoSecurityProtocolType.Default;
431                         }
432                 }
433
434                 public override IAsyncResult BeginWrite (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
435                 {
436                         CheckConnectionAuthenticated ();
437
438                         return ssl_stream.BeginWrite (buffer, offset, count, asyncCallback, asyncState);
439                 }
440
441                 public virtual void AuthenticateAsClient (string targetHost)
442                 {
443                         AuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false);
444                 }
445
446                 public virtual void AuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
447                 {
448                         EndAuthenticateAsClient (BeginAuthenticateAsClient (
449                                 targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, null, null));
450                 }
451
452                 public virtual void AuthenticateAsServer (X509Certificate serverCertificate)
453                 {
454                         AuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false);
455                 }
456
457                 public virtual void AuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
458                 {
459                         EndAuthenticateAsServer (BeginAuthenticateAsServer (
460                                 serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, null, null));
461                 }
462
463                 protected override void Dispose (bool disposing)
464                 {
465                         if (disposing) {
466                                 if (ssl_stream != null)
467                                         ssl_stream.Dispose ();
468                                 ssl_stream = null;
469                         }
470                         base.Dispose (disposing);
471                 }
472
473                 public virtual void EndAuthenticateAsClient (IAsyncResult asyncResult)
474                 {
475                         CheckConnectionAuthenticated ();
476
477                         if (CanRead)
478                                 ssl_stream.EndRead (asyncResult);
479                         else
480                                 ssl_stream.EndWrite (asyncResult);
481                 }
482
483                 public virtual void EndAuthenticateAsServer (IAsyncResult asyncResult)
484                 {
485                         CheckConnectionAuthenticated ();
486
487                         if (CanRead)
488                                 ssl_stream.EndRead (asyncResult);
489                         else
490                                 ssl_stream.EndWrite (asyncResult);
491                 }
492
493                 public override int EndRead (IAsyncResult asyncResult)
494                 {
495                         CheckConnectionAuthenticated ();
496
497                         return ssl_stream.EndRead (asyncResult);
498                 }
499
500                 public override void EndWrite (IAsyncResult asyncResult)
501                 {
502                         CheckConnectionAuthenticated ();
503
504                         ssl_stream.EndWrite (asyncResult);
505                 }
506
507                 public override void Flush ()
508                 {
509                         CheckConnectionAuthenticated ();
510
511                         InnerStream.Flush ();
512                 }
513
514                 public override int Read (byte[] buffer, int offset, int count)
515                 {
516                         return EndRead (BeginRead (buffer, offset, count, null, null));
517                 }
518
519                 public override long Seek (long offset, SeekOrigin origin)
520                 {
521                         throw new NotSupportedException ("This stream does not support seek operations");
522                 }
523
524                 public override void SetLength (long value)
525                 {
526                         InnerStream.SetLength (value);
527                 }
528
529                 public override void Write (byte[] buffer, int offset, int count)
530                 {
531                         EndWrite (BeginWrite (buffer, offset, count, null, null));
532                 }
533
534                 public void Write (byte[] buffer)
535                 {
536                         Write (buffer, 0, buffer.Length);
537                 }
538
539                 void CheckConnectionAuthenticated ()
540                 {
541                         if (!IsAuthenticated)
542                                 throw new InvalidOperationException ("This operation is invalid until it is successfully authenticated");
543                 }
544
545                 public virtual Task AuthenticateAsClientAsync (string targetHost)
546                 {
547                         return Task.Factory.FromAsync (BeginAuthenticateAsClient, EndAuthenticateAsClient, targetHost, null);
548                 }
549
550                 public virtual Task AuthenticateAsClientAsync (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
551                 {
552                         var t = Tuple.Create (targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, this);
553
554                         return Task.Factory.FromAsync ((callback, state) => {
555                                 var d = (Tuple<string, X509CertificateCollection, SslProtocols, bool, LegacySslStream>) state;
556                                 return d.Item5.BeginAuthenticateAsClient (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
557                         }, EndAuthenticateAsClient, t);
558                 }
559
560                 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate)
561                 {
562                         return Task.Factory.FromAsync (BeginAuthenticateAsServer, EndAuthenticateAsServer, serverCertificate, null);
563                 }
564
565                 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
566                 {
567                         var t = Tuple.Create (serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, this);
568
569                         return Task.Factory.FromAsync ((callback, state) => {
570                                 var d = (Tuple<X509Certificate, bool, SslProtocols, bool, LegacySslStream>) state;
571                                 return d.Item5.BeginAuthenticateAsServer (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
572                         }, EndAuthenticateAsServer, t);
573                 }
574
575                 #endregion // Methods
576
577                 #region IMonoSslStream
578
579                 AuthenticatedStream IMonoSslStream.AuthenticatedStream {
580                         get { return this; }
581                 }
582
583                 TransportContext IMonoSslStream.TransportContext {
584                         get { throw new NotSupportedException (); }
585                 }
586
587                 #endregion
588         }
589 }
590
591 #endif