package me.prettyprint.cassandra.connection.security; import java.io.IOException; import java.net.Socket; import java.security.PrivilegedAction; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSManager; import org.ietf.jgss.GSSName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class KerberosHelper { private static Logger log = LoggerFactory.getLogger(KerberosHelper.class); /** * Log in using the service name for jaas.conf file and .keytab instead of specifying username and password * * @param serviceName service name defined in jass.conf file * @return the authenticated Subject or <code>null</code> is the authentication failed * @throws LoginException if there is any error during the login */ public static Subject loginService(String serviceName) throws LoginException { LoginContext loginCtx = new LoginContext(serviceName, new CallbackHandler() { // as we use .keytab file there is no need to specify any options in callback public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { } }); loginCtx.login(); return loginCtx.getSubject(); } /** * * @param serviceName service name defined in jass.conf file * @param username username * @param password password * @return the authenticated Subject or <code>null</code> is the authentication failed * @throws LoginException if there is any error during the login */ public static Subject loginService(String serviceName, String username, String password) throws LoginException { LoginContext loginCtx = new LoginContext(serviceName, new LoginCallbackHandler(username, password)); loginCtx.login(); return loginCtx.getSubject(); } /** * Authenticate client to use this service and return secure context * * @param socket * The socket used for communication * @param subject * The Kerberos service subject * @param servicePrincipalName * Service principal name * * @return context if authorized or null */ public static GSSContext authenticateClient(final Socket socket, Subject subject, final String servicePrincipalName) { return Subject.doAs(subject, new PrivilegedAction<GSSContext>() { public GSSContext run() { try { GSSManager manager = GSSManager.getInstance(); GSSName peerName = manager.createName(servicePrincipalName, GSSName.NT_HOSTBASED_SERVICE); GSSContext context = manager.createContext(peerName, null, null, GSSContext.DEFAULT_LIFETIME); // Loop while the context is still not established while (!context.isEstablished()) { context.initSecContext(socket.getInputStream(), socket.getOutputStream()); } return context; } catch (Exception e) { log.error("Unable to authenticate client against Kerberos", e); return null; } } }); } public static String getSourcePrinciple(GSSContext context) { try { return context.getSrcName().toString(); } catch (GSSException e) { throw new RuntimeException(e); } } /** * Password callback handler for resolving password/usernames for a JAAS login. * * @author patricioe (Patricio Echague - patricioe@gmail.com) */ static class LoginCallbackHandler implements CallbackHandler { public LoginCallbackHandler() { super(); } public LoginCallbackHandler( String name, String password) { super(); this.username = name; this.password = password; } public LoginCallbackHandler( String password) { super(); this.password = password; } private String password; private String username; /** * Handles the callbacks, and sets the user/password. * @param callbacks the callbacks to handle * @throws IOException if an input or output error occurs. */ public void handle( Callback[] callbacks) throws IOException, UnsupportedCallbackException { for ( int i=0; i<callbacks.length; i++) { if ( callbacks[i] instanceof NameCallback && username != null) { NameCallback nc = (NameCallback) callbacks[i]; nc.setName( username); } else if ( callbacks[i] instanceof PasswordCallback) { PasswordCallback pc = (PasswordCallback) callbacks[i]; pc.setPassword( password.toCharArray()); } else { /*throw new UnsupportedCallbackException( callbacks[i], "Unrecognized Callback");*/ } } } } }