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