Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Ready for 6.0.33

  • Loading branch information...
commit 3a7e54bd1c3ffdb3a9a4da054f1c24d3f83a3db1 2 parents 57f4642 + a62183b
Jean-Frederic Clere authored
View
26 STATUS.txt
@@ -45,19 +45,6 @@ PATCHES PROPOSED TO BACKPORT:
and fix it later if needed? I think that actually nobody besides the release manager
uses this, so I am letting this pass.
-* Expose the new WebappLoader flag in the VirtualWebappLoader,
- but allow alternative name searchVirtualFirst to make it
- consistent with the "virtual" terminology.
- Now you can decide, whether the virtual paths will
- be searched before the webapp or after it.
- If searched before, external resources take precendence
- over internal ones. Before that change one couldn't overwrite
- resources already present in the webapp.
- http://svn.apache.org/viewvc?view=revision&revision=936825
- http://people.apache.org/~rjung/patches/2010-05-14-loader-backport-r936825.patch
- +1: rjung, funkman, kkolinko
- -1:
-
* Backport JSP unloading patch (BZ48358).
The patch has substantially changed since the original version.
Original revisions are: 937787, 1028377, 1028389, 1028396, 1028861, 1028862, 1028863,
@@ -73,12 +60,6 @@ PATCHES PROPOSED TO BACKPORT:
We can stall this item until we get some feedback about 7.0.5.
-1:
-* Add additional configuration options to the DIGEST authenticator
- http://people.apache.org/~markt/patches/2011-04-01-digest-tc6.patch
- +1: markt
- +1: schultz : if s/nOnce/nonce/g for the whole file, not just some of it
- -1:
-
* Add StuckThreadDetectionValve
https://github.com/sylvainlaurent/tomcat60/commit/252334f958877221ecb2dc64ee0fd12bb77e360b
+1: slaurent
@@ -102,10 +83,3 @@ PATCHES PROPOSED TO BACKPORT:
I wonder whether ConcurrentHashMap.DEFAULT_CONCURRENCY_LEVEL which is 16 is enough.
- getStuckThreadIds() returns a list of ids. It might be useful to
have a similar method that returns Thread.getName() names.
-
-* Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=51515
- Prevent immediate socket close when comet is used over HTTPS
- http://people.apache.org/~markt/patches/2011-07-22-bug51515-tc6.patch
- (note: The only change to NioEndpoint is adding a sync)
- +1: markt
- -1:
View
507 java/org/apache/catalina/authenticator/DigestAuthenticator.java
@@ -23,11 +23,14 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
+import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServletResponse;
+import org.apache.catalina.LifecycleException;
import org.apache.catalina.Realm;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
@@ -47,8 +50,8 @@
* @version $Id$
*/
-public class DigestAuthenticator
- extends AuthenticatorBase {
+public class DigestAuthenticator extends AuthenticatorBase {
+
private static Log log = LogFactory.getLog(DigestAuthenticator.class);
@@ -67,6 +70,11 @@
"org.apache.catalina.authenticator.DigestAuthenticator/1.0";
+ /**
+ * Tomcat's DIGEST implementation only supports auth quality of protection.
+ */
+ protected static final String QOP = "auth";
+
// ----------------------------------------------------------- Constructors
@@ -92,17 +100,49 @@ public DigestAuthenticator() {
/**
+ * List of client nonce values currently being tracked
+ */
+ protected Map<String,NonceInfo> cnonces;
+
+
+ /**
+ * Maximum number of client nonces to keep in the cache. If not specified,
+ * the default value of 1000 is used.
+ */
+ protected int cnonceCacheSize = 1000;
+
+
+ /**
* Private key.
*/
- protected String key = "Catalina";
+ protected String key = null;
- // ------------------------------------------------------------- Properties
+ /**
+ * How long server nonces are valid for in milliseconds. Defaults to 5
+ * minutes.
+ */
+ protected long nonceValidity = 5 * 60 * 1000;
+
+
+ /**
+ * Opaque string.
+ */
+ protected String opaque;
/**
+ * Should the URI be validated as required by RFC2617? Can be disabled in
+ * reverse proxies where the proxy has modified the URI.
+ */
+ protected boolean validateUri = true;
+
+ // ------------------------------------------------------------- Properties
+
+ /**
* Return descriptive information about this Valve implementation.
*/
+ @Override
public String getInfo() {
return (info);
@@ -110,9 +150,58 @@ public String getInfo() {
}
- // --------------------------------------------------------- Public Methods
+ public int getCnonceCacheSize() {
+ return cnonceCacheSize;
+ }
+ public void setCnonceCacheSize(int cnonceCacheSize) {
+ this.cnonceCacheSize = cnonceCacheSize;
+ }
+
+
+ public String getKey() {
+ return key;
+ }
+
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+
+ public long getNonceValidity() {
+ return nonceValidity;
+ }
+
+
+ public void setNonceValidity(long nonceValidity) {
+ this.nonceValidity = nonceValidity;
+ }
+
+
+ public String getOpaque() {
+ return opaque;
+ }
+
+
+ public void setOpaque(String opaque) {
+ this.opaque = opaque;
+ }
+
+
+ public boolean isValidateUri() {
+ return validateUri;
+ }
+
+
+ public void setValidateUri(boolean validateUri) {
+ this.validateUri = validateUri;
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
/**
* Authenticate the user making this request, based on the specified
* login configuration. Return <code>true</code> if any specified
@@ -126,6 +215,7 @@ public String getInfo() {
*
* @exception IOException if an input/output error occurs
*/
+ @Override
public boolean authenticate(Request request,
Response response,
LoginConfig config)
@@ -172,8 +262,13 @@ public boolean authenticate(Request request,
// Validate any credentials already included with this request
String authorization = request.getHeader("authorization");
+ DigestInfo digestInfo = new DigestInfo(getOpaque(), getNonceValidity(),
+ getKey(), cnonces, isValidateUri());
if (authorization != null) {
- principal = findPrincipal(request, authorization, context.getRealm());
+ if (digestInfo.validate(request, authorization, config)) {
+ principal = digestInfo.authenticate(context.getRealm());
+ }
+
if (principal != null) {
String username = parseUsername(authorization);
register(request, response, principal,
@@ -185,11 +280,12 @@ public boolean authenticate(Request request,
// Send an "unauthorized" response and an appropriate challenge
- // Next, generate a nOnce token (that is a token which is supposed
+ // Next, generate a nonce token (that is a token which is supposed
// to be unique).
- String nOnce = generateNOnce(request);
+ String nonce = generateNonce(request);
- setAuthenticateHeader(request, response, config, nOnce);
+ setAuthenticateHeader(request, response, config, nonce,
+ digestInfo.isNonceStale());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
// hres.flushBuffer();
return (false);
@@ -201,92 +297,6 @@ public boolean authenticate(Request request,
/**
- * Parse the specified authorization credentials, and return the
- * associated Principal that these credentials authenticate (if any)
- * from the specified Realm. If there is no such Principal, return
- * <code>null</code>.
- *
- * @param request HTTP servlet request
- * @param authorization Authorization credentials from this request
- * @param realm Realm used to authenticate Principals
- */
- protected static Principal findPrincipal(Request request,
- String authorization,
- Realm realm) {
-
- //System.out.println("Authorization token : " + authorization);
- // Validate the authorization credentials format
- if (authorization == null)
- return (null);
- if (!authorization.startsWith("Digest "))
- return (null);
- authorization = authorization.substring(7).trim();
-
- // Bugzilla 37132: http://issues.apache.org/bugzilla/show_bug.cgi?id=37132
- String[] tokens = authorization.split(",(?=(?:[^\"]*\"[^\"]*\")+$)");
-
- String userName = null;
- String realmName = null;
- String nOnce = null;
- String nc = null;
- String cnonce = null;
- String qop = null;
- String uri = null;
- String response = null;
- String method = request.getMethod();
-
- for (int i = 0; i < tokens.length; i++) {
- String currentToken = tokens[i];
- if (currentToken.length() == 0)
- continue;
-
- int equalSign = currentToken.indexOf('=');
- if (equalSign < 0)
- return null;
- String currentTokenName =
- currentToken.substring(0, equalSign).trim();
- String currentTokenValue =
- currentToken.substring(equalSign + 1).trim();
- if ("username".equals(currentTokenName))
- userName = removeQuotes(currentTokenValue);
- if ("realm".equals(currentTokenName))
- realmName = removeQuotes(currentTokenValue, true);
- if ("nonce".equals(currentTokenName))
- nOnce = removeQuotes(currentTokenValue);
- if ("nc".equals(currentTokenName))
- nc = removeQuotes(currentTokenValue);
- if ("cnonce".equals(currentTokenName))
- cnonce = removeQuotes(currentTokenValue);
- if ("qop".equals(currentTokenName))
- qop = removeQuotes(currentTokenValue);
- if ("uri".equals(currentTokenName))
- uri = removeQuotes(currentTokenValue);
- if ("response".equals(currentTokenName))
- response = removeQuotes(currentTokenValue);
- }
-
- if ( (userName == null) || (realmName == null) || (nOnce == null)
- || (uri == null) || (response == null) )
- return null;
-
- // Second MD5 digest used to calculate the digest :
- // MD5(Method + ":" + uri)
- String a2 = method + ":" + uri;
- //System.out.println("A2:" + a2);
-
- byte[] buffer = null;
- synchronized (md5Helper) {
- buffer = md5Helper.digest(a2.getBytes());
- }
- String md5a2 = md5Encoder.encode(buffer);
-
- return (realm.authenticate(userName, response, nOnce, nc, cnonce, qop,
- realmName, md5a2));
-
- }
-
-
- /**
* Parse the username from the specified authorization string. If none
* can be identified, return <code>null</code>
*
@@ -294,7 +304,6 @@ protected static Principal findPrincipal(Request request,
*/
protected String parseUsername(String authorization) {
- //System.out.println("Authorization token : " + authorization);
// Validate the authorization credentials format
if (authorization == null)
return (null);
@@ -354,20 +363,20 @@ protected static String removeQuotes(String quotedString) {
*
* @param request HTTP Servlet request
*/
- protected String generateNOnce(Request request) {
+ protected String generateNonce(Request request) {
long currentTime = System.currentTimeMillis();
- String nOnceValue = request.getRemoteAddr() + ":" +
- currentTime + ":" + key;
+
+ String ipTimeKey =
+ request.getRemoteAddr() + ":" + currentTime + ":" + getKey();
- byte[] buffer = null;
+ byte[] buffer;
synchronized (md5Helper) {
- buffer = md5Helper.digest(nOnceValue.getBytes());
+ buffer = md5Helper.digest(ipTimeKey.getBytes());
}
- nOnceValue = md5Encoder.encode(buffer);
- return nOnceValue;
+ return currentTime + ":" + md5Encoder.encode(buffer);
}
@@ -379,7 +388,7 @@ protected String generateNOnce(Request request) {
* WWW-Authenticate = "WWW-Authenticate" ":" "Digest"
* digest-challenge
*
- * digest-challenge = 1#( realm | [ domain ] | nOnce |
+ * digest-challenge = 1#( realm | [ domain ] | nonce |
* [ digest-opaque ] |[ stale ] | [ algorithm ] )
*
* realm = "realm" "=" realm-value
@@ -396,29 +405,303 @@ protected String generateNOnce(Request request) {
* @param response HTTP Servlet response
* @param config Login configuration describing how authentication
* should be performed
- * @param nOnce nonce token
+ * @param nonce nonce token
*/
protected void setAuthenticateHeader(Request request,
Response response,
LoginConfig config,
- String nOnce) {
+ String nonce,
+ boolean isNonceStale) {
// Get the realm name
String realmName = config.getRealmName();
if (realmName == null)
realmName = REALM_NAME;
- byte[] buffer = null;
- synchronized (md5Helper) {
- buffer = md5Helper.digest(nOnce.getBytes());
+ String authenticateHeader;
+ if (isNonceStale) {
+ authenticateHeader = "Digest realm=\"" + realmName + "\", " +
+ "qop=\"" + QOP + "\", nonce=\"" + nonce + "\", " + "opaque=\"" +
+ getOpaque() + "\", stale=true";
+ } else {
+ authenticateHeader = "Digest realm=\"" + realmName + "\", " +
+ "qop=\"" + QOP + "\", nonce=\"" + nonce + "\", " + "opaque=\"" +
+ getOpaque() + "\"";
}
- String authenticateHeader = "Digest realm=\"" + realmName + "\", "
- + "qop=\"auth\", nonce=\"" + nOnce + "\", " + "opaque=\""
- + md5Encoder.encode(buffer) + "\"";
response.setHeader("WWW-Authenticate", authenticateHeader);
}
+ // ------------------------------------------------------- Lifecycle Methods
+
+ @Override
+ public void start() throws LifecycleException {
+ super.start();
+
+ // Generate a random secret key
+ if (getKey() == null) {
+ setKey(generateSessionId());
+ }
+
+ // Generate the opaque string the same way
+ if (getOpaque() == null) {
+ setOpaque(generateSessionId());
+ }
+
+ cnonces = new LinkedHashMap<String, DigestAuthenticator.NonceInfo>() {
+
+ private static final long serialVersionUID = 1L;
+ private static final long LOG_SUPPRESS_TIME = 5 * 60 * 1000;
+
+ private long lastLog = 0;
+
+ @Override
+ protected boolean removeEldestEntry(
+ Map.Entry<String,NonceInfo> eldest) {
+ // This is called from a sync so keep it simple
+ long currentTime = System.currentTimeMillis();
+ if (size() > getCnonceCacheSize()) {
+ if (lastLog < currentTime &&
+ currentTime - eldest.getValue().getTimestamp() <
+ getNonceValidity()) {
+ // Replay attack is possible
+ log.warn(sm.getString(
+ "digestAuthenticator.cacheRemove"));
+ lastLog = currentTime + LOG_SUPPRESS_TIME;
+ }
+ return true;
+ }
+ return false;
+ }
+ };
+ }
+
+ private static class DigestInfo {
+
+ private String opaque;
+ private long nonceValidity;
+ private String key;
+ private Map<String,NonceInfo> cnonces;
+ private boolean validateUri = true;
+
+ private String userName = null;
+ private String method = null;
+ private String uri = null;
+ private String response = null;
+ private String nonce = null;
+ private String nc = null;
+ private String cnonce = null;
+ private String realmName = null;
+ private String qop = null;
+
+ private boolean nonceStale = false;
+
+
+ public DigestInfo(String opaque, long nonceValidity, String key,
+ Map<String,NonceInfo> cnonces, boolean validateUri) {
+ this.opaque = opaque;
+ this.nonceValidity = nonceValidity;
+ this.key = key;
+ this.cnonces = cnonces;
+ this.validateUri = validateUri;
+ }
+
+ public boolean validate(Request request, String authorization,
+ LoginConfig config) {
+ // Validate the authorization credentials format
+ if (authorization == null) {
+ return false;
+ }
+ if (!authorization.startsWith("Digest ")) {
+ return false;
+ }
+ authorization = authorization.substring(7).trim();
+
+ // Bugzilla 37132: http://issues.apache.org/bugzilla/show_bug.cgi?id=37132
+ String[] tokens = authorization.split(",(?=(?:[^\"]*\"[^\"]*\")+$)");
+
+ method = request.getMethod();
+ String opaque = null;
+
+ for (int i = 0; i < tokens.length; i++) {
+ String currentToken = tokens[i];
+ if (currentToken.length() == 0)
+ continue;
+
+ int equalSign = currentToken.indexOf('=');
+ if (equalSign < 0) {
+ return false;
+ }
+ String currentTokenName =
+ currentToken.substring(0, equalSign).trim();
+ String currentTokenValue =
+ currentToken.substring(equalSign + 1).trim();
+ if ("username".equals(currentTokenName))
+ userName = removeQuotes(currentTokenValue);
+ if ("realm".equals(currentTokenName))
+ realmName = removeQuotes(currentTokenValue, true);
+ if ("nonce".equals(currentTokenName))
+ nonce = removeQuotes(currentTokenValue);
+ if ("nc".equals(currentTokenName))
+ nc = removeQuotes(currentTokenValue);
+ if ("cnonce".equals(currentTokenName))
+ cnonce = removeQuotes(currentTokenValue);
+ if ("qop".equals(currentTokenName))
+ qop = removeQuotes(currentTokenValue);
+ if ("uri".equals(currentTokenName))
+ uri = removeQuotes(currentTokenValue);
+ if ("response".equals(currentTokenName))
+ response = removeQuotes(currentTokenValue);
+ if ("opaque".equals(currentTokenName))
+ opaque = removeQuotes(currentTokenValue);
+ }
+
+ if ( (userName == null) || (realmName == null) || (nonce == null)
+ || (uri == null) || (response == null) ) {
+ return false;
+ }
+
+ // Validate the URI - should match the request line sent by client
+ if (validateUri) {
+ String uriQuery;
+ String query = request.getQueryString();
+ if (query == null) {
+ uriQuery = request.getRequestURI();
+ } else {
+ uriQuery = request.getRequestURI() + "?" + query;
+ }
+ if (!uri.equals(uriQuery)) {
+ return false;
+ }
+ }
+
+ // Validate the Realm name
+ String lcRealm = config.getRealmName();
+ if (lcRealm == null) {
+ lcRealm = REALM_NAME;
+ }
+ if (!lcRealm.equals(realmName)) {
+ return false;
+ }
+
+ // Validate the opaque string
+ if (!this.opaque.equals(opaque)) {
+ return false;
+ }
+
+ // Validate nonce
+ int i = nonce.indexOf(":");
+ if (i < 0 || (i + 1) == nonce.length()) {
+ return false;
+ }
+ long nonceTime;
+ try {
+ nonceTime = Long.parseLong(nonce.substring(0, i));
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ String md5clientIpTimeKey = nonce.substring(i + 1);
+ long currentTime = System.currentTimeMillis();
+ if ((currentTime - nonceTime) > nonceValidity) {
+ nonceStale = true;
+ return false;
+ }
+ String serverIpTimeKey =
+ request.getRemoteAddr() + ":" + nonceTime + ":" + key;
+ byte[] buffer = null;
+ synchronized (md5Helper) {
+ buffer = md5Helper.digest(serverIpTimeKey.getBytes());
+ }
+ String md5ServerIpTimeKey = md5Encoder.encode(buffer);
+ if (!md5ServerIpTimeKey.equals(md5clientIpTimeKey)) {
+ return false;
+ }
+
+ // Validate qop
+ if (qop != null && !QOP.equals(qop)) {
+ return false;
+ }
+
+ // Validate cnonce and nc
+ // Check if presence of nc and nonce is consistent with presence of qop
+ if (qop == null) {
+ if (cnonce != null || nc != null) {
+ return false;
+ }
+ } else {
+ if (cnonce == null || nc == null) {
+ return false;
+ }
+ if (nc.length() != 8) {
+ return false;
+ }
+ long count;
+ try {
+ count = Long.parseLong(nc, 16);
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ NonceInfo info;
+ synchronized (cnonces) {
+ info = cnonces.get(cnonce);
+ }
+ if (info == null) {
+ info = new NonceInfo();
+ } else {
+ if (count <= info.getCount()) {
+ return false;
+ }
+ }
+ info.setCount(count);
+ info.setTimestamp(currentTime);
+ synchronized (cnonces) {
+ cnonces.put(cnonce, info);
+ }
+ }
+ return true;
+ }
+
+ public boolean isNonceStale() {
+ return nonceStale;
+ }
+
+ public Principal authenticate(Realm realm) {
+ // Second MD5 digest used to calculate the digest :
+ // MD5(Method + ":" + uri)
+ String a2 = method + ":" + uri;
+
+ byte[] buffer;
+ synchronized (md5Helper) {
+ buffer = md5Helper.digest(a2.getBytes());
+ }
+ String md5a2 = md5Encoder.encode(buffer);
+
+ return realm.authenticate(userName, response, nonce, nc, cnonce,
+ qop, realmName, md5a2);
+ }
+
+ }
+
+ private static class NonceInfo {
+ private volatile long count;
+ private volatile long timestamp;
+
+ public void setCount(long l) {
+ count = l;
+ }
+
+ public long getCount() {
+ return count;
+ }
+
+ public void setTimestamp(long l) {
+ timestamp = l;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+ }
}
View
2  java/org/apache/catalina/authenticator/LocalStrings.properties
@@ -28,5 +28,7 @@ authenticator.sessionExpired=The time allowed for the login process has been exc
authenticator.unauthorized=Cannot authenticate with the provided credentials
authenticator.userDataConstraint=This request violates a User Data constraint for this application
+digestAuthenticator.cacheRemove=A valid entry has been removed from client nonce cache to make room for new entries. A replay attack is now possible. To prevent the possibility of replay attacks, reduce nonceValidity or increase cnonceCacheSize. Further warnings of this type will be suppressed for 5 minutes.
+
formAuthenticator.forwardErrorFail=Unexpected error forwarding to error page
formAuthenticator.forwardLoginFail=Unexpected error forwarding to login page
View
22 java/org/apache/catalina/authenticator/mbeans-descriptors.xml
@@ -60,10 +60,30 @@
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
-
+
+ <attribute name="cnonceCacheSize"
+ description="The size of the cnonce cache used to prevent replay attacks"
+ type="int"/>
+
<attribute name="entropy"
description="A String initialization parameter used to increase the entropy of the initialization of our random number generator"
type="java.lang.String"/>
+
+ <attribute name="key"
+ description="The secret key used by digest authentication"
+ type="java.lang.String"/>
+
+ <attribute name="nonceValidity"
+ description="The time, in milliseconds, for which a server issued nonce will be valid"
+ type="long"/>
+
+ <attribute name="opaque"
+ description="The opaque server string used by digest authentication"
+ type="java.lang.String"/>
+
+ <attribute name="validateUri"
+ description="Should the uri be validated as required by RFC2617?"
+ type="boolean"/>
</mbean>
<mbean name="FormAuthenticator"
View
14 java/org/apache/catalina/loader/VirtualWebappLoader.java
@@ -91,6 +91,20 @@ public void setVirtualClasspath(String path) {
virtualClasspath = path;
}
+ /**
+ * @return Returns searchVirtualFirst.
+ */
+ public boolean getSearchVirtualFirst() {
+ return getSearchExternalFirst();
+ }
+
+ /**
+ * @param searchVirtualFirst Whether the virtual class path should be searched before the webapp
+ */
+ public void setSearchVirtualFirst(boolean searchVirtualFirst) {
+ setSearchExternalFirst(searchVirtualFirst);
+ }
+
@Override
public void start() throws LifecycleException {
View
4 java/org/apache/catalina/loader/mbeans-descriptors.xml
@@ -85,6 +85,10 @@
description="The reloadable flag for this Loader"
type="boolean"/>
+ <attribute name="searchVirtualFirst"
+ description="The searchVirtualFirst flag for this Loader"
+ type="boolean"/>
+
<attribute name="repositories"
description="Extra repositories managed by this loader"
type="[Ljava.lang.String;"/>
View
15 java/org/apache/catalina/realm/RealmBase.java
@@ -353,22 +353,27 @@ public Principal authenticate(String username, byte[] credentials) {
*
* @param username Username of the Principal to look up
* @param clientDigest Digest which has been submitted by the client
- * @param nOnce Unique (or supposedly unique) token which has been used
+ * @param nonce Unique (or supposedly unique) token which has been used
* for this request
* @param realm Realm name
* @param md5a2 Second MD5 digest used to calculate the digest :
* MD5(Method + ":" + uri)
*/
public Principal authenticate(String username, String clientDigest,
- String nOnce, String nc, String cnonce,
+ String nonce, String nc, String cnonce,
String qop, String realm,
String md5a2) {
String md5a1 = getDigest(username, realm);
if (md5a1 == null)
return null;
- String serverDigestValue = md5a1 + ":" + nOnce + ":" + nc + ":"
- + cnonce + ":" + qop + ":" + md5a2;
+ String serverDigestValue;
+ if (qop == null) {
+ serverDigestValue = md5a1 + ":" + nonce + ":" + md5a2;
+ } else {
+ serverDigestValue = md5a1 + ":" + nonce + ":" + nc + ":" +
+ cnonce + ":" + qop + ":" + md5a2;
+ }
byte[] valueBytes = null;
if(getDigestEncoding() == null) {
@@ -390,7 +395,7 @@ public Principal authenticate(String username, String clientDigest,
if (log.isDebugEnabled()) {
log.debug("Digest : " + clientDigest + " Username:" + username
- + " ClientSigest:" + clientDigest + " nOnce:" + nOnce
+ + " ClientSigest:" + clientDigest + " nonce:" + nonce
+ " nc:" + nc + " cnonce:" + cnonce + " qop:" + qop
+ " realm:" + realm + "md5a2:" + md5a2
+ " Server digest:" + serverDigest);
View
8 java/org/apache/coyote/http11/Http11NioProtocol.java
@@ -746,13 +746,7 @@ public SocketState process(NioChannel socket) {
// In the middle of processing a request/response. Keep the
// socket associated with the processor.
connections.put(socket, processor);
-
- if (processor.comet) {
- NioEndpoint.KeyAttachment att = (NioEndpoint.KeyAttachment)socket.getAttachment(false);
- socket.getPoller().add(socket,att.getCometOps());
- } else {
- socket.getPoller().add(socket);
- }
+ socket.getPoller().add(socket);
} else if (state == SocketState.OPEN) {
// In keep-alive but between requests. OK to recycle
// processor. Continue to poll for the next request.
View
142 java/org/apache/tomcat/util/net/NioEndpoint.java
@@ -2251,83 +2251,85 @@ public void reset(NioChannel socket, SocketStatus status) {
}
public void run() {
- NioEndpoint.this.activeSocketProcessors.addAndGet(1);
- SelectionKey key = null;
- try {
- key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
- int handshake = -1;
-
+ synchronized (socket) {
+ NioEndpoint.this.activeSocketProcessors.addAndGet(1);
+ SelectionKey key = null;
try {
- if (key!=null) handshake = socket.handshake(key.isReadable(), key.isWritable());
- }catch ( IOException x ) {
- handshake = -1;
- if ( log.isDebugEnabled() ) log.debug("Error during SSL handshake",x);
- }catch ( CancelledKeyException ckx ) {
- handshake = -1;
- }
- if ( handshake == 0 ) {
- // Process the request from this socket
- boolean closed = (status==null)?(handler.process(socket)==Handler.SocketState.CLOSED) :
- (handler.event(socket,status)==Handler.SocketState.CLOSED);
-
- if (closed) {
- // Close socket and pool
- try {
- KeyAttachment ka = null;
- if (key!=null) {
- ka = (KeyAttachment) key.attachment();
- if (ka!=null) ka.setComet(false);
- socket.getPoller().cancelledKey(key, SocketStatus.ERROR, false);
+ key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+ int handshake = -1;
+
+ try {
+ if (key!=null) handshake = socket.handshake(key.isReadable(), key.isWritable());
+ }catch ( IOException x ) {
+ handshake = -1;
+ if ( log.isDebugEnabled() ) log.debug("Error during SSL handshake",x);
+ }catch ( CancelledKeyException ckx ) {
+ handshake = -1;
+ }
+ if ( handshake == 0 ) {
+ // Process the request from this socket
+ boolean closed = (status==null)?(handler.process(socket)==Handler.SocketState.CLOSED) :
+ (handler.event(socket,status)==Handler.SocketState.CLOSED);
+
+ if (closed) {
+ // Close socket and pool
+ try {
+ KeyAttachment ka = null;
+ if (key!=null) {
+ ka = (KeyAttachment) key.attachment();
+ if (ka!=null) ka.setComet(false);
+ socket.getPoller().cancelledKey(key, SocketStatus.ERROR, false);
+ }
+ if (socket!=null) nioChannels.offer(socket);
+ socket = null;
+ if ( ka!=null ) keyCache.offer(ka);
+ ka = null;
+ }catch ( Exception x ) {
+ log.error("",x);
}
- if (socket!=null) nioChannels.offer(socket);
- socket = null;
- if ( ka!=null ) keyCache.offer(ka);
- ka = null;
- }catch ( Exception x ) {
- log.error("",x);
+ }
+ } else if (handshake == -1 ) {
+ KeyAttachment ka = null;
+ if (key!=null) {
+ ka = (KeyAttachment) key.attachment();
+ socket.getPoller().cancelledKey(key, SocketStatus.DISCONNECT, false);
}
- }
- } else if (handshake == -1 ) {
- KeyAttachment ka = null;
- if (key!=null) {
- ka = (KeyAttachment) key.attachment();
- socket.getPoller().cancelledKey(key, SocketStatus.DISCONNECT, false);
+ if (socket!=null) nioChannels.offer(socket);
+ socket = null;
+ if ( ka!=null ) keyCache.offer(ka);
+ ka = null;
+ } else {
+ final SelectionKey fk = key;
+ final int intops = handshake;
+ final KeyAttachment ka = (KeyAttachment)fk.attachment();
+ ka.getPoller().add(socket,intops);
}
- if (socket!=null) nioChannels.offer(socket);
- socket = null;
- if ( ka!=null ) keyCache.offer(ka);
- ka = null;
- } else {
- final SelectionKey fk = key;
- final int intops = handshake;
- final KeyAttachment ka = (KeyAttachment)fk.attachment();
- ka.getPoller().add(socket,intops);
- }
- }catch(CancelledKeyException cx) {
- socket.getPoller().cancelledKey(key,null,false);
- } catch (OutOfMemoryError oom) {
- try {
- oomParachuteData = null;
- socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false);
- releaseCaches();
- log.error("", oom);
- }catch ( Throwable oomt ) {
+ }catch(CancelledKeyException cx) {
+ socket.getPoller().cancelledKey(key,null,false);
+ } catch (OutOfMemoryError oom) {
try {
- System.err.println(oomParachuteMsg);
- oomt.printStackTrace();
- }catch (Throwable letsHopeWeDontGetHere){}
+ oomParachuteData = null;
+ socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false);
+ releaseCaches();
+ log.error("", oom);
+ }catch ( Throwable oomt ) {
+ try {
+ System.err.println(oomParachuteMsg);
+ oomt.printStackTrace();
+ }catch (Throwable letsHopeWeDontGetHere){}
+ }
+ }catch ( Throwable t ) {
+ log.error("",t);
+ socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false);
+ } finally {
+ socket = null;
+ status = null;
+ //return to cache
+ processorCache.offer(this);
+ NioEndpoint.this.activeSocketProcessors.addAndGet(-1);
}
- }catch ( Throwable t ) {
- log.error("",t);
- socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false);
- } finally {
- socket = null;
- status = null;
- //return to cache
- processorCache.offer(this);
- NioEndpoint.this.activeSocketProcessors.addAndGet(-1); }
+ }
}
-
}
// ---------------------------------------------- TaskQueue Inner Class
View
11 webapps/docs/changelog.xml
@@ -46,6 +46,9 @@
<section name="Tomcat 6.0.33 (jfclere)">
<subsection name="Catalina">
<changelog>
+ <add>
+ Allow to search the virtual paths before the webapp or after it. (rjung)
+ </add>
<fix>
<bug>27988</bug>: Improve reporting of missing files. (markt)
</fix>
@@ -187,6 +190,10 @@
<fix>
Unregister DataSource MBeans when web application stops. (kfujino)
</fix>
+ <add>
+ Add additional configuration options to the DIGEST authenticator.
+ (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Coyote">
@@ -226,6 +233,10 @@
Prevent NPEs when a socket is closed in non-error conditions after
sendfile processing when using the HTTP NIO connector. (markt)
</fix>
+ <fix>
+ <bug>51515</bug>: Prevent immediate socket close when comet is used over
+ HTTPS. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Jasper">
View
34 webapps/docs/config/valve.xml
@@ -469,6 +469,12 @@
used.</p>
</attribute>
+ <attribute name="cnonceCacheSize" required="false">
+ <p>To protect against replay attacks, the DIGEST authenticator tracks
+ client nonce and nonce count values. This attribute controls the size
+ of that cache. If not specified, the default value of 1000 is used.</p>
+ </attribute>
+
<attribute name="disableProxyCaching" required="false">
<p>Controls the caching of pages that are protected by security
constraints. Setting this to <code>false</code> may help work around
@@ -479,6 +485,26 @@
<code>true</code> will be used.</p>
</attribute>
+ <attribute name="key" required="false">
+ <p>The secret key used by digest authentication. If not set, a secure
+ random value is generated. This should normally only be set when it is
+ necessary to keep key values constant either across server restarts
+ and/or across a cluster.</p>
+ </attribute>
+
+ <attribute name="nonceValidity" required="false">
+ <p>The time, in milliseconds, that a server generated nonce will be
+ considered valid for use in authentication. If not specified, the
+ default value of 300000 (5 minutes) will be used.</p>
+ </attribute>
+
+ <attribute name="opaque" required="false">
+ <p>The opaque server string used by digest authentication. If not set, a
+ random value is generated. This should normally only be set when it is
+ necessary to keep opaque values constant either across server restarts
+ and/or across a cluster.</p>
+ </attribute>
+
<attribute name="securePagesWithPragma" required="false">
<p>Controls the caching of pages that are protected by security
constraints. Setting this to <code>false</code> may help work around
@@ -488,6 +514,14 @@
If not set, the default value of <code>true</code> will be used.</p>
</attribute>
+ <attribute name="validateUri" required="false">
+ <p>Should the URI be validated as required by RFC2617? If not specified,
+ the default value of <code>true</code> will be used. This should
+ normally only be set when Tomcat is located behind a reverse proxy and
+ the proxy is modifying the URI passed to Tomcat such that DIGEST
+ authentication always fails.</p>
+ </attribute>
+
</attributes>
</subsection>
Please sign in to comment.
Something went wrong with that request. Please try again.