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