1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 namespace IceSSL
6 {
7     using System;
8     using System.Diagnostics;
9     using System.Collections.Generic;
10     using System.IO;
11     using System.Net.Security;
12     using System.Net.Sockets;
13     using System.Security.Authentication;
14     using System.Security.Cryptography.X509Certificates;
15     using System.Text;
16 
17     sealed class TransceiverI : IceInternal.Transceiver
18     {
fd()19         public Socket fd()
20         {
21             return _delegate.fd();
22         }
23 
initialize(IceInternal.Buffer readBuffer, IceInternal.Buffer writeBuffer, ref bool hasMoreData)24         public int initialize(IceInternal.Buffer readBuffer, IceInternal.Buffer writeBuffer, ref bool hasMoreData)
25         {
26             if(!_isConnected)
27             {
28                 int status = _delegate.initialize(readBuffer, writeBuffer, ref hasMoreData);
29                 if(status != IceInternal.SocketOperation.None)
30                 {
31                     return status;
32                 }
33                 _isConnected  = true;
34             }
35 
36             IceInternal.Network.setBlock(fd(), true); // SSL requires a blocking socket
37 
38             //
39             // For timeouts to work properly, we need to receive/send
40             // the data in several chunks. Otherwise, we would only be
41             // notified when all the data is received/written. The
42             // connection timeout could easily be triggered when
43             // receiging/sending large messages.
44             //
45             _maxSendPacketSize = Math.Max(512, IceInternal.Network.getSendBufferSize(fd()));
46             _maxRecvPacketSize = Math.Max(512, IceInternal.Network.getRecvBufferSize(fd()));
47 
48             if(_sslStream == null)
49             {
50                 _sslStream = new SslStream(new NetworkStream(_delegate.fd(), false),
51                                            false,
52                                            new RemoteCertificateValidationCallback(validationCallback),
53                                            new LocalCertificateSelectionCallback(selectCertificate));
54                 return IceInternal.SocketOperation.Connect;
55             }
56 
57             Debug.Assert(_sslStream.IsAuthenticated);
58             _authenticated = true;
59 
60             _cipher = _sslStream.CipherAlgorithm.ToString();
61             List<string> certs = new List<string>();
62             if(_chain.ChainElements != null && _chain.ChainElements.Count > 0)
63             {
64                 _certs = new X509Certificate2[_chain.ChainElements.Count];
65                 for(int i = 0; i < _chain.ChainElements.Count; ++i)
66                 {
67                     _certs[i] = _chain.ChainElements[i].Certificate;
68                 }
69             }
70 
71             _instance.verifyPeer(_host, (ConnectionInfo)getInfo(), ToString());
72 
73             if(_instance.securityTraceLevel() >= 1)
74             {
75                 _instance.traceStream(_sslStream, ToString());
76             }
77             return IceInternal.SocketOperation.None;
78         }
79 
closing(bool initiator, Ice.LocalException ex)80         public int closing(bool initiator, Ice.LocalException ex)
81         {
82             return _delegate.closing(initiator, ex);
83         }
84 
close()85         public void close()
86         {
87             if(_sslStream != null)
88             {
89                 _sslStream.Close(); // Closing the stream also closes the socket.
90                 _sslStream = null;
91             }
92 
93             _delegate.close();
94         }
95 
bind()96         public IceInternal.EndpointI bind()
97         {
98             Debug.Assert(false);
99             return null;
100         }
101 
destroy()102         public void destroy()
103         {
104             _delegate.destroy();
105         }
106 
write(IceInternal.Buffer buf)107         public int write(IceInternal.Buffer buf)
108         {
109             //
110             // Force caller to use async write.
111             //
112             return buf.b.hasRemaining() ? IceInternal.SocketOperation.Write : IceInternal.SocketOperation.None;
113         }
114 
read(IceInternal.Buffer buf, ref bool hasMoreData)115         public int read(IceInternal.Buffer buf, ref bool hasMoreData)
116         {
117             //
118             // Force caller to use async read.
119             //
120             return buf.b.hasRemaining() ? IceInternal.SocketOperation.Read : IceInternal.SocketOperation.None;
121         }
122 
startRead(IceInternal.Buffer buf, IceInternal.AsyncCallback callback, object state)123         public bool startRead(IceInternal.Buffer buf, IceInternal.AsyncCallback callback, object state)
124         {
125             if(!_isConnected)
126             {
127                 return _delegate.startRead(buf, callback, state);
128             }
129 
130             Debug.Assert(_sslStream != null && _sslStream.IsAuthenticated);
131 
132             int packetSz = getRecvPacketSize(buf.b.remaining());
133             try
134             {
135                 _readCallback = callback;
136                 _readResult = _sslStream.BeginRead(buf.b.rawBytes(), buf.b.position(), packetSz, readCompleted, state);
137                 return _readResult.CompletedSynchronously;
138             }
139             catch(IOException ex)
140             {
141                 if(IceInternal.Network.connectionLost(ex))
142                 {
143                     throw new Ice.ConnectionLostException(ex);
144                 }
145                 if(IceInternal.Network.timeout(ex))
146                 {
147                     throw new Ice.TimeoutException();
148                 }
149                 throw new Ice.SocketException(ex);
150             }
151             catch(ObjectDisposedException ex)
152             {
153                 throw new Ice.ConnectionLostException(ex);
154             }
155             catch(Exception ex)
156             {
157                 throw new Ice.SyscallException(ex);
158             }
159         }
160 
finishRead(IceInternal.Buffer buf)161         public void finishRead(IceInternal.Buffer buf)
162         {
163             if(!_isConnected)
164             {
165                 _delegate.finishRead(buf);
166                 return;
167             }
168             else if(_sslStream == null) // Transceiver was closed
169             {
170                 _readResult = null;
171                 return;
172             }
173 
174             Debug.Assert(_readResult != null);
175             try
176             {
177                 int ret = _sslStream.EndRead(_readResult);
178                 _readResult = null;
179 
180                 if(ret == 0)
181                 {
182                     throw new Ice.ConnectionLostException();
183                 }
184                 Debug.Assert(ret > 0);
185                 buf.b.position(buf.b.position() + ret);
186             }
187             catch(Ice.LocalException)
188             {
189                 throw;
190             }
191             catch(IOException ex)
192             {
193                 if(IceInternal.Network.connectionLost(ex))
194                 {
195                     throw new Ice.ConnectionLostException(ex);
196                 }
197                 if(IceInternal.Network.timeout(ex))
198                 {
199                     throw new Ice.TimeoutException();
200                 }
201                 throw new Ice.SocketException(ex);
202             }
203             catch(ObjectDisposedException ex)
204             {
205                 throw new Ice.ConnectionLostException(ex);
206             }
207             catch(Exception ex)
208             {
209                 throw new Ice.SyscallException(ex);
210             }
211         }
212 
startWrite(IceInternal.Buffer buf, IceInternal.AsyncCallback cb, object state, out bool completed)213         public bool startWrite(IceInternal.Buffer buf, IceInternal.AsyncCallback cb, object state, out bool completed)
214         {
215             if(!_isConnected)
216             {
217                 return _delegate.startWrite(buf, cb, state, out completed);
218             }
219 
220             Debug.Assert(_sslStream != null);
221             if(!_authenticated)
222             {
223                 completed = false;
224                 return startAuthenticate(cb, state);
225             }
226 
227             //
228             // We limit the packet size for beingWrite to ensure connection timeouts are based
229             // on a fixed packet size.
230             //
231             int packetSize = getSendPacketSize(buf.b.remaining());
232             try
233             {
234                 _writeCallback = cb;
235                 _writeResult = _sslStream.BeginWrite(buf.b.rawBytes(), buf.b.position(), packetSize, writeCompleted,
236                                                      state);
237                 completed = packetSize == buf.b.remaining();
238                 return _writeResult.CompletedSynchronously;
239             }
240             catch(IOException ex)
241             {
242                 if(IceInternal.Network.connectionLost(ex))
243                 {
244                     throw new Ice.ConnectionLostException(ex);
245                 }
246                 if(IceInternal.Network.timeout(ex))
247                 {
248                     throw new Ice.TimeoutException();
249                 }
250                 throw new Ice.SocketException(ex);
251             }
252             catch(ObjectDisposedException ex)
253             {
254                 throw new Ice.ConnectionLostException(ex);
255             }
256             catch(Exception ex)
257             {
258                 throw new Ice.SyscallException(ex);
259             }
260         }
261 
finishWrite(IceInternal.Buffer buf)262         public void finishWrite(IceInternal.Buffer buf)
263         {
264             if(!_isConnected)
265             {
266                 _delegate.finishWrite(buf);
267                 return;
268             }
269             else if(_sslStream == null) // Transceiver was closed
270             {
271                 if(getSendPacketSize(buf.b.remaining()) == buf.b.remaining()) // Sent last packet
272                 {
273                     buf.b.position(buf.b.limit()); // Assume all the data was sent for at-most-once semantics.
274                 }
275                 _writeResult = null;
276                 return;
277             }
278             else if(!_authenticated)
279             {
280                 finishAuthenticate();
281                 return;
282             }
283 
284             Debug.Assert(_writeResult != null);
285             int sent = getSendPacketSize(buf.b.remaining());
286             try
287             {
288                 _sslStream.EndWrite(_writeResult);
289                 _writeResult = null;
290                 buf.b.position(buf.b.position() + sent);
291             }
292             catch(IOException ex)
293             {
294                 if(IceInternal.Network.connectionLost(ex))
295                 {
296                     throw new Ice.ConnectionLostException(ex);
297                 }
298                 if(IceInternal.Network.timeout(ex))
299                 {
300                     throw new Ice.TimeoutException();
301                 }
302                 throw new Ice.SocketException(ex);
303             }
304             catch(ObjectDisposedException ex)
305             {
306                 throw new Ice.ConnectionLostException(ex);
307             }
308             catch(Exception ex)
309             {
310                 throw new Ice.SyscallException(ex);
311             }
312         }
313 
protocol()314         public string protocol()
315         {
316             return _delegate.protocol();
317         }
318 
getInfo()319         public Ice.ConnectionInfo getInfo()
320         {
321             ConnectionInfo info = new ConnectionInfo();
322             info.underlying = _delegate.getInfo();
323             info.incoming = _incoming;
324             info.adapterName = _adapterName;
325             info.cipher = _cipher;
326             info.certs = _certs;
327             info.verified = _verified;
328             return info;
329         }
330 
checkSendSize(IceInternal.Buffer buf)331         public void checkSendSize(IceInternal.Buffer buf)
332         {
333             _delegate.checkSendSize(buf);
334         }
335 
setBufferSize(int rcvSize, int sndSize)336         public void setBufferSize(int rcvSize, int sndSize)
337         {
338             _delegate.setBufferSize(rcvSize, sndSize);
339         }
340 
ToString()341         public override string ToString()
342         {
343             return _delegate.ToString();
344         }
345 
toDetailedString()346         public string toDetailedString()
347         {
348             return _delegate.toDetailedString();
349         }
350 
351         //
352         // Only for use by ConnectorI, AcceptorI.
353         //
TransceiverI(Instance instance, IceInternal.Transceiver del, string hostOrAdapterName, bool incoming)354         internal TransceiverI(Instance instance, IceInternal.Transceiver del, string hostOrAdapterName, bool incoming)
355         {
356             _instance = instance;
357             _delegate = del;
358             _incoming = incoming;
359             if(_incoming)
360             {
361                 _adapterName = hostOrAdapterName;
362             }
363             else
364             {
365                 _host = hostOrAdapterName;
366             }
367 
368             _sslStream = null;
369 
370             _verifyPeer = _instance.properties().getPropertyAsIntWithDefault("IceSSL.VerifyPeer", 2);
371 
372             _chain = new X509Chain(_instance.engine().useMachineContext());
373 
374             if(_instance.checkCRL() == 0)
375             {
376                 _chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
377             }
378 
379             X509Certificate2Collection caCerts = _instance.engine().caCerts();
380             if(caCerts != null)
381             {
382                 //
383                 // We need to set this flag to be able to use a certificate authority from the extra store.
384                 //
385                 _chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
386                 foreach(X509Certificate2 cert in caCerts)
387                 {
388                     _chain.ChainPolicy.ExtraStore.Add(cert);
389                 }
390             }
391         }
392 
startAuthenticate(IceInternal.AsyncCallback callback, object state)393         private bool startAuthenticate(IceInternal.AsyncCallback callback, object state)
394         {
395             try
396             {
397                 _writeCallback = callback;
398                 if(!_incoming)
399                 {
400                     //
401                     // Client authentication.
402                     //
403                     _writeResult = _sslStream.BeginAuthenticateAsClient(_host,
404                                                                         _instance.certs(),
405                                                                         _instance.protocols(),
406                                                                         _instance.checkCRL() > 0,
407                                                                         writeCompleted,
408                                                                         state);
409                 }
410                 else
411                 {
412                     //
413                     // Server authentication.
414                     //
415                     // Get the certificate collection and select the first one.
416                     //
417                     X509Certificate2Collection certs = _instance.certs();
418                     X509Certificate2 cert = null;
419                     if(certs.Count > 0)
420                     {
421                         cert = certs[0];
422                     }
423 
424                     _writeResult = _sslStream.BeginAuthenticateAsServer(cert,
425                                                                         _verifyPeer > 0,
426                                                                         _instance.protocols(),
427                                                                         _instance.checkCRL() > 0,
428                                                                         writeCompleted,
429                                                                         state);
430                 }
431             }
432             catch(IOException ex)
433             {
434                 if(IceInternal.Network.connectionLost(ex))
435                 {
436                     //
437                     // This situation occurs when connectToSelf is called; the "remote" end
438                     // closes the socket immediately.
439                     //
440                     throw new Ice.ConnectionLostException();
441                 }
442                 throw new Ice.SocketException(ex);
443             }
444             catch(AuthenticationException ex)
445             {
446                 Ice.SecurityException e = new Ice.SecurityException(ex);
447                 e.reason = ex.Message;
448                 throw e;
449             }
450             catch(Exception ex)
451             {
452                 throw new Ice.SyscallException(ex);
453             }
454 
455             Debug.Assert(_writeResult != null);
456             return _writeResult.CompletedSynchronously;
457         }
458 
finishAuthenticate()459         private void finishAuthenticate()
460         {
461             Debug.Assert(_writeResult != null);
462 
463             try
464             {
465                 if(!_incoming)
466                 {
467                     _sslStream.EndAuthenticateAsClient(_writeResult);
468                 }
469                 else
470                 {
471                     _sslStream.EndAuthenticateAsServer(_writeResult);
472                 }
473             }
474             catch(IOException ex)
475             {
476                 if(IceInternal.Network.connectionLost(ex))
477                 {
478                     //
479                     // This situation occurs when connectToSelf is called; the "remote" end
480                     // closes the socket immediately.
481                     //
482                     throw new Ice.ConnectionLostException();
483                 }
484                 throw new Ice.SocketException(ex);
485             }
486             catch(AuthenticationException ex)
487             {
488                 Ice.SecurityException e = new Ice.SecurityException(ex);
489                 e.reason = ex.Message;
490                 throw e;
491             }
492             catch(Exception ex)
493             {
494                 throw new Ice.SyscallException(ex);
495             }
496         }
497 
selectCertificate(object sender, string targetHost, X509CertificateCollection certs, X509Certificate remoteCertificate, string[] acceptableIssuers)498         private X509Certificate selectCertificate(object sender, string targetHost, X509CertificateCollection certs,
499                                                   X509Certificate remoteCertificate, string[] acceptableIssuers)
500         {
501             if(certs == null || certs.Count == 0)
502             {
503                 return null;
504             }
505             else if(certs.Count == 1)
506             {
507                 return certs[0];
508             }
509 
510             //
511             // Use the first certificate that match the acceptable issuers.
512             //
513             if(acceptableIssuers != null && acceptableIssuers.Length > 0)
514             {
515                 foreach(X509Certificate certificate in certs)
516                 {
517                     if(Array.IndexOf(acceptableIssuers, certificate.Issuer) != -1)
518                     {
519                         return certificate;
520                     }
521                 }
522             }
523             return certs[0];
524         }
525 
validationCallback(object sender, X509Certificate certificate, X509Chain chainEngine, SslPolicyErrors policyErrors)526         private bool validationCallback(object sender, X509Certificate certificate, X509Chain chainEngine,
527                                         SslPolicyErrors policyErrors)
528         {
529             string message = "";
530             int errors = (int)policyErrors;
531             if(certificate != null)
532             {
533                 _chain.Build(new X509Certificate2(certificate));
534                 if(_chain.ChainStatus != null && _chain.ChainStatus.Length > 0)
535                 {
536                     errors |= (int)SslPolicyErrors.RemoteCertificateChainErrors;
537                 }
538                 else if(_instance.engine().caCerts() != null)
539                 {
540                     X509ChainElement e = _chain.ChainElements[_chain.ChainElements.Count - 1];
541                     if(!_chain.ChainPolicy.ExtraStore.Contains(e.Certificate))
542                     {
543                         if(_verifyPeer > 0)
544                         {
545                             message = message + "\npuntrusted root certificate";
546                         }
547                         else
548                         {
549                             message = message + "\nuntrusted root certificate (ignored)";
550                             _verified = false;
551                         }
552                         errors |= (int)SslPolicyErrors.RemoteCertificateChainErrors;
553                     }
554                     else
555                     {
556                         _verified = true;
557                         return true;
558                     }
559                 }
560                 else
561                 {
562                     _verified = true;
563                     return true;
564                 }
565             }
566 
567             if((errors & (int)SslPolicyErrors.RemoteCertificateNotAvailable) > 0)
568             {
569                 //
570                 // The RemoteCertificateNotAvailable case does not appear to be possible
571                 // for an outgoing connection. Since .NET requires an authenticated
572                 // connection, the remote peer closes the socket if it does not have a
573                 // certificate to provide.
574                 //
575 
576                 if(_incoming)
577                 {
578                     if(_verifyPeer > 1)
579                     {
580                         if(_instance.securityTraceLevel() >= 1)
581                         {
582                             _instance.logger().trace(_instance.securityTraceCategory(),
583                                           "SSL certificate validation failed - client certificate not provided");
584                         }
585                         return false;
586                     }
587                     errors ^= (int)SslPolicyErrors.RemoteCertificateNotAvailable;
588                     message = message + "\nremote certificate not provided (ignored)";
589                 }
590             }
591 
592             if((errors & (int)SslPolicyErrors.RemoteCertificateNameMismatch) > 0)
593             {
594                 if(_instance.engine().getCheckCertName() && !string.IsNullOrEmpty(_host))
595                 {
596                     if(_instance.securityTraceLevel() >= 1)
597                     {
598                         _instance.logger().trace(_instance.securityTraceCategory(),
599                                       "SSL certificate validation failed - Hostname mismatch");
600                     }
601                     return false;
602                 }
603                 else
604                 {
605                     errors ^= (int)SslPolicyErrors.RemoteCertificateNameMismatch;
606                 }
607             }
608 
609             if((errors & (int)SslPolicyErrors.RemoteCertificateChainErrors) > 0 &&
610                 _chain.ChainStatus != null && _chain.ChainStatus.Length > 0)
611             {
612                 int errorCount = 0;
613                 foreach(X509ChainStatus status in _chain.ChainStatus)
614                 {
615                     if(status.Status == X509ChainStatusFlags.UntrustedRoot && _instance.engine().caCerts() != null)
616                     {
617                         //
618                         // Untrusted root is OK when using our custom chain engine if
619                         // the CA certificate is present in the chain policy extra store.
620                         //
621                         X509ChainElement e = _chain.ChainElements[_chain.ChainElements.Count - 1];
622                         if(!_chain.ChainPolicy.ExtraStore.Contains(e.Certificate))
623                         {
624                             if(_verifyPeer > 0)
625                             {
626                                 message = message + "\npuntrusted root certificate";
627                                 ++errorCount;
628                             }
629                             else
630                             {
631                                 message = message + "\nuntrusted root certificate (ignored)";
632                             }
633                         }
634                         else
635                         {
636                             _verified = true;
637                         }
638                     }
639                     else if(status.Status == X509ChainStatusFlags.Revoked)
640                     {
641                         if(_instance.checkCRL() > 0)
642                         {
643                             message = message + "\ncertificate revoked";
644                             ++errorCount;
645                         }
646                         else
647                         {
648                             message = message + "\ncertificate revoked (ignored)";
649                         }
650                     }
651                     else if(status.Status == X509ChainStatusFlags.RevocationStatusUnknown)
652                     {
653                         //
654                         // If a certificate's revocation status cannot be determined, the strictest
655                         // policy is to reject the connection.
656                         //
657                         if(_instance.checkCRL() > 1)
658                         {
659                             message = message + "\ncertificate revocation status unknown";
660                             ++errorCount;
661                         }
662                         else
663                         {
664                             message = message + "\ncertificate revocation status unknown (ignored)";
665                         }
666                     }
667                     else if(status.Status == X509ChainStatusFlags.PartialChain)
668                     {
669                         if(_verifyPeer > 0)
670                         {
671                             message = message + "\npartial certificate chain";
672                             ++errorCount;
673                         }
674                         else
675                         {
676                             message = message + "\npartial certificate chain (ignored)";
677                         }
678                     }
679                     else if(status.Status != X509ChainStatusFlags.NoError)
680                     {
681                         message = message + "\ncertificate chain error: " + status.Status.ToString();
682                         ++errorCount;
683                     }
684                 }
685 
686                 if(errorCount == 0)
687                 {
688                     errors ^= (int)SslPolicyErrors.RemoteCertificateChainErrors;
689                 }
690             }
691 
692             if(errors > 0)
693             {
694                 if(_instance.securityTraceLevel() >= 1)
695                 {
696                     if(message.Length > 0)
697                     {
698                         _instance.logger().trace(_instance.securityTraceCategory(),
699                                                  "SSL certificate validation failed:" + message);
700                     }
701                     else
702                     {
703                         _instance.logger().trace(_instance.securityTraceCategory(),
704                                                  "SSL certificate validation failed");
705                     }
706                 }
707                 return false;
708             }
709             else if(message.Length > 0 && _instance.securityTraceLevel() >= 1)
710             {
711                 _instance.logger().trace(_instance.securityTraceCategory(),
712                                          "SSL certificate validation status:" + message);
713             }
714             return true;
715         }
716 
readCompleted(IAsyncResult result)717         internal void readCompleted(IAsyncResult result)
718         {
719             if(!result.CompletedSynchronously)
720             {
721                 _readCallback(result.AsyncState);
722             }
723         }
724 
writeCompleted(IAsyncResult result)725         internal void writeCompleted(IAsyncResult result)
726         {
727             if(!result.CompletedSynchronously)
728             {
729                 _writeCallback(result.AsyncState);
730             }
731         }
732 
getSendPacketSize(int length)733         private int getSendPacketSize(int length)
734         {
735             return _maxSendPacketSize > 0 ? Math.Min(length, _maxSendPacketSize) : length;
736         }
737 
getRecvPacketSize(int length)738         public int getRecvPacketSize(int length)
739         {
740             return _maxRecvPacketSize > 0 ? Math.Min(length, _maxRecvPacketSize) : length;
741         }
742 
743         private Instance _instance;
744         private IceInternal.Transceiver _delegate;
745         private string _host = "";
746         private string _adapterName = "";
747         private bool _incoming;
748         private SslStream _sslStream;
749         private int _verifyPeer;
750         private bool _isConnected;
751         private bool _authenticated;
752         private IAsyncResult _writeResult;
753         private IAsyncResult _readResult;
754         private IceInternal.AsyncCallback _readCallback;
755         private IceInternal.AsyncCallback _writeCallback;
756         private X509Chain _chain;
757         private int _maxSendPacketSize;
758         private int _maxRecvPacketSize;
759         private string _cipher;
760         private X509Certificate2[] _certs;
761         private bool _verified;
762     }
763 }
764