You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by pl...@apache.org on 2017/11/28 03:04:11 UTC
[10/15] directory-kerby git commit: Change the Maven groupId in HAS
folder to org.apache.kerby.
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/KeyStoresFactory.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/KeyStoresFactory.java b/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/KeyStoresFactory.java
new file mode 100644
index 0000000..a7ae53a
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/KeyStoresFactory.java
@@ -0,0 +1,254 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.kerby.has.common.ssl;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.kerby.has.common.HasConfig;
+import org.apache.kerby.has.common.util.StringUtils;
+import org.apache.kerby.kerberos.kerb.client.KrbConfig;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.text.MessageFormat;
+
+/**
+ * Borrow the class from Apache Hadoop
+ */
+
+/**
+ * Interface that gives access to {@link KeyManager} and {@link TrustManager}
+ * implementations.
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Evolving
+public class KeyStoresFactory extends KrbConfig {
+
+ private static final Log LOG =
+ LogFactory.getLog(KeyStoresFactory.class);
+
+ public static final String SSL_KEYSTORE_LOCATION_TPL_KEY =
+ "ssl.{0}.keystore.location";
+ public static final String SSL_KEYSTORE_PASSWORD_TPL_KEY =
+ "ssl.{0}.keystore.password";
+ public static final String SSL_KEYSTORE_KEYPASSWORD_TPL_KEY =
+ "ssl.{0}.keystore.keypassword";
+ public static final String SSL_KEYSTORE_TYPE_TPL_KEY =
+ "ssl.{0}.keystore.type";
+
+ public static final String SSL_TRUSTSTORE_RELOAD_INTERVAL_TPL_KEY =
+ "ssl.{0}.truststore.reload.interval";
+ public static final String SSL_TRUSTSTORE_LOCATION_TPL_KEY =
+ "ssl.{0}.truststore.location";
+ public static final String SSL_TRUSTSTORE_PASSWORD_TPL_KEY =
+ "ssl.{0}.truststore.password";
+ public static final String SSL_TRUSTSTORE_TYPE_TPL_KEY =
+ "ssl.{0}.truststore.type";
+
+ /**
+ * Default format of the keystore files.
+ */
+ public static final String DEFAULT_KEYSTORE_TYPE = "jks";
+
+ /**
+ * Reload interval in milliseconds.
+ */
+ public static final long DEFAULT_SSL_TRUSTSTORE_RELOAD_INTERVAL = 10000;
+
+ private HasConfig conf;
+ private KeyManager[] keyManagers;
+ private TrustManager[] trustManagers;
+ private ReloadingX509TrustManager trustManager;
+
+ /**
+ * Sets the configuration for the factory.
+ *
+ * @param conf the configuration for the factory.
+ */
+ public void setConf(HasConfig conf) {
+ this.conf = conf;
+ }
+
+ /**
+ * Returns the configuration of the factory.
+ *
+ * @return the configuration of the factory.
+ */
+ public HasConfig getConf() {
+ return conf;
+ }
+
+
+ /**
+ * Initializes the keystores of the factory.
+ *
+ * @param mode if the keystores are to be used in client or server mode.
+ * @throws IOException thrown if the keystores could not be initialized due
+ * to an IO error.
+ * @throws GeneralSecurityException thrown if the keystores could not be
+ * initialized due to an security error.
+ */
+ public void init(SSLFactory.Mode mode) throws IOException, GeneralSecurityException {
+ boolean requireClientCert =
+ conf.getBoolean(SSLFactory.SSL_REQUIRE_CLIENT_CERT_KEY,
+ SSLFactory.DEFAULT_SSL_REQUIRE_CLIENT_CERT);
+
+ // certificate store
+ String keystoreType =
+ conf.getString(resolvePropertyName(mode, SSL_KEYSTORE_TYPE_TPL_KEY),
+ DEFAULT_KEYSTORE_TYPE);
+ KeyStore keystore = KeyStore.getInstance(keystoreType);
+ String keystoreKeyPassword = null;
+ if (requireClientCert || mode == SSLFactory.Mode.SERVER) {
+ String locationProperty =
+ resolvePropertyName(mode, SSL_KEYSTORE_LOCATION_TPL_KEY);
+ String keystoreLocation = conf.getString(locationProperty, "");
+ if (keystoreLocation.isEmpty()) {
+ throw new GeneralSecurityException("The property '" + locationProperty
+ + "' has not been set in the ssl configuration file.");
+ }
+ String passwordProperty =
+ resolvePropertyName(mode, SSL_KEYSTORE_PASSWORD_TPL_KEY);
+ String keystorePassword = getPassword(conf, passwordProperty, "");
+ if (keystorePassword.isEmpty()) {
+ throw new GeneralSecurityException("The property '" + passwordProperty
+ + "' has not been set in the ssl configuration file.");
+ }
+ String keyPasswordProperty =
+ resolvePropertyName(mode, SSL_KEYSTORE_KEYPASSWORD_TPL_KEY);
+ // Key password defaults to the same value as store password for
+ // compatibility with legacy configurations that did not use a separate
+ // configuration property for key password.
+ keystoreKeyPassword = getPassword(
+ conf, keyPasswordProperty, keystorePassword);
+ LOG.debug(mode.toString() + " KeyStore: " + keystoreLocation);
+
+ InputStream is = new FileInputStream(keystoreLocation);
+ try {
+ keystore.load(is, keystorePassword.toCharArray());
+ } finally {
+ is.close();
+ }
+ LOG.debug(mode.toString() + " Loaded KeyStore: " + keystoreLocation);
+ } else {
+ keystore.load(null, null);
+ }
+ KeyManagerFactory keyMgrFactory = KeyManagerFactory
+ .getInstance(SSLFactory.SSLCERTIFICATE);
+
+ keyMgrFactory.init(keystore, (keystoreKeyPassword != null)
+ ? keystoreKeyPassword.toCharArray() : null);
+ keyManagers = keyMgrFactory.getKeyManagers();
+
+ //trust store
+ String truststoreType =
+ conf.getString(resolvePropertyName(mode, SSL_TRUSTSTORE_TYPE_TPL_KEY),
+ DEFAULT_KEYSTORE_TYPE);
+
+ String locationProperty =
+ resolvePropertyName(mode, SSL_TRUSTSTORE_LOCATION_TPL_KEY);
+ String truststoreLocation = conf.getString(locationProperty, "");
+ if (!truststoreLocation.isEmpty()) {
+ String passwordProperty = resolvePropertyName(mode,
+ SSL_TRUSTSTORE_PASSWORD_TPL_KEY);
+ String truststorePassword = getPassword(conf, passwordProperty, "");
+ if (truststorePassword.isEmpty()) {
+ throw new GeneralSecurityException("The property '" + passwordProperty
+ + "' has not been set in the ssl configuration file.");
+ }
+ long truststoreReloadInterval =
+ conf.getLong(resolvePropertyName(mode, SSL_TRUSTSTORE_RELOAD_INTERVAL_TPL_KEY),
+ DEFAULT_SSL_TRUSTSTORE_RELOAD_INTERVAL);
+
+ LOG.debug(mode.toString() + " TrustStore: " + truststoreLocation);
+
+ trustManager = new ReloadingX509TrustManager(truststoreType,
+ truststoreLocation,
+ truststorePassword,
+ truststoreReloadInterval);
+ trustManager.init();
+ LOG.debug(mode.toString() + " Loaded TrustStore: " + truststoreLocation);
+ trustManagers = new TrustManager[]{trustManager};
+ } else {
+ LOG.debug("The property '" + locationProperty + "' has not been set, "
+ + "no TrustStore will be loaded");
+ trustManagers = null;
+ }
+ }
+
+ String getPassword(HasConfig conf, String alias, String defaultPass) {
+ String password = defaultPass;
+ password = conf.getString(alias);
+ return password;
+ }
+
+ /**
+ * Releases any resources being used.
+ */
+ public void destroy() {
+ if (trustManager != null) {
+ trustManager.destroy();
+ trustManager = null;
+ keyManagers = null;
+ trustManagers = null;
+ }
+ }
+
+ /**
+ * Returns the keymanagers for owned certificates.
+ *
+ * @return the keymanagers for owned certificates.
+ */
+ public KeyManager[] getKeyManagers() {
+ return keyManagers;
+ }
+
+ /**
+ * Returns the trustmanagers for trusted certificates.
+ *
+ * @return the trustmanagers for trusted certificates.
+ */
+ public TrustManager[] getTrustManagers() {
+ return trustManagers;
+ }
+
+ /**
+ * Resolves a property name to its client/server version if applicable.
+ * <p/>
+ * NOTE: This method is public for testing purposes.
+ *
+ * @param mode client/server mode.
+ * @param template property name template.
+ * @return the resolved property name.
+ */
+ @VisibleForTesting
+ public static String resolvePropertyName(SSLFactory.Mode mode,
+ String template) {
+ return MessageFormat.format(
+ template, StringUtils.toLowerCase(mode.toString()));
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/ReloadingX509TrustManager.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/ReloadingX509TrustManager.java b/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/ReloadingX509TrustManager.java
new file mode 100644
index 0000000..29ed038
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/ReloadingX509TrustManager.java
@@ -0,0 +1,208 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kerby.has.common.ssl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Borrow the class from Apache Hadoop
+ */
+
+/**
+ * A {@link TrustManager} implementation that reloads its configuration when
+ * the truststore file on disk changes.
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Evolving
+public final class ReloadingX509TrustManager
+ implements X509TrustManager, Runnable {
+
+ private static final Log LOG =
+ LogFactory.getLog(ReloadingX509TrustManager.class);
+
+ private String type;
+ private File file;
+ private String password;
+ private long lastLoaded;
+ private long reloadInterval;
+ private AtomicReference<X509TrustManager> trustManagerRef;
+
+ private volatile boolean running;
+ private Thread reloader;
+
+ /**
+ * Creates a reloadable trustmanager. The trustmanager reloads itself
+ * if the underlying trustore file has changed.
+ *
+ * @param type type of truststore file, typically 'jks'.
+ * @param location local path to the truststore file.
+ * @param password password of the truststore file.
+ * @param reloadInterval interval to check if the truststore file has
+ * changed, in milliseconds.
+ * @throws IOException thrown if the truststore could not be initialized due
+ * to an IO error.
+ * @throws GeneralSecurityException thrown if the truststore could not be
+ * initialized due to a security error.
+ */
+ public ReloadingX509TrustManager(String type, String location,
+ String password, long reloadInterval)
+ throws IOException, GeneralSecurityException {
+ this.type = type;
+ file = new File(location);
+ this.password = password;
+ trustManagerRef = new AtomicReference<X509TrustManager>();
+ trustManagerRef.set(loadTrustManager());
+ this.reloadInterval = reloadInterval;
+ }
+
+ /**
+ * Starts the reloader thread.
+ */
+ public void init() {
+ reloader = new Thread(this, "Truststore reloader thread");
+ reloader.setDaemon(true);
+ running = true;
+ reloader.start();
+ }
+
+ /**
+ * Stops the reloader thread.
+ */
+ public void destroy() {
+ running = false;
+ reloader.interrupt();
+ }
+
+ /**
+ * Returns the reload check interval.
+ *
+ * @return the reload check interval, in milliseconds.
+ */
+ public long getReloadInterval() {
+ return reloadInterval;
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ X509TrustManager tm = trustManagerRef.get();
+ if (tm != null) {
+ tm.checkClientTrusted(chain, authType);
+ } else {
+ throw new CertificateException("Unknown client chain certificate: "
+ + chain[0].toString());
+ }
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ X509TrustManager tm = trustManagerRef.get();
+ if (tm != null) {
+ tm.checkServerTrusted(chain, authType);
+ } else {
+ throw new CertificateException("Unknown server chain certificate: "
+ + chain[0].toString());
+ }
+ }
+
+ private static final X509Certificate[] EMPTY = new X509Certificate[0];
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ X509Certificate[] issuers = EMPTY;
+ X509TrustManager tm = trustManagerRef.get();
+ if (tm != null) {
+ issuers = tm.getAcceptedIssuers();
+ }
+ return issuers;
+ }
+
+ boolean needsReload() {
+ boolean reload = true;
+ if (file.exists()) {
+ if (file.lastModified() == lastLoaded) {
+ reload = false;
+ }
+ } else {
+ lastLoaded = 0;
+ }
+ return reload;
+ }
+
+ X509TrustManager loadTrustManager()
+ throws IOException, GeneralSecurityException {
+ X509TrustManager trustManager = null;
+ KeyStore ks = KeyStore.getInstance(type);
+ lastLoaded = file.lastModified();
+ FileInputStream in = new FileInputStream(file);
+ try {
+ ks.load(in, password.toCharArray());
+ LOG.debug("Loaded truststore '" + file + "'");
+ } finally {
+ in.close();
+ }
+
+ TrustManagerFactory trustManagerFactory =
+ TrustManagerFactory.getInstance(SSLFactory.SSLCERTIFICATE);
+ trustManagerFactory.init(ks);
+ TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+ for (TrustManager trustManager1 : trustManagers) {
+ if (trustManager1 instanceof X509TrustManager) {
+ trustManager = (X509TrustManager) trustManager1;
+ break;
+ }
+ }
+ return trustManager;
+ }
+
+ @Override
+ public void run() {
+ while (running) {
+ try {
+ Thread.sleep(reloadInterval);
+ } catch (InterruptedException e) {
+ //NOP
+ }
+ if (running && needsReload()) {
+ try {
+ trustManagerRef.set(loadTrustManager());
+ } catch (Exception ex) {
+ LOG.warn("Could not load truststore (keep using existing one) : "
+ + ex.toString(), ex);
+ }
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/SSLFactory.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/SSLFactory.java b/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/SSLFactory.java
new file mode 100644
index 0000000..c16d0f4
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/SSLFactory.java
@@ -0,0 +1,290 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.kerby.has.common.ssl;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.common.util.ConnectionConfigurator;
+import org.apache.kerby.has.common.HasConfig;
+import org.apache.kerby.has.common.util.StringUtils;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocketFactory;
+import java.io.File;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.security.GeneralSecurityException;
+
+import static org.apache.kerby.has.common.util.PlatformName.IBM_JAVA;
+
+/**
+ * Borrow the class from Apache Hadoop
+ */
+
+/**
+ * Factory that creates SSLEngine and SSLSocketFactory instances using
+ * Hadoop configuration information.
+ * <p/>
+ * which reloads public keys if the truststore file changes.
+ * <p/>
+ * This factory is used to configure HTTPS in Hadoop HTTP based endpoints, both
+ * client and server.
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Evolving
+public class SSLFactory implements ConnectionConfigurator {
+
+ @InterfaceAudience.Private
+ public enum Mode {
+ CLIENT, SERVER
+ }
+
+ public static final String SSL_REQUIRE_CLIENT_CERT_KEY =
+ "hadoop.ssl.require.client.CERT";
+ public static final String SSL_HOSTNAME_VERIFIER_KEY =
+ "hadoop.ssl.hostname.verifier";
+ public static final String SSL_CLIENT_CONF_KEY =
+ "hadoop.ssl.client.conf";
+ public static final String SSL_SERVER_CONF_KEY =
+ "hadoop.ssl.server.conf";
+ public static final String SSLCERTIFICATE = IBM_JAVA ? "ibmX509" : "SunX509";
+
+ public static final boolean DEFAULT_SSL_REQUIRE_CLIENT_CERT = false;
+
+ public static final String KEYSTORES_FACTORY_CLASS_KEY =
+ "hadoop.ssl.keystores.factory.class";
+
+ public static final String SSL_ENABLED_PROTOCOLS =
+ "hadoop.ssl.enabled.protocols";
+ public static final String DEFAULT_SSL_ENABLED_PROTOCOLS = "TLSv1";
+
+ private HasConfig conf;
+ private Mode mode;
+ private boolean requireClientCert;
+ private SSLContext context;
+ private HostnameVerifier hostnameVerifier;
+ private KeyStoresFactory keystoresFactory;
+
+ private String[] enabledProtocols = null;
+
+ /**
+ * Creates an SSLFactory.
+ *
+ * @param mode SSLFactory mode, client or server.
+ * @param conf Hadoop configuration from where the SSLFactory configuration
+ * will be read.
+ */
+ public SSLFactory(Mode mode, HasConfig conf) throws HasException {
+ this.conf = conf;
+ if (mode == null) {
+ throw new IllegalArgumentException("mode cannot be NULL");
+ }
+ this.mode = mode;
+ requireClientCert = conf.getBoolean(SSL_REQUIRE_CLIENT_CERT_KEY,
+ DEFAULT_SSL_REQUIRE_CLIENT_CERT);
+ HasConfig sslConf = readSSLConfiguration(mode);
+
+ keystoresFactory = new KeyStoresFactory();
+ keystoresFactory.setConf(sslConf);
+
+ enabledProtocols = new String[] {DEFAULT_SSL_ENABLED_PROTOCOLS};
+ }
+
+ private HasConfig readSSLConfiguration(Mode mode) throws HasException {
+ HasConfig sslConf = new HasConfig();
+ sslConf.setBoolean(SSL_REQUIRE_CLIENT_CERT_KEY, requireClientCert);
+ String sslConfResource;
+ if (mode == Mode.CLIENT) {
+ sslConfResource = conf.getString(SSLFactory.SSL_CLIENT_CONF_KEY);
+ } else {
+ sslConfResource = conf.getString(SSLFactory.SSL_CLIENT_CONF_KEY);
+ }
+ try {
+ sslConf.addIniConfig(new File(sslConfResource));
+ } catch (IOException e) {
+ throw new HasException(e);
+ }
+ return sslConf;
+ }
+
+ /**
+ * Initializes the factory.
+ *
+ * @throws GeneralSecurityException thrown if an SSL initialization error
+ * happened.
+ * @throws IOException thrown if an IO error happened while reading the SSL
+ * configuration.
+ */
+ public void init() throws GeneralSecurityException, IOException {
+ keystoresFactory.init(mode);
+ context = SSLContext.getInstance("TLS");
+ context.init(keystoresFactory.getKeyManagers(),
+ keystoresFactory.getTrustManagers(), null);
+ context.getDefaultSSLParameters().setProtocols(enabledProtocols);
+ hostnameVerifier = getHostnameVerifier(conf);
+ }
+
+ private HostnameVerifier getHostnameVerifier(HasConfig conf)
+ throws GeneralSecurityException, IOException {
+ return getHostnameVerifier(StringUtils.toUpperCase(
+ conf.getString(SSL_HOSTNAME_VERIFIER_KEY, "DEFAULT").trim()));
+ }
+
+ public static HostnameVerifier getHostnameVerifier(String verifier)
+ throws GeneralSecurityException, IOException {
+ HostnameVerifier hostnameVerifier;
+ if (verifier.equals("DEFAULT")) {
+ hostnameVerifier = SSLHostnameVerifier.DEFAULT;
+ } else if (verifier.equals("DEFAULT_AND_LOCALHOST")) {
+ hostnameVerifier = SSLHostnameVerifier.DEFAULT_AND_LOCALHOST;
+ } else if (verifier.equals("STRICT")) {
+ hostnameVerifier = SSLHostnameVerifier.STRICT;
+ } else if (verifier.equals("STRICT_IE6")) {
+ hostnameVerifier = SSLHostnameVerifier.STRICT_IE6;
+ } else if (verifier.equals("ALLOW_ALL")) {
+ hostnameVerifier = SSLHostnameVerifier.ALLOW_ALL;
+ } else {
+ throw new GeneralSecurityException("Invalid hostname verifier: "
+ + verifier);
+ }
+ return hostnameVerifier;
+ }
+
+ /**
+ * Releases any resources being used.
+ */
+ public void destroy() {
+ keystoresFactory.destroy();
+ }
+ /**
+ * Returns the SSLFactory KeyStoresFactory instance.
+ *
+ * @return the SSLFactory KeyStoresFactory instance.
+ */
+ public KeyStoresFactory getKeystoresFactory() {
+ return keystoresFactory;
+ }
+
+ /**
+ * Returns a configured SSLEngine.
+ *
+ * @return the configured SSLEngine.
+ * @throws GeneralSecurityException thrown if the SSL engine could not
+ * be initialized.
+ * @throws IOException thrown if and IO error occurred while loading
+ * the server keystore.
+ */
+ public SSLEngine createSSLEngine()
+ throws GeneralSecurityException, IOException {
+ SSLEngine sslEngine = context.createSSLEngine();
+ if (mode == Mode.CLIENT) {
+ sslEngine.setUseClientMode(true);
+ } else {
+ sslEngine.setUseClientMode(false);
+ sslEngine.setNeedClientAuth(requireClientCert);
+ }
+ sslEngine.setEnabledProtocols(enabledProtocols);
+ return sslEngine;
+ }
+
+ /**
+ * Returns a configured SSLServerSocketFactory.
+ *
+ * @return the configured SSLSocketFactory.
+ * @throws GeneralSecurityException thrown if the SSLSocketFactory could not
+ * be initialized.
+ * @throws IOException thrown if and IO error occurred while loading
+ * the server keystore.
+ */
+ public SSLServerSocketFactory createSSLServerSocketFactory()
+ throws GeneralSecurityException, IOException {
+ if (mode != Mode.SERVER) {
+ throw new IllegalStateException("Factory is in CLIENT mode");
+ }
+ return context.getServerSocketFactory();
+ }
+
+ /**
+ * Returns a configured SSLSocketFactory.
+ *
+ * @return the configured SSLSocketFactory.
+ * @throws GeneralSecurityException thrown if the SSLSocketFactory could not
+ * be initialized.
+ * @throws IOException thrown if and IO error occurred while loading
+ * the server keystore.
+ */
+ public SSLSocketFactory createSSLSocketFactory()
+ throws GeneralSecurityException, IOException {
+ if (mode != Mode.CLIENT) {
+ throw new IllegalStateException("Factory is in CLIENT mode");
+ }
+ return context.getSocketFactory();
+ }
+
+ /**
+ * Returns the hostname verifier it should be used in HttpsURLConnections.
+ *
+ * @return the hostname verifier.
+ */
+ public HostnameVerifier getHostnameVerifier() {
+ if (mode != Mode.CLIENT) {
+ throw new IllegalStateException("Factory is in CLIENT mode");
+ }
+ return hostnameVerifier;
+ }
+
+ /**
+ * Returns if client certificates are required or not.
+ *
+ * @return if client certificates are required or not.
+ */
+ public boolean isClientCertRequired() {
+ return requireClientCert;
+ }
+
+ /**
+ * If the given {@link HttpURLConnection} is an {@link HttpsURLConnection}
+ * configures the connection with the {@link SSLSocketFactory} and
+ * {@link HostnameVerifier} of this SSLFactory, otherwise does nothing.
+ *
+ * @param conn the {@link HttpURLConnection} instance to configure.
+ * @return the configured {@link HttpURLConnection} instance.
+ *
+ * @throws IOException if an IO error occurred.
+ */
+ @Override
+ public HttpURLConnection configure(HttpURLConnection conn)
+ throws IOException {
+ if (conn instanceof HttpsURLConnection) {
+ HttpsURLConnection sslConn = (HttpsURLConnection) conn;
+ try {
+ sslConn.setSSLSocketFactory(createSSLSocketFactory());
+ } catch (GeneralSecurityException ex) {
+ throw new IOException(ex);
+ }
+ sslConn.setHostnameVerifier(getHostnameVerifier());
+ conn = sslConn;
+ }
+ return conn;
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/SSLHostnameVerifier.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/SSLHostnameVerifier.java b/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/SSLHostnameVerifier.java
new file mode 100644
index 0000000..d3be435
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/kerby/has/common/ssl/SSLHostnameVerifier.java
@@ -0,0 +1,615 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.kerby.has.common.ssl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.kerby.has.common.util.StringUtils;
+
+/**
+ * Borrow the class from Apache Hadoop
+ */
+
+/**
+ ************************************************************************
+ * Copied from the not-yet-commons-ssl project at
+ * http://juliusdavies.ca/commons-ssl/
+ * This project is not yet in Apache, but it is Apache 2.0 licensed.
+ ************************************************************************
+ * Interface for checking if a hostname matches the names stored inside the
+ * server's X.509 certificate. Correctly implements
+ * javax.net.ssl.HostnameVerifier, but that interface is not recommended.
+ * Instead we added several check() methods that take SSLSocket,
+ * or X509Certificate, or ultimately (they all end up calling this one),
+ * String. (It's easier to supply JUnit with Strings instead of mock
+ * SSLSession objects!)
+ * </p><p>Our check() methods throw exceptions if the name is
+ * invalid, whereas javax.net.ssl.HostnameVerifier just returns true/false.
+ * <p/>
+ * We provide the HostnameVerifier.DEFAULT, HostnameVerifier.STRICT, and
+ * HostnameVerifier.ALLOW_ALL implementations. We also provide the more
+ * specialized HostnameVerifier.DEFAULT_AND_LOCALHOST, as well as
+ * HostnameVerifier.STRICT_IE6. But feel free to define your own
+ * implementations!
+ * <p/>
+ * Inspired by Sebastian Hauer's original StrictSSLProtocolSocketFactory in the
+ * HttpClient "contrib" repository.
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Evolving
+public interface SSLHostnameVerifier extends javax.net.ssl.HostnameVerifier {
+
+ @Override
+ boolean verify(String host, SSLSession session);
+
+ void check(String host, SSLSocket ssl) throws IOException;
+
+ void check(String host, X509Certificate cert) throws SSLException;
+
+ void check(String host, String[] cns, String[] subjectAlts)
+ throws SSLException;
+
+ void check(String[] hosts, SSLSocket ssl) throws IOException;
+
+ void check(String[] hosts, X509Certificate cert) throws SSLException;
+
+
+ /**
+ * Checks to see if the supplied hostname matches any of the supplied CNs
+ * or "DNS" Subject-Alts. Most implementations only look at the first CN,
+ * and ignore any additional CNs. Most implementations do look at all of
+ * the "DNS" Subject-Alts. The CNs or Subject-Alts may contain wildcards
+ * according to RFC 2818.
+ *
+ * @param cns CN fields, in order, as extracted from the X.509
+ * certificate.
+ * @param subjectAlts Subject-Alt fields of type 2 ("DNS"), as extracted
+ * from the X.509 certificate.
+ * @param hosts The array of hostnames to verify.
+ * @throws SSLException If verification failed.
+ */
+ void check(String[] hosts, String[] cns, String[] subjectAlts)
+ throws SSLException;
+
+
+ /**
+ * The DEFAULT HostnameVerifier works the same way as Curl and Firefox.
+ * <p/>
+ * The hostname must match either the first CN, or any of the subject-alts.
+ * A wildcard can occur in the CN, and in any of the subject-alts.
+ * <p/>
+ * The only difference between DEFAULT and STRICT is that a wildcard (such
+ * as "*.foo.com") with DEFAULT matches all subdomains, including
+ * "a.b.foo.com".
+ */
+ SSLHostnameVerifier DEFAULT =
+ new AbstractVerifier() {
+ @Override
+ public final void check(final String[] hosts, final String[] cns,
+ final String[] subjectAlts)
+ throws SSLException {
+ check(hosts, cns, subjectAlts, false, false);
+ }
+
+ @Override
+ public final String toString() {
+ return "DEFAULT";
+ }
+ };
+
+
+ /**
+ * The DEFAULT_AND_LOCALHOST HostnameVerifier works like the DEFAULT
+ * one with one additional relaxation: a host of "localhost",
+ * "localhost.localdomain", "127.0.0.1", "::1" will always pass, no matter
+ * what is in the server's certificate.
+ */
+ SSLHostnameVerifier DEFAULT_AND_LOCALHOST =
+ new AbstractVerifier() {
+ @Override
+ public final void check(final String[] hosts, final String[] cns,
+ final String[] subjectAlts)
+ throws SSLException {
+ if (isLocalhost(hosts[0])) {
+ return;
+ }
+ check(hosts, cns, subjectAlts, false, false);
+ }
+
+ @Override
+ public final String toString() {
+ return "DEFAULT_AND_LOCALHOST";
+ }
+ };
+
+ /**
+ * The STRICT HostnameVerifier works the same way as java.net.URL in Sun
+ * Java 1.4, Sun Java 5, Sun Java 6. It's also pretty close to IE6.
+ * This implementation appears to be compliant with RFC 2818 for dealing
+ * with wildcards.
+ * <p/>
+ * The hostname must match either the first CN, or any of the subject-alts.
+ * A wildcard can occur in the CN, and in any of the subject-alts. The
+ * one divergence from IE6 is how we only check the first CN. IE6 allows
+ * a match against any of the CNs present. We decided to follow in
+ * Sun Java 1.4's footsteps and only check the first CN.
+ * <p/>
+ * A wildcard such as "*.foo.com" matches only subdomains in the same
+ * level, for example "a.foo.com". It does not match deeper subdomains
+ * such as "a.b.foo.com".
+ */
+ SSLHostnameVerifier STRICT =
+ new AbstractVerifier() {
+ @Override
+ public final void check(final String[] host, final String[] cns,
+ final String[] subjectAlts)
+ throws SSLException {
+ check(host, cns, subjectAlts, false, true);
+ }
+
+ @Override
+ public final String toString() {
+ return "STRICT";
+ }
+ };
+
+ /**
+ * The STRICT_IE6 HostnameVerifier works just like the STRICT one with one
+ * minor variation: the hostname can match against any of the CN's in the
+ * server's certificate, not just the first one. This behaviour is
+ * identical to IE6's behaviour.
+ */
+ SSLHostnameVerifier STRICT_IE6 =
+ new AbstractVerifier() {
+ @Override
+ public final void check(final String[] host, final String[] cns,
+ final String[] subjectAlts)
+ throws SSLException {
+ check(host, cns, subjectAlts, true, true);
+ }
+
+ @Override
+ public final String toString() {
+ return "STRICT_IE6";
+ }
+ };
+
+ /**
+ * The ALLOW_ALL HostnameVerifier essentially turns hostname verification
+ * off. This implementation is a no-op, and never throws the SSLException.
+ */
+ SSLHostnameVerifier ALLOW_ALL =
+ new AbstractVerifier() {
+ @Override
+ public final void check(final String[] host, final String[] cns,
+ final String[] subjectAlts) {
+ // Allow everything - so never blowup.
+ }
+
+ @Override
+ public final String toString() {
+ return "ALLOW_ALL";
+ }
+ };
+
+ abstract class AbstractVerifier implements SSLHostnameVerifier {
+
+ /**
+ * This contains a list of 2nd-level domains that aren't allowed to
+ * have wildcards when combined with country-codes.
+ * For example: [*.co.uk].
+ * <p/>
+ * The [*.co.uk] problem is an interesting one. Should we just hope
+ * that CA's would never foolishly allow such a certificate to happen?
+ * Looks like we're the only implementation guarding against this.
+ * Firefox, Curl, Sun Java 1.4, 5, 6 don't bother with this check.
+ */
+ private static final String[] BAD_COUNTRY_2LDS =
+ {"ac", "co", "com", "ed", "edu", "go", "gouv", "gov", "info",
+ "lg", "ne", "net", "or", "org"};
+
+ private static final String[] LOCALHOSTS = {"::1", "127.0.0.1",
+ "localhost",
+ "localhost.localdomain"};
+
+
+ static {
+ // Just in case developer forgot to manually sort the array. :-)
+ Arrays.sort(BAD_COUNTRY_2LDS);
+ Arrays.sort(LOCALHOSTS);
+ }
+
+ protected AbstractVerifier() {
+ }
+
+ /**
+ * The javax.net.ssl.HostnameVerifier contract.
+ *
+ * @param host 'hostname' we used to create our socket
+ * @param session SSLSession with the remote server
+ * @return true if the host matched the one in the certificate.
+ */
+ @Override
+ public boolean verify(String host, SSLSession session) {
+ try {
+ Certificate[] certs = session.getPeerCertificates();
+ X509Certificate x509 = (X509Certificate) certs[0];
+ check(new String[]{host}, x509);
+ return true;
+ } catch (SSLException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public void check(String host, SSLSocket ssl) throws IOException {
+ check(new String[]{host}, ssl);
+ }
+
+ @Override
+ public void check(String host, X509Certificate cert)
+ throws SSLException {
+ check(new String[]{host}, cert);
+ }
+
+ @Override
+ public void check(String host, String[] cns, String[] subjectAlts)
+ throws SSLException {
+ check(new String[]{host}, cns, subjectAlts);
+ }
+
+ @Override
+ public void check(String[] host, SSLSocket ssl)
+ throws IOException {
+ if (host == null) {
+ throw new NullPointerException("host to verify is null");
+ }
+
+ SSLSession session = ssl.getSession();
+ if (session == null) {
+ // In our experience this only happens under IBM 1.4.x when
+ // spurious (unrelated) certificates show up in the server'
+ // chain. Hopefully this will unearth the real problem:
+ InputStream in = ssl.getInputStream();
+ in.available();
+ /*
+ If you're looking at the 2 lines of code above because
+ you're running into a problem, you probably have two
+ options:
+
+ #1. Clean up the certificate chain that your server
+ is presenting (e.g. edit "/etc/apache2/server.crt"
+ or wherever it is your server's certificate chain
+ is defined).
+
+ OR
+
+ #2. Upgrade to an IBM 1.5.x or greater JVM, or switch
+ to a non-IBM JVM.
+ */
+
+ // If ssl.getInputStream().available() didn't cause an
+ // exception, maybe at least now the session is available?
+ session = ssl.getSession();
+ if (session == null) {
+ // If it's still null, probably a startHandshake() will
+ // unearth the real problem.
+ ssl.startHandshake();
+
+ // Okay, if we still haven't managed to cause an exception,
+ // might as well go for the NPE. Or maybe we're okay now?
+ session = ssl.getSession();
+ }
+ }
+ Certificate[] certs;
+ try {
+ certs = session.getPeerCertificates();
+ } catch (SSLPeerUnverifiedException spue) {
+ InputStream in = ssl.getInputStream();
+ in.available();
+ // Didn't trigger anything interesting? Okay, just throw
+ // original.
+ throw spue;
+ }
+ X509Certificate x509 = (X509Certificate) certs[0];
+ check(host, x509);
+ }
+
+ @Override
+ public void check(String[] host, X509Certificate cert)
+ throws SSLException {
+ String[] cns = Certificates.getCNs(cert);
+ String[] subjectAlts = Certificates.getDNSSubjectAlts(cert);
+ check(host, cns, subjectAlts);
+ }
+
+ public void check(final String[] hosts, final String[] cns,
+ final String[] subjectAlts, final boolean ie6,
+ final boolean strictWithSubDomains)
+ throws SSLException {
+ // Build up lists of allowed hosts For logging/debugging purposes.
+ StringBuffer buf = new StringBuffer(32);
+ buf.append('<');
+ for (int i = 0; i < hosts.length; i++) {
+ String h = hosts[i];
+ h = h != null ? StringUtils.toLowerCase(h.trim()) : "";
+ hosts[i] = h;
+ if (i > 0) {
+ buf.append('/');
+ }
+ buf.append(h);
+ }
+ buf.append('>');
+ String hostnames = buf.toString();
+ // Build the list of names we're going to check. Our DEFAULT and
+ // STRICT implementations of the HostnameVerifier only use the
+ // first CN provided. All other CNs are ignored.
+ // (Firefox, wget, curl, Sun Java 1.4, 5, 6 all work this way).
+ final Set<String> names = new TreeSet<String>();
+ if (cns != null && cns.length > 0 && cns[0] != null) {
+ names.add(cns[0]);
+ if (ie6) {
+ for (int i = 1; i < cns.length; i++) {
+ names.add(cns[i]);
+ }
+ }
+ }
+ if (subjectAlts != null) {
+ for (int i = 0; i < subjectAlts.length; i++) {
+ if (subjectAlts[i] != null) {
+ names.add(subjectAlts[i]);
+ }
+ }
+ }
+ if (names.isEmpty()) {
+ String msg = "Certificate for " + hosts[0] + " doesn't contain CN or DNS subjectAlt";
+ throw new SSLException(msg);
+ }
+
+ // StringBuffer for building the error message.
+ buf = new StringBuffer();
+
+ boolean match = false;
+ out:
+ for (Iterator<String> it = names.iterator(); it.hasNext();) {
+ // Don't trim the CN, though!
+ final String cn = StringUtils.toLowerCase(it.next());
+ // Store CN in StringBuffer in case we need to report an error.
+ buf.append(" <");
+ buf.append(cn);
+ buf.append('>');
+ if (it.hasNext()) {
+ buf.append(" OR");
+ }
+
+ // The CN better have at least two dots if it wants wildcard
+ // action. It also can't be [*.co.uk] or [*.co.jp] or
+ // [*.org.uk], etc...
+ boolean doWildcard = cn.startsWith("*.")
+ && cn.lastIndexOf('.') >= 0
+ && !isIP4Address(cn)
+ && acceptableCountryWildcard(cn);
+
+ for (int i = 0; i < hosts.length; i++) {
+ final String hostName =
+ StringUtils.toLowerCase(hosts[i].trim());
+ if (doWildcard) {
+ match = hostName.endsWith(cn.substring(1));
+ if (match && strictWithSubDomains) {
+ // If we're in strict mode, then [*.foo.com] is not
+ // allowed to match [a.b.foo.com]
+ match = countDots(hostName) == countDots(cn);
+ }
+ } else {
+ match = hostName.equals(cn);
+ }
+ if (match) {
+ break out;
+ }
+ }
+ }
+ if (!match) {
+ throw new SSLException("hostname in certificate didn't match: " + hostnames + " !=" + buf);
+ }
+ }
+
+ public static boolean isIP4Address(final String cn) {
+ boolean isIP4 = true;
+ String tld = cn;
+ int x = cn.lastIndexOf('.');
+ // We only bother analyzing the characters after the final dot
+ // in the name.
+ if (x >= 0 && x + 1 < cn.length()) {
+ tld = cn.substring(x + 1);
+ }
+ for (int i = 0; i < tld.length(); i++) {
+ if (!Character.isDigit(tld.charAt(0))) {
+ isIP4 = false;
+ break;
+ }
+ }
+ return isIP4;
+ }
+
+ public static boolean acceptableCountryWildcard(final String cn) {
+ int cnLen = cn.length();
+ if (cnLen >= 7 && cnLen <= 9) {
+ // Look for the '.' in the 3rd-last position:
+ if (cn.charAt(cnLen - 3) == '.') {
+ // Trim off the [*.] and the [.XX].
+ String s = cn.substring(2, cnLen - 3);
+ // And test against the sorted array of bad 2lds:
+ int x = Arrays.binarySearch(BAD_COUNTRY_2LDS, s);
+ return x < 0;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isLocalhost(String host) {
+ host = host != null ? StringUtils.toLowerCase(host.trim()) : "";
+ if (host.startsWith("::1")) {
+ int x = host.lastIndexOf('%');
+ if (x >= 0) {
+ host = host.substring(0, x);
+ }
+ }
+ int x = Arrays.binarySearch(LOCALHOSTS, host);
+ return x >= 0;
+ }
+
+ /**
+ * Counts the number of dots "." in a string.
+ *
+ * @param s string to count dots from
+ * @return number of dots
+ */
+ public static int countDots(final String s) {
+ int count = 0;
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) == '.') {
+ count++;
+ }
+ }
+ return count;
+ }
+ }
+
+ class Certificates {
+ public static String[] getCNs(X509Certificate cert) {
+ final List<String> cnList = new LinkedList<String>();
+ /*
+ Sebastian Hauer's original StrictSSLProtocolSocketFactory used
+ getName() and had the following comment:
+
+ Parses a X.500 distinguished name for the value of the
+ "Common Name" field. This is done a bit sloppy right
+ now and should probably be done a bit more according to
+ <code>RFC 2253</code>.
+
+ I've noticed that toString() seems to do a better job than
+ getName() on these X500Principal objects, so I'm hoping that
+ addresses Sebastian's concern.
+
+ For example, getName() gives me this:
+ 1.2.840.113549.1.9.1=#16166a756c6975736461766965734063756362632e636f6d
+
+ whereas toString() gives me this:
+ EMAILADDRESS=juliusdavies@cucbc.com
+
+ Looks like toString() even works with non-ascii domain names!
+ I tested it with "花子.co.jp" and it worked fine.
+ */
+ String subjectPrincipal = cert.getSubjectX500Principal().toString();
+ StringTokenizer st = new StringTokenizer(subjectPrincipal, ",");
+ while (st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ int x = tok.indexOf("CN=");
+ if (x >= 0) {
+ cnList.add(tok.substring(x + 3));
+ }
+ }
+ if (!cnList.isEmpty()) {
+ String[] cns = new String[cnList.size()];
+ cnList.toArray(cns);
+ return cns;
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * Extracts the array of SubjectAlt DNS names from an X509Certificate.
+ * Returns null if there aren't any.
+ * <p/>
+ * Note: Java doesn't appear able to extract international characters
+ * from the SubjectAlts. It can only extract international characters
+ * from the CN field.
+ * <p/>
+ * (Or maybe the version of OpenSSL I'm using to test isn't storing the
+ * international characters correctly in the SubjectAlts?).
+ *
+ * @param cert X509Certificate
+ * @return Array of SubjectALT DNS names stored in the certificate.
+ */
+ public static String[] getDNSSubjectAlts(X509Certificate cert) {
+ final List<String> subjectAltList = new LinkedList<String>();
+ Collection<List<?>> c = null;
+ try {
+ c = cert.getSubjectAlternativeNames();
+ } catch (CertificateParsingException cpe) {
+ // Should probably log.debug() this?
+ cpe.printStackTrace();
+ }
+ if (c != null) {
+ Iterator<List<?>> it = c.iterator();
+ while (it.hasNext()) {
+ List<?> list = it.next();
+ int type = ((Integer) list.get(0)).intValue();
+ // If type is 2, then we've got a dNSName
+ if (type == 2) {
+ String s = (String) list.get(1);
+ subjectAltList.add(s);
+ }
+ }
+ }
+ if (!subjectAltList.isEmpty()) {
+ String[] subjectAlts = new String[subjectAltList.size()];
+ subjectAltList.toArray(subjectAlts);
+ return subjectAlts;
+ } else {
+ return null;
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-common/src/main/java/org/apache/kerby/has/common/util/ConnectionConfigurator.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/kerby/has/common/util/ConnectionConfigurator.java b/has/has-common/src/main/java/org/apache/kerby/has/common/util/ConnectionConfigurator.java
new file mode 100644
index 0000000..c913e59
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/kerby/has/common/util/ConnectionConfigurator.java
@@ -0,0 +1,39 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. See accompanying LICENSE file.
+ */
+package org.apache.kerby.has.common.util;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+/**
+ * Borrow the class from Apache Hadoop
+ */
+
+/**
+ * Interface to configure {@link HttpURLConnection} created by
+ * {@link org.apache.kerby.has.common.spnego.AuthenticatedURL} instances.
+ */
+public interface ConnectionConfigurator {
+
+ /**
+ * Configures the given {@link HttpURLConnection} instance.
+ *
+ * @param conn the {@link HttpURLConnection} instance to configure.
+ * @return the configured {@link HttpURLConnection} instance.
+ *
+ * @throws IOException if an IO error occurred.
+ */
+ HttpURLConnection configure(HttpURLConnection conn) throws IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-common/src/main/java/org/apache/kerby/has/common/util/HasJaasLoginUtil.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/kerby/has/common/util/HasJaasLoginUtil.java b/has/has-common/src/main/java/org/apache/kerby/has/common/util/HasJaasLoginUtil.java
new file mode 100644
index 0000000..57f57f3
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/kerby/has/common/util/HasJaasLoginUtil.java
@@ -0,0 +1,261 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.kerby.has.common.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import java.io.File;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * JAAS utilities for Has login.
+ */
+public class HasJaasLoginUtil {
+ public static final Logger LOG = LoggerFactory.getLogger(HasJaasLoginUtil.class);
+
+ public static final boolean ENABLE_DEBUG = true;
+
+ private static String getKrb5LoginModuleName() {
+ return System.getProperty("java.vendor").contains("IBM")
+ ? "com.ibm.security.auth.module.Krb5LoginModule"
+ : "org.apache.kerby.has.client.HasLoginModule";
+ }
+
+ /**
+ * Log a user in from a tgt ticket.
+ *
+ * @throws IOException
+ */
+ public static synchronized Subject loginUserFromTgtTicket(String hadoopSecurityHas) throws IOException {
+
+ TICKET_KERBEROS_OPTIONS.put("hadoopSecurityHas", hadoopSecurityHas);
+ Subject subject = new Subject();
+ Configuration conf = new HasJaasConf();
+ String confName = "ticket-kerberos";
+ LoginContext loginContext = null;
+ try {
+ loginContext = new LoginContext(confName, subject, null, conf);
+ } catch (LoginException e) {
+ throw new IOException("Fail to create LoginContext for " + e);
+ }
+ try {
+ loginContext.login();
+ LOG.info("Login successful for user "
+ + subject.getPrincipals().iterator().next().getName());
+ } catch (LoginException e) {
+ throw new IOException("Login failure for " + e);
+ }
+ return loginContext.getSubject();
+ }
+
+ /**
+ * Has Jaas config.
+ */
+ static class HasJaasConf extends Configuration {
+ @Override
+ public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+
+ return new AppConfigurationEntry[]{
+ TICKET_KERBEROS_LOGIN};
+ }
+ }
+
+ private static final Map<String, String> BASIC_JAAS_OPTIONS =
+ new HashMap<String, String>();
+
+ static {
+ String jaasEnvVar = System.getenv("HADOOP_JAAS_DEBUG");
+ if (jaasEnvVar != null && "true".equalsIgnoreCase(jaasEnvVar)) {
+ BASIC_JAAS_OPTIONS.put("debug", String.valueOf(ENABLE_DEBUG));
+ }
+ }
+
+ private static final Map<String, String> TICKET_KERBEROS_OPTIONS =
+ new HashMap<String, String>();
+
+ static {
+ TICKET_KERBEROS_OPTIONS.put("doNotPrompt", "true");
+ TICKET_KERBEROS_OPTIONS.put("useTgtTicket", "true");
+ TICKET_KERBEROS_OPTIONS.putAll(BASIC_JAAS_OPTIONS);
+ }
+
+ private static final AppConfigurationEntry TICKET_KERBEROS_LOGIN =
+ new AppConfigurationEntry(getKrb5LoginModuleName(),
+ AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL,
+ TICKET_KERBEROS_OPTIONS);
+
+
+ public static Subject loginUsingTicketCache(
+ String principal, File cacheFile) throws IOException {
+ Set<Principal> principals = new HashSet<Principal>();
+ principals.add(new KerberosPrincipal(principal));
+
+ Subject subject = new Subject(false, principals,
+ new HashSet<Object>(), new HashSet<Object>());
+
+ Configuration conf = useTicketCache(principal, cacheFile);
+ String confName = "TicketCacheConf";
+ LoginContext loginContext = null;
+ try {
+ loginContext = new LoginContext(confName, subject, null, conf);
+ } catch (LoginException e) {
+ throw new IOException("Faill to create LoginContext for " + e);
+ }
+ try {
+ loginContext.login();
+ LOG.info("Login successful for user "
+ + subject.getPrincipals().iterator().next().getName());
+ } catch (LoginException e) {
+ throw new IOException("Login failure for " + e);
+ }
+ return loginContext.getSubject();
+ }
+
+ public static Subject loginUsingKeytab(
+ String principal, File keytabFile) throws IOException {
+ Set<Principal> principals = new HashSet<Principal>();
+ principals.add(new KerberosPrincipal(principal));
+
+ Subject subject = new Subject(false, principals,
+ new HashSet<Object>(), new HashSet<Object>());
+
+ Configuration conf = useKeytab(principal, keytabFile);
+ String confName = "KeytabConf";
+ LoginContext loginContext = null;
+ try {
+ loginContext = new LoginContext(confName, subject, null, conf);
+ } catch (LoginException e) {
+ throw new IOException("Fail to create LoginContext for " + e);
+ }
+ try {
+ loginContext.login();
+ LOG.info("Login successful for user "
+ + subject.getPrincipals().iterator().next().getName());
+ } catch (LoginException e) {
+ throw new IOException("Login failure for " + e);
+ }
+ return loginContext.getSubject();
+ }
+
+ public static LoginContext loginUsingKeytabReturnContext(
+ String principal, File keytabFile) throws IOException {
+ Set<Principal> principals = new HashSet<Principal>();
+ principals.add(new KerberosPrincipal(principal));
+
+ Subject subject = new Subject(false, principals,
+ new HashSet<Object>(), new HashSet<Object>());
+
+ Configuration conf = useKeytab(principal, keytabFile);
+ String confName = "KeytabConf";
+ LoginContext loginContext = null;
+ try {
+ loginContext = new LoginContext(confName, subject, null, conf);
+ } catch (LoginException e) {
+ throw new IOException("Fail to create LoginContext for " + e);
+ }
+ try {
+ loginContext.login();
+ LOG.info("Login successful for user "
+ + subject.getPrincipals().iterator().next().getName());
+ } catch (LoginException e) {
+ throw new IOException("Login failure for " + e);
+ }
+ return loginContext;
+ }
+
+ public static Configuration useTicketCache(String principal,
+ File credentialFile) {
+ return new TicketCacheJaasConf(principal, credentialFile);
+ }
+
+ public static Configuration useKeytab(String principal, File keytabFile) {
+ return new KeytabJaasConf(principal, keytabFile);
+ }
+
+ static class TicketCacheJaasConf extends Configuration {
+ private String principal;
+ private File clientCredentialFile;
+
+ TicketCacheJaasConf(String principal, File clientCredentialFile) {
+ this.principal = principal;
+ this.clientCredentialFile = clientCredentialFile;
+ }
+
+ @Override
+ public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+ Map<String, String> options = new HashMap<String, String>();
+ options.put("principal", principal);
+ options.put("storeKey", "false");
+ options.put("doNotPrompt", "false");
+ options.put("useTicketCache", "true");
+ options.put("renewTGT", "true");
+ options.put("refreshKrb5Config", "true");
+ options.put("isInitiator", "true");
+ options.put("ticketCache", clientCredentialFile.getAbsolutePath());
+ options.putAll(BASIC_JAAS_OPTIONS);
+
+ return new AppConfigurationEntry[]{
+ new AppConfigurationEntry(getKrb5LoginModuleName(),
+ AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+ options)};
+ }
+ }
+
+ static class KeytabJaasConf extends Configuration {
+ private String principal;
+ private File keytabFile;
+
+ KeytabJaasConf(String principal, File keytab) {
+ this.principal = principal;
+ this.keytabFile = keytab;
+ }
+
+ @Override
+ public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+ Map<String, String> options = new HashMap<String, String>();
+ options.put("keyTab", keytabFile.getAbsolutePath());
+ options.put("principal", principal);
+ options.put("useKeyTab", "true");
+ options.put("storeKey", "true");
+ options.put("doNotPrompt", "true");
+ options.put("renewTGT", "false");
+ options.put("refreshKrb5Config", "true");
+ options.put("isInitiator", "true");
+ options.putAll(BASIC_JAAS_OPTIONS);
+
+ return new AppConfigurationEntry[]{
+ new AppConfigurationEntry(getKrb5LoginModuleName(),
+ AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+ options)};
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-common/src/main/java/org/apache/kerby/has/common/util/HasUtil.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/kerby/has/common/util/HasUtil.java b/has/has-common/src/main/java/org/apache/kerby/has/common/util/HasUtil.java
new file mode 100644
index 0000000..03a9d9d
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/kerby/has/common/util/HasUtil.java
@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kerby.has.common.util;
+
+import org.apache.kerby.has.common.HasConfig;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.crypto.EncryptionHandler;
+import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
+import org.apache.kerby.kerberos.kerb.type.base.EncryptionType;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+
+public class HasUtil {
+
+ public static EncryptionKey getClientKey(String userName, String passPhrase,
+ EncryptionType type) throws KrbException {
+ EncryptionKey clientKey = EncryptionHandler.string2Key(userName,
+ passPhrase, type);
+ return clientKey;
+ }
+
+ /**
+ * Get has configuration
+ * @param hasConfFile configuration directory
+ * @return has configuration
+ * @throws KrbException e
+ */
+ public static HasConfig getHasConfig(File hasConfFile) throws HasException {
+
+ if (hasConfFile.exists()) {
+ HasConfig hasConfig = new HasConfig();
+ try {
+ hasConfig.addIniConfig(hasConfFile);
+ } catch (IOException e) {
+ throw new HasException("Can not load the has configuration file "
+ + hasConfFile.getAbsolutePath());
+ }
+ return hasConfig;
+ }
+
+ return null;
+ }
+
+ public static void setEnableConf(File hasConfFile, String value)
+ throws HasException, IOException {
+ String oldValue = getHasConfig(hasConfFile).getEnableConf();
+ if (oldValue == null) {
+ throw new HasException("Please set enable_conf in has-server.conf.");
+ }
+ if (oldValue.equals(value)) {
+ return;
+ }
+ try {
+ BufferedReader bf = new BufferedReader(new FileReader(hasConfFile));
+ StringBuilder sb = new StringBuilder();
+ String tempString;
+ while ((tempString = bf.readLine()) != null) {
+ if (tempString.trim().startsWith("enable_conf")) {
+ tempString = tempString.replace(oldValue, value);
+ }
+ sb.append(tempString + "\n");
+ }
+ PrintStream ps = new PrintStream(new FileOutputStream(hasConfFile));
+ ps.print(sb.toString());
+ bf.close();
+ } catch (FileNotFoundException e) {
+ throw new HasException("Can not load the has configuration file "
+ + hasConfFile.getAbsolutePath());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-common/src/main/java/org/apache/kerby/has/common/util/PlatformName.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/kerby/has/common/util/PlatformName.java b/has/has-common/src/main/java/org/apache/kerby/has/common/util/PlatformName.java
new file mode 100644
index 0000000..8a2c961
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/kerby/has/common/util/PlatformName.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kerby.has.common.util;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * Borrow the class from Apache Hadoop
+ */
+
+/**
+ * A helper class for getting build-info of the java-vm.
+ *
+ */
+@InterfaceAudience.LimitedPrivate({"HBase"})
+@InterfaceStability.Unstable
+public class PlatformName {
+ /**
+ * The complete platform 'name' to identify the platform as
+ * per the java-vm.
+ */
+ public static final String PLATFORM_NAME =
+ (System.getProperty("os.name").startsWith("Windows")
+ ? System.getenv("os") : System.getProperty("os.name"))
+ + "-" + System.getProperty("os.arch")
+ + "-" + System.getProperty("sun.arch.data.model");
+
+ /**
+ * The java vendor name used in this platform.
+ */
+ public static final String JAVA_VENDOR_NAME = System.getProperty("java.vendor");
+
+ /**
+ * A public static variable to indicate the current java vendor is
+ * IBM java or not.
+ */
+ public static final boolean IBM_JAVA = JAVA_VENDOR_NAME.contains("IBM");
+
+ public static void main(String[] args) {
+ System.out.println(PLATFORM_NAME);
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-common/src/main/java/org/apache/kerby/has/common/util/StringUtils.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/kerby/has/common/util/StringUtils.java b/has/has-common/src/main/java/org/apache/kerby/has/common/util/StringUtils.java
new file mode 100644
index 0000000..b9c323d
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/kerby/has/common/util/StringUtils.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kerby.has.common.util;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+import java.util.Locale;
+
+/**
+ * General string utils
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public class StringUtils {
+
+ /**
+ * Converts all of the characters in this String to lower case with
+ * Locale.ENGLISH.
+ *
+ * @param str string to be converted
+ * @return the str, converted to lowercase.
+ */
+ public static String toLowerCase(String str) {
+ return str.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Converts all of the characters in this String to upper case with
+ * Locale.ENGLISH.
+ *
+ * @param str string to be converted
+ * @return the str, converted to uppercase.
+ */
+ public static String toUpperCase(String str) {
+ return str.toUpperCase(Locale.ENGLISH);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-common/src/main/java/org/apache/kerby/has/common/util/URLConnectionFactory.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/kerby/has/common/util/URLConnectionFactory.java b/has/has-common/src/main/java/org/apache/kerby/has/common/util/URLConnectionFactory.java
new file mode 100644
index 0000000..d3a61cf
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/kerby/has/common/util/URLConnectionFactory.java
@@ -0,0 +1,215 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kerby.has.common.util;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.kerby.has.common.HasConfig;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.common.spnego.AuthenticatedURL;
+import org.apache.kerby.has.common.spnego.AuthenticationException;
+import org.apache.kerby.has.common.spnego.KerberosHasAuthenticator;
+import org.apache.kerby.has.common.ssl.SSLFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSocketFactory;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.GeneralSecurityException;
+
+/**
+ * Borrow the class from Apache Hadoop
+ */
+
+/**
+ * Utilities for handling URLs
+ */
+@InterfaceStability.Unstable
+public class URLConnectionFactory {
+ private static final Logger LOG = LoggerFactory
+ .getLogger(URLConnectionFactory.class);
+
+ /**
+ * Timeout for socket connects and reads
+ */
+ // 1 minute
+ public static final int DEFAULT_SOCKET_TIMEOUT = 60 * 1000;
+ private final ConnectionConfigurator connConfigurator;
+
+ private static final ConnectionConfigurator DEFAULT_TIMEOUT_CONN_CONFIGURATOR
+ = new ConnectionConfigurator() {
+ @Override
+ public HttpURLConnection configure(HttpURLConnection conn)
+ throws IOException {
+ URLConnectionFactory.setTimeouts(conn,
+ DEFAULT_SOCKET_TIMEOUT,
+ DEFAULT_SOCKET_TIMEOUT);
+ return conn;
+ }
+ };
+
+ /**
+ * The URLConnectionFactory that sets the default timeout and it only trusts
+ * Java's SSL certificates.
+ */
+ public static final URLConnectionFactory DEFAULT_SYSTEM_CONNECTION_FACTORY =
+ new URLConnectionFactory(DEFAULT_TIMEOUT_CONN_CONFIGURATOR);
+
+ /**
+ * Construct a new URLConnectionFactory based on the configuration. It will
+ * try to load SSL certificates when it is specified.
+ */
+ public static URLConnectionFactory newDefaultURLConnectionFactory(HasConfig conf) {
+ ConnectionConfigurator conn = null;
+ try {
+ conn = newSslConnConfigurator(DEFAULT_SOCKET_TIMEOUT, conf);
+ } catch (Exception e) {
+ LOG.debug(
+ "Cannot load customized ssl related configuration. Fallback to system-generic settings.",
+ e);
+ conn = DEFAULT_TIMEOUT_CONN_CONFIGURATOR;
+ }
+ return new URLConnectionFactory(conn);
+ }
+
+ private static ConnectionConfigurator getSSLConnectionConfiguration(
+ HasConfig conf) {
+ ConnectionConfigurator conn;
+ try {
+ conn = newSslConnConfigurator(DEFAULT_SOCKET_TIMEOUT, conf);
+ } catch (Exception e) {
+ LOG.warn(
+ "Cannot load customized ssl related configuration. Fallback to"
+ + " system-generic settings.",
+ e);
+ conn = DEFAULT_TIMEOUT_CONN_CONFIGURATOR;
+ }
+
+ return conn;
+ }
+
+ @VisibleForTesting
+ URLConnectionFactory(ConnectionConfigurator connConfigurator) {
+ this.connConfigurator = connConfigurator;
+ }
+
+ /**
+ * Create a new ConnectionConfigurator for SSL connections
+ */
+ private static ConnectionConfigurator newSslConnConfigurator(
+ final int defaultTimeout, HasConfig conf)
+ throws IOException, GeneralSecurityException, HasException {
+ final SSLFactory factory;
+ final SSLSocketFactory sf;
+ final HostnameVerifier hv;
+ final int connectTimeout;
+ final int readTimeout;
+
+ factory = new SSLFactory(SSLFactory.Mode.CLIENT, conf);
+ factory.init();
+ sf = factory.createSSLSocketFactory();
+ hv = factory.getHostnameVerifier();
+
+ connectTimeout = defaultTimeout;
+
+ readTimeout = defaultTimeout;
+
+ return new ConnectionConfigurator() {
+ @Override
+ public HttpURLConnection configure(HttpURLConnection conn)
+ throws IOException {
+ if (conn instanceof HttpsURLConnection) {
+ HttpsURLConnection c = (HttpsURLConnection) conn;
+ c.setSSLSocketFactory(sf);
+ c.setHostnameVerifier(hv);
+ }
+ URLConnectionFactory.setTimeouts(conn, connectTimeout, readTimeout);
+ return conn;
+ }
+ };
+ }
+
+ /**
+ * Opens a url with read and connect timeouts
+ *
+ * @param url
+ * to open
+ * @return URLConnection
+ * @throws IOException
+ */
+ public URLConnection openConnection(URL url) throws IOException {
+ try {
+ return openConnection(url, false, null);
+ } catch (AuthenticationException e) {
+ // Unreachable
+ LOG.error("Open connection {} failed", url, e);
+ return null;
+ }
+ }
+
+ /**
+ * Opens a url with read and connect timeouts
+ *
+ * @param url
+ * URL to open
+ * @param isSpnego
+ * whether the url should be authenticated via SPNEGO
+ * @return URLConnection
+ * @throws IOException
+ * @throws AuthenticationException
+ */
+ public URLConnection openConnection(URL url, boolean isSpnego, HasConfig hasConfig)
+ throws IOException, AuthenticationException {
+ if (isSpnego && (hasConfig != null)) {
+ LOG.debug("open AuthenticatedURL connection {}", url);
+// UserGroupInformation.getCurrentUser().checkTGTAndReloginFromKeytab();
+ final AuthenticatedURL.Token authToken = new AuthenticatedURL.Token();
+ return new AuthenticatedURL(new KerberosHasAuthenticator(hasConfig.getAdminKeytab(),
+ hasConfig.getAdminKeytabPrincipal()),
+ connConfigurator).openConnection(url, authToken);
+ } else {
+ LOG.debug("open URL connection");
+ URLConnection connection = url.openConnection();
+ if (connection instanceof HttpURLConnection) {
+ connConfigurator.configure((HttpURLConnection) connection);
+ }
+ return connection;
+ }
+ }
+
+ /**
+ * Sets timeout parameters on the given URLConnection.
+ *
+ * @param connection
+ * URLConnection to set
+ * @param connectTimeout
+ * the connection and read timeout of the connection.
+ */
+ private static void setTimeouts(URLConnection connection,
+ int connectTimeout,
+ int readTimeout) {
+ connection.setConnectTimeout(connectTimeout);
+ connection.setReadTimeout(readTimeout);
+ }
+}
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-dist/bin/hadmin-local.sh
----------------------------------------------------------------------
diff --git a/has/has-dist/bin/hadmin-local.sh b/has/has-dist/bin/hadmin-local.sh
index 5a7eb3e..1acf3a0 100644
--- a/has/has-dist/bin/hadmin-local.sh
+++ b/has/has-dist/bin/hadmin-local.sh
@@ -16,7 +16,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-APP_MAIN=org.apache.hadoop.has.tool.server.hadmin.local.HadminLocalTool
+APP_MAIN=org.apache.kerby.has.tool.server.hadmin.local.HadminLocalTool
# Reset HAS_CONF_DIR if CONF_DIR not null
if [ X"$1" = X"-k" ]; then
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-dist/bin/hadmin-remote.sh
----------------------------------------------------------------------
diff --git a/has/has-dist/bin/hadmin-remote.sh b/has/has-dist/bin/hadmin-remote.sh
index 233c056..1e59294 100644
--- a/has/has-dist/bin/hadmin-remote.sh
+++ b/has/has-dist/bin/hadmin-remote.sh
@@ -17,7 +17,7 @@
# limitations under the License.
CONF_DIR=$1
-APP_MAIN=org.apache.hadoop.has.tool.client.hadmin.remote.HadminRemoteTool
+APP_MAIN=org.apache.kerby.has.tool.client.hadmin.remote.HadminRemoteTool
# Reset HAS_CONF_DIR if CONF_DIR not null
if [ "$CONF_DIR" != "" ]; then
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-dist/bin/kdcinit.sh
----------------------------------------------------------------------
diff --git a/has/has-dist/bin/kdcinit.sh b/has/has-dist/bin/kdcinit.sh
index f6e30c3..f3216e0 100644
--- a/has/has-dist/bin/kdcinit.sh
+++ b/has/has-dist/bin/kdcinit.sh
@@ -17,7 +17,7 @@
# limitations under the License.
CONF_DIR=$1
-APP_MAIN=org.apache.hadoop.has.tool.client.kdcinit.HasInitTool
+APP_MAIN=org.apache.kerby.has.tool.client.kdcinit.HasInitTool
# Reset HAS_CONF_DIR if CONF_DIR not null
if [ "$CONF_DIR" != "" ]; then
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-dist/bin/kinit.sh
----------------------------------------------------------------------
diff --git a/has/has-dist/bin/kinit.sh b/has/has-dist/bin/kinit.sh
index 3d605d6..97f33aa 100644
--- a/has/has-dist/bin/kinit.sh
+++ b/has/has-dist/bin/kinit.sh
@@ -16,7 +16,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-APP_MAIN=org.apache.hadoop.has.tool.client.kinit.KinitTool
+APP_MAIN=org.apache.kerby.has.tool.client.kinit.KinitTool
# Get HAS_HOME directory
bin=`dirname "$0"`
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-dist/bin/klist.sh
----------------------------------------------------------------------
diff --git a/has/has-dist/bin/klist.sh b/has/has-dist/bin/klist.sh
index 0643ae7..04103ec 100644
--- a/has/has-dist/bin/klist.sh
+++ b/has/has-dist/bin/klist.sh
@@ -16,7 +16,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-APP_MAIN=org.apache.hadoop.has.tool.client.klist.KlistTool
+APP_MAIN=org.apache.kerby.has.tool.client.klist.KlistTool
# Get HAS_HOME directory
bin=`dirname "$0"`
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-dist/bin/login-test.sh
----------------------------------------------------------------------
diff --git a/has/has-dist/bin/login-test.sh b/has/has-dist/bin/login-test.sh
index f26b1df..0699945 100644
--- a/has/has-dist/bin/login-test.sh
+++ b/has/has-dist/bin/login-test.sh
@@ -16,7 +16,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-APP_MAIN=org.apache.hadoop.has.tool.client.hclient.HasClientLoginTool
+APP_MAIN=org.apache.kerby.has.tool.client.hclient.HasClientLoginTool
# Get HAS_HOME directory
bin=`dirname "$0"`
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-dist/bin/start-has.sh
----------------------------------------------------------------------
diff --git a/has/has-dist/bin/start-has.sh b/has/has-dist/bin/start-has.sh
index 95a6913..cfb8d08 100644
--- a/has/has-dist/bin/start-has.sh
+++ b/has/has-dist/bin/start-has.sh
@@ -27,7 +27,7 @@ usage()
CONF_DIR=$1
WORK_DIR=$2
pid=/tmp/has.pid # Pid file to save pid numbers
-APP_MAIN=org.apache.hadoop.has.server.HasServer
+APP_MAIN=org.apache.kerby.has.server.HasServer
# Reset HAS_CONF_DIR and HAS_WORK_DIR if CONF_DIR or WORK_DIR not null
if [ "$CONF_DIR" != "" ]; then
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-dist/pom.xml
----------------------------------------------------------------------
diff --git a/has/has-dist/pom.xml b/has/has-dist/pom.xml
index 81eccc5..470dd73 100644
--- a/has/has-dist/pom.xml
+++ b/has/has-dist/pom.xml
@@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
- <groupId>org.apache.hadoop</groupId>
+ <groupId>org.apache.kerby</groupId>
<artifactId>has-project</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
@@ -16,32 +16,32 @@
<dependencies>
<dependency>
- <groupId>org.apache.hadoop</groupId>
+ <groupId>org.apache.kerby</groupId>
<artifactId>has-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.apache.hadoop</groupId>
+ <groupId>org.apache.kerby</groupId>
<artifactId>has-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.apache.hadoop</groupId>
+ <groupId>org.apache.kerby</groupId>
<artifactId>has-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.apache.hadoop</groupId>
+ <groupId>org.apache.kerby</groupId>
<artifactId>has-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.apache.hadoop</groupId>
+ <groupId>org.apache.kerby</groupId>
<artifactId>has-client-tool</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.apache.hadoop</groupId>
+ <groupId>org.apache.kerby</groupId>
<artifactId>has-server-tool</artifactId>
<version>${project.version}</version>
</dependency>
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-plugins/pom.xml
----------------------------------------------------------------------
diff --git a/has/has-plugins/pom.xml b/has/has-plugins/pom.xml
index d5b2195..950011b 100644
--- a/has/has-plugins/pom.xml
+++ b/has/has-plugins/pom.xml
@@ -4,7 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>has-project</artifactId>
- <groupId>org.apache.hadoop</groupId>
+ <groupId>org.apache.kerby</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -13,7 +13,7 @@
<dependencies>
<dependency>
- <groupId>org.apache.hadoop</groupId>
+ <groupId>org.apache.kerby</groupId>
<artifactId>has-common</artifactId>
<version>${project.version}</version>
</dependency>
@@ -24,12 +24,12 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.apache.hadoop</groupId>
+ <groupId>org.apache.kerby</groupId>
<artifactId>has-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.apache.hadoop</groupId>
+ <groupId>org.apache.kerby</groupId>
<artifactId>has-server</artifactId>
<version>${project.version}</version>
</dependency>