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