Index: java/org/apache/catalina/authenticator/Constants.java =================================================================== --- java/org/apache/catalina/authenticator/Constants.java (revision 925628) +++ java/org/apache/catalina/authenticator/Constants.java (working copy) @@ -28,6 +28,7 @@ public static final String CERT_METHOD = "CLIENT_CERT"; public static final String DIGEST_METHOD = "DIGEST"; public static final String FORM_METHOD = "FORM"; + public static final String SPNEGO_METHOD="SPNEGO"; // User data constraints for transport guarantee public static final String NONE_TRANSPORT = "NONE"; Index: java/org/apache/catalina/authenticator/SpnegoAuthenticator.java =================================================================== --- java/org/apache/catalina/authenticator/SpnegoAuthenticator.java (revision 0) +++ java/org/apache/catalina/authenticator/SpnegoAuthenticator.java (revision 0) @@ -0,0 +1,127 @@ +package org.apache.catalina.authenticator; + +import java.io.IOException; +import java.security.Principal; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.deploy.LoginConfig; +import org.apache.catalina.util.Base64; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.buf.CharChunk; +import org.apache.tomcat.util.buf.MessageBytes; + +/** + * A custom authenticator which provides Spnego Login capabilities in Tomcat. + * In web.xml use the SPNEGO to invoke this authenticator. + * @author Ashish Jain + * + */ +public class SpnegoAuthenticator extends AuthenticatorBase { + + private static Log log = LogFactory.getLog(SpnegoAuthenticator.class); + + /** + * Authenticate bytes. + */ + public static final byte[] AUTHENTICATE_BYTES = { + (byte) 'W', + (byte) 'W', + (byte) 'W', + (byte) '-', + (byte) 'A', + (byte) 'u', + (byte) 't', + (byte) 'h', + (byte) 'e', + (byte) 'n', + (byte) 't', + (byte) 'i', + (byte) 'c', + (byte) 'a', + (byte) 't', + (byte) 'e' + }; + + @Override + protected boolean authenticate(Request request, Response response, LoginConfig config) throws IOException { + HttpServletResponse httpResponse = response.getResponse(); + HttpServletRequest httpRequest = request.getRequest(); + String header = httpRequest.getHeader("Authorization"); + if (header == null) { + httpResponse.setHeader("WWW-Authenticate", "Negotiate"); + httpResponse.setStatus(401); + return (false); + } else if (header != null && header.startsWith("Negotiate")) { + Principal principal = request.getUserPrincipal(); + String username = header.substring(10); + String password = null; + principal = context.getRealm().authenticate(username, password); + if (principal != null) { + register(request, response, principal, Constants.SPNEGO_METHOD, username, password); + return (true); + } else + request.getCoyoteRequest().getMimeHeaders().removeHeader("authorization"); + } + + // Validate any credentials already included with this request + String username = null; + String password = null; + Principal principal = request.getUserPrincipal(); + + MessageBytes authorization = request.getCoyoteRequest().getMimeHeaders().getValue("authorization"); + + if (authorization != null) { + authorization.toBytes(); + ByteChunk authorizationBC = authorization.getByteChunk(); + if (authorizationBC.startsWithIgnoreCase("basic ", 0)) { + authorizationBC.setOffset(authorizationBC.getOffset() + 6); + // FIXME: Add trimming + // authorizationBC.trim(); + + CharChunk authorizationCC = authorization.getCharChunk(); + Base64.decode(authorizationBC, authorizationCC); + + // Get username and password + int colon = authorizationCC.indexOf(':'); + if (colon < 0) { + username = authorizationCC.toString(); + } else { + char[] buf = authorizationCC.getBuffer(); + username = new String(buf, 0, colon); + password = new String(buf, colon + 1, authorizationCC.getEnd() - colon - 1); + } + + authorizationBC.setOffset(authorizationBC.getOffset() - 6); + } + principal = context.getRealm().authenticate(username, password); + if (principal != null) { + register(request, response, principal, Constants.SPNEGO_METHOD, username, password); + return (true); + } + } + + // Send an "unauthorized" response and an appropriate challenge + MessageBytes authenticate = response.getCoyoteResponse().getMimeHeaders().addValue(AUTHENTICATE_BYTES, 0, + AUTHENTICATE_BYTES.length); + CharChunk authenticateCC = authenticate.getCharChunk(); + authenticateCC.append("Basic realm=\""); + if (config.getRealmName() == null) { + authenticateCC.append(request.getServerName()); + authenticateCC.append(':'); + authenticateCC.append(Integer.toString(request.getServerPort())); + } else { + authenticateCC.append(config.getRealmName()); + } + authenticateCC.append('\"'); + authenticate.toChars(); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + // response.flushBuffer(); + return (false); + } +} Index: java/org/apache/catalina/startup/Authenticators.properties =================================================================== --- java/org/apache/catalina/startup/Authenticators.properties (revision 925628) +++ java/org/apache/catalina/startup/Authenticators.properties (working copy) @@ -18,3 +18,4 @@ DIGEST=org.apache.catalina.authenticator.DigestAuthenticator FORM=org.apache.catalina.authenticator.FormAuthenticator NONE=org.apache.catalina.authenticator.NonLoginAuthenticator +SPNEGO=org.apache.catalina.authenticator.SpnegoAuthenticator