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 2018/01/22 03:28:30 UTC

directory-kerby git commit: DIRKRB-681 Add new LoginModule

Repository: directory-kerby
Updated Branches:
  refs/heads/trunk a8a284d9c -> 34ccabec6


DIRKRB-681 Add new LoginModule


Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/commit/34ccabec
Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/34ccabec
Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/34ccabec

Branch: refs/heads/trunk
Commit: 34ccabec68b2b83b683240801e3929ef43eec26e
Parents: a8a284d
Author: plusplusjiajia <ji...@intel.com>
Authored: Mon Jan 22 11:25:06 2018 +0800
Committer: plusplusjiajia <ji...@intel.com>
Committed: Mon Jan 22 11:25:06 2018 +0800

----------------------------------------------------------------------
 .../apache/kerby/has/client/HasLoginModule.java | 456 +++++++++++++++++++
 1 file changed, 456 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/34ccabec/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java
----------------------------------------------------------------------
diff --git a/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java b/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java
new file mode 100644
index 0000000..8debda5
--- /dev/null
+++ b/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java
@@ -0,0 +1,456 @@
+/**
+ * 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.kerby.has.client;
+
+import com.sun.security.auth.module.Krb5LoginModule;
+import org.apache.kerby.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.security.Principal;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This <code>LoginModule</code> authenticates users using tgt ticket
+ * The client's TGT will be retrieved from the API of HasClient
+ */
+public class HasLoginModule implements LoginModule {
+
+    public static final Logger LOG = LoggerFactory.getLogger(HasLoginModule.class);
+
+    Krb5LoginModule krb5LoginModule;
+
+    // initial state
+    private Subject subject;
+
+    // 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>.
+     *
+     * @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;
+
+        // 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
+     *
+     * @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();
+                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()
+        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);
+
+                // get the principal name from the ticket cache
+                if (cred != null && 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("*") && 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) {
+            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");
+                }
+
+                Set<Object> privCredSet = subject.getPrivateCredentials();
+                Set<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 && 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.
+     *
+     * @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;
+    }
+}


Re: directory-kerby git commit: DIRKRB-681 Add new LoginModule

Posted by Colm O hEigeartaigh <co...@apache.org>.
Great, thanks!

Colm.

On Fri, Jan 26, 2018 at 8:24 AM, Li, Jiajia <ji...@intel.com> wrote:

> The error occurred after upgrading the mvn version to 3.5.2, I've fixed
> it. Thanks Colm.
>
> Regards,
> Jiajia
>
> -----Original Message-----
> From: Colm O hEigeartaigh [mailto:coheigea@apache.org]
> Sent: Thursday, January 25, 2018 5:43 PM
> To: Li, Jiajia <ji...@intel.com>
> Cc: kerby@directory.apache.org
> Subject: Re: directory-kerby git commit: DIRKRB-681 Add new LoginModule
>
> Apache Maven 3.5.2 (138edd61fd100ec658bfa2d307c43b76940a5d7d;
> 2017-10-18T08:58:13+01:00)
> Maven home: /opt/apache-maven-3.5.2
> Java version: 1.8.0_151, vendor: Oracle Corporation Java home:
> /opt/jdk1.8.0_151/jre Default locale: en_GB, platform encoding: UTF-8 OS
> name: "linux", version: "4.13.0-25-generic", arch: "amd64", family:
> "unix"
>
> Is the modernizer plugin running when you do "mvn clean install"?
>
> Colm.
>
> On Thu, Jan 25, 2018 at 8:13 AM, Li, Jiajia <ji...@intel.com> wrote:
>
> > Hi Colm,
> >
> >
> >
> > I can’t  reproduce this build failure, could you provide the java and
> > OS version?
> >
> >
> >
> > Thanks,
> >
> > Jiajia
> >
> >
> >
> > *From:* Colm O hEigeartaigh [mailto:coheigea@apache.org]
> > *Sent:* Wednesday, January 24, 2018 11:30 PM
> > *To:* kerby@directory.apache.org; Li, Jiajia <ji...@intel.com>
> > *Subject:* Re: directory-kerby git commit: DIRKRB-681 Add new
> > LoginModule
> >
> >
> >
> > Hi Jiajia,
> >
> > This commit is causing a build failure:
> >
> > [INFO] --- modernizer-maven-plugin:1.5.0:modernizer (modernizer-check)
> > @ has-client --- [ERROR]
> > /home/colm/src/apache/directory-kerby/has-project/
> > has-client/src/main/java/org/apache/kerby/has/client/
> HasLoginModule.java:160:
> > Prefer java.lang.StringBuilder
> > [ERROR] /home/colm/src/apache/directory-kerby/has-project/
> > has-client/src/main/java/org/apache/kerby/has/client/
> HasLoginModule.java:163:
> > Prefer java.lang.StringBuilder
> >
> > Colm.
> >
> >
> >
> > On Mon, Jan 22, 2018 at 3:28 AM, <pl...@apache.org> wrote:
> >
> > Repository: directory-kerby
> > Updated Branches:
> >   refs/heads/trunk a8a284d9c -> 34ccabec6
> >
> >
> > DIRKRB-681 Add new LoginModule
> >
> >
> > Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
> > Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/
> > commit/34ccabec
> > Tree:
> > http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/34ccabec
> > Diff:
> > http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/34ccabec
> >
> > Branch: refs/heads/trunk
> > Commit: 34ccabec68b2b83b683240801e3929ef43eec26e
> > Parents: a8a284d
> > Author: plusplusjiajia <ji...@intel.com>
> > Authored: Mon Jan 22 11:25:06 2018 +0800
> > Committer: plusplusjiajia <ji...@intel.com>
> > Committed: Mon Jan 22 11:25:06 2018 +0800
> >
> > ----------------------------------------------------------------------
> >  .../apache/kerby/has/client/HasLoginModule.java | 456
> > +++++++++++++++++++
> >  1 file changed, 456 insertions(+)
> > ----------------------------------------------------------------------
> >
> >
> > http://git-wip-us.apache.org/repos/asf/directory-kerby/
> > blob/34ccabec/has-project/has-client/src/main/java/org/
> > apache/kerby/has/client/HasLoginModule.java
> > ----------------------------------------------------------------------
> > diff --git
> > a/has-project/has-client/src/main/java/org/apache/kerby/has/client/Has
> > LoginModule.java
> > b/has-project/has-client/src/main/java/org/apache/kerby/
> > has/client/HasLoginModule.java
> > new file mode 100644
> > index 0000000..8debda5
> > --- /dev/null
> > +++ b/has-project/has-client/src/main/java/org/apache/kerby/
> > has/client/HasLoginModule.java
> > @@ -0,0 +1,456 @@
> > +/**
> > + * 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.kerby.has.client;
> > +
> > +import com.sun.security.auth.module.Krb5LoginModule;
> > +import org.apache.kerby.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.security.Principal;
> > +import java.util.Date;
> > +import java.util.Iterator;
> > +import java.util.Map;
> > +import java.util.Set;
> > +
> > +/**
> > + * This <code>LoginModule</code> authenticates users using tgt ticket
> > + * The client's TGT will be retrieved from the API of HasClient  */
> > +public class HasLoginModule implements LoginModule {
> > +
> > +    public static final Logger LOG = LoggerFactory.getLogger(
> > HasLoginModule.class);
> > +
> > +    Krb5LoginModule krb5LoginModule;
> > +
> > +    // initial state
> > +    private Subject subject;
> > +
> > +    // 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>.
> > +     *
> > +     * @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;
> > +
> > +        // 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
> > +     *
> > +     * @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();
> > +                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()
> > +        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);
> > +
> > +                // get the principal name from the ticket cache
> > +                if (cred != null && 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("*")
> > && 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) {
> > +            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");
> > +                }
> > +
> > +                Set<Object> privCredSet =
> > + subject.getPrivateCredentials(
> > );
> > +                Set<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 &&
> > + 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.
> > +     *
> > +     * @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;
> > +    }
> > +}
> >
> >
> >
> >
> >
> > --
> >
> > Colm O hEigeartaigh
> >
> > Talend Community Coder
> > http://coders.talend.com
> >
>
>
>
> --
> Colm O hEigeartaigh
>
> Talend Community Coder
> http://coders.talend.com
>



-- 
Colm O hEigeartaigh

Talend Community Coder
http://coders.talend.com

RE: directory-kerby git commit: DIRKRB-681 Add new LoginModule

Posted by "Li, Jiajia" <ji...@intel.com>.
The error occurred after upgrading the mvn version to 3.5.2, I've fixed it. Thanks Colm.

Regards,
Jiajia

-----Original Message-----
From: Colm O hEigeartaigh [mailto:coheigea@apache.org] 
Sent: Thursday, January 25, 2018 5:43 PM
To: Li, Jiajia <ji...@intel.com>
Cc: kerby@directory.apache.org
Subject: Re: directory-kerby git commit: DIRKRB-681 Add new LoginModule

Apache Maven 3.5.2 (138edd61fd100ec658bfa2d307c43b76940a5d7d;
2017-10-18T08:58:13+01:00)
Maven home: /opt/apache-maven-3.5.2
Java version: 1.8.0_151, vendor: Oracle Corporation Java home: /opt/jdk1.8.0_151/jre Default locale: en_GB, platform encoding: UTF-8 OS name: "linux", version: "4.13.0-25-generic", arch: "amd64", family:
"unix"

Is the modernizer plugin running when you do "mvn clean install"?

Colm.

On Thu, Jan 25, 2018 at 8:13 AM, Li, Jiajia <ji...@intel.com> wrote:

> Hi Colm,
>
>
>
> I can’t  reproduce this build failure, could you provide the java and 
> OS version?
>
>
>
> Thanks,
>
> Jiajia
>
>
>
> *From:* Colm O hEigeartaigh [mailto:coheigea@apache.org]
> *Sent:* Wednesday, January 24, 2018 11:30 PM
> *To:* kerby@directory.apache.org; Li, Jiajia <ji...@intel.com>
> *Subject:* Re: directory-kerby git commit: DIRKRB-681 Add new 
> LoginModule
>
>
>
> Hi Jiajia,
>
> This commit is causing a build failure:
>
> [INFO] --- modernizer-maven-plugin:1.5.0:modernizer (modernizer-check) 
> @ has-client --- [ERROR] 
> /home/colm/src/apache/directory-kerby/has-project/
> has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java:160:
> Prefer java.lang.StringBuilder
> [ERROR] /home/colm/src/apache/directory-kerby/has-project/
> has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java:163:
> Prefer java.lang.StringBuilder
>
> Colm.
>
>
>
> On Mon, Jan 22, 2018 at 3:28 AM, <pl...@apache.org> wrote:
>
> Repository: directory-kerby
> Updated Branches:
>   refs/heads/trunk a8a284d9c -> 34ccabec6
>
>
> DIRKRB-681 Add new LoginModule
>
>
> Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
> Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/
> commit/34ccabec
> Tree: 
> http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/34ccabec
> Diff: 
> http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/34ccabec
>
> Branch: refs/heads/trunk
> Commit: 34ccabec68b2b83b683240801e3929ef43eec26e
> Parents: a8a284d
> Author: plusplusjiajia <ji...@intel.com>
> Authored: Mon Jan 22 11:25:06 2018 +0800
> Committer: plusplusjiajia <ji...@intel.com>
> Committed: Mon Jan 22 11:25:06 2018 +0800
>
> ----------------------------------------------------------------------
>  .../apache/kerby/has/client/HasLoginModule.java | 456 
> +++++++++++++++++++
>  1 file changed, 456 insertions(+)
> ----------------------------------------------------------------------
>
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/34ccabec/has-project/has-client/src/main/java/org/
> apache/kerby/has/client/HasLoginModule.java
> ----------------------------------------------------------------------
> diff --git 
> a/has-project/has-client/src/main/java/org/apache/kerby/has/client/Has
> LoginModule.java 
> b/has-project/has-client/src/main/java/org/apache/kerby/
> has/client/HasLoginModule.java
> new file mode 100644
> index 0000000..8debda5
> --- /dev/null
> +++ b/has-project/has-client/src/main/java/org/apache/kerby/
> has/client/HasLoginModule.java
> @@ -0,0 +1,456 @@
> +/**
> + * 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.kerby.has.client;
> +
> +import com.sun.security.auth.module.Krb5LoginModule;
> +import org.apache.kerby.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.security.Principal;
> +import java.util.Date;
> +import java.util.Iterator;
> +import java.util.Map;
> +import java.util.Set;
> +
> +/**
> + * This <code>LoginModule</code> authenticates users using tgt ticket
> + * The client's TGT will be retrieved from the API of HasClient  */ 
> +public class HasLoginModule implements LoginModule {
> +
> +    public static final Logger LOG = LoggerFactory.getLogger(
> HasLoginModule.class);
> +
> +    Krb5LoginModule krb5LoginModule;
> +
> +    // initial state
> +    private Subject subject;
> +
> +    // 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>.
> +     *
> +     * @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;
> +
> +        // 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
> +     *
> +     * @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();
> +                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()
> +        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);
> +
> +                // get the principal name from the ticket cache
> +                if (cred != null && 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("*")
> && 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) {
> +            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");
> +                }
> +
> +                Set<Object> privCredSet = 
> + subject.getPrivateCredentials(
> );
> +                Set<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 && 
> + 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.
> +     *
> +     * @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;
> +    }
> +}
>
>
>
>
>
> --
>
> Colm O hEigeartaigh
>
> Talend Community Coder
> http://coders.talend.com
>



--
Colm O hEigeartaigh

Talend Community Coder
http://coders.talend.com

Re: directory-kerby git commit: DIRKRB-681 Add new LoginModule

Posted by Colm O hEigeartaigh <co...@apache.org>.
Apache Maven 3.5.2 (138edd61fd100ec658bfa2d307c43b76940a5d7d;
2017-10-18T08:58:13+01:00)
Maven home: /opt/apache-maven-3.5.2
Java version: 1.8.0_151, vendor: Oracle Corporation
Java home: /opt/jdk1.8.0_151/jre
Default locale: en_GB, platform encoding: UTF-8
OS name: "linux", version: "4.13.0-25-generic", arch: "amd64", family:
"unix"

Is the modernizer plugin running when you do "mvn clean install"?

Colm.

On Thu, Jan 25, 2018 at 8:13 AM, Li, Jiajia <ji...@intel.com> wrote:

> Hi Colm,
>
>
>
> I can’t  reproduce this build failure, could you provide the java and OS
> version?
>
>
>
> Thanks,
>
> Jiajia
>
>
>
> *From:* Colm O hEigeartaigh [mailto:coheigea@apache.org]
> *Sent:* Wednesday, January 24, 2018 11:30 PM
> *To:* kerby@directory.apache.org; Li, Jiajia <ji...@intel.com>
> *Subject:* Re: directory-kerby git commit: DIRKRB-681 Add new LoginModule
>
>
>
> Hi Jiajia,
>
> This commit is causing a build failure:
>
> [INFO] --- modernizer-maven-plugin:1.5.0:modernizer (modernizer-check) @
> has-client ---
> [ERROR] /home/colm/src/apache/directory-kerby/has-project/
> has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java:160:
> Prefer java.lang.StringBuilder
> [ERROR] /home/colm/src/apache/directory-kerby/has-project/
> has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java:163:
> Prefer java.lang.StringBuilder
>
> Colm.
>
>
>
> On Mon, Jan 22, 2018 at 3:28 AM, <pl...@apache.org> wrote:
>
> Repository: directory-kerby
> Updated Branches:
>   refs/heads/trunk a8a284d9c -> 34ccabec6
>
>
> DIRKRB-681 Add new LoginModule
>
>
> Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
> Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/
> commit/34ccabec
> Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/34ccabec
> Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/34ccabec
>
> Branch: refs/heads/trunk
> Commit: 34ccabec68b2b83b683240801e3929ef43eec26e
> Parents: a8a284d
> Author: plusplusjiajia <ji...@intel.com>
> Authored: Mon Jan 22 11:25:06 2018 +0800
> Committer: plusplusjiajia <ji...@intel.com>
> Committed: Mon Jan 22 11:25:06 2018 +0800
>
> ----------------------------------------------------------------------
>  .../apache/kerby/has/client/HasLoginModule.java | 456 +++++++++++++++++++
>  1 file changed, 456 insertions(+)
> ----------------------------------------------------------------------
>
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/34ccabec/has-project/has-client/src/main/java/org/
> apache/kerby/has/client/HasLoginModule.java
> ----------------------------------------------------------------------
> diff --git a/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java
> b/has-project/has-client/src/main/java/org/apache/kerby/
> has/client/HasLoginModule.java
> new file mode 100644
> index 0000000..8debda5
> --- /dev/null
> +++ b/has-project/has-client/src/main/java/org/apache/kerby/
> has/client/HasLoginModule.java
> @@ -0,0 +1,456 @@
> +/**
> + * 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.kerby.has.client;
> +
> +import com.sun.security.auth.module.Krb5LoginModule;
> +import org.apache.kerby.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.security.Principal;
> +import java.util.Date;
> +import java.util.Iterator;
> +import java.util.Map;
> +import java.util.Set;
> +
> +/**
> + * This <code>LoginModule</code> authenticates users using tgt ticket
> + * The client's TGT will be retrieved from the API of HasClient
> + */
> +public class HasLoginModule implements LoginModule {
> +
> +    public static final Logger LOG = LoggerFactory.getLogger(
> HasLoginModule.class);
> +
> +    Krb5LoginModule krb5LoginModule;
> +
> +    // initial state
> +    private Subject subject;
> +
> +    // 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>.
> +     *
> +     * @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;
> +
> +        // 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
> +     *
> +     * @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();
> +                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()
> +        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);
> +
> +                // get the principal name from the ticket cache
> +                if (cred != null && 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("*")
> && 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) {
> +            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");
> +                }
> +
> +                Set<Object> privCredSet = subject.getPrivateCredentials(
> );
> +                Set<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 && 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.
> +     *
> +     * @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;
> +    }
> +}
>
>
>
>
>
> --
>
> Colm O hEigeartaigh
>
> Talend Community Coder
> http://coders.talend.com
>



-- 
Colm O hEigeartaigh

Talend Community Coder
http://coders.talend.com

RE: directory-kerby git commit: DIRKRB-681 Add new LoginModule

Posted by "Li, Jiajia" <ji...@intel.com>.
Hi Colm,

I can’t  reproduce this build failure, could you provide the java and OS version?

Thanks,
Jiajia

From: Colm O hEigeartaigh [mailto:coheigea@apache.org]
Sent: Wednesday, January 24, 2018 11:30 PM
To: kerby@directory.apache.org; Li, Jiajia <ji...@intel.com>
Subject: Re: directory-kerby git commit: DIRKRB-681 Add new LoginModule

Hi Jiajia,
This commit is causing a build failure:

[INFO] --- modernizer-maven-plugin:1.5.0:modernizer (modernizer-check) @ has-client ---
[ERROR] /home/colm/src/apache/directory-kerby/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java:160: Prefer java.lang.StringBuilder
[ERROR] /home/colm/src/apache/directory-kerby/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java:163: Prefer java.lang.StringBuilder

Colm.

On Mon, Jan 22, 2018 at 3:28 AM, <pl...@apache.org>> wrote:
Repository: directory-kerby
Updated Branches:
  refs/heads/trunk a8a284d9c -> 34ccabec6


DIRKRB-681 Add new LoginModule


Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/commit/34ccabec
Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/34ccabec
Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/34ccabec

Branch: refs/heads/trunk
Commit: 34ccabec68b2b83b683240801e3929ef43eec26e
Parents: a8a284d
Author: plusplusjiajia <ji...@intel.com>>
Authored: Mon Jan 22 11:25:06 2018 +0800
Committer: plusplusjiajia <ji...@intel.com>>
Committed: Mon Jan 22 11:25:06 2018 +0800

----------------------------------------------------------------------
 .../apache/kerby/has/client/HasLoginModule.java | 456 +++++++++++++++++++
 1 file changed, 456 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/34ccabec/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java
----------------------------------------------------------------------
diff --git a/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java b/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java
new file mode 100644
index 0000000..8debda5
--- /dev/null
+++ b/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java
@@ -0,0 +1,456 @@
+/**
+ * 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.kerby.has.client;
+
+import com.sun.security.auth.module.Krb5LoginModule;
+import org.apache.kerby.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.security.Principal;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This <code>LoginModule</code> authenticates users using tgt ticket
+ * The client's TGT will be retrieved from the API of HasClient
+ */
+public class HasLoginModule implements LoginModule {
+
+    public static final Logger LOG = LoggerFactory.getLogger(HasLoginModule.class);
+
+    Krb5LoginModule krb5LoginModule;
+
+    // initial state
+    private Subject subject;
+
+    // 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>.
+     *
+     * @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;
+
+        // 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
+     *
+     * @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();
+                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()
+        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);
+
+                // get the principal name from the ticket cache
+                if (cred != null && 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("*") && 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) {
+            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");
+                }
+
+                Set<Object> privCredSet = subject.getPrivateCredentials();
+                Set<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 && 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.
+     *
+     * @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;
+    }
+}



--
Colm O hEigeartaigh

Talend Community Coder
http://coders.talend.com

Re: directory-kerby git commit: DIRKRB-681 Add new LoginModule

Posted by Colm O hEigeartaigh <co...@apache.org>.
Hi Jiajia,

This commit is causing a build failure:

[INFO] --- modernizer-maven-plugin:1.5.0:modernizer (modernizer-check) @
has-client ---
[ERROR]
/home/colm/src/apache/directory-kerby/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java:160:
Prefer java.lang.StringBuilder
[ERROR]
/home/colm/src/apache/directory-kerby/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java:163:
Prefer java.lang.StringBuilder


Colm.

On Mon, Jan 22, 2018 at 3:28 AM, <pl...@apache.org> wrote:

> Repository: directory-kerby
> Updated Branches:
>   refs/heads/trunk a8a284d9c -> 34ccabec6
>
>
> DIRKRB-681 Add new LoginModule
>
>
> Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
> Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/
> commit/34ccabec
> Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/34ccabec
> Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/34ccabec
>
> Branch: refs/heads/trunk
> Commit: 34ccabec68b2b83b683240801e3929ef43eec26e
> Parents: a8a284d
> Author: plusplusjiajia <ji...@intel.com>
> Authored: Mon Jan 22 11:25:06 2018 +0800
> Committer: plusplusjiajia <ji...@intel.com>
> Committed: Mon Jan 22 11:25:06 2018 +0800
>
> ----------------------------------------------------------------------
>  .../apache/kerby/has/client/HasLoginModule.java | 456 +++++++++++++++++++
>  1 file changed, 456 insertions(+)
> ----------------------------------------------------------------------
>
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/34ccabec/has-project/has-client/src/main/java/org/
> apache/kerby/has/client/HasLoginModule.java
> ----------------------------------------------------------------------
> diff --git a/has-project/has-client/src/main/java/org/apache/kerby/has/client/HasLoginModule.java
> b/has-project/has-client/src/main/java/org/apache/kerby/
> has/client/HasLoginModule.java
> new file mode 100644
> index 0000000..8debda5
> --- /dev/null
> +++ b/has-project/has-client/src/main/java/org/apache/kerby/
> has/client/HasLoginModule.java
> @@ -0,0 +1,456 @@
> +/**
> + * 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.kerby.has.client;
> +
> +import com.sun.security.auth.module.Krb5LoginModule;
> +import org.apache.kerby.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.security.Principal;
> +import java.util.Date;
> +import java.util.Iterator;
> +import java.util.Map;
> +import java.util.Set;
> +
> +/**
> + * This <code>LoginModule</code> authenticates users using tgt ticket
> + * The client's TGT will be retrieved from the API of HasClient
> + */
> +public class HasLoginModule implements LoginModule {
> +
> +    public static final Logger LOG = LoggerFactory.getLogger(
> HasLoginModule.class);
> +
> +    Krb5LoginModule krb5LoginModule;
> +
> +    // initial state
> +    private Subject subject;
> +
> +    // 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>.
> +     *
> +     * @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;
> +
> +        // 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
> +     *
> +     * @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();
> +                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()
> +        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);
> +
> +                // get the principal name from the ticket cache
> +                if (cred != null && 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("*")
> && 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) {
> +            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");
> +                }
> +
> +                Set<Object> privCredSet = subject.getPrivateCredentials(
> );
> +                Set<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 && 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.
> +     *
> +     * @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;
> +    }
> +}
>
>


-- 
Colm O hEigeartaigh

Talend Community Coder
http://coders.talend.com