Index: java/org/apache/catalina/Realm.java =================================================================== --- java/org/apache/catalina/Realm.java (revision 1666747) +++ 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. * Index: java/org/apache/catalina/authenticator/BasicAuthenticator.java =================================================================== --- java/org/apache/catalina/authenticator/BasicAuthenticator.java (revision 1666747) +++ java/org/apache/catalina/authenticator/BasicAuthenticator.java (working copy) @@ -94,6 +94,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.BASIC_AUTH, remoteUser, null); + return (true); + } + return (false); + } + // Validate any credentials already included with this request MessageBytes authorization = request.getCoyoteRequest().getMimeHeaders() Index: java/org/apache/catalina/authenticator/Constants.java =================================================================== --- java/org/apache/catalina/authenticator/Constants.java (revision 1666747) +++ java/org/apache/catalina/authenticator/Constants.java (working copy) @@ -57,6 +57,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 Index: java/org/apache/catalina/authenticator/DigestAuthenticator.java =================================================================== --- java/org/apache/catalina/authenticator/DigestAuthenticator.java (revision 1666747) +++ java/org/apache/catalina/authenticator/DigestAuthenticator.java (working copy) @@ -238,6 +238,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(), Index: java/org/apache/catalina/authenticator/FormAuthenticator.java =================================================================== --- java/org/apache/catalina/authenticator/FormAuthenticator.java (revision 1666747) +++ java/org/apache/catalina/authenticator/FormAuthenticator.java (working copy) @@ -169,6 +169,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); Index: java/org/apache/catalina/authenticator/SSLAuthenticator.java =================================================================== --- java/org/apache/catalina/authenticator/SSLAuthenticator.java (revision 1666747) +++ java/org/apache/catalina/authenticator/SSLAuthenticator.java (working copy) @@ -65,6 +65,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 Index: java/org/apache/catalina/authenticator/SpnegoAuthenticator.java =================================================================== --- java/org/apache/catalina/authenticator/SpnegoAuthenticator.java (revision 1666747) +++ java/org/apache/catalina/authenticator/SpnegoAuthenticator.java (working copy) @@ -159,6 +159,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"); Index: java/org/apache/catalina/realm/RealmBase.java =================================================================== --- java/org/apache/catalina/realm/RealmBase.java (revision 1666747) +++ java/org/apache/catalina/realm/RealmBase.java (working copy) @@ -416,6 +416,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. * Index: java/org/apache/coyote/ajp/AjpProcessor.java =================================================================== --- java/org/apache/coyote/ajp/AjpProcessor.java (revision 1666747) +++ java/org/apache/coyote/ajp/AjpProcessor.java (working copy) @@ -303,6 +303,16 @@ /** + * Use Tomcat authorization ? + */ + protected boolean tomcatAuthorization = true; + public boolean getTomcatAuthorization() { return tomcatAuthorization; } + public void setTomcatAuthorization(boolean tomcatAuthorization) { + this.tomcatAuthorization = tomcatAuthorization; + } + + + /** * Required secret. */ private String requiredSecret = null; @@ -1162,11 +1172,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; Index: webapps/docs/config/ajp.xml =================================================================== --- webapps/docs/config/ajp.xml (revision 1666747) +++ webapps/docs/config/ajp.xml (working copy) @@ -430,9 +430,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.

+
+ Index: webapps/docs/security-howto.xml =================================================================== --- webapps/docs/security-howto.xml (revision 1666747) +++ webapps/docs/security-howto.xml (working copy) @@ -287,7 +287,8 @@ rel="nofollow">Qualys SSL/TLS test is a useful tool for configuring these settings.

-

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.

Index: webapps/docs/windows-auth-howto.xml =================================================================== --- webapps/docs/windows-auth-howto.xml (revision 1666747) +++ webapps/docs/windows-auth-howto.xml (working copy) @@ -301,7 +301,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.