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