--- java/org/apache/catalina/Realm.java (revision 1666658) +++ java/org/apache/catalina/Realm.java (working copy) @@ -76,6 +76,15 @@ /** + * Return the Principal associated with the specified username, if there + * is one; otherwise return null. + * + * @param username Username of the Principal to look up + */ + public Principal authenticate(String username); + + + /** * Return the Principal associated with the specified username and * credentials, if there is one; otherwise return null. * --- java/org/apache/catalina/authenticator/BasicAuthenticator.java (revision 1666658) +++ java/org/apache/catalina/authenticator/BasicAuthenticator.java (working copy) @@ -120,6 +120,21 @@ return true; } + // If we are preauthenticated, run the authorization + String remoteUser = (String) + request.getCoyoteRequest().getAttribute(Constants.REQ_REMOTE_USER_NOTE); + if (remoteUser != null) { + if (log.isDebugEnabled()) + log.debug("Already authenticated '" + remoteUser + "', authorizing"); + principal = context.getRealm().authenticate(remoteUser); + if (principal != null) { + register(request, response, principal, + HttpServletRequest.BASIC_AUTH, remoteUser, null); + return (true); + } + return (false); + } + // Validate any credentials already included with this request String username = null; String password = null; --- java/org/apache/catalina/authenticator/Constants.java (revision 1666658) +++ java/org/apache/catalina/authenticator/Constants.java (working copy) @@ -85,6 +85,12 @@ public static final String REQ_SSOID_NOTE = "org.apache.catalina.request.SSOID"; + /** + * The notes key to track the principal name when the user is + * pre-authenticated, but where tomcat must perform authorization. + */ + public static final String REQ_REMOTE_USER_NOTE = + "org.apache.catalina.request.REMOTE_USER"; // ---------------------------------------------------------- Session Notes --- java/org/apache/catalina/authenticator/DigestAuthenticator.java (revision 1666658) +++ java/org/apache/catalina/authenticator/DigestAuthenticator.java (working copy) @@ -291,6 +291,21 @@ } */ + // If we are preauthenticated, run the authorization + String remoteUser = (String) + request.getCoyoteRequest().getAttribute(Constants.REQ_REMOTE_USER_NOTE); + if (remoteUser != null) { + if (log.isDebugEnabled()) + log.debug("Already authenticated '" + remoteUser + "', authorizing"); + principal = context.getRealm().authenticate(remoteUser); + if (principal != null) { + register(request, response, principal, + HttpServletRequest.DIGEST_AUTH, remoteUser, null); + return (true); + } + return (false); + } + // Validate any credentials already included with this request String authorization = request.getHeader("authorization"); DigestInfo digestInfo = new DigestInfo(getOpaque(), getNonceValidity(), --- java/org/apache/catalina/authenticator/FormAuthenticator.java (revision 1666658) +++ java/org/apache/catalina/authenticator/FormAuthenticator.java (working copy) @@ -184,6 +184,22 @@ } } + // If we are preauthenticated, run the authorization + String remoteUser = (String) + request.getCoyoteRequest().getAttribute(Constants.REQ_REMOTE_USER_NOTE); + if (remoteUser != null) { + if (log.isDebugEnabled()) + log.debug("Already authenticated '" + remoteUser + "', authorizing"); + principal = context.getRealm().authenticate(remoteUser); + if (principal != null) { + session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal); + register(request, response, principal, + HttpServletRequest.FORM_AUTH, remoteUser, null); + return (true); + } + return (false); + } + // Have we authenticated this user before but have caching disabled? if (!cache) { session = request.getSessionInternal(true); --- java/org/apache/catalina/authenticator/SSLAuthenticator.java (revision 1666658) +++ java/org/apache/catalina/authenticator/SSLAuthenticator.java (working copy) @@ -98,6 +98,21 @@ return (true); } + // If we are preauthenticated, run the authorization + String remoteUser = (String) + request.getCoyoteRequest().getAttribute(Constants.REQ_REMOTE_USER_NOTE); + if (remoteUser != null) { + if (containerLog.isDebugEnabled()) + containerLog.debug("Already authenticated '" + remoteUser + "', authorizing"); + principal = context.getRealm().authenticate(remoteUser); + if (principal != null) { + register(request, response, principal, + HttpServletRequest.CLIENT_CERT_AUTH, remoteUser, null); + return (true); + } + return (false); + } + // NOTE: We don't try to reauthenticate using any existing SSO session, // because that will only work if the original authentication was // BASIC or FORM, which are less secure than the CLIENT_CERT auth-type --- java/org/apache/catalina/authenticator/SpnegoAuthenticator.java (revision 1666658) +++ java/org/apache/catalina/authenticator/SpnegoAuthenticator.java (working copy) @@ -167,6 +167,21 @@ } } + // If we are preauthenticated, run the authorization + String remoteUser = (String) + request.getCoyoteRequest().getAttribute(Constants.REQ_REMOTE_USER_NOTE); + if (remoteUser != null) { + if (log.isDebugEnabled()) + log.debug("Already authenticated '" + remoteUser + "', authorizing"); + principal = context.getRealm().authenticate(remoteUser); + if (principal != null) { + register(request, response, principal, + Constants.SPNEGO_METHOD, remoteUser, null); + return (true); + } + return (false); + } + MessageBytes authorization = request.getCoyoteRequest().getMimeHeaders() .getValue("authorization"); --- java/org/apache/catalina/realm/RealmBase.java (revision 1666658) +++ java/org/apache/catalina/realm/RealmBase.java (working copy) @@ -347,6 +347,28 @@ /** + * Return the Principal associated with the specified username, if there + * is one; otherwise return null. + * + * @param username Username of the Principal to look up + */ + @Override + public Principal authenticate(String username) { + + if (username == null) { + return null; + } + + if (containerLog.isTraceEnabled()) { + containerLog.trace(sm.getString("realmBase.authenticateSuccess", + username)); + } + + return getPrincipal(username); + } + + + /** * Return the Principal associated with the specified username and * credentials, if there is one; otherwise return null. * --- java/org/apache/coyote/ajp/AbstractAjpProcessor.java (revision 1666658) +++ java/org/apache/coyote/ajp/AbstractAjpProcessor.java (working copy) @@ -277,6 +277,16 @@ /** + * Use Tomcat authorization ? + */ + protected boolean tomcatAuthorization = false; + public boolean getTomcatAuthorization() { return tomcatAuthorization; } + public void setTomcatAuthorization(boolean tomcatAuthorization) { + this.tomcatAuthorization = tomcatAuthorization; + } + + + /** * Required secret. */ protected String requiredSecret = null; @@ -828,11 +838,17 @@ break; case Constants.SC_A_REMOTE_USER : - if (tomcatAuthentication) { + if (tomcatAuthorization) { + requestHeaderMessage.getBytes(tmpMB); + request.setAttribute( + org.apache.catalina.authenticator.Constants.REQ_REMOTE_USER_NOTE, + tmpMB.toString()); + } + else if (tomcatAuthentication) { // ignore server requestHeaderMessage.getBytes(tmpMB); } else { - requestHeaderMessage.getBytes(request.getRemoteUser()); + requestHeaderMessage.getBytes(request.getRemoteUser()); } break; --- java/org/apache/coyote/ajp/AbstractAjpProtocol.java (revision 1666658) +++ java/org/apache/coyote/ajp/AbstractAjpProtocol.java (working copy) @@ -52,6 +52,13 @@ } + protected boolean tomcatAuthorization = false; + public boolean getTomcatAuthorization() { return tomcatAuthorization; } + public void setTomcatAuthorization(boolean tomcatAuthorization) { + this.tomcatAuthorization = tomcatAuthorization; + } + + /** * Required secret. */ --- java/org/apache/coyote/ajp/AjpAprProtocol.java (revision 1666658) +++ java/org/apache/coyote/ajp/AjpAprProtocol.java (working copy) @@ -146,6 +146,7 @@ AjpAprProcessor processor = new AjpAprProcessor(proto.packetSize, (AprEndpoint)proto.endpoint); processor.setAdapter(proto.adapter); processor.setTomcatAuthentication(proto.tomcatAuthentication); + processor.setTomcatAuthorization(proto.tomcatAuthorization); processor.setRequiredSecret(proto.requiredSecret); processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); processor.setClientCertProvider(proto.getClientCertProvider()); --- java/org/apache/coyote/ajp/AjpNioProtocol.java (revision 1666658) +++ java/org/apache/coyote/ajp/AjpNioProtocol.java (working copy) @@ -172,6 +172,7 @@ AjpNioProcessor processor = new AjpNioProcessor(proto.packetSize, (NioEndpoint)proto.endpoint); processor.setAdapter(proto.adapter); processor.setTomcatAuthentication(proto.tomcatAuthentication); + processor.setTomcatAuthorization(proto.tomcatAuthorization); processor.setRequiredSecret(proto.requiredSecret); processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); processor.setClientCertProvider(proto.getClientCertProvider()); --- java/org/apache/coyote/ajp/AjpProtocol.java (revision 1666658) +++ java/org/apache/coyote/ajp/AjpProtocol.java (working copy) @@ -134,6 +134,7 @@ AjpProcessor processor = new AjpProcessor(proto.packetSize, (JIoEndpoint)proto.endpoint); processor.setAdapter(proto.adapter); processor.setTomcatAuthentication(proto.tomcatAuthentication); + processor.setTomcatAuthorization(proto.tomcatAuthorization); processor.setRequiredSecret(proto.requiredSecret); processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); processor.setClientCertProvider(proto.getClientCertProvider()); --- webapps/docs/config/ajp.xml (revision 1666658) +++ webapps/docs/config/ajp.xml (working copy) @@ -432,9 +432,18 @@

If set to true, the authentication will be done in Tomcat. Otherwise, the authenticated principal will be propagated from the native webserver and used for authorization in Tomcat. - The default value is true.

+ The default value is true. If + tomcatAuthorization is set to true this + attribute has no effect.

+ +

If set to true, the authenticated principal will be + propagated from the native webserver and considered already authenticated + in Tomcat. Authorization will then be performed by Tomcat as normal. + The default value is false.

+
+ --- webapps/docs/security-howto.xml (revision 1666658) +++ webapps/docs/security-howto.xml (working copy) @@ -276,7 +276,8 @@ the list of available ciphers. Secure environments will normally want to configure a more limited set of ciphers.

-

The tomcatAuthentication attribute is used with the +

The tomcatAuthentication and + tomcatAuthorization attributes are used with the AJP connectors to determine if Tomcat should authenticate the user or if authentication can be delegated to the reverse proxy that will then pass the authenticated username to Tomcat as part of the AJP protocol.

--- webapps/docs/windows-auth-howto.xml (revision 1666658) +++ webapps/docs/windows-auth-howto.xml (working copy) @@ -299,7 +299,9 @@
  • Configure IIS to use Windows authentication
  • Configure Tomcat to use the authentication user information from IIS by setting the tomcatAuthentication attribute on the - AJP connector to false.
  • + AJP connector to false. Alternatively, set the + tomcatAuthorization attribute to true to allow Windows to + authenticate, while Tomcat performs the authorization.