directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From plusplusjia...@apache.org
Subject [08/10] directory-kerby git commit: Add the HAS project to Kerby.
Date Wed, 15 Nov 2017 06:09:24 GMT
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginModule.java
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginModule.java b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginModule.java
new file mode 100644
index 0000000..6c71236
--- /dev/null
+++ b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginModule.java
@@ -0,0 +1,491 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.has.client;
+
+import com.sun.security.auth.module.Krb5LoginModule;
+import org.apache.hadoop.has.common.HasException;
+import org.apache.kerby.kerberos.kerb.ccache.Credential;
+import org.apache.kerby.kerberos.kerb.type.ticket.TgtTicket;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import sun.security.jgss.krb5.Krb5Util;
+import sun.security.krb5.Credentials;
+import sun.security.krb5.KrbException;
+import sun.security.krb5.PrincipalName;
+
+import javax.security.auth.DestroyFailedException;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KerberosTicket;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Login with tgt ticket
+ * The client's TGT will be retrieved from the API of HasClient
+ */
+//CHECKSTYLE.OFF
+public class HasLoginModule implements LoginModule {
+
+    public static final Logger LOG = LoggerFactory.getLogger(HasLoginModule.class);
+
+    Krb5LoginModule krb5LoginModule;
+
+    // initial state
+    private Subject subject;
+    private CallbackHandler callbackHandler;
+    private Map<String, Object> sharedState;
+    private Map<String, ?> options;
+
+    // configurable option
+    private boolean debug = false;
+    private boolean doNotPrompt = false;
+    private boolean useTgtTicket = false;
+    private String hadoopSecurityHas = null;
+    private String princName = null;
+
+    private boolean refreshKrb5Config = false;
+
+    // specify if initiator.
+    // perform authentication exchange if initiator
+    private boolean isInitiator = true;
+
+    // the authentication status
+    private boolean succeeded = false;
+    private boolean commitSucceeded = false;
+
+    private Credentials cred = null;
+
+    private PrincipalName principal = null;
+    private KerberosPrincipal kerbClientPrinc = null;
+    private KerberosTicket kerbTicket = null;
+    private StringBuffer krb5PrincName = null;
+    private boolean unboundServer = false;
+
+    /**
+     * Initialize this <code>LoginModule</code>.
+     * <p>
+     * <p>
+     *
+     * @param subject         the <code>Subject</code> to be authenticated. <p>
+     * @param callbackHandler a <code>CallbackHandler</code> for
+     *                        communication with the end user (prompting for
+     *                        usernames and passwords, for example). <p>
+     * @param sharedState     shared <code>LoginModule</code> state. <p>
+     * @param options         options specified in the login
+     *                        <code>Configuration</code> for this particular
+     *                        <code>LoginModule</code>.
+     */
+    public void initialize(Subject subject,
+                           CallbackHandler callbackHandler,
+                           Map<String, ?> sharedState,
+                           Map<String, ?> options) {
+
+        this.subject = subject;
+        this.callbackHandler = callbackHandler;
+        this.sharedState = (Map<String, Object>) sharedState;
+        this.options = options;
+
+        // initialize any configured options
+        useTgtTicket = "true".equalsIgnoreCase((String) options.get("useTgtTicket"));
+
+        if (useTgtTicket) {
+            debug = "true".equalsIgnoreCase((String) options.get("debug"));
+            doNotPrompt = "true".equalsIgnoreCase((String) options.get("doNotPrompt"));
+            useTgtTicket = "true".equalsIgnoreCase((String) options.get("useTgtTicket"));
+            hadoopSecurityHas = (String) options.get("hadoopSecurityHas");
+            princName = (String) options.get("principal");
+            refreshKrb5Config =
+                "true".equalsIgnoreCase((String) options.get("refreshKrb5Config"));
+
+            // check isInitiator value
+            String isInitiatorValue = ((String) options.get("isInitiator"));
+            if (isInitiatorValue != null) {
+                // use default, if value not set
+                isInitiator = "true".equalsIgnoreCase(isInitiatorValue);
+            }
+
+            if (debug) {
+                System.out.print("Debug is  " + debug
+                    + " doNotPrompt " + doNotPrompt
+                    + " isInitiator " + isInitiator
+                    + " refreshKrb5Config is " + refreshKrb5Config
+                    + " principal is " + princName + "\n");
+            }
+        } else {
+            krb5LoginModule = new Krb5LoginModule();
+            krb5LoginModule.initialize(subject, callbackHandler, sharedState, options);
+        }
+    }
+
+    /**
+     * Authenticate the user
+     * <p>
+     * <p>
+     *
+     * @return true in all cases since this <code>LoginModule</code>
+     * should not be ignored.
+     * @throws LoginException       if this <code>LoginModule</code>
+     *                              is unable to perform the authentication.
+     */
+    public boolean login() throws LoginException {
+
+        if (useTgtTicket) {
+            if (refreshKrb5Config) {
+                try {
+                    if (debug) {
+                        System.out.println("Refreshing Kerberos configuration");
+                    }
+                    sun.security.krb5.Config.refresh();
+                } catch (KrbException ke) {
+                    LoginException le = new LoginException(ke.getMessage());
+                    le.initCause(ke);
+                    throw le;
+                }
+            }
+            String principalProperty = System.getProperty("sun.security.krb5.principal");
+            if (principalProperty != null) {
+                krb5PrincName = new StringBuffer(principalProperty);
+            } else {
+                if (princName != null) {
+                    krb5PrincName = new StringBuffer(princName);
+                }
+            }
+
+            validateConfiguration();
+
+            if (krb5PrincName != null && krb5PrincName.toString().equals("*")) {
+                unboundServer = true;
+            }
+
+            // attempt the authentication by getting the username and pwd
+            // by prompting or configuration i.e. not from shared state
+
+            try {
+                attemptAuthentication(false);
+                succeeded = true;
+                cleanState();
+                return true;
+            } catch (LoginException e) {
+                // authentication failed -- clean out state
+                if (debug) {
+                    System.out.println("\t\t[HasLoginModule] "
+                        + "authentication failed \n"
+                        + e.getMessage());
+                }
+                succeeded = false;
+                cleanState();
+                throw e;
+            }
+        } else {
+            succeeded = krb5LoginModule.login();
+            return succeeded;
+        }
+    }
+
+    /**
+     * Process the configuration options
+     * Get the TGT from Has Client
+     */
+
+    private void attemptAuthentication(boolean getPasswdFromSharedState)
+        throws LoginException {
+
+        /*
+         * Check the creds cache to see whether
+         * we have TGT for this client principal
+         */
+        if (krb5PrincName != null) {
+            try {
+                principal = new PrincipalName(krb5PrincName.toString(),
+                        PrincipalName.KRB_NT_PRINCIPAL);
+            } catch (KrbException e) {
+                LoginException le = new LoginException(e.getMessage());
+                le.initCause(e);
+                throw le;
+            }
+        }
+
+        try {
+            if (useTgtTicket) {
+                if (debug) {
+                    System.out.println("use tgt ticket to login, acquire TGT TICKET...");
+                }
+
+                HasClient hasClient = new HasClient(hadoopSecurityHas);
+                TgtTicket tgtTicket = null;
+                try {
+                    tgtTicket = hasClient.requestTgt();
+                } catch (HasException e) {
+                    LoginException le = new LoginException(e.getMessage());
+                    le.initCause(e);
+                    throw le;
+                }
+                Credential credential = new Credential(tgtTicket);
+                boolean[] flags = new boolean[7];
+                int flag = credential.getTicketFlags().getFlags();
+                for (int i = 6; i >= 0; i--) {
+                    flags[i] = (flag & (1 << i)) != 0;
+                }
+                Date startTime = null;
+                if (credential.getStartTime() != null) {
+                    startTime = credential.getStartTime().getValue();
+                }
+                cred = new Credentials(credential.getTicket().encode(),
+                    credential.getClientName().getName(),
+                    credential.getServerName().getName(),
+                    credential.getKey().getKeyData(),
+                    credential.getKey().getKeyType().getValue(),
+                    flags,
+                    credential.getAuthTime().getValue(),
+                    startTime,
+                    credential.getEndTime().getValue(),
+                    credential.getRenewTill().getValue(),
+                    null);
+
+                if (cred != null) {
+                    // get the principal name from the ticket cache
+                    if (principal == null) {
+                        principal = cred.getClient();
+                    }
+                }
+                if (debug) {
+                    System.out.println("Principal is " + principal);
+                    if (cred == null) {
+                        System.out.println("null credentials from TGT Ticket");
+                    }
+                }
+            }
+        } catch (KrbException e) {
+            LoginException le = new LoginException(e.getMessage());
+            le.initCause(e);
+            throw le;
+        } catch (IOException ioe) {
+            LoginException ie = new LoginException(ioe.getMessage());
+            ie.initCause(ioe);
+            throw ie;
+        }
+    }
+
+    private void validateConfiguration() throws LoginException {
+        if (doNotPrompt && !useTgtTicket) {
+            throw new LoginException("Configuration Error"
+                + " - either doNotPrompt should be "
+                + " false or"
+                + " useTgtTicket"
+                + " should be true");
+        }
+
+        if (krb5PrincName != null && krb5PrincName.toString().equals("*")) {
+            if (isInitiator) {
+                throw new LoginException("Configuration Error"
+                        + " - principal cannot be * when isInitiator is true");
+            }
+        }
+    }
+
+    /**
+     * <p> This method is called if the LoginContext's
+     * overall authentication succeeded
+     *
+     * @return true if this LoginModule's own login and commit
+     * attempts succeeded, or false otherwise.
+     * @throws LoginException if the commit fails.
+     */
+
+    public boolean commit() throws LoginException {
+        if (debug) {
+            System.out.println("Login success? " + succeeded);
+        }
+
+        if (useTgtTicket) {
+        /*
+         * Let us add the Krb5 Creds to the Subject's
+         * private credentials. The credentials are of type
+         * KerberosKey or KerberosTicket
+         */
+            if (succeeded == false) {
+                return false;
+            } else {
+
+                if (isInitiator && (cred == null)) {
+                    succeeded = false;
+                    throw new LoginException("Null Client Credential");
+                }
+
+                if (subject.isReadOnly()) {
+                    cleanKerberosCred();
+                    throw new LoginException("Subject is Readonly");
+                }
+
+            /*
+             * Add the Principal (authenticated identity)
+             * to the Subject's principal set and
+             * add the credentials (TGT or Service key) to the
+             * Subject's private credentials
+             */
+
+                Set<Object> privCredSet = subject.getPrivateCredentials();
+                Set<java.security.Principal> princSet = subject.getPrincipals();
+                kerbClientPrinc = new KerberosPrincipal(principal.getName());
+
+                // create Kerberos Ticket
+                if (isInitiator) {
+                    kerbTicket = Krb5Util.credsToTicket(cred);
+                }
+
+                // Let us add the kerbClientPrinc,kerbTicket
+
+                // We won't add "*" as a KerberosPrincipal
+                if (!unboundServer
+                    && !princSet.contains(kerbClientPrinc)) {
+                    princSet.add(kerbClientPrinc);
+                }
+
+                // add the TGT
+                if (kerbTicket != null) {
+                    if (!privCredSet.contains(kerbTicket)) {
+                        privCredSet.add(kerbTicket);
+                    }
+                }
+            }
+            commitSucceeded = true;
+            if (debug) {
+                System.out.println("Commit Succeeded \n");
+            }
+            return true;
+        } else {
+            return krb5LoginModule.commit();
+        }
+    }
+
+    /**
+     * <p> This method is called if the LoginContext's
+     * overall authentication failed.
+     *
+     * @return false if this LoginModule's own login and/or commit attempts
+     * failed, and true otherwise.
+     * @throws LoginException if the abort fails.
+     */
+
+    public boolean abort() throws LoginException {
+        if (useTgtTicket) {
+            if (succeeded == false) {
+                return false;
+            } else if (succeeded == true && commitSucceeded == false) {
+                // login succeeded but overall authentication failed
+                succeeded = false;
+                cleanKerberosCred();
+            } else {
+                // overall authentication succeeded and commit succeeded,
+                // but someone else's commit failed
+                logout();
+            }
+            return true;
+        } else {
+            return krb5LoginModule.abort();
+        }
+    }
+
+    /**
+     * Logout the user.
+     * <p>
+     * <p> This method removes the <code>Krb5Principal</code>
+     * that was added by the <code>commit</code> method.
+     * <p>
+     * <p>
+     *
+     * @return true in all cases since this <code>LoginModule</code>
+     * should not be ignored.
+     * @throws LoginException if the logout fails.
+     */
+    public boolean logout() throws LoginException {
+
+        if (useTgtTicket) {
+            if (debug) {
+                System.out.println("\t\t[Krb5LoginModule]: "
+                    + "Entering logout");
+            }
+
+            if (subject.isReadOnly()) {
+                cleanKerberosCred();
+                throw new LoginException("Subject is Readonly");
+            }
+
+            subject.getPrincipals().remove(kerbClientPrinc);
+            // Let us remove all Kerberos credentials stored in the Subject
+            Iterator<Object> it = subject.getPrivateCredentials().iterator();
+            while (it.hasNext()) {
+                Object o = it.next();
+                if (o instanceof KerberosTicket) {
+                    it.remove();
+                }
+            }
+            // clean the kerberos ticket and keys
+            cleanKerberosCred();
+
+            succeeded = false;
+            commitSucceeded = false;
+            if (debug) {
+                System.out.println("\t\t[HasLoginModule]: "
+                    + "logged out Subject");
+            }
+            return true;
+        } else {
+            return krb5LoginModule.logout();
+        }
+    }
+
+    /**
+     * Clean Kerberos credentials
+     */
+    private void cleanKerberosCred() throws LoginException {
+        // Clean the ticket and server key
+        try {
+            if (kerbTicket != null) {
+                kerbTicket.destroy();
+            }
+        } catch (DestroyFailedException e) {
+            throw new LoginException("Destroy Failed on Kerberos Private Credentials");
+        }
+        kerbTicket = null;
+        kerbClientPrinc = null;
+    }
+
+    /**
+     * Clean out the state
+     */
+    private void cleanState() {
+
+        if (!succeeded) {
+            // remove temp results for the next try
+            principal = null;
+        }
+        if (krb5PrincName != null && krb5PrincName.length() != 0) {
+            krb5PrincName.delete(0, krb5PrincName.length());
+        }
+        krb5PrincName = null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-client/src/main/resources/ssl-client.conf.template
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/resources/ssl-client.conf.template b/has/has-client/src/main/resources/ssl-client.conf.template
new file mode 100644
index 0000000..c5ca70a
--- /dev/null
+++ b/has/has-client/src/main/resources/ssl-client.conf.template
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+ssl.client.truststore.location = _location_
+ssl.client.truststore.password = _password_

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-common/pom.xml
----------------------------------------------------------------------
diff --git a/has/has-common/pom.xml b/has/has-common/pom.xml
new file mode 100644
index 0000000..3046871
--- /dev/null
+++ b/has/has-common/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.hadoop</groupId>
+    <artifactId>has-project</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>has-common</artifactId>
+  <description>HAS common</description>
+  <name>HAS common</name>
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>token-provider</artifactId>
+      <version>${kerby.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerby-config</artifactId>
+      <version>${kerby.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerb-client-api-all</artifactId>
+      <version>${kerby.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hadoop</groupId>
+      <artifactId>hadoop-annotations</artifactId>
+      <version>3.0.0-alpha2</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+      <version>1.4</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>22.0-rc1-android</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+      <version>1.2</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>${slf4j.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.12</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-common/src/main/java/org/apache/hadoop/has/common/HasAdmin.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/hadoop/has/common/HasAdmin.java b/has/has-common/src/main/java/org/apache/hadoop/has/common/HasAdmin.java
new file mode 100644
index 0000000..94dc5df
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/hadoop/has/common/HasAdmin.java
@@ -0,0 +1,140 @@
+/**
+ *  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.hadoop.has.common;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Server side admin facilities from remote, similar to MIT kadmin remote mode.
+ */
+public interface HasAdmin {
+
+    /**
+     * Get the hadmin principal name.
+     *
+     * @return The hadmin principal name.
+     */
+    String getHadminPrincipal();
+
+    /**
+     * Add principal to backend.
+     *
+     * @param principal The principal to be added into backend
+     * @throws HasException e
+     */
+    void addPrincipal(String principal) throws HasException;
+
+    /**
+     * Add principal to backend.
+     *
+     * @param principal The principal to be added into backend
+     * @param password  The password to create encryption key
+     * @throws HasException e
+     */
+    void addPrincipal(String principal, String password) throws HasException;
+
+    /**
+     * Export all the keys of the specified principal into the specified keytab
+     * file.
+     *
+     * @param keytabFile The keytab file
+     * @param principal The principal name
+     * @throws HasException e
+     */
+    void exportKeytab(File keytabFile, String principal) throws HasException;
+
+    /**
+     * Export all the keys of the specified principals into the specified keytab
+     * file.
+     *
+     * @param keytabFile The keytab file
+     * @param principals The principal names
+     * @throws HasException e
+     */
+    void exportKeytab(File keytabFile, List<String> principals) throws HasException;
+
+    /**
+     * Delete the principal in backend.
+     *
+     * @param principal The principal to be deleted from backend
+     * @throws HasException e
+     */
+    void deletePrincipal(String principal) throws HasException;
+
+    /**
+     * Rename the principal.
+     *
+     * @param oldPrincipalName The original principal name
+     * @param newPrincipalName The new principal name
+     * @throws HasException e
+     */
+    void renamePrincipal(String oldPrincipalName,
+                         String newPrincipalName) throws HasException;
+
+    /**
+     * Get all the principal names from backend.
+     *
+     * @return principal list
+     * @throws HasException e
+     */
+    List<String> getPrincipals() throws HasException;
+
+    /**
+     * Get all the principal names that meets the pattern
+     *
+     * @param globString The glob string for matching
+     * @return Principal names
+     * @throws HasException e
+     */
+    List<String> getPrincipals(String globString) throws HasException;
+
+    /**
+     * Change the password of specified principal.
+     *
+     * @param principal The principal to be updated password
+     * @param newPassword The new password
+     * @throws HasException e
+     */
+//    void changePassword(String principal, String newPassword) throws HasException;
+
+    /**
+     * Update the random keys of specified principal.
+     *
+     * @param principal The principal to be updated keys
+     * @throws HasException e
+     */
+//    void updateKeys(String principal) throws HasException;
+
+    /**
+     * Release any resources associated.
+     *
+     * @throws HasException e
+     */
+//    void release() throws HasException;
+
+    String addPrincByRole(String host, String role) throws HasException;
+
+    File getKeytabByHostAndRole(String host, String role) throws HasException;
+
+    int size() throws HasException;
+
+    void setEnableOfConf(String isEnable) throws HasException;
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-common/src/main/java/org/apache/hadoop/has/common/HasConfig.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/hadoop/has/common/HasConfig.java b/has/has-common/src/main/java/org/apache/hadoop/has/common/HasConfig.java
new file mode 100644
index 0000000..3fc0998
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/hadoop/has/common/HasConfig.java
@@ -0,0 +1,103 @@
+/**
+ *  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.hadoop.has.common;
+
+import org.apache.kerby.kerberos.kerb.common.Krb5Conf;
+
+import java.io.File;
+
+/**
+ * AK configuration API.
+ */
+public class HasConfig extends Krb5Conf {
+    private File confDir;
+
+    public void setConfDir(File dir) {
+        this.confDir = dir;
+    }
+
+    public File getConfDir() {
+        return confDir;
+    }
+
+    public String getHttpsHost() {
+        return getString(HasConfigKey.HTTPS_HOST, false, "HAS");
+    }
+
+    public String getHttpsPort() {
+        return getString(HasConfigKey.HTTPS_PORT, false, "HAS");
+    }
+
+    public String getHttpHost() {
+        return getString(HasConfigKey.HTTP_HOST, false, "HAS");
+    }
+
+    public String getHttpPort() {
+        return getString(HasConfigKey.HTTP_PORT, false, "HAS");
+    }
+
+    public String getPluginName() {
+        return getString(HasConfigKey.AUTH_TYPE, true, "PLUGIN");
+    }
+
+    public String getRealm() {
+        return getString(HasConfigKey.REALM, false, "HAS");
+    }
+
+    public String getSslServerConf() {
+        return getString(HasConfigKey.SSL_SERVER_CONF, true, "HAS");
+    }
+
+    public String getSslClientConf() {
+        return getString(HasConfigKey.SSL_CLIENT_CONF, true, "HAS");
+    }
+
+    public String getFilterAuthType() {
+        return getString(HasConfigKey.FILTER_AUTH_TYPE, true, "HAS");
+    }
+
+    public String getKerberosPrincipal() {
+        return getString(HasConfigKey.KERBEROS_PRINCIPAL, false, "HAS");
+    }
+
+    public String getKerberosKeytab() {
+        return getString(HasConfigKey.KERBEROS_KEYTAB, false, "HAS");
+    }
+
+    public String getKerberosNameRules() {
+        return getString(HasConfigKey.KERBEROS_NAME_RULES, false, "HAS");
+    }
+
+    public String getAdminKeytab() {
+        return getString(HasConfigKey.ADMIN_KEYTAB, false, "HAS");
+    }
+
+    public String getAdminKeytabPrincipal() {
+        return getString(HasConfigKey.ADMIN_KEYTAB_PRINCIPAL, false, "HAS");
+    }
+
+    public String getEnableConf() {
+        return getString(HasConfigKey.ENABLE_CONF, false, "HAS");
+    }
+
+    public String getSslClientCert() {
+        return getString(HasConfigKey.SSL_CLIENT_CERT, true, "HAS");
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-common/src/main/java/org/apache/hadoop/has/common/HasConfigKey.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/hadoop/has/common/HasConfigKey.java b/has/has-common/src/main/java/org/apache/hadoop/has/common/HasConfigKey.java
new file mode 100644
index 0000000..07db8d4
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/hadoop/has/common/HasConfigKey.java
@@ -0,0 +1,61 @@
+/**
+ *  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.hadoop.has.common;
+
+import org.apache.kerby.config.ConfigKey;
+
+public enum HasConfigKey implements ConfigKey {
+    HTTP_HOST,
+    HTTP_PORT,
+    HTTPS_HOST,
+    HTTPS_PORT,
+    AUTH_TYPE("RAM"),
+    REALM,
+    ENABLE_CONF,
+    SSL_SERVER_CONF("/etc/has/ssl-server.conf"),
+    SSL_CLIENT_CONF("/etc/has/ssl-client.conf"),
+    SSL_CLIENT_CERT("/etc/has/cert-signed"),
+    FILTER_AUTH_TYPE("kerberos"),
+    KERBEROS_PRINCIPAL,
+    KERBEROS_KEYTAB,
+    KERBEROS_NAME_RULES,
+    ADMIN_KEYTAB,
+    ADMIN_KEYTAB_PRINCIPAL;
+
+    private Object defaultValue;
+
+    HasConfigKey() {
+        this.defaultValue = null;
+    }
+
+    HasConfigKey(Object defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    @Override
+    public String getPropertyKey() {
+        return name().toLowerCase();
+    }
+
+    @Override
+    public Object getDefaultValue() {
+        return this.defaultValue;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-common/src/main/java/org/apache/hadoop/has/common/HasException.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/hadoop/has/common/HasException.java b/has/has-common/src/main/java/org/apache/hadoop/has/common/HasException.java
new file mode 100644
index 0000000..f8fc3b3
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/hadoop/has/common/HasException.java
@@ -0,0 +1,53 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.has.common;
+
+public class HasException extends Exception {
+
+    private static final long serialVersionUID = -1916788959202646914L;
+
+    /**
+     * Creates an {@link HasException}.
+     *
+     * @param cause original exception.
+     */
+    public HasException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Creates an {@link HasException}.
+     *
+     * @param message exception message.
+     */
+    public HasException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates an {@link HasException}.
+     *
+     * @param message exception message.
+     * @param cause   original exception.
+     */
+    public HasException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/AuthToken.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/AuthToken.java b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/AuthToken.java
new file mode 100644
index 0000000..c7a18da
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/AuthToken.java
@@ -0,0 +1,217 @@
+/**
+ * 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.hadoop.has.common.spnego;
+
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * Borrow the class from Apache hadoop
+ */
+public class AuthToken implements Principal {
+
+  /**
+   * Constant that identifies an anonymous request.
+   */
+
+  private static final String ATTR_SEPARATOR = "&";
+  private static final String USER_NAME = "u";
+  private static final String PRINCIPAL = "p";
+  private static final String EXPIRES = "e";
+  private static final String TYPE = "t";
+
+  private static final Set<String> ATTRIBUTES =
+    new HashSet<String>(Arrays.asList(USER_NAME, PRINCIPAL, EXPIRES, TYPE));
+
+  private String userName;
+  private String principal;
+  private String type;
+  private long expires;
+  private String tokenStr;
+
+  protected AuthToken() {
+    userName = null;
+    principal = null;
+    type = null;
+    expires = -1;
+    tokenStr = "ANONYMOUS";
+    generateToken();
+  }
+
+  private static final String ILLEGAL_ARG_MSG = " is NULL, empty or contains a '" + ATTR_SEPARATOR + "'";
+
+  /**
+   * Creates an authentication token.
+   *
+   * @param userName user name.
+   * @param principal principal (commonly matches the user name, with Kerberos is the full/long principal
+   * name while the userName is the short name).
+   * @param type the authentication mechanism name.
+   * (<code>System.currentTimeMillis() + validityPeriod</code>).
+   */
+  public AuthToken(String userName, String principal, String type) {
+    checkForIllegalArgument(userName, "userName");
+    checkForIllegalArgument(principal, "principal");
+    checkForIllegalArgument(type, "type");
+    this.userName = userName;
+    this.principal = principal;
+    this.type = type;
+    this.expires = -1;
+  }
+  
+  /**
+   * Check if the provided value is invalid. Throw an error if it is invalid, NOP otherwise.
+   * 
+   * @param value the value to check.
+   * @param name the parameter name to use in an error message if the value is invalid.
+   */
+  protected static void checkForIllegalArgument(String value, String name) {
+    if (value == null || value.length() == 0 || value.contains(ATTR_SEPARATOR)) {
+      throw new IllegalArgumentException(name + ILLEGAL_ARG_MSG);
+    }
+  }
+
+  /**
+   * Sets the expiration of the token.
+   *
+   * @param expires expiration time of the token in milliseconds since the epoch.
+   */
+  public void setExpires(long expires) {
+    this.expires = expires;
+      generateToken();
+  }
+
+  /**
+   * Returns true if the token has expired.
+   *
+   * @return true if the token has expired.
+   */
+  public boolean isExpired() {
+    return getExpires() != -1 && System.currentTimeMillis() > getExpires();
+  }
+
+  /**
+   * Generates the token.
+   */
+  private void generateToken() {
+    StringBuffer sb = new StringBuffer();
+    sb.append(USER_NAME).append("=").append(getUserName()).append(ATTR_SEPARATOR);
+    sb.append(PRINCIPAL).append("=").append(getName()).append(ATTR_SEPARATOR);
+    sb.append(TYPE).append("=").append(getType()).append(ATTR_SEPARATOR);
+    sb.append(EXPIRES).append("=").append(getExpires());
+    tokenStr = sb.toString();
+  }
+
+  /**
+   * Returns the user name.
+   *
+   * @return the user name.
+   */
+  public String getUserName() {
+    return userName;
+  }
+
+  /**
+   * Returns the principal name (this method name comes from the JDK {@link Principal} interface).
+   *
+   * @return the principal name.
+   */
+  @Override
+  public String getName() {
+    return principal;
+  }
+
+  /**
+   * Returns the authentication mechanism of the token.
+   *
+   * @return the authentication mechanism of the token.
+   */
+  public String getType() {
+    return type;
+  }
+
+  /**
+   * Returns the expiration time of the token.
+   *
+   * @return the expiration time of the token, in milliseconds since Epoc.
+   */
+  public long getExpires() {
+    return expires;
+  }
+
+  /**
+   * Returns the string representation of the token.
+   * <p>
+   * This string representation is parseable by the {@link #parse} method.
+   *
+   * @return the string representation of the token.
+   */
+  @Override
+  public String toString() {
+    return tokenStr;
+  }
+
+  public static AuthToken parse(String tokenStr) throws AuthenticationException {
+    if (tokenStr.length() >= 2) {
+      // strip the \" at the two ends of the tokenStr
+      if (tokenStr.charAt(0) == '\"'
+          && tokenStr.charAt(tokenStr.length() - 1) == '\"') {
+        tokenStr = tokenStr.substring(1, tokenStr.length() - 1);
+      }
+    }
+    Map<String, String> map = split(tokenStr);
+    // remove the signature part, since client doesn't care about it
+    map.remove("s");
+
+    if (!map.keySet().equals(ATTRIBUTES)) {
+      throw new AuthenticationException("Invalid token string, missing attributes");
+    }
+    long expires = Long.parseLong(map.get(EXPIRES));
+    AuthToken token = new AuthToken(map.get(USER_NAME), map.get(PRINCIPAL), map.get(TYPE));
+    token.setExpires(expires);
+    return token;
+  }
+
+  /**
+   * Splits the string representation of a token into attributes pairs.
+   *
+   * @param tokenStr string representation of a token.
+   *
+   * @return a map with the attribute pairs of the token.
+   *
+   * @throws AuthenticationException thrown if the string representation of the token could not be broken into
+   * attribute pairs.
+   */
+  private static Map<String, String> split(String tokenStr) throws AuthenticationException {
+    Map<String, String> map = new HashMap<String, String>();
+    StringTokenizer st = new StringTokenizer(tokenStr, ATTR_SEPARATOR);
+    while (st.hasMoreTokens()) {
+      String part = st.nextToken();
+      int separator = part.indexOf('=');
+      if (separator == -1) {
+        throw new AuthenticationException("Invalid authentication token");
+      }
+      String key = part.substring(0, separator);
+      String value = part.substring(separator + 1);
+      map.put(key, value);
+    }
+    return map;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/AuthenticatedURL.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/AuthenticatedURL.java b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/AuthenticatedURL.java
new file mode 100644
index 0000000..ccd7ea4
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/AuthenticatedURL.java
@@ -0,0 +1,282 @@
+/**
+ * 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.hadoop.has.common.spnego;
+
+import org.apache.hadoop.has.common.util.ConnectionConfigurator;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Borrow the class from Apache Hadoop
+ */
+
+/**
+ * <p>
+ * The authentication mechanisms supported by default are Hadoop Simple  authentication
+ * (also known as pseudo authentication) and Kerberos SPNEGO authentication.
+ * <p>
+ * Additional authentication mechanisms can be supported via {@link Authenticator} implementations.
+ * <p>
+ * The default {@link Authenticator} is the {@link KerberosAuthenticator} class which supports
+ * automatic fallback from Kerberos SPNEGO to Hadoop Simple authentication.
+ * <p>
+ * <code>AuthenticatedURL</code> instances are not thread-safe.
+ * <p>
+ * The usage pattern of the {@link AuthenticatedURL} is:
+ * <pre>
+ *
+ * // establishing an initial connection
+ *
+ * URL url = new URL("http://foo:8080/bar");
+ * AuthenticatedURL.Token token = new AuthenticatedURL.Token();
+ * AuthenticatedURL aUrl = new AuthenticatedURL();
+ * HttpURLConnection conn = new AuthenticatedURL(url, token).openConnection();
+ * ....
+ * // use the 'conn' instance
+ * ....
+ *
+ * // establishing a follow up connection using a token from the previous connection
+ *
+ * HttpURLConnection conn = new AuthenticatedURL(url, token).openConnection();
+ * ....
+ * // use the 'conn' instance
+ * ....
+ *
+ * </pre>
+ */
+public class AuthenticatedURL {
+
+  /**
+   * Name of the HTTP cookie used for the authentication token between the client and the server.
+   */
+  public static final String AUTH_COOKIE = "hadoop.auth";
+
+  private static final String AUTH_COOKIE_EQ = AUTH_COOKIE + "=";
+
+  /**
+   * Client side authentication token.
+   */
+  public static class Token {
+
+    private String token;
+
+    /**
+     * Creates a token.
+     */
+    public Token() {
+    }
+
+    /**
+     * Creates a token using an existing string representation of the token.
+     *
+     * @param tokenStr string representation of the tokenStr.
+     */
+    public Token(String tokenStr) {
+      if (tokenStr == null) {
+        throw new IllegalArgumentException("tokenStr cannot be null");
+      }
+      set(tokenStr);
+    }
+
+    /**
+     * Returns if a token from the server has been set.
+     *
+     * @return if a token from the server has been set.
+     */
+    public boolean isSet() {
+      return token != null;
+    }
+
+    /**
+     * Sets a token.
+     *
+     * @param tokenStr string representation of the tokenStr.
+     */
+    void set(String tokenStr) {
+      token = tokenStr;
+    }
+
+    /**
+     * Returns the string representation of the token.
+     *
+     * @return the string representation of the token.
+     */
+    @Override
+    public String toString() {
+      return token;
+    }
+
+  }
+
+  private static Class<? extends Authenticator> defaultAuthenticator
+      = KerberosAuthenticator.class;
+
+  /**
+   * Sets the default {@link Authenticator} class to use when an {@link AuthenticatedURL} instance
+   * is created without specifying an authenticator.
+   *
+   * @param authenticator the authenticator class to use as default.
+   */
+  public static void setDefaultAuthenticator(Class<? extends Authenticator> authenticator) {
+    defaultAuthenticator = authenticator;
+  }
+
+  /**
+   * Returns the default {@link Authenticator} class to use when an {@link AuthenticatedURL} instance
+   * is created without specifying an authenticator.
+   *
+   * @return the authenticator class to use as default.
+   */
+  public static Class<? extends Authenticator> getDefaultAuthenticator() {
+    return defaultAuthenticator;
+  }
+
+  private Authenticator authenticator;
+  private ConnectionConfigurator connConfigurator;
+
+  /**
+   * Creates an {@link AuthenticatedURL}.
+   */
+  public AuthenticatedURL() {
+    this(null);
+  }
+
+  /**
+   * Creates an <code>AuthenticatedURL</code>.
+   *
+   * @param authenticator the {@link Authenticator} instance to use, if <code>null</code> a {@link
+   * KerberosAuthenticator} is used.
+   */
+  public AuthenticatedURL(Authenticator authenticator) {
+    this(authenticator, null);
+  }
+
+  /**
+   * Creates an <code>AuthenticatedURL</code>.
+   *
+   * @param authenticator the {@link Authenticator} instance to use, if <code>null</code> a {@link
+   * KerberosAuthenticator} is used.
+   * @param connConfigurator a connection configurator.
+   */
+  public AuthenticatedURL(Authenticator authenticator,
+                          ConnectionConfigurator connConfigurator) {
+    try {
+      this.authenticator = (authenticator != null) ? authenticator : defaultAuthenticator.newInstance();
+    } catch (Exception ex) {
+      throw new RuntimeException(ex);
+    }
+    this.connConfigurator = connConfigurator;
+    this.authenticator.setConnectionConfigurator(connConfigurator);
+  }
+
+  /**
+   * Returns the {@link Authenticator} instance used by the
+   * <code>AuthenticatedURL</code>.
+   *
+   * @return the {@link Authenticator} instance
+   */
+  protected Authenticator getAuthenticator() {
+    return authenticator;
+  }
+
+  /**
+   * Returns an authenticated {@link HttpURLConnection}.
+   *
+   * @param url the URL to connect to. Only HTTP/S URLs are supported.
+   * @param token the authentication token being used for the user.
+   *
+   * @return an authenticated {@link HttpURLConnection}.
+   *
+   * @throws IOException if an IO error occurred.
+   * @throws AuthenticationException if an authentication exception occurred.
+   */
+  public HttpURLConnection openConnection(URL url, Token token) throws IOException, AuthenticationException {
+    if (url == null) {
+      throw new IllegalArgumentException("url cannot be NULL");
+    }
+    if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) {
+      throw new IllegalArgumentException("url must be for a HTTP or HTTPS resource");
+    }
+    if (token == null) {
+      throw new IllegalArgumentException("token cannot be NULL");
+    }
+    authenticator.authenticate(url, token);
+    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+    if (connConfigurator != null) {
+      conn = connConfigurator.configure(conn);
+    }
+    injectToken(conn, token);
+    return conn;
+  }
+
+  /**
+   * Helper method that injects an authentication token to send with a connection.
+   *
+   * @param conn connection to inject the authentication token into.
+   * @param token authentication token to inject.
+   */
+  public static void injectToken(HttpURLConnection conn, Token token) {
+    String t = token.token;
+    if (t != null) {
+      if (!t.startsWith("\"")) {
+        t = "\"" + t + "\"";
+      }
+      conn.addRequestProperty("Cookie", AUTH_COOKIE_EQ + t);
+    }
+  }
+
+  /**
+   * Helper method that extracts an authentication token received from a connection.
+   * <p>
+   * This method is used by {@link Authenticator} implementations.
+   *
+   * @param conn connection to extract the authentication token from.
+   * @param token the authentication token.
+   *
+   * @throws IOException if an IO error occurred.
+   * @throws AuthenticationException if an authentication exception occurred.
+   */
+  public static void extractToken(HttpURLConnection conn, Token token) throws IOException, AuthenticationException {
+    int respCode = conn.getResponseCode();
+    if (respCode == HttpURLConnection.HTTP_OK
+        || respCode == HttpURLConnection.HTTP_CREATED
+        || respCode == HttpURLConnection.HTTP_ACCEPTED) {
+      Map<String, List<String>> headers = conn.getHeaderFields();
+      List<String> cookies = headers.get("Set-Cookie");
+      if (cookies != null) {
+        for (String cookie : cookies) {
+          if (cookie.startsWith(AUTH_COOKIE_EQ)) {
+            String value = cookie.substring(AUTH_COOKIE_EQ.length());
+            int separator = value.indexOf(";");
+            if (separator > -1) {
+              value = value.substring(0, separator);
+            }
+            if (value.length() > 0) {
+              token.set(value);
+            }
+          }
+        }
+      }
+    } else {
+      token.set(null);
+      throw new AuthenticationException("Authentication failed, status: " + conn.getResponseCode()
+          + ", message: " + conn.getResponseMessage());
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/AuthenticationException.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/AuthenticationException.java b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/AuthenticationException.java
new file mode 100644
index 0000000..62a5d38
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/AuthenticationException.java
@@ -0,0 +1,54 @@
+/**
+ * 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.hadoop.has.common.spnego;
+
+/**
+ * Borrow the class from Apache Hadoop
+ */
+
+/**
+ * Exception thrown when an authentication error occurrs.
+ */
+public class AuthenticationException extends Exception {
+  
+  static final long serialVersionUID = 0;
+
+  /**
+   * Creates an {@link AuthenticationException}.
+   *
+   * @param cause original exception.
+   */
+  public AuthenticationException(Throwable cause) {
+    super(cause);
+  }
+
+  /**
+   * Creates an {@link AuthenticationException}.
+   *
+   * @param msg exception message.
+   */
+  public AuthenticationException(String msg) {
+    super(msg);
+  }
+
+  /**
+   * Creates an {@link AuthenticationException}.
+   *
+   * @param msg exception message.
+   * @param cause original exception.
+   */
+  public AuthenticationException(String msg, Throwable cause) {
+    super(msg, cause);
+  }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/Authenticator.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/Authenticator.java b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/Authenticator.java
new file mode 100644
index 0000000..91eb1a0
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/Authenticator.java
@@ -0,0 +1,52 @@
+/**
+ * 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.hadoop.has.common.spnego;
+
+import org.apache.hadoop.has.common.util.ConnectionConfigurator;
+
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * Borrow the class from Apache Hadoop
+ */
+
+/**
+ * Interface for client authentication mechanisms.
+ * <p>
+ * Implementations are use-once instances, they don't need to be thread safe.
+ */
+public interface Authenticator {
+
+  /**
+   * Sets a {@link ConnectionConfigurator} instance to use for
+   * configuring connections.
+   *
+   * @param configurator the {@link ConnectionConfigurator} instance.
+   */
+  void setConnectionConfigurator(ConnectionConfigurator configurator);
+
+  /**
+   * Authenticates against a URL and returns a {@link AuthenticatedURL.Token} to be
+   * used by subsequent requests.
+   *
+   * @param url the URl to authenticate against.
+   * @param token the authentication token being used for the user.
+   *
+   * @throws IOException if an IO error occurred.
+   * @throws AuthenticationException if an authentication error occurred.
+   */
+  void authenticate(URL url, AuthenticatedURL.Token token) throws IOException, AuthenticationException;
+
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/KerberosAuthenticator.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/KerberosAuthenticator.java b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/KerberosAuthenticator.java
new file mode 100644
index 0000000..c785430
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/KerberosAuthenticator.java
@@ -0,0 +1,359 @@
+/**
+ * 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.hadoop.has.common.spnego;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.hadoop.has.common.util.ConnectionConfigurator;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosTicket;
+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.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.hadoop.has.common.util.PlatformName.IBM_JAVA;
+
+/**
+ * Borrow the class from Apache Hadoop
+ */
+
+/**
+ * The {@link KerberosAuthenticator} implements the Kerberos SPNEGO authentication sequence.
+ * <p>
+ * It uses the default principal for the Kerberos cache (normally set via kinit).
+ * <p>
+ */
+public class KerberosAuthenticator implements Authenticator {
+  
+  private static final Logger LOG = LoggerFactory.getLogger(KerberosAuthenticator.class);
+
+  /**
+   * HTTP header used by the SPNEGO server endpoint during an authentication sequence.
+   */
+  public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
+
+  /**
+   * HTTP header used by the SPNEGO client endpoint during an authentication sequence.
+   */
+  public static final String AUTHORIZATION = "Authorization";
+
+  /**
+   * HTTP header prefix used by the SPNEGO client/server endpoints during an authentication sequence.
+   */
+  public static final String NEGOTIATE = "Negotiate";
+
+  private static final String AUTH_HTTP_METHOD = "OPTIONS";
+
+  private static String keytabPrincipal = null;
+  private static String keytabFile = null;
+
+  /*
+  * Defines the Kerberos configuration that will be used to obtain the Kerberos principal from the
+  * Kerberos cache.
+  */
+  private static class KerberosConfiguration extends Configuration {
+
+    private static final String OS_LOGIN_MODULE_NAME;
+    private static final boolean WINDOWS = System.getProperty("os.name").startsWith("Windows");
+    private static final boolean IS_64_BIT = System.getProperty("os.arch").contains("64");
+    private static final boolean AIX = System.getProperty("os.name").equals("AIX");
+
+    /* Return the OS login module class name */
+    private static String getOSLoginModuleName() {
+      if (IBM_JAVA) {
+        if (WINDOWS) {
+          return IS_64_BIT ? "com.ibm.security.auth.module.Win64LoginModule"
+              : "com.ibm.security.auth.module.NTLoginModule";
+        } else if (AIX) {
+          return IS_64_BIT ? "com.ibm.security.auth.module.AIX64LoginModule"
+              : "com.ibm.security.auth.module.AIXLoginModule";
+        } else {
+          return "com.ibm.security.auth.module.LinuxLoginModule";
+        }
+      } else {
+        return WINDOWS ? "com.sun.security.auth.module.NTLoginModule"
+            : "com.sun.security.auth.module.UnixLoginModule";
+      }
+    }
+
+    static {
+      OS_LOGIN_MODULE_NAME = getOSLoginModuleName();
+    }
+
+    private static final AppConfigurationEntry OS_SPECIFIC_LOGIN =
+      new AppConfigurationEntry(OS_LOGIN_MODULE_NAME,
+                                AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+                                new HashMap<String, String>());
+
+    private static final Map<String, String> KEYTAB_KERBEROS_OPTIONS
+        = new HashMap<String, String>();
+    static {
+      if (IBM_JAVA) {
+        KEYTAB_KERBEROS_OPTIONS.put("credsType", "both");
+        KEYTAB_KERBEROS_OPTIONS.put("useKeytab",
+            prependFileAuthority(keytabFile));
+      } else {
+        KEYTAB_KERBEROS_OPTIONS.put("doNotPrompt", "true");
+        KEYTAB_KERBEROS_OPTIONS.put("useKeyTab", "true");
+        KEYTAB_KERBEROS_OPTIONS.put("storeKey", "true");
+        KEYTAB_KERBEROS_OPTIONS.put("keyTab", keytabFile);
+      }
+      KEYTAB_KERBEROS_OPTIONS.put("principal", keytabPrincipal);
+      KEYTAB_KERBEROS_OPTIONS.put("refreshKrb5Config", "true");
+      KEYTAB_KERBEROS_OPTIONS.put("debug", "false");
+    }
+
+    private static final AppConfigurationEntry USER_KERBEROS_LOGIN =
+      new AppConfigurationEntry(KerberosUtil.getKrb5LoginModuleName(),
+                                AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL,
+                                KEYTAB_KERBEROS_OPTIONS);
+
+    private static final AppConfigurationEntry[] USER_KERBEROS_CONF =
+      new AppConfigurationEntry[]{OS_SPECIFIC_LOGIN, USER_KERBEROS_LOGIN};
+
+    @Override
+    public AppConfigurationEntry[] getAppConfigurationEntry(String appName) {
+      return USER_KERBEROS_CONF;
+    }
+
+    private static String prependFileAuthority(String keytabPath) {
+      return keytabPath.startsWith("file://") ? keytabPath
+          : "file://" + keytabPath;
+    }
+  }
+  
+  private URL url;
+  private HttpURLConnection conn;
+  private Base64 base64;
+  private ConnectionConfigurator connConfigurator;
+
+  /**
+   * Sets a {@link ConnectionConfigurator} instance to use for
+   * configuring connections.
+   *
+   * @param configurator the {@link ConnectionConfigurator} instance.
+   */
+  @Override
+  public void setConnectionConfigurator(ConnectionConfigurator configurator) {
+    connConfigurator = configurator;
+  }
+
+  /**
+   * Performs SPNEGO authentication against the specified URL.
+   * <p>
+   * If a token is given it does a NOP and returns the given token.
+   * <p>
+   * If no token is given, it will perform the SPNEGO authentication sequence using an
+   * HTTP <code>OPTIONS</code> request.
+   *
+   * @param url the URl to authenticate against.
+   * @param token the authentication token being used for the user.
+   *
+   * @throws IOException if an IO error occurred.
+   * @throws AuthenticationException if an authentication error occurred.
+   */
+  @Override
+  public void authenticate(URL url, AuthenticatedURL.Token token)
+    throws IOException, AuthenticationException {
+
+    if (!token.isSet()) {
+      this.url = url;
+      base64 = new Base64(0);
+      conn = (HttpURLConnection) url.openConnection();
+      if (connConfigurator != null) {
+        conn = connConfigurator.configure(conn);
+      }
+      conn.setRequestMethod(AUTH_HTTP_METHOD);
+      conn.connect();
+      
+      boolean needFallback = false;
+      if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
+        LOG.debug("JDK performed authentication on our behalf.");
+        // If the JDK already did the SPNEGO back-and-forth for
+        // us, just pull out the token.
+        AuthenticatedURL.extractToken(conn, token);
+        if (isTokenKerberos(token)) {
+          return;
+        }
+        needFallback = true;
+      }
+      if (!needFallback && isNegotiate()) {
+        LOG.debug("Performing our own SPNEGO sequence.");
+        doSpnegoSequence(token);
+      } else {
+        throw new IOException("Should perform our own SPNEGO sequence");
+      }
+    }
+  }
+
+  public void setKeyTab(String keytabFile, String keytabPrincipal) {
+    this.keytabFile = keytabFile;
+    this.keytabPrincipal = keytabPrincipal;
+  }
+
+  /*
+   * Check if the passed token is of type "kerberos" or "kerberos-dt"
+   */
+  private boolean isTokenKerberos(AuthenticatedURL.Token token)
+      throws AuthenticationException {
+    if (token.isSet()) {
+      AuthToken aToken = AuthToken.parse(token.toString());
+      if (aToken.getType().equals("kerberos")
+          || aToken.getType().equals("kerberos-dt")) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /*
+  * Indicates if the response is starting a SPNEGO negotiation.
+  */
+  private boolean isNegotiate() throws IOException {
+    boolean negotiate = false;
+    if (conn.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
+      String authHeader = conn.getHeaderField(WWW_AUTHENTICATE);
+      negotiate = authHeader != null && authHeader.trim().startsWith(NEGOTIATE);
+    }
+    return negotiate;
+  }
+
+  /**
+   * Implements the SPNEGO authentication sequence interaction using the current default principal
+   * in the Kerberos cache (normally set via kinit).
+   *
+   * @param token the authentication token being used for the user.
+   *
+   * @throws IOException if an IO error occurred.
+   * @throws AuthenticationException if an authentication error occurred.
+   */
+  private void doSpnegoSequence(AuthenticatedURL.Token token) throws IOException, AuthenticationException {
+    try {
+      AccessControlContext context = AccessController.getContext();
+      Subject subject = Subject.getSubject(context);
+      if (subject == null
+          || (subject.getPrivateCredentials(KerberosKey.class).isEmpty()
+              && subject.getPrivateCredentials(KerberosTicket.class).isEmpty())) {
+        LOG.debug("No subject in context, logging in");
+        subject = new Subject();
+        LoginContext login = new LoginContext("", subject,
+            null, new KerberosConfiguration());
+        login.login();
+      }
+
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Using subject: " + subject);
+      }
+      Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
+
+        @Override
+        public Void run() throws Exception {
+          GSSContext gssContext = null;
+          try {
+            GSSManager gssManager = GSSManager.getInstance();
+            String servicePrincipal = KerberosUtil.getServicePrincipal("HTTP",
+                KerberosAuthenticator.this.url.getHost());
+            LOG.info("service principal is:" + servicePrincipal);
+            Oid oid = KerberosUtil.getOidInstance("NT_GSS_KRB5_PRINCIPAL");
+            GSSName serviceName = gssManager.createName(servicePrincipal,
+                                                        oid);
+            oid = KerberosUtil.getOidInstance("GSS_KRB5_MECH_OID");
+            gssContext = gssManager.createContext(serviceName, oid, null,
+                                                  GSSContext.DEFAULT_LIFETIME);
+            gssContext.requestCredDeleg(true);
+            gssContext.requestMutualAuth(true);
+
+            byte[] inToken = new byte[0];
+            byte[] outToken;
+            boolean established = false;
+
+            // Loop while the context is still not established
+            while (!established) {
+              outToken = gssContext.initSecContext(inToken, 0, inToken.length);
+              if (outToken != null) {
+                sendToken(outToken);
+              }
+
+              if (!gssContext.isEstablished()) {
+                inToken = readToken();
+              } else {
+                established = true;
+              }
+            }
+          } finally {
+            if (gssContext != null) {
+              gssContext.dispose();
+              gssContext = null;
+            }
+          }
+          return null;
+        }
+      });
+    } catch (PrivilegedActionException ex) {
+      throw new AuthenticationException(ex.getException());
+    } catch (LoginException ex) {
+      throw new AuthenticationException(ex);
+    }
+    AuthenticatedURL.extractToken(conn, token);
+  }
+
+  /*
+  * Sends the Kerberos token to the server.
+  */
+  private void sendToken(byte[] outToken) throws IOException {
+    String token = base64.encodeToString(outToken);
+    conn = (HttpURLConnection) url.openConnection();
+    if (connConfigurator != null) {
+      conn = connConfigurator.configure(conn);
+    }
+    conn.setRequestMethod(AUTH_HTTP_METHOD);
+    conn.setRequestProperty(AUTHORIZATION, NEGOTIATE + " " + token);
+    conn.connect();
+  }
+
+  /*
+  * Retrieves the Kerberos token returned by the server.
+  */
+  private byte[] readToken() throws IOException, AuthenticationException {
+    int status = conn.getResponseCode();
+    if (status == HttpURLConnection.HTTP_OK || status == HttpURLConnection.HTTP_UNAUTHORIZED) {
+      String authHeader = conn.getHeaderField(WWW_AUTHENTICATE);
+      if (authHeader == null || !authHeader.trim().startsWith(NEGOTIATE)) {
+        throw new AuthenticationException("Invalid SPNEGO sequence, '" + WWW_AUTHENTICATE
+            + "' header incorrect: " + authHeader);
+      }
+      String negotiation = authHeader.trim().substring((NEGOTIATE + " ").length()).trim();
+      return base64.decode(negotiation);
+    }
+    throw new AuthenticationException("Invalid SPNEGO sequence, status code: " + status);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/KerberosHasAuthenticator.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/KerberosHasAuthenticator.java b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/KerberosHasAuthenticator.java
new file mode 100644
index 0000000..7db0551
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/KerberosHasAuthenticator.java
@@ -0,0 +1,25 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.has.common.spnego;
+
+public class KerberosHasAuthenticator extends KerberosAuthenticator {
+
+    public KerberosHasAuthenticator(String keytabFile, String keytabPrincipal) {
+        setKeyTab(keytabFile, keytabPrincipal);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/KerberosUtil.java
----------------------------------------------------------------------
diff --git a/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/KerberosUtil.java b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/KerberosUtil.java
new file mode 100644
index 0000000..b6e330d
--- /dev/null
+++ b/has/has-common/src/main/java/org/apache/hadoop/has/common/spnego/KerberosUtil.java
@@ -0,0 +1,262 @@
+/**
+ * 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.hadoop.has.common.spnego;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.apache.kerby.kerberos.kerb.keytab.Keytab;
+import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.Oid;
+
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosTicket;
+import javax.security.auth.kerberos.KeyTab;
+
+import static org.apache.hadoop.has.common.util.PlatformName.IBM_JAVA;
+
+/**
+ * Borrow the class from Apache Hadoop
+ */
+
+public class KerberosUtil {
+
+  /* Return the Kerberos login module name */
+  public static String getKrb5LoginModuleName() {
+    return (IBM_JAVA)
+      ? "com.ibm.security.auth.module.Krb5LoginModule"
+      : "com.sun.security.auth.module.Krb5LoginModule";
+  }
+
+  public static Oid getOidInstance(String oidName)
+      throws ClassNotFoundException, GSSException, NoSuchFieldException,
+      IllegalAccessException {
+    Class<?> oidClass;
+    if (IBM_JAVA) {
+      if ("NT_GSS_KRB5_PRINCIPAL".equals(oidName)) {
+        // IBM JDK GSSUtil class does not have field for krb5 principal oid
+        return new Oid("1.2.840.113554.1.2.2.1");
+      }
+      oidClass = Class.forName("com.ibm.security.jgss.GSSUtil");
+    } else {
+      oidClass = Class.forName("sun.security.jgss.GSSUtil");
+    }
+    Field oidField = oidClass.getDeclaredField(oidName);
+    return (Oid) oidField.get(oidClass);
+  }
+
+  public static String getDefaultRealm() 
+      throws ClassNotFoundException, NoSuchMethodException, 
+      IllegalArgumentException, IllegalAccessException, 
+      InvocationTargetException {
+    Object kerbConf;
+    Class<?> classRef;
+    Method getInstanceMethod;
+    Method getDefaultRealmMethod;
+    if (IBM_JAVA) {
+      classRef = Class.forName("com.ibm.security.krb5.internal.Config");
+    } else {
+      classRef = Class.forName("sun.security.krb5.Config");
+    }
+    getInstanceMethod = classRef.getMethod("getInstance", new Class[0]);
+    kerbConf = getInstanceMethod.invoke(classRef, new Object[0]);
+    getDefaultRealmMethod = classRef.getDeclaredMethod("getDefaultRealm",
+        new Class[0]);
+    return (String) getDefaultRealmMethod.invoke(kerbConf, new Object[0]);
+  }
+
+  public static String getDefaultRealmProtected() {
+    String realmString = null;
+    try {
+      realmString = getDefaultRealm();
+    } catch (RuntimeException rte) {
+      //silently catch everything
+    } catch (Exception e) {
+      //silently return null
+    }
+    return realmString;
+  }
+
+  /*
+   * For a Service Host Principal specification, map the host's domain
+   * to kerberos realm, as specified by krb5.conf [domain_realm] mappings.
+   * Unfortunately the mapping routines are private to the security.krb5
+   * package, so have to construct a PrincipalName instance to derive the realm.
+   *
+   * Many things can go wrong with Kerberos configuration, and this is not
+   * the place to be throwing exceptions to help debug them.  Nor do we choose
+   * to make potentially voluminous logs on every call to a communications API.
+   * So we simply swallow all exceptions from the underlying libraries and
+   * return null if we can't get a good value for the realmString.
+   *
+   * @param shortprinc A service principal name with host fqdn as instance, e.g.
+   *     "HTTP/myhost.mydomain"
+   * @return String value of Kerberos realm, mapped from host fqdn
+   *     May be default realm, or may be null.
+   */
+  public static String getDomainRealm(String shortprinc) {
+    Class<?> classRef;
+    Object principalName; //of type sun.security.krb5.PrincipalName or IBM equiv
+    String realmString = null;
+    try {
+      if (IBM_JAVA) {
+        classRef = Class.forName("com.ibm.security.krb5.PrincipalName");
+      } else {
+        classRef = Class.forName("sun.security.krb5.PrincipalName");
+      }
+      int tKrbNtSrvHst = classRef.getField("KRB_NT_SRV_HST").getInt(null);
+      principalName = classRef.getConstructor(String.class, int.class).
+          newInstance(shortprinc, tKrbNtSrvHst);
+      realmString = (String) classRef.getMethod("getRealmString", new Class[0]).
+          invoke(principalName, new Object[0]);
+    } catch (RuntimeException rte) {
+      //silently catch everything
+    } catch (Exception e) {
+      //silently return default realm (which may itself be null)
+    }
+    if (null == realmString || realmString.equals("")) {
+      return getDefaultRealmProtected();
+    } else {
+      return realmString;
+    }
+  }
+
+  /* Return fqdn of the current host */
+  static String getLocalHostName() throws UnknownHostException {
+    return InetAddress.getLocalHost().getCanonicalHostName();
+  }
+  
+  /**
+   * Create Kerberos principal for a given service and hostname,
+   * inferring realm from the fqdn of the hostname. It converts
+   * hostname to lower case. If hostname is null or "0.0.0.0", it uses
+   * dynamically looked-up fqdn of the current host instead.
+   * If domain_realm mappings are inadequately specified, it will
+   * use default_realm, per usual Kerberos behavior.
+   * If default_realm also gives a null value, then a principal
+   * without realm will be returned, which by Kerberos definitions is
+   * just another way to specify default realm.
+   *
+   * @param service
+   *          Service for which you want to generate the principal.
+   * @param hostname
+   *          Fully-qualified domain name.
+   * @return Converted Kerberos principal name.
+   * @throws UnknownHostException
+   *           If no IP address for the local host could be found.
+   */
+  public static final String getServicePrincipal(String service,
+      String hostname)
+      throws UnknownHostException {
+    String fqdn = hostname;
+    String shortprinc = null;
+    String realmString = null;
+    if (null == fqdn || fqdn.equals("") || fqdn.equals("0.0.0.0")) {
+      fqdn = getLocalHostName();
+    }
+    // convert hostname to lowercase as kerberos does not work with hostnames
+    // with uppercase characters.
+    fqdn = fqdn.toLowerCase(Locale.US);
+    shortprinc = service + "/" + fqdn;
+    // Obtain the realm name inferred from the domain of the host
+    realmString = getDomainRealm(shortprinc);
+    if (null == realmString || realmString.equals("")) {
+      return shortprinc;
+    } else {
+      return shortprinc + "@" + realmString;
+    }
+  }
+
+  /**
+   * Get all the unique principals present in the keytabfile.
+   * 
+   * @param keytabFileName 
+   *          Name of the keytab file to be read.
+   * @return list of unique principals in the keytab.
+   * @throws IOException 
+   *          If keytab entries cannot be read from the file.
+   */
+  static final String[] getPrincipalNames(String keytabFileName) throws IOException {
+    Keytab keytab = Keytab.loadKeytab(new File(keytabFileName));
+    Set<String> principals = new HashSet<String>();
+    List<PrincipalName> entries = keytab.getPrincipals();
+    for (PrincipalName entry : entries) {
+      principals.add(entry.getName().replace("\\", "/"));
+    }
+    return principals.toArray(new String[0]);
+  }
+
+  /**
+   * Get all the unique principals from keytabfile which matches a pattern.
+   * 
+   * @param keytab Name of the keytab file to be read.
+   * @param pattern pattern to be matched.
+   * @return list of unique principals which matches the pattern.
+   * @throws IOException if cannot get the principal name
+   */
+  public static final String[] getPrincipalNames(String keytab,
+      Pattern pattern) throws IOException {
+    String[] principals = getPrincipalNames(keytab);
+    if (principals.length != 0) {
+      List<String> matchingPrincipals = new ArrayList<String>();
+      for (String principal : principals) {
+        if (pattern.matcher(principal).matches()) {
+          matchingPrincipals.add(principal);
+        }
+      }
+      principals = matchingPrincipals.toArray(new String[0]);
+    }
+    return principals;
+  }
+
+  /**
+   * Check if the subject contains Kerberos keytab related objects.
+   * The Kerberos keytab object attached in subject has been changed
+   * from KerberosKey (JDK 7) to KeyTab (JDK 8)
+   *
+   *
+   * @param subject subject to be checked
+   * @return true if the subject contains Kerberos keytab
+   */
+  public static boolean hasKerberosKeyTab(Subject subject) {
+    return !subject.getPrivateCredentials(KeyTab.class).isEmpty();
+  }
+
+  /**
+   * Check if the subject contains Kerberos ticket.
+   *
+   *
+   * @param subject subject to be checked
+   * @return true if the subject contains Kerberos ticket
+   */
+  public static boolean hasKerberosTicket(Subject subject) {
+    return !subject.getPrivateCredentials(KerberosTicket.class).isEmpty();
+  }
+}


Mime
View raw message