Пример #1
0
  @Test
  public void testRfc3163Example() throws Exception {
    // This test uses the example from page 10 in RFC 3163
    // (https://tools.ietf.org/html/rfc3163#section-5)
    mockRandom(new byte[] {18, 56, -105, 88, 121, -121, 71, -104});

    KeyStore emptyTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
    emptyTrustStore.load(null, null);
    final SaslServer saslServer =
        createSaslServer(
            SaslMechanismInformation.Names.IEC_ISO_9798_U_RSA_SHA1_ENC,
            "",
            getX509KeyManager(serverKeyStore, KEYSTORE_PASSWORD),
            getX509TrustManager(emptyTrustStore));
    assertNotNull(saslServer);
    assertFalse(saslServer.isComplete());

    byte[] tokenBA1 = saslServer.evaluateResponse(new byte[0]);
    byte[] expectedTokenBA1 = CodePointIterator.ofString("MAoECBI4l1h5h0eY").base64Decode().drain();
    assertArrayEquals(expectedTokenBA1, tokenBA1);
    assertFalse(saslServer.isComplete());

    byte[] tokenAB =
        CodePointIterator.ofString(
                "MIIBAgQIIxh5I0h5RYegD4INc2FzbC1yLXVzLmNvbaFPFk1odHRwOi8vY2VydHMtci11cy5jb20vY2VydD9paD1odmNOQVFFRkJRQURnWUVBZ2hBR2hZVFJna0ZqJnNuPUVQOXVFbFkzS0RlZ2pscjCBkzANBgkqhkiG9w0BAQUFAAOBgQCkuC2GgtYcxGG1NEzLA4bh5lqJGOZySACMmc+mDrV7A7KAgbpO2OuZpMCl7zvNt/L3OjQZatiX8d1XbuQ40l+g2TJzJt06o7ogomxdDwqlA/3zp2WMohlI0MotHmfDSWEDZmEYDEA3/eGgkWyi1v1lEVdFuYmrTr8E4wE9hxdQrA==")
            .base64Decode()
            .drain();
    try {
      saslServer.evaluateResponse(tokenAB);
      fail("Expected SaslException not thrown");
    } catch (SaslException expected) {
      // The example specifies the client's certificate using a fake URL
      // (http://certs-r-us.com/cert?ih=hvcNAQEFBQADgYEAghAGhYTRgkFj&sn=EP9uElY3KDegjlr)
      // so we can actually make use of it.
      assertTrue(expected.getCause().getMessage().contains("certificate"));
    }
    assertFalse(saslServer.isComplete());
  }
Пример #2
0
  /**
   * Create transport per the connection options Supported transport options are: - SASL based
   * transports over + Kerberos + Delegation token + SSL + non-SSL - Raw (non-SASL) socket
   *
   * <p>Kerberos and Delegation token supports SASL QOP configurations
   *
   * @throws SQLException, TTransportException
   */
  private TTransport createBinaryTransport() throws SQLException, TTransportException {
    try {
      // handle secure connection if specified
      if (!JdbcConnectionParams.AUTH_SIMPLE.equals(
          sessConfMap.get(JdbcConnectionParams.AUTH_TYPE))) {
        // If Kerberos
        Map<String, String> saslProps = new HashMap<String, String>();
        SaslQOP saslQOP = SaslQOP.AUTH;
        if (sessConfMap.containsKey(JdbcConnectionParams.AUTH_QOP)) {
          try {
            saslQOP = SaslQOP.fromString(sessConfMap.get(JdbcConnectionParams.AUTH_QOP));
          } catch (IllegalArgumentException e) {
            throw new SQLException(
                "Invalid " + JdbcConnectionParams.AUTH_QOP + " parameter. " + e.getMessage(),
                "42000",
                e);
          }
          saslProps.put(Sasl.QOP, saslQOP.toString());
        } else {
          // If the client did not specify qop then just negotiate the one supported by server
          saslProps.put(Sasl.QOP, "auth-conf,auth-int,auth");
        }
        saslProps.put(Sasl.SERVER_AUTH, "true");
        if (sessConfMap.containsKey(JdbcConnectionParams.AUTH_PRINCIPAL)) {
          transport =
              KerberosSaslHelper.getKerberosTransport(
                  sessConfMap.get(JdbcConnectionParams.AUTH_PRINCIPAL),
                  host,
                  HiveAuthFactory.getSocketTransport(host, port, loginTimeout),
                  saslProps,
                  assumeSubject);
        } else {
          // If there's a delegation token available then use token based connection
          String tokenStr = getClientDelegationToken(sessConfMap);
          if (tokenStr != null) {
            transport =
                KerberosSaslHelper.getTokenTransport(
                    tokenStr,
                    host,
                    HiveAuthFactory.getSocketTransport(host, port, loginTimeout),
                    saslProps);
          } else {
            // we are using PLAIN Sasl connection with user/password
            String userName = getUserName();
            String passwd = getPassword();
            // Note: Thrift returns an SSL socket that is already bound to the specified host:port
            // Therefore an open called on this would be a no-op later
            // Hence, any TTransportException related to connecting with the peer are thrown here.
            // Bubbling them up the call hierarchy so that a retry can happen in openTransport,
            // if dynamic service discovery is configured.
            if (isSslConnection()) {
              // get SSL socket
              String sslTrustStore = sessConfMap.get(JdbcConnectionParams.SSL_TRUST_STORE);
              String sslTrustStorePassword =
                  sessConfMap.get(JdbcConnectionParams.SSL_TRUST_STORE_PASSWORD);

              if (sslTrustStore == null || sslTrustStore.isEmpty()) {
                transport = HiveAuthFactory.getSSLSocket(host, port, loginTimeout);
              } else {
                transport =
                    HiveAuthFactory.getSSLSocket(
                        host, port, loginTimeout, sslTrustStore, sslTrustStorePassword);
              }
            } else {
              // get non-SSL socket transport
              transport = HiveAuthFactory.getSocketTransport(host, port, loginTimeout);
            }
            // Overlay the SASL transport on top of the base socket transport (SSL or non-SSL)
            transport = PlainSaslHelper.getPlainTransport(userName, passwd, transport);
          }
        }
      } else {
        // Raw socket connection (non-sasl)
        transport = HiveAuthFactory.getSocketTransport(host, port, loginTimeout);
      }
    } catch (SaslException e) {
      throw new SQLException(
          "Could not create secure connection to " + jdbcUriString + ": " + e.getMessage(),
          " 08S01",
          e);
    }
    return transport;
  }
  public boolean authenticate(
      String[] mechs, final String realm, final String authzid, final String u, final String p)
      throws ProtocolException {

    synchronized (pr) { // authenticate method should be synchronized
      List<Response> v = new ArrayList<Response>();
      String tag = null;
      Response r = null;
      boolean done = false;
      if (logger.isLoggable(Level.FINE)) {
        logger.fine("SASL Mechanisms:");
        for (int i = 0; i < mechs.length; i++) logger.fine(" " + mechs[i]);
        logger.fine("");
      }

      SaslClient sc;
      CallbackHandler cbh =
          new CallbackHandler() {
            public void handle(Callback[] callbacks) {
              if (logger.isLoggable(Level.FINE))
                logger.fine("SASL callback length: " + callbacks.length);
              for (int i = 0; i < callbacks.length; i++) {
                if (logger.isLoggable(Level.FINE))
                  logger.fine("SASL callback " + i + ": " + callbacks[i]);
                if (callbacks[i] instanceof NameCallback) {
                  NameCallback ncb = (NameCallback) callbacks[i];
                  ncb.setName(u);
                } else if (callbacks[i] instanceof PasswordCallback) {
                  PasswordCallback pcb = (PasswordCallback) callbacks[i];
                  pcb.setPassword(p.toCharArray());
                } else if (callbacks[i] instanceof RealmCallback) {
                  RealmCallback rcb = (RealmCallback) callbacks[i];
                  rcb.setText(realm != null ? realm : rcb.getDefaultText());
                } else if (callbacks[i] instanceof RealmChoiceCallback) {
                  RealmChoiceCallback rcb = (RealmChoiceCallback) callbacks[i];
                  if (realm == null) rcb.setSelectedIndex(rcb.getDefaultChoice());
                  else {
                    // need to find specified realm in list
                    String[] choices = rcb.getChoices();
                    for (int k = 0; k < choices.length; k++) {
                      if (choices[k].equals(realm)) {
                        rcb.setSelectedIndex(k);
                        break;
                      }
                    }
                  }
                }
              }
            }
          };

      try {
        sc = Sasl.createSaslClient(mechs, authzid, name, host, (Map) props, cbh);
      } catch (SaslException sex) {
        logger.log(Level.FINE, "Failed to create SASL client", sex);
        throw new UnsupportedOperationException(sex.getMessage(), sex);
      }
      if (sc == null) {
        logger.fine("No SASL support");
        throw new UnsupportedOperationException("No SASL support");
      }
      if (logger.isLoggable(Level.FINE)) logger.fine("SASL client " + sc.getMechanismName());

      try {
        Argument args = new Argument();
        args.writeAtom(sc.getMechanismName());
        if (pr.hasCapability("SASL-IR") && sc.hasInitialResponse()) {
          String irs;
          byte[] ba = sc.evaluateChallenge(new byte[0]);
          if (ba.length > 0) {
            ba = BASE64EncoderStream.encode(ba);
            irs = ASCIIUtility.toString(ba, 0, ba.length);
          } else irs = "=";
          args.writeAtom(irs);
        }
        tag = pr.writeCommand("AUTHENTICATE", args);
      } catch (Exception ex) {
        logger.log(Level.FINE, "SASL AUTHENTICATE Exception", ex);
        return false;
      }

      OutputStream os = pr.getIMAPOutputStream(); // stream to IMAP server

      /*
       * Wrap a BASE64Encoder around a ByteArrayOutputstream
       * to craft b64 encoded username and password strings
       *
       * Note that the encoded bytes should be sent "as-is" to the
       * server, *not* as literals or quoted-strings.
       *
       * Also note that unlike the B64 definition in MIME, CRLFs
       * should *not* be inserted during the encoding process. So, I
       * use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the bytesPerLine,
       * which should be sufficiently large !
       */

      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      byte[] CRLF = {(byte) '\r', (byte) '\n'};

      // Hack for Novell GroupWise XGWTRUSTEDAPP authentication mechanism
      // http://www.novell.com/developer/documentation/gwimap/?
      //   page=/developer/documentation/gwimap/gwimpenu/data/al7te9j.html
      boolean isXGWTRUSTEDAPP =
          sc.getMechanismName().equals("XGWTRUSTEDAPP")
              && PropUtil.getBooleanProperty(
                  props, "mail." + name + ".sasl.xgwtrustedapphack.enable", true);
      while (!done) { // loop till we are done
        try {
          r = pr.readResponse();
          if (r.isContinuation()) {
            byte[] ba = null;
            if (!sc.isComplete()) {
              ba = r.readByteArray().getNewBytes();
              if (ba.length > 0) ba = BASE64DecoderStream.decode(ba);
              if (logger.isLoggable(Level.FINE))
                logger.fine("SASL challenge: " + ASCIIUtility.toString(ba, 0, ba.length) + " :");
              ba = sc.evaluateChallenge(ba);
            }
            if (ba == null) {
              logger.fine("SASL no response");
              os.write(CRLF); // write out empty line
              os.flush(); // flush the stream
              bos.reset(); // reset buffer
            } else {
              if (logger.isLoggable(Level.FINE))
                logger.fine("SASL response: " + ASCIIUtility.toString(ba, 0, ba.length) + " :");
              ba = BASE64EncoderStream.encode(ba);
              if (isXGWTRUSTEDAPP) bos.write(ASCIIUtility.getBytes("XGWTRUSTEDAPP "));
              bos.write(ba);

              bos.write(CRLF); // CRLF termination
              os.write(bos.toByteArray()); // write out line
              os.flush(); // flush the stream
              bos.reset(); // reset buffer
            }
          } else if (r.isTagged() && r.getTag().equals(tag))
            // Ah, our tagged response
            done = true;
          else if (r.isBYE()) // outta here
          done = true;
          else // hmm .. unsolicited response here ?!
          v.add(r);
        } catch (Exception ioex) {
          logger.log(Level.FINE, "SASL Exception", ioex);
          // convert this into a BYE response
          r = Response.byeResponse(ioex);
          done = true;
          // XXX - ultimately return true???
        }
      }

      if (sc.isComplete() /*&& res.status == SUCCESS*/) {
        String qop = (String) sc.getNegotiatedProperty(Sasl.QOP);
        if (qop != null
            && (qop.equalsIgnoreCase("auth-int") || qop.equalsIgnoreCase("auth-conf"))) {
          // XXX - NOT SUPPORTED!!!
          logger.fine("SASL Mechanism requires integrity or confidentiality");
          return false;
        }
      }

      /* Dispatch untagged responses.
       * NOTE: in our current upper level IMAP classes, we add the
       * responseHandler to the Protocol object only *after* the
       * connection has been authenticated. So, for now, the below
       * code really ends up being just a no-op.
       */
      Response[] responses = v.toArray(new Response[v.size()]);
      pr.notifyResponseHandlers(responses);

      // Handle the final OK, NO, BAD or BYE response
      pr.handleResult(r);
      pr.setCapabilities(r);

      /*
       * If we're using the Novell Groupwise XGWTRUSTEDAPP mechanism
       * to run as a specified authorization ID, we have to issue a
       * LOGIN command to select the user we want to operate as.
       */
      if (isXGWTRUSTEDAPP && authzid != null) {
        Argument args = new Argument();
        args.writeString(authzid);

        responses = pr.command("LOGIN", args);

        // dispatch untagged responses
        pr.notifyResponseHandlers(responses);

        // Handle result of this command
        pr.handleResult(responses[responses.length - 1]);
        // If the response includes a CAPABILITY response code, process it
        pr.setCapabilities(responses[responses.length - 1]);
      }
      return true;
    }
  }
    public void handleEvent(final ConnectedMessageChannel channel) {
      final Pooled<ByteBuffer> pooledReceiveBuffer = connection.allocate();
      try {
        final ByteBuffer receiveBuffer = pooledReceiveBuffer.getResource();
        synchronized (connection.getLock()) {
          int res;
          try {
            res = channel.receive(receiveBuffer);
          } catch (IOException e) {
            connection.handleException(e);
            return;
          }
          if (res == -1) {
            connection.handleException(client.abruptClose(connection));
            return;
          }
          if (res == 0) {
            return;
          }
        }
        receiveBuffer.flip();
        boolean starttls = false;
        final Set<String> serverSaslMechs = new LinkedHashSet<String>();
        final byte msgType = receiveBuffer.get();
        switch (msgType) {
          case Protocol.CONNECTION_ALIVE:
            {
              client.trace("Client received connection alive");
              connection.sendAliveResponse();
              return;
            }
          case Protocol.CONNECTION_ALIVE_ACK:
            {
              client.trace("Client received connection alive ack");
              return;
            }
          case Protocol.CONNECTION_CLOSE:
            {
              client.trace("Client received connection close request");
              connection.handlePreAuthCloseRequest();
              return;
            }
          case Protocol.CAPABILITIES:
            {
              client.trace("Client received capabilities response");
              String remoteEndpointName = null;
              int version = Protocol.VERSION;
              int behavior = Protocol.BH_FAULTY_MSG_SIZE;
              boolean useDefaultChannels = true;
              int channelsIn = 40;
              int channelsOut = 40;
              while (receiveBuffer.hasRemaining()) {
                final byte type = receiveBuffer.get();
                final int len = receiveBuffer.get() & 0xff;
                final ByteBuffer data = Buffers.slice(receiveBuffer, len);
                switch (type) {
                  case Protocol.CAP_VERSION:
                    {
                      version = data.get() & 0xff;
                      client.tracef(
                          "Client received capability: version %d",
                          Integer.valueOf(version & 0xff));
                      break;
                    }
                  case Protocol.CAP_SASL_MECH:
                    {
                      final String mechName = Buffers.getModifiedUtf8(data);
                      client.tracef("Client received capability: SASL mechanism %s", mechName);
                      if (!failedMechs.containsKey(mechName)
                          && !disallowedMechs.contains(mechName)
                          && (allowedMechs == null || allowedMechs.contains(mechName))) {
                        client.tracef("SASL mechanism %s added to allowed set", mechName);
                        serverSaslMechs.add(mechName);
                      }
                      break;
                    }
                  case Protocol.CAP_STARTTLS:
                    {
                      client.trace("Client received capability: STARTTLS");
                      starttls = true;
                      break;
                    }
                  case Protocol.CAP_ENDPOINT_NAME:
                    {
                      remoteEndpointName = Buffers.getModifiedUtf8(data);
                      client.tracef(
                          "Client received capability: remote endpoint name \"%s\"",
                          remoteEndpointName);
                      break;
                    }
                  case Protocol.CAP_MESSAGE_CLOSE:
                    {
                      behavior |= Protocol.BH_MESSAGE_CLOSE;
                      // remote side must be >= 3.2.11.GA
                      // but, we'll assume it's >= 3.2.14.GA because no AS or EAP release included
                      // 3.2.8.SP1 < x < 3.2.14.GA
                      behavior &= ~Protocol.BH_FAULTY_MSG_SIZE;
                      client.tracef("Client received capability: message close protocol supported");
                      break;
                    }
                  case Protocol.CAP_VERSION_STRING:
                    {
                      // remote side must be >= 3.2.16.GA
                      behavior &= ~Protocol.BH_FAULTY_MSG_SIZE;
                      final String remoteVersionString = Buffers.getModifiedUtf8(data);
                      client.tracef(
                          "Client received capability: remote version is \"%s\"",
                          remoteVersionString);
                      break;
                    }
                  case Protocol.CAP_CHANNELS_IN:
                    {
                      useDefaultChannels = false;
                      // their channels in is our channels out
                      channelsOut = ProtocolUtils.readIntData(data, len);
                      client.tracef(
                          "Client received capability: remote channels in is \"%d\"", channelsOut);
                      break;
                    }
                  case Protocol.CAP_CHANNELS_OUT:
                    {
                      useDefaultChannels = false;
                      // their channels out is our channels in
                      channelsIn = ProtocolUtils.readIntData(data, len);
                      client.tracef(
                          "Client received capability: remote channels out is \"%d\"", channelsIn);
                      break;
                    }
                  default:
                    {
                      client.tracef(
                          "Client received unknown capability %02x", Integer.valueOf(type & 0xff));
                      // unknown, skip it for forward compatibility.
                      break;
                    }
                }
              }
              if (useDefaultChannels) {
                channelsIn = 40;
                channelsOut = 40;
              }
              if (starttls) {
                // only initiate starttls if not forbidden by config
                if (optionMap.get(Options.SSL_STARTTLS, true)) {
                  // Prepare the request message body
                  final Pooled<ByteBuffer> pooledSendBuffer = connection.allocate();
                  boolean ok = false;
                  try {
                    final ByteBuffer sendBuffer = pooledSendBuffer.getResource();
                    sendBuffer.put(Protocol.STARTTLS);
                    sendBuffer.flip();
                    connection.setReadListener(new StartTls(remoteServerName), true);
                    connection.send(pooledSendBuffer);
                    ok = true;
                    // all set
                    return;
                  } finally {
                    if (!ok) pooledSendBuffer.free();
                  }
                }
              }

              if (serverSaslMechs.isEmpty()) {
                if (failedMechs.isEmpty()) {
                  connection.handleException(
                      new SaslException(
                          "Authentication failed: the server presented no authentication mechanisms"));
                } else {
                  // At this point we have been attempting to use mechanisms as they have been
                  // presented to us but we have now exhausted the list.
                  connection.handleException(allMechanismsFailed());
                }
                return;
              }

              // OK now send our authentication request
              final AuthenticationContextConfigurationClient configurationClient =
                  AUTH_CONFIGURATION_CLIENT;
              AuthenticationConfiguration configuration =
                  configurationClient.getAuthenticationConfiguration(uri, authenticationContext);
              final SaslClient saslClient;
              try {
                saslClient =
                    configurationClient.createSaslClient(
                        uri, configuration, saslClientFactory, serverSaslMechs);
              } catch (SaslException e) {
                // apparently no more mechanisms can succeed
                connection.handleException(e);
                return;
              }
              if (saslClient == null) {
                connection.handleException(allMechanismsFailed());
                return;
              }
              final String mechanismName = saslClient.getMechanismName();
              client.tracef("Client initiating authentication using mechanism %s", mechanismName);

              connection.getChannel().suspendReads();
              final int negotiatedVersion = version;
              final SaslClient usedSaslClient = saslClient;
              final Authentication authentication =
                  new Authentication(
                      usedSaslClient,
                      remoteServerName,
                      remoteEndpointName,
                      behavior,
                      channelsIn,
                      channelsOut);
              connection
                  .getExecutor()
                  .execute(
                      () -> {
                        final byte[] response;
                        try {
                          response =
                              usedSaslClient.hasInitialResponse()
                                  ? usedSaslClient.evaluateChallenge(EMPTY_BYTES)
                                  : null;
                        } catch (SaslException e) {
                          client.tracef("Client authentication failed: %s", e);
                          saslDispose(usedSaslClient);
                          failedMechs.put(mechanismName, e.toString());
                          sendCapRequest(remoteServerName);
                          return;
                        }
                        // Prepare the request message body
                        final Pooled<ByteBuffer> pooledSendBuffer = connection.allocate();
                        boolean ok = false;
                        try {
                          final ByteBuffer sendBuffer = pooledSendBuffer.getResource();
                          sendBuffer.put(Protocol.AUTH_REQUEST);
                          if (negotiatedVersion < 1) {
                            sendBuffer.put(mechanismName.getBytes(Protocol.UTF_8));
                          } else {
                            ProtocolUtils.writeString(sendBuffer, mechanismName);
                            if (response != null) {
                              sendBuffer.put(response);
                            }
                          }

                          sendBuffer.flip();
                          connection.send(pooledSendBuffer);
                          ok = true;
                          connection.setReadListener(authentication, true);
                          return;
                        } finally {
                          if (!ok) pooledSendBuffer.free();
                        }
                      });
              return;
            }
          default:
            {
              client.unknownProtocolId(msgType);
              connection.handleException(client.invalidMessage(connection));
              return;
            }
        }
      } catch (BufferUnderflowException | BufferOverflowException e) {
        connection.handleException(client.invalidMessage(connection));
        return;
      } finally {
        pooledReceiveBuffer.free();
      }
    }