Source for gnu.javax.net.ssl.provider.SSLEngineImpl

   1: /* SSLEngineImpl.java -- implementation of SSLEngine.
   2:    Copyright (C) 2006  Free Software Foundation, Inc.
   3: 
   4: This file is a part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2 of the License, or (at
   9: your option) any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; if not, write to the Free Software
  18: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  19: USA
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version.  */
  37: 
  38: 
  39: package gnu.javax.net.ssl.provider;
  40: 
  41: import gnu.classpath.debug.Component;
  42: import gnu.classpath.debug.SystemLogger;
  43: 
  44: import gnu.java.security.util.ByteBufferOutputStream;
  45: import gnu.javax.net.ssl.Session;
  46: import gnu.javax.net.ssl.SSLRecordHandler;
  47: 
  48: import java.nio.BufferOverflowException;
  49: import java.nio.ByteBuffer;
  50: import java.nio.ByteOrder;
  51: 
  52: import java.security.NoSuchAlgorithmException;
  53: import java.util.ArrayList;
  54: import java.util.List;
  55: import java.util.zip.DataFormatException;
  56: 
  57: import javax.crypto.IllegalBlockSizeException;
  58: import javax.crypto.ShortBufferException;
  59: import javax.net.ssl.SSLEngine;
  60: import javax.net.ssl.SSLEngineResult;
  61: import javax.net.ssl.SSLException;
  62: import javax.net.ssl.SSLSession;
  63: import javax.net.ssl.SSLEngineResult.HandshakeStatus;
  64: import javax.net.ssl.SSLEngineResult.Status;
  65: 
  66: public final class SSLEngineImpl extends SSLEngine
  67: {
  68:   final SSLContextImpl contextImpl;
  69:   private SSLRecordHandler[] handlers;
  70:   private static final SystemLogger logger = SystemLogger.SYSTEM;
  71:   private SessionImpl session;
  72:   private InputSecurityParameters insec;
  73:   private OutputSecurityParameters outsec;
  74:   private boolean inClosed;
  75:   private boolean outClosed;
  76:   private boolean createSessions;
  77:   private boolean needClientAuth;
  78:   private boolean wantClientAuth;
  79:   private boolean initialHandshakeDone;
  80:   private AbstractHandshake handshake;
  81:   private Alert lastAlert;
  82:   private SSLEngineResult.HandshakeStatus handshakeStatus;
  83:   private boolean changeCipherSpec;
  84: 
  85:   private String[] enabledSuites;
  86:   private String[] enabledProtocols;
  87: 
  88:   /**
  89:    * We can receive any message chunked across multiple records,
  90:    * including alerts, even though all alert messages are only two
  91:    * bytes long. Handshake messages are de-chunked in the handshake
  92:    * handler, change-cipher-spec messages are always empty, and we
  93:    * don't care about chunking of application messages.
  94:    *
  95:    * This buffer will hold the incomplete alert that we receive, if
  96:    * any.
  97:    */
  98:   private final ByteBuffer alertBuffer;
  99: 
 100:   private Mode mode;
 101: 
 102:   private enum Mode { SERVER, CLIENT }
 103: 
 104:   SSLEngineImpl (SSLContextImpl contextImpl, String host, int port)
 105:   {
 106:     super(host, port);
 107:     this.contextImpl = contextImpl;
 108:     handlers = new SSLRecordHandler[256];
 109:     session = new SessionImpl();
 110:     session.suite = CipherSuite.TLS_NULL_WITH_NULL_NULL;
 111:     session.version = ProtocolVersion.TLS_1_1;
 112:     byte[] sid = new byte[32];
 113:     contextImpl.random.nextBytes(sid);
 114:     session.setId(new Session.ID(sid));
 115:     session.setRandom(contextImpl.random);
 116: 
 117:     if (Debug.DEBUG)
 118:       logger.logv(Component.SSL_RECORD_LAYER, "generated session ID {0} with random {1}",
 119:                   session.id(), contextImpl.random);
 120: 
 121:     // Begin with no encryption.
 122:     insec = new InputSecurityParameters (null, null, null, session,
 123:                                          CipherSuite.TLS_NULL_WITH_NULL_NULL);
 124:     outsec = new OutputSecurityParameters (null, null, null, session,
 125:                                            CipherSuite.TLS_NULL_WITH_NULL_NULL);
 126:     inClosed = false;
 127:     outClosed = false;
 128:     needClientAuth = false;
 129:     wantClientAuth = false;
 130:     createSessions = true;
 131:     initialHandshakeDone = false;
 132:     alertBuffer = ByteBuffer.wrap (new byte[2]);
 133:     mode = null;
 134:     lastAlert = null;
 135:     handshakeStatus = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
 136:     changeCipherSpec = false;
 137: 
 138:     // Set up default protocols and suites.
 139:     enabledProtocols = new String[] {
 140:       ProtocolVersion.TLS_1_1.toString(),
 141:       ProtocolVersion.TLS_1.toString(),
 142:       ProtocolVersion.SSL_3.toString()
 143:     };
 144:     enabledSuites = defaultSuites();
 145:   }
 146: 
 147:   static String[] defaultSuites()
 148:   {
 149:     return new String[] {
 150:       CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA.toString(),
 151:       CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA.toString(),
 152:       CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA.toString(),
 153:       CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA.toString(),
 154:       CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA.toString(),
 155:       CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA.toString(),
 156:       CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA.toString(),
 157:       CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA.toString(),
 158:       CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA.toString(),
 159:       CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.toString(),
 160:       CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA.toString(),
 161:       CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA.toString(),
 162:       CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA.toString(),
 163:       CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA.toString(),
 164:       CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA.toString(),
 165:       CipherSuite.TLS_RSA_WITH_RC4_128_MD5.toString(),
 166:       CipherSuite.TLS_RSA_WITH_RC4_128_SHA.toString(),
 167:       CipherSuite.TLS_DHE_DSS_WITH_DES_CBC_SHA.toString(),
 168:       CipherSuite.TLS_DHE_RSA_WITH_DES_CBC_SHA.toString(),
 169:       CipherSuite.TLS_DH_DSS_WITH_DES_CBC_SHA.toString(),
 170:       CipherSuite.TLS_DH_RSA_WITH_DES_CBC_SHA.toString(),
 171:       CipherSuite.TLS_RSA_WITH_DES_CBC_SHA.toString(),
 172:       CipherSuite.TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA.toString(),
 173:       CipherSuite.TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(),
 174:       CipherSuite.TLS_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(),
 175:       CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5.toString(),
 176:       CipherSuite.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA.toString(),
 177:       CipherSuite.TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(),
 178:       CipherSuite.TLS_RSA_WITH_NULL_MD5.toString(),
 179:       CipherSuite.TLS_RSA_WITH_NULL_SHA.toString()
 180:     };
 181:   }
 182: 
 183:   // XXX implement?
 184:   /*public void registerHandler (final int contentType,
 185:                                SSLRecordHandler handler)
 186:     throws SSLException
 187:   {
 188:     if (type.equals (ContentType.CHANGE_CIPHER_SPEC)
 189:         || type.equals (ContentType.ALERT)
 190:         || type.equals (ContentType.HANDSHAKE)
 191:         || type.equals (ContentType.APPLICATION_DATA))
 192:       throw new SSLException ("can't override handler for content type " + type);
 193:     int i = type.getValue ();
 194:     if (i < 0 || i > 255)
 195:       throw new SSLException ("illegal content type: " + type);
 196:     handlers[i] = handler;
 197:   }*/
 198: 
 199:   @Override
 200:   public void beginHandshake () throws SSLException
 201:   {
 202:     if (Debug.DEBUG)
 203:       logger.log(Component.SSL_HANDSHAKE, "{0} handshake begins", mode);
 204: 
 205:     if (mode == null)
 206:       throw new IllegalStateException("setUseClientMode was never used");
 207: 
 208:     switch (mode)
 209:       {
 210:       case SERVER:
 211:         if (getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
 212:           throw new SSLException("handshake already in progress");
 213:         try
 214:           {
 215:             handshake = new ServerHandshake(initialHandshakeDone, this);
 216:           }
 217:         catch (NoSuchAlgorithmException nsae)
 218:           {
 219:             throw new SSLException(nsae);
 220:           }
 221:         break;
 222: 
 223:       case CLIENT:
 224:         try
 225:           {
 226:             handshake = new ClientHandshake(this);
 227:           }
 228:         catch (NoSuchAlgorithmException nsae)
 229:           {
 230:             throw new SSLException(nsae);
 231:           }
 232:         break;
 233:       }
 234:   }
 235: 
 236:   @Override
 237:   public void closeInbound()
 238:   {
 239:     inClosed = true;
 240:   }
 241: 
 242:   @Override
 243:   public void closeOutbound()
 244:   {
 245:     lastAlert = new Alert(Alert.Level.WARNING, Alert.Description.CLOSE_NOTIFY);
 246:   }
 247: 
 248:   @Override
 249:   public Runnable getDelegatedTask()
 250:   {
 251:     if (handshake == null)
 252:       return null;
 253:     return handshake.getTask();
 254:   }
 255: 
 256:   @Override
 257:   public String[] getEnabledCipherSuites()
 258:   {
 259:     return (String[]) enabledSuites.clone();
 260:   }
 261: 
 262:   @Override
 263:   public String[] getEnabledProtocols()
 264:   {
 265:     return (String[]) enabledProtocols.clone();
 266:   }
 267: 
 268:   @Override
 269:   public boolean getEnableSessionCreation()
 270:   {
 271:     return createSessions;
 272:   }
 273: 
 274:   @Override
 275:   public HandshakeStatus getHandshakeStatus()
 276:   {
 277:     if (handshake == null)
 278:       return HandshakeStatus.NOT_HANDSHAKING;
 279:     return handshake.status();
 280:   }
 281: 
 282:   @Override
 283:   public boolean getNeedClientAuth()
 284:   {
 285:     return needClientAuth;
 286:   }
 287: 
 288:   @Override
 289:   public SSLSession getSession()
 290:   {
 291:     return session;
 292:   }
 293: 
 294:   @Override
 295:   public boolean getUseClientMode ()
 296:   {
 297:     return (mode == Mode.CLIENT);
 298:   }
 299: 
 300:   @Override
 301:   public boolean getWantClientAuth()
 302:   {
 303:     return wantClientAuth;
 304:   }
 305: 
 306:   @Override
 307:   public boolean isInboundDone()
 308:   {
 309:     return inClosed;
 310:   }
 311: 
 312:   @Override
 313:   public boolean isOutboundDone()
 314:   {
 315:     return outClosed;
 316:   }
 317: 
 318:   @Override
 319:   public void setEnableSessionCreation(final boolean createSessions)
 320:   {
 321:     this.createSessions = createSessions;
 322:   }
 323: 
 324:   @Override
 325:   public void setEnabledCipherSuites(final String[] suites)
 326:   {
 327:     if (suites.length == 0)
 328:       throw new IllegalArgumentException("need at least one suite");
 329:     enabledSuites = (String[]) suites.clone();
 330:   }
 331: 
 332:   @Override
 333:   public void setEnabledProtocols(final String[] protocols)
 334:   {
 335:     if (protocols.length == 0)
 336:       throw new IllegalArgumentException("need at least one protocol");
 337:     enabledProtocols = (String[]) protocols.clone();
 338:   }
 339: 
 340:   @Override
 341:   public String[] getSupportedCipherSuites()
 342:   {
 343:     // XXX if we ever want to support "pluggable" cipher suites, we'll need
 344:     // to figure this out.
 345: 
 346:     return CipherSuite.availableSuiteNames().toArray(new String[0]);
 347:   }
 348: 
 349:   @Override
 350:   public String[] getSupportedProtocols()
 351:   {
 352:     return new String[] { ProtocolVersion.SSL_3.toString(),
 353:                           ProtocolVersion.TLS_1.toString(),
 354:                           ProtocolVersion.TLS_1_1.toString() };
 355:   }
 356: 
 357:   @Override
 358:   public void setNeedClientAuth(final boolean needClientAuth)
 359:   {
 360:     this.needClientAuth = needClientAuth;
 361:   }
 362: 
 363:   @Override
 364:   public void setUseClientMode (final boolean clientMode)
 365:   {
 366:     if (clientMode)
 367:       mode = Mode.CLIENT;
 368:     else
 369:       mode = Mode.SERVER;
 370:   }
 371: 
 372:   public @Override void setWantClientAuth(final boolean wantClientAuth)
 373:   {
 374:     this.wantClientAuth = wantClientAuth;
 375:   }
 376: 
 377:   public @Override SSLEngineResult unwrap (final ByteBuffer source,
 378:                                            final ByteBuffer[] sinks,
 379:                                            final int offset, final int length)
 380:     throws SSLException
 381:   {
 382:     if (mode == null)
 383:       throw new IllegalStateException ("setUseClientMode was never called");
 384: 
 385:     if (inClosed)
 386:       return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
 387:                                  handshakeStatus, 0, 0);
 388: 
 389:     if (source.remaining() < 5)
 390:       {
 391:         return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW,
 392:                                    handshakeStatus, 0, 0);
 393:       }
 394: 
 395:     Record record = null;
 396:     boolean helloV2 = false;
 397: 
 398:     // XXX: messages may be chunked across multiple records; does this
 399:     // include the SSLv2 message? I don't think it does, but we should
 400:     // make sure.
 401:     if (!getUseClientMode() && (source.get(source.position()) & 0x80) == 0x80)
 402:       {
 403:         if (handshake == null)
 404:           beginHandshake();
 405:         int hellolen = source.getShort(source.position()) & 0x7FFF;
 406:         this.handshake.handleV2Hello(source.slice());
 407:         if (!insec.cipherSuite().equals (CipherSuite.TLS_NULL_WITH_NULL_NULL))
 408:           throw new SSLException ("received SSLv2 client hello in encrypted "
 409:                                   + "session; this is invalid.");
 410:         if (Debug.DEBUG)
 411:           logger.log (Component.SSL_RECORD_LAYER,
 412:                       "converting SSLv2 client hello to version 3 hello");
 413: 
 414:         source.getShort(); // skip length
 415:         ClientHelloV2 v2 = new ClientHelloV2(source.slice());
 416: 
 417:         if (Debug.DEBUG)
 418:           logger.log(Component.SSL_RECORD_LAYER, "v2 hello: {0}", v2);
 419: 
 420:         List<CipherSuite> suites = v2.cipherSpecs();
 421: 
 422:         ClientHelloBuilder hello = new ClientHelloBuilder();
 423:         hello.setVersion(v2.version ());
 424: 
 425:         Random random = hello.random();
 426:         byte[] challenge = v2.challenge();
 427:         if (challenge.length < 32)
 428:           {
 429:             byte[] b = new byte[32];
 430:             System.arraycopy(challenge, 0, b, b.length - challenge.length,
 431:                              challenge.length);
 432:             challenge = b;
 433:           }
 434:         random.setGmtUnixTime((challenge[0] & 0xFF) << 24
 435:                               | (challenge[1] & 0xFF) << 16
 436:                               | (challenge[2] & 0xFF) <<  8
 437:                               | (challenge[3] & 0xFF));
 438:         random.setRandomBytes(challenge, 4);
 439: 
 440:         byte[] sessionId = v2.sessionId();
 441:         hello.setSessionId(sessionId, 0, sessionId.length);
 442:         hello.setCipherSuites(suites);
 443:         ArrayList<CompressionMethod> comps = new ArrayList<CompressionMethod>(1);
 444:         comps.add(CompressionMethod.NULL);
 445:         hello.setCompressionMethods(comps);
 446: 
 447:         record = new Record(ByteBuffer.allocate(hello.length() + 9));
 448:         record.setContentType(ContentType.HANDSHAKE);
 449:         record.setVersion(v2.version());
 450:         record.setLength(hello.length() + 4);
 451: 
 452:         Handshake handshake = new Handshake(record.fragment());
 453:         handshake.setLength(hello.length());
 454:         handshake.setType(Handshake.Type.CLIENT_HELLO);
 455: 
 456:         handshake.bodyBuffer().put(hello.buffer());
 457:         source.position(source.position() + hellolen);
 458:         helloV2 = true;
 459:       }
 460:     else
 461:       record = new Record(source);
 462: 
 463:     ContentType type = record.contentType ();
 464: 
 465:     if (Debug.DEBUG)
 466:       logger.log(Component.SSL_RECORD_LAYER, "input record:\n{0}", record);
 467: 
 468:     if (record.length() > session.getPacketBufferSize() - 5)
 469:       {
 470:         lastAlert = new Alert(Alert.Level.FATAL,
 471:                               Alert.Description.RECORD_OVERFLOW);
 472:         throw new AlertException(lastAlert);
 473:       }
 474: 
 475:     ByteBufferOutputStream sysMsg = null;
 476:     ByteBuffer msg = null;
 477: 
 478:     int produced = 0;
 479:     try
 480:       {
 481:         // Application data will get decrypted directly into the user's
 482:         // output buffers.
 483:         if (record.contentType() == ContentType.APPLICATION_DATA)
 484:           produced = insec.decrypt(record, sinks, offset, length);
 485:         else
 486:           {
 487:             if (insec.cipherSuite() == CipherSuite.TLS_NULL_WITH_NULL_NULL)
 488:               msg = record.fragment();
 489:             else
 490:               {
 491:                 sysMsg = new ByteBufferOutputStream();
 492:                 insec.decrypt(record, sysMsg);
 493:               }
 494:           }
 495: 
 496:         // Advance the input buffer past the record we just read.
 497:         if (!helloV2)
 498:           source.position(source.position() + record.length() + 5);
 499:       }
 500:     catch (BufferOverflowException boe)
 501:       {
 502:         // We throw this if the output buffers are not large enough; signal
 503:         // the caller about this.
 504:         logger.log(Component.SSL_RECORD_LAYER, "buffer overflow when decrypting", boe);
 505:         return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW,
 506:                                    handshakeStatus, 0, 0);
 507:       }
 508:     catch (IllegalBlockSizeException ibse)
 509:       {
 510:         lastAlert = new Alert(Alert.Level.FATAL,
 511:                               Alert.Description.BAD_RECORD_MAC);
 512:         throw new AlertException(lastAlert, ibse);
 513:       }
 514:     catch (DataFormatException dfe)
 515:       {
 516:         lastAlert = new Alert(Alert.Level.FATAL,
 517:                               Alert.Description.DECOMPRESSION_FAILURE);
 518:         throw new AlertException(lastAlert, dfe);
 519:       }
 520:     catch (MacException me)
 521:       {
 522:         lastAlert = new Alert(Alert.Level.FATAL,
 523:                               Alert.Description.BAD_RECORD_MAC);
 524:         throw new AlertException(lastAlert, me);
 525:       }
 526:     catch (ShortBufferException sbe)
 527:       {
 528:         // We've messed up if this happens.
 529:         lastAlert = new Alert(Alert.Level.FATAL,
 530:                               Alert.Description.INTERNAL_ERROR);
 531:         throw new AlertException(lastAlert, sbe);
 532:       }
 533: 
 534:     SSLEngineResult result = null;
 535: 
 536:     // If we need to handle the output here, do it. Otherwise, the output
 537:     // has been stored in the supplied output buffers.
 538:     if (sysMsg != null)
 539:       {
 540:         if (Debug.DEBUG)
 541:           logger.logv(Component.SSL_RECORD_LAYER, "sysmessage {0}", sysMsg);
 542:         msg = sysMsg.buffer();
 543:       }
 544: 
 545:     if (type == ContentType.CHANGE_CIPHER_SPEC)
 546:       {
 547:         // We *may* get a partial message, even though the message is only
 548:         // one byte long.
 549:         if (msg.remaining() == 0)
 550:           {
 551:             result = new SSLEngineResult (SSLEngineResult.Status.OK,
 552:                                           handshakeStatus,
 553:                                           record.length() + 5, 0);
 554:           }
 555:         else
 556:           {
 557:             byte b = msg.get();
 558:             if (b != 1)
 559:               throw new SSLException ("unknown ChangeCipherSpec value: " + (b & 0xFF));
 560:             InputSecurityParameters params = handshake.getInputParams();
 561:             logger.log (Component.SSL_RECORD_LAYER,
 562:                         "switching to input security parameters {0}",
 563:                         params.cipherSuite());
 564:             insec = params;
 565:             result = new SSLEngineResult (SSLEngineResult.Status.OK,
 566:                                           handshakeStatus,
 567:                                           record.length() + 5, 0);
 568:           }
 569:       }
 570:     else if (type == ContentType.ALERT)
 571:       {
 572:         int len = 0;
 573:         if (alertBuffer.position() > 0)
 574:           {
 575:             alertBuffer.put(msg.get());
 576:             len = 1;
 577:           }
 578:         if (Debug.DEBUG)
 579:           logger.logv(Component.SSL_RECORD_LAYER, "processing alerts {0}",
 580:                       Util.wrapBuffer(msg));
 581:         len += msg.remaining() / 2;
 582:         Alert[] alerts = new Alert[len];
 583:         int i = 0;
 584:         if (alertBuffer.position() > 0)
 585:           {
 586:             alertBuffer.flip();
 587:             alerts[0] = new Alert(alertBuffer);
 588:             i++;
 589:           }
 590:         while (i < alerts.length)
 591:           {
 592:             alerts[i++] = new Alert(msg.duplicate());
 593:             msg.position(msg.position() + 2);
 594:           }
 595:         if (Debug.DEBUG)
 596:           logger.logv(Component.SSL_RECORD_LAYER, "alerts: {0}", alerts.length);
 597: 
 598:         for (i = 0; i < alerts.length; i++)
 599:           {
 600:             if (alerts[i].level() == Alert.Level.FATAL)
 601:               throw new AlertException(alerts[i], false);
 602:             if (alerts[i].description() != Alert.Description.CLOSE_NOTIFY)
 603:               logger.log(java.util.logging.Level.WARNING,
 604:                          "received alert: {0}", alerts[i]);
 605:             if (alerts[i].description() == Alert.Description.CLOSE_NOTIFY)
 606:               inClosed = true;
 607:           }
 608: 
 609:         if (msg.hasRemaining())
 610:           alertBuffer.position(0).limit(2);
 611: 
 612:         result = new SSLEngineResult (SSLEngineResult.Status.OK,
 613:                                       handshakeStatus,
 614:                                       record.length() + 5, 0);
 615:       }
 616:     else if (type == ContentType.HANDSHAKE)
 617:       {
 618:         if (handshake == null)
 619:           beginHandshake();
 620:         try
 621:           {
 622:             handshakeStatus = handshake.handleInput(msg);
 623:           }
 624:         catch (AlertException ae)
 625:           {
 626:             lastAlert = ae.alert();
 627:             return new SSLEngineResult(SSLEngineResult.Status.OK,
 628:                                        SSLEngineResult.HandshakeStatus.NEED_WRAP,
 629:                                        0, 0);
 630:           }
 631:         if (Debug.DEBUG)
 632:           logger.logv(Component.SSL_HANDSHAKE, "handshake status {0}", handshakeStatus);
 633:         result = new SSLEngineResult(SSLEngineResult.Status.OK,
 634:                                      handshakeStatus,
 635:                                      record.length() + 5,
 636:                                      0);
 637:         if (handshakeStatus == HandshakeStatus.FINISHED)
 638:           {
 639:             handshake = null;
 640:             handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
 641:           }
 642:       }
 643:     else if (type == ContentType.APPLICATION_DATA)
 644:       {
 645:         // Do nothing more; the application data has been put into
 646:         // the output buffers.
 647:         result = new SSLEngineResult(SSLEngineResult.Status.OK,
 648:                                      handshakeStatus,
 649:                                      record.length() + 5,
 650:                                      produced);
 651:       }
 652:     else
 653:       {
 654:         SSLRecordHandler handler = handlers[type.getValue()];
 655:         if (handler != null)
 656:           {
 657:             result = new SSLEngineResult(SSLEngineResult.Status.OK,
 658:                                          handshakeStatus,
 659:                                          record.length() + 5,
 660:                                          0);
 661:           }
 662:         else
 663:           throw new SSLException ("unknown content type: " + type);
 664:       }
 665: 
 666:     if (Debug.DEBUG)
 667:       logger.logv(Component.SSL_RECORD_LAYER, "return result: {0}", result);
 668: 
 669:     return result;
 670:   }
 671: 
 672:   public @Override SSLEngineResult wrap (ByteBuffer[] sources, int offset, int length,
 673:                                          ByteBuffer sink)
 674:     throws SSLException
 675:   {
 676:     if (mode == null)
 677:       throw new IllegalStateException ("setUseClientMode was never called");
 678: 
 679:     if (outClosed)
 680:       return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
 681:                                  handshakeStatus, 0, 0);
 682: 
 683:     ContentType type = null;
 684:     ByteBuffer sysMessage = null;
 685:     if (Debug.DEBUG)
 686:       logger.logv(Component.SSL_RECORD_LAYER, "wrap {0} {1} {2} {3} / {4}",
 687:                   sources, offset, length, sink, getHandshakeStatus());
 688:     if (lastAlert != null)
 689:       {
 690:         type = ContentType.ALERT;
 691:         sysMessage = ByteBuffer.allocate(2);
 692:         Alert alert = new Alert(sysMessage);
 693:         alert.setDescription(lastAlert.description());
 694:         alert.setLevel(lastAlert.level());
 695:         if (lastAlert.description() == Alert.Description.CLOSE_NOTIFY)
 696:           outClosed = true;
 697:       }
 698:     else if (changeCipherSpec)
 699:       {
 700:         type = ContentType.CHANGE_CIPHER_SPEC;
 701:         sysMessage = ByteBuffer.allocate(1);
 702:         sysMessage.put(0, (byte) 1);
 703:       }
 704:     else if (getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP)
 705:       {
 706:         // If we are not encrypting, optimize the handshake to fill
 707:         // the buffer directly.
 708:         if (outsec.suite() == CipherSuite.TLS_NULL_WITH_NULL_NULL)
 709:           {
 710:             int orig = sink.position();
 711:             sink.order(ByteOrder.BIG_ENDIAN);
 712:             sink.put((byte) ContentType.HANDSHAKE.getValue());
 713:             sink.putShort((short) session.version.rawValue());
 714:             sink.putShort((short) 0);
 715:             handshakeStatus = handshake.handleOutput(sink);
 716:             int produced = sink.position() - orig;
 717:             sink.putShort(orig + 3, (short) (produced - 5));
 718:             if (Debug.DEBUG)
 719:               logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}",
 720:                           new Record((ByteBuffer) sink.duplicate().position(orig)));
 721:             SSLEngineResult result = new SSLEngineResult(SSLEngineResult.Status.OK,
 722:                                                          handshakeStatus, 0, produced);
 723: 
 724:             // Note, this will only happen if we transition from
 725:             // TLS_NULL_WITH_NULL_NULL *to* TLS_NULL_WITH_NULL_NULL, which
 726:             // doesn't make a lot of sense, but we support it anyway.
 727:             if (handshakeStatus == HandshakeStatus.FINISHED)
 728:               {
 729:                 handshake = null; // finished with it.
 730:                 handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
 731:               }
 732:             return result;
 733:           }
 734: 
 735:         // Rough guideline; XXX.
 736:         sysMessage = ByteBuffer.allocate(sink.remaining() - 2048);
 737:         type = ContentType.HANDSHAKE;
 738:         try
 739:           {
 740:             handshakeStatus = handshake.handleOutput(sysMessage);
 741:           }
 742:         catch (AlertException ae)
 743:           {
 744:             lastAlert = ae.alert();
 745:             return new SSLEngineResult(Status.OK,
 746:                                        HandshakeStatus.NEED_WRAP, 0, 0);
 747:           }
 748:         sysMessage.flip();
 749:         if (Debug.DEBUG)
 750:           logger.logv(Component.SSL_HANDSHAKE, "handshake status {0}",
 751:                       handshakeStatus);
 752:       }
 753: 
 754:     int produced = 0;
 755:     int consumed = 0;
 756: 
 757:     try
 758:       {
 759:         int orig = sink.position();
 760:         int[] inout = null;
 761:         if (sysMessage != null)
 762:           {
 763:             if (Debug.DEBUG)
 764:               logger.logv(Component.SSL_RECORD_LAYER, "encrypt system message {0} to {1}", sysMessage, sink);
 765:             inout = outsec.encrypt(new ByteBuffer[] { sysMessage }, 0, 1,
 766:                                    type, sink);
 767:             produced = inout[1];
 768:           }
 769:         else
 770:           {
 771:             if (outsec.needToSplitPayload())
 772:               {
 773:                 inout = outsec.encrypt(sources, offset, 1,
 774:                                        ContentType.APPLICATION_DATA, sink);
 775:                 consumed = inout[0];
 776:                 produced = inout[1];
 777:                 if (length > 1)
 778:                   {
 779:                     inout = outsec.encrypt(sources, offset+1, length-1,
 780:                                            ContentType.APPLICATION_DATA, sink);
 781:                     consumed += inout[0];
 782:                     produced += inout[1];
 783:                   }
 784:               }
 785:             else
 786:               {
 787:                 inout = outsec.encrypt(sources, offset, length,
 788:                                        ContentType.APPLICATION_DATA, sink);
 789:                 consumed = inout[0];
 790:                 produced = inout[1];
 791:               }
 792:           }
 793: 
 794:         if (Debug.DEBUG)
 795:           logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}",
 796:                       new Record((ByteBuffer) sink.duplicate().position(orig).limit(produced)));
 797:       }
 798:     catch (ShortBufferException sbe)
 799:       {
 800:         // We don't expect this to happen, except for bugs; signal an
 801:         // internal error.
 802:         lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR);
 803:         return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0);
 804:       }
 805:     catch (IllegalBlockSizeException ibse)
 806:       {
 807:         // We don't expect this to happen, except for bugs; signal an
 808:         // internal error.
 809:         lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR);
 810:         return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0);
 811:       }
 812:     catch (DataFormatException dfe)
 813:       {
 814:         // We don't expect this to happen; signal an internal error.
 815:         lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR);
 816:         return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0);
 817:       }
 818: 
 819:     if (lastAlert != null && lastAlert.level() == Alert.Level.FATAL)
 820:       {
 821:         AlertException ae = new AlertException(lastAlert);
 822:         lastAlert = null;
 823:         throw ae;
 824:       }
 825: 
 826:     if (changeCipherSpec)
 827:       {
 828:         outsec = handshake.getOutputParams();
 829:         changeCipherSpec = false;
 830:       }
 831:     SSLEngineResult result
 832:       = new SSLEngineResult(outClosed ? SSLEngineResult.Status.CLOSED
 833:                                       : SSLEngineResult.Status.OK,
 834:                             handshakeStatus, consumed, produced);
 835:     if (handshakeStatus == HandshakeStatus.FINISHED)
 836:       {
 837:         handshake = null; // done with it.
 838:         handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
 839:       }
 840:     return result;
 841:   }
 842: 
 843:   // Package-private methods.
 844: 
 845:   SessionImpl session ()
 846:   {
 847:     return session;
 848:   }
 849: 
 850:   void setSession(SessionImpl session)
 851:   {
 852:     this.session = session;
 853:   }
 854: 
 855:   void changeCipherSpec()
 856:   {
 857:     changeCipherSpec = true;
 858:   }
 859: }