1 // 2 // Copyright (c) ZeroC, Inc. All rights reserved. 3 // 4 5 package com.zeroc.IceSSL; 6 7 import java.nio.*; 8 import javax.net.ssl.*; 9 import javax.net.ssl.SSLEngineResult.*; 10 import com.zeroc.IceInternal.SocketOperation; 11 12 final class TransceiverI implements com.zeroc.IceInternal.Transceiver 13 { 14 @Override fd()15 public java.nio.channels.SelectableChannel fd() 16 { 17 return _delegate.fd(); 18 } 19 20 @Override setReadyCallback(com.zeroc.IceInternal.ReadyCallback callback)21 public void setReadyCallback(com.zeroc.IceInternal.ReadyCallback callback) 22 { 23 _readyCallback = callback; 24 _delegate.setReadyCallback(callback); 25 } 26 27 @Override initialize(com.zeroc.IceInternal.Buffer readBuffer, com.zeroc.IceInternal.Buffer writeBuffer)28 public int initialize(com.zeroc.IceInternal.Buffer readBuffer, com.zeroc.IceInternal.Buffer writeBuffer) 29 { 30 if(!_isConnected) 31 { 32 int status = _delegate.initialize(readBuffer, writeBuffer); 33 if(status != SocketOperation.None) 34 { 35 return status; 36 } 37 _isConnected = true; 38 39 com.zeroc.Ice.IPConnectionInfo ipInfo = null; 40 for(com.zeroc.Ice.ConnectionInfo p = _delegate.getInfo(); p != null; p = p.underlying) 41 { 42 if(p instanceof com.zeroc.Ice.IPConnectionInfo) 43 { 44 ipInfo = (com.zeroc.Ice.IPConnectionInfo)p; 45 } 46 } 47 final String host = _incoming ? (ipInfo != null ? ipInfo.remoteAddress : "") : _host; 48 final int port = ipInfo != null ? ipInfo.remotePort : -1; 49 _engine = _instance.createSSLEngine(_incoming, host, port); 50 _appInput = ByteBuffer.allocate(_engine.getSession().getApplicationBufferSize() * 2); 51 52 // Require BIG_ENDIAN byte buffers. This is needed for Android >= 8.0 which can read 53 // the SSL messages directly with these buffers. 54 int bufSize = _engine.getSession().getPacketBufferSize() * 2; 55 _netInput = new com.zeroc.IceInternal.Buffer(ByteBuffer.allocateDirect(bufSize * 2), 56 java.nio.ByteOrder.BIG_ENDIAN); 57 _netOutput = new com.zeroc.IceInternal.Buffer(ByteBuffer.allocateDirect(bufSize * 2), 58 java.nio.ByteOrder.BIG_ENDIAN); 59 } 60 61 int status = handshakeNonBlocking(); 62 if(status != SocketOperation.None) 63 { 64 return status; 65 } 66 67 assert(_engine != null); 68 69 SSLSession session = _engine.getSession(); 70 _cipher = session.getCipherSuite(); 71 try 72 { 73 java.security.cert.Certificate[] pcerts = session.getPeerCertificates(); 74 java.security.cert.Certificate[] vcerts = _instance.engine().getVerifiedCertificateChain(pcerts); 75 _verified = vcerts != null; 76 _certs = _verified ? vcerts : pcerts; 77 } 78 catch(javax.net.ssl.SSLPeerUnverifiedException ex) 79 { 80 // No peer certificates. 81 } 82 83 // 84 // Additional verification. 85 // 86 _instance.verifyPeer(_host, (com.zeroc.IceSSL.ConnectionInfo)getInfo(), _delegate.toString()); 87 88 if(_instance.securityTraceLevel() >= 1) 89 { 90 _instance.traceConnection(_delegate.toString(), _engine, _incoming); 91 } 92 return SocketOperation.None; 93 } 94 95 @Override closing(boolean initiator, com.zeroc.Ice.LocalException ex)96 public int closing(boolean initiator, com.zeroc.Ice.LocalException ex) 97 { 98 // If we are initiating the connection closure, wait for the peer 99 // to close the TCP/IP connection. Otherwise, close immediately. 100 return initiator ? SocketOperation.Read : SocketOperation.None; 101 } 102 103 @Override close()104 public void close() 105 { 106 if(_engine != null) 107 { 108 try 109 { 110 // 111 // Send the close_notify message. 112 // 113 _engine.closeOutbound(); 114 // Cast to java.nio.Buffer to avoid incompatible covariant 115 // return type used in Java 9 java.nio.ByteBuffer 116 ((java.nio.Buffer)_netOutput.b).clear(); 117 while(!_engine.isOutboundDone()) 118 { 119 _engine.wrap(_emptyBuffer, _netOutput.b); 120 try 121 { 122 // 123 // Note: we can't block to send the close_notify message. In some cases, the 124 // close_notify message might therefore not be received by the peer. This is 125 // not a big issue since the Ice protocol isn't subject to truncation attacks. 126 // 127 flushNonBlocking(); 128 } 129 catch(com.zeroc.Ice.LocalException ex) 130 { 131 // Ignore. 132 } 133 } 134 } 135 catch(SSLException ex) 136 { 137 // 138 // We can't throw in close. 139 // 140 // Ice.SecurityException se = new Ice.SecurityException( 141 // "IceSSL: SSL failure while shutting down socket", ex); 142 // 143 } 144 145 try 146 { 147 _engine.closeInbound(); 148 } 149 catch(SSLException ex) 150 { 151 // 152 // SSLEngine always raises an exception with this message: 153 // 154 // Inbound closed before receiving peer's close_notify: possible truncation attack? 155 // 156 // We would probably need to wait for a response in shutdown() to avoid this. 157 // For now, we'll ignore this exception. 158 // 159 //_instance.logger().error("IceSSL: error during close\n" + ex.getMessage()); 160 } 161 } 162 163 _delegate.close(); 164 } 165 166 @Override bind()167 public com.zeroc.IceInternal.EndpointI bind() 168 { 169 assert(false); 170 return null; 171 } 172 173 @Override write(com.zeroc.IceInternal.Buffer buf)174 public int write(com.zeroc.IceInternal.Buffer buf) 175 { 176 if(!_isConnected) 177 { 178 return _delegate.write(buf); 179 } 180 181 int status = writeNonBlocking(buf.b); 182 assert(status == SocketOperation.None || status == SocketOperation.Write); 183 return status; 184 } 185 186 @Override read(com.zeroc.IceInternal.Buffer buf)187 public int read(com.zeroc.IceInternal.Buffer buf) 188 { 189 if(!_isConnected) 190 { 191 return _delegate.read(buf); 192 } 193 194 _readyCallback.ready(SocketOperation.Read, false); 195 196 // 197 // Try to satisfy the request from data we've already decrypted. 198 // 199 fill(buf.b); 200 201 // 202 // Read and decrypt more data if necessary. Note that we might read 203 // more data from the socket than is actually necessary to fill the 204 // caller's stream. 205 // 206 try 207 { 208 while(buf.b.hasRemaining()) 209 { 210 _netInput.flip(); 211 SSLEngineResult result = _engine.unwrap(_netInput.b, _appInput); 212 _netInput.b.compact(); 213 214 Status status = result.getStatus(); 215 assert status != Status.BUFFER_OVERFLOW; 216 217 if(status == Status.CLOSED) 218 { 219 throw new com.zeroc.Ice.ConnectionLostException(); 220 } 221 // Android API 21 SSLEngine doesn't report underflow, so look at the absence of 222 // network data and application data to signal a network read. 223 else if(status == Status.BUFFER_UNDERFLOW || (_appInput.position() == 0 && _netInput.b.position() == 0)) 224 { 225 int s = _delegate.read(_netInput); 226 if(s != SocketOperation.None && _netInput.b.position() == 0) 227 { 228 return s; 229 } 230 continue; 231 } 232 233 fill(buf.b); 234 } 235 236 // If there is no more application data, do one further unwrap to ensure 237 // that the SSLEngine has no buffered data (Android R21 and greater only). 238 if(_appInput.position() == 0) 239 { 240 _netInput.flip(); 241 _engine.unwrap(_netInput.b, _appInput); 242 _netInput.b.compact(); 243 244 // Don't check the status here since we may have already filled 245 // the buffer with a complete request which must be processed. 246 } 247 } 248 catch(SSLException ex) 249 { 250 throw new com.zeroc.Ice.SecurityException("IceSSL: error during read", ex); 251 } 252 253 // 254 // Indicate whether more data is available. 255 // 256 if(_netInput.b.position() > 0 || _appInput.position() > 0) 257 { 258 _readyCallback.ready(SocketOperation.Read, true); 259 } 260 261 return SocketOperation.None; 262 } 263 264 @Override protocol()265 public String protocol() 266 { 267 return _delegate.protocol(); 268 } 269 270 @Override toString()271 public String toString() 272 { 273 return _delegate.toString(); 274 } 275 276 @Override toDetailedString()277 public String toDetailedString() 278 { 279 return toString(); 280 } 281 282 @Override getInfo()283 public com.zeroc.Ice.ConnectionInfo getInfo() 284 { 285 ConnectionInfo info = new ConnectionInfo(); 286 info.underlying = _delegate.getInfo(); 287 info.incoming = _incoming; 288 info.adapterName = _adapterName; 289 info.cipher = _cipher; 290 info.certs = _certs; 291 info.verified = _verified; 292 return info; 293 } 294 295 @Override setBufferSize(int rcvSize, int sndSize)296 public void setBufferSize(int rcvSize, int sndSize) 297 { 298 _delegate.setBufferSize(rcvSize, sndSize); 299 } 300 301 @Override checkSendSize(com.zeroc.IceInternal.Buffer buf)302 public void checkSendSize(com.zeroc.IceInternal.Buffer buf) 303 { 304 _delegate.checkSendSize(buf); 305 } 306 TransceiverI(Instance instance, com.zeroc.IceInternal.Transceiver delegate, String hostOrAdapterName, boolean incoming)307 TransceiverI(Instance instance, com.zeroc.IceInternal.Transceiver delegate, String hostOrAdapterName, 308 boolean incoming) 309 { 310 _instance = instance; 311 _delegate = delegate; 312 _incoming = incoming; 313 if(_incoming) 314 { 315 _adapterName = hostOrAdapterName; 316 } 317 else 318 { 319 _host = hostOrAdapterName; 320 } 321 } 322 handshakeNonBlocking()323 private int handshakeNonBlocking() 324 { 325 try 326 { 327 HandshakeStatus status = _engine.getHandshakeStatus(); 328 while(!_engine.isOutboundDone() && !_engine.isInboundDone()) 329 { 330 SSLEngineResult result = null; 331 switch(status) 332 { 333 case FINISHED: 334 case NOT_HANDSHAKING: 335 { 336 return SocketOperation.None; 337 } 338 case NEED_TASK: 339 { 340 Runnable task; 341 while((task = _engine.getDelegatedTask()) != null) 342 { 343 task.run(); 344 } 345 status = _engine.getHandshakeStatus(); 346 break; 347 } 348 case NEED_UNWRAP: 349 { 350 if(_netInput.b.position() == 0) 351 { 352 int s = _delegate.read(_netInput); 353 if(s != SocketOperation.None && _netInput.b.position() == 0) 354 { 355 return s; 356 } 357 } 358 359 // 360 // The engine needs more data. We might already have enough data in 361 // the _netInput buffer to satisfy the engine. If not, the engine 362 // responds with BUFFER_UNDERFLOW and we'll read from the socket. 363 // 364 _netInput.flip(); 365 result = _engine.unwrap(_netInput.b, _appInput); 366 _netInput.b.compact(); 367 // 368 // FINISHED is only returned from wrap or unwrap, not from engine.getHandshakeResult(). 369 // 370 status = result.getHandshakeStatus(); 371 switch(result.getStatus()) 372 { 373 case BUFFER_OVERFLOW: 374 { 375 assert(false); 376 break; 377 } 378 case BUFFER_UNDERFLOW: 379 { 380 assert(status == javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_UNWRAP); 381 int position = _netInput.b.position(); 382 int s = _delegate.read(_netInput); 383 if(s != SocketOperation.None && _netInput.b.position() == position) 384 { 385 return s; 386 } 387 break; 388 } 389 case CLOSED: 390 { 391 throw new com.zeroc.Ice.ConnectionLostException(); 392 } 393 case OK: 394 { 395 break; 396 } 397 } 398 break; 399 } 400 case NEED_WRAP: 401 { 402 // 403 // The engine needs to send a message. 404 // 405 result = _engine.wrap(_emptyBuffer, _netOutput.b); 406 if(result.bytesProduced() > 0) 407 { 408 int s = flushNonBlocking(); 409 if(s != SocketOperation.None) 410 { 411 return s; 412 } 413 } 414 415 // 416 // FINISHED is only returned from wrap or unwrap, not from engine.getHandshakeResult(). 417 // 418 status = result.getHandshakeStatus(); 419 break; 420 } 421 } 422 423 if(result != null) 424 { 425 switch(result.getStatus()) 426 { 427 case BUFFER_OVERFLOW: 428 assert(false); 429 break; 430 case BUFFER_UNDERFLOW: 431 // Need to read again. 432 assert(status == HandshakeStatus.NEED_UNWRAP); 433 break; 434 case CLOSED: 435 throw new com.zeroc.Ice.ConnectionLostException(); 436 case OK: 437 break; 438 } 439 } 440 } 441 } 442 catch(SSLException ex) 443 { 444 throw new com.zeroc.Ice.SecurityException("IceSSL: handshake error", ex); 445 } 446 return SocketOperation.None; 447 } 448 writeNonBlocking(ByteBuffer buf)449 private int writeNonBlocking(ByteBuffer buf) 450 { 451 // 452 // This method has two purposes: encrypt the application's message buffer into our 453 // _netOutput buffer, and write the contents of _netOutput to the socket without 454 // blocking. 455 // 456 try 457 { 458 while(buf.hasRemaining() || _netOutput.b.position() > 0) 459 { 460 if(buf.hasRemaining()) 461 { 462 // 463 // Encrypt the buffer. 464 // 465 SSLEngineResult result = _engine.wrap(buf, _netOutput.b); 466 switch(result.getStatus()) 467 { 468 case BUFFER_OVERFLOW: 469 // 470 // Need to make room in _netOutput.b. 471 // 472 break; 473 case BUFFER_UNDERFLOW: 474 assert(false); 475 break; 476 case CLOSED: 477 throw new com.zeroc.Ice.ConnectionLostException(); 478 case OK: 479 break; 480 } 481 } 482 483 // 484 // Write the encrypted data to the socket. We continue writing until we've written 485 // all of _netOutput, or until flushNonBlocking indicates that it cannot write 486 // (i.e., by returning SocketOperation.Write). 487 // 488 if(_netOutput.b.position() > 0) 489 { 490 int s = flushNonBlocking(); 491 if(s != SocketOperation.None) 492 { 493 return s; 494 } 495 } 496 } 497 } 498 catch(SSLException ex) 499 { 500 throw new com.zeroc.Ice.SecurityException("IceSSL: error while encoding message", ex); 501 } 502 503 assert(_netOutput.b.position() == 0); 504 return SocketOperation.None; 505 } 506 flushNonBlocking()507 private int flushNonBlocking() 508 { 509 _netOutput.flip(); 510 511 try 512 { 513 int s = _delegate.write(_netOutput); 514 if(s != SocketOperation.None) 515 { 516 _netOutput.b.compact(); 517 return s; 518 } 519 } 520 catch(com.zeroc.Ice.SocketException ex) 521 { 522 throw new com.zeroc.Ice.ConnectionLostException(ex); 523 } 524 // Cast to java.nio.Buffer to avoid incompatible covariant 525 // return type used in Java 9 java.nio.ByteBuffer 526 ((java.nio.Buffer)_netOutput.b).clear(); 527 return SocketOperation.None; 528 } 529 fill(ByteBuffer buf)530 private void fill(ByteBuffer buf) 531 { 532 ((java.nio.Buffer)_appInput).flip(); 533 if(_appInput.hasRemaining()) 534 { 535 int bytesAvailable = _appInput.remaining(); 536 int bytesNeeded = buf.remaining(); 537 if(bytesAvailable > bytesNeeded) 538 { 539 bytesAvailable = bytesNeeded; 540 } 541 if(buf.hasArray()) 542 { 543 // 544 // Copy directly into the destination buffer's backing array. 545 // 546 byte[] arr = buf.array(); 547 _appInput.get(arr, buf.arrayOffset() + buf.position(), bytesAvailable); 548 // Cast to java.nio.Buffer to avoid incompatible covariant 549 // return type used in Java 9 java.nio.ByteBuffer 550 ((Buffer)buf).position(buf.position() + bytesAvailable); 551 } 552 else if(_appInput.hasArray()) 553 { 554 // 555 // Copy directly from the source buffer's backing array. 556 // 557 byte[] arr = _appInput.array(); 558 buf.put(arr, _appInput.arrayOffset() + _appInput.position(), bytesAvailable); 559 // Cast to java.nio.Buffer to avoid incompatible covariant 560 // return type used in Java 9 java.nio.ByteBuffer 561 ((Buffer)_appInput).position(_appInput.position() + bytesAvailable); 562 } 563 else 564 { 565 // 566 // Copy using a temporary array. 567 // 568 byte[] arr = new byte[bytesAvailable]; 569 _appInput.get(arr); 570 buf.put(arr); 571 } 572 } 573 _appInput.compact(); 574 } 575 576 private Instance _instance; 577 private com.zeroc.IceInternal.Transceiver _delegate; 578 private javax.net.ssl.SSLEngine _engine; 579 private String _host = ""; 580 private String _adapterName = ""; 581 private boolean _incoming; 582 private com.zeroc.IceInternal.ReadyCallback _readyCallback; 583 private boolean _isConnected = false; 584 585 private ByteBuffer _appInput; // Holds clear-text data to be read by the application. 586 private com.zeroc.IceInternal.Buffer _netInput; // Holds encrypted data read from the socket. 587 private com.zeroc.IceInternal.Buffer _netOutput; // Holds encrypted data to be written to the socket. 588 private static ByteBuffer _emptyBuffer = ByteBuffer.allocate(0); // Used during handshaking. 589 590 private String _cipher; 591 private java.security.cert.Certificate[] _certs; 592 private boolean _verified; 593 } 594