Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 321232

Summary: StringIndexOutOfBoundsException: String index out of range: -1 in BasicAuthenticator
Product: [RT] Jetty Reporter: Tom Smith <download2003>
Component: serverAssignee: Greg Wilkins <gregw>
Status: CLOSED FIXED QA Contact:
Severity: normal    
Priority: P3 CC: dblock, jesse.mcconnell, jetty-inbox, mgorovoy
Version: 7.1.5   
Target Milestone: 7.1.x   
Hardware: PC   
OS: Windows Server 2008   
Whiteboard:

Description Tom Smith CLA 2010-07-29 10:37:37 EDT
If there another another authorization answer as Basic then it break with a StringIndexOutOfBoundsException.


 java.lang.StringIndexOutOfBoundsException: String index out of range: -1
 java.lang.StringIndexOutOfBoundsException: String index out of range: -1
 	at java.lang.String.substring(Unknown Source)
 	at org.eclipse.jetty.security.authentication.BasicAuthenticator.validateRequest(BasicAuthenticator.java:72)
 	at org.eclipse.jetty.security.authentication.DeferredAuthentication.authenticate(DeferredAuthentication.java:74)
 	at org.eclipse.jetty.server.Request.getUserPrincipal(Request.java:1204)
 	at waffle.servlet.NegotiateSecurityFilter.doFilterPrincipal(Unknown Source)
 	at waffle.servlet.NegotiateSecurityFilter.doFilter(Unknown Source)
Comment 1 dB. CLA 2010-07-30 16:42:49 EDT
The filter calling this is sitting in front of other Jetty filters (I think). It's waffle NegotiateSecurityFilter (http://waffle.codeplex.com). All it did until this problem is 

HttpServletRequest request = (HttpServletRequest) sreq;
request.getUserPrincipal()

Also, I don't understand why this method is trying to do deferred authentication and therefore needs to do anything with BasicAuthenticator.
Comment 2 Greg Wilkins CLA 2010-08-04 00:38:28 EDT
Tom, DB,

I will fix this issue so that BasicAuthenticator will not fail if their is no :, but I'm a bit confused by what is happening?

What is setting the Authorization header, and why is it not set to basic if you are using basic auth?

The idea of deferred authentication is that just because there is an Authorization header does not mean that we want to call a possible remote realm to check those credentials.  We defer checking those credentials until either a security constraint is hit or somebody calls getUserPrincipal or isUserInRole.

It looks to me that the Authorization header is being reused for a different scheme, but Basic has been configured for the webapp?
Comment 3 Greg Wilkins CLA 2010-08-04 00:48:53 EDT
I've committed a fix in r2205 that will make it into 7.2

But I wont mark this as resolved until you can confirm this fixes the issue, as I'm not 100% sure I understand what is going on with your filter.
Comment 4 dB. CLA 2010-08-04 07:55:11 EDT
Tom should post his setup, but I think this is hiding a bigger problem.

There's a generic servlet filter added in a web application (Waffle), which in doFilter does HttpServletRequest request = (HttpServletRequest) sreq;
request.getUserPrincipal(). Then you see BasicAuthenticator in the call stack.

This shouldn't happen, should it? Why does getUserPrincipal call DeferredAuthentication and eventually call Basic authenticator? It sounds like a good idea in theory - "if anyone calls getUserPrincipal, we'll try to make sure authentication has happened", but this doesn't work if we're in the middle of authentication in some other filter introduced by a web app.
Comment 5 Greg Wilkins CLA 2010-08-05 07:43:17 EDT
I don't see why this is a problem? 

If the webapp is configured for basic Auth, then getUserPrincipal must return the authenticated user if it can.

Note that calling getUserPrincipal will never cause a 401 response to be sent, that is only done if there is a security constraint, and that happens before filters.
Comment 6 dB. CLA 2010-08-05 08:04:22 EDT
I think it's a problem because it's a side effect. An authentication filter that sits in front of BASIC auth filter calls getUserPrincipal to find out whether the user is already authenticated (by a filter in front of it) and ends up authenticated with BASIC auth. So you just reordered filters for the user.
Comment 7 Tom Smith CLA 2010-08-10 10:48:26 EDT
The problem occur because there are more as one Servlet. The Basic authentication was set only for one Servlet in the web.xml.

The second servlet use its own filter to use another authentication.

I think there are 3 bugs:
1.) The basic authentication was span to all servlets in one war file if there one is configured.
2.) A browser that come from another page on the same host can send the authentication data from a previous call. The server should ignore it in this case.
3.) I not sure but if the answer will be parse first on getUserPrincipal() when will be send the 401 answer. On getUserPrincipal() it is to late.



The constraint look like:

        <security-constraint>
            <web-resource-collection>
                <web-resource-name>Report Login Page</web-resource-name>
                <url-pattern>/LoginServlet</url-pattern>
                <url-pattern>/LoginServlet/*</url-pattern>
            </web-resource-collection>
            <auth-constraint>
                <role-name>*</role-name>
            </auth-constraint>
        </security-constraint>
  
        <security-role>
            <role-name>*</role-name>
        </security-role>

        <login-config>
            <auth-method>BASIC</auth-method>
            <realm-name>i-net Crystal-Clear</realm-name>
        </login-config>


What we want do is that under Linux the LoginServlet is used (redirect) and under Windows the Waffle filter.
Comment 8 Greg Wilkins CLA 2010-09-14 19:11:37 EDT
Did the patch resolve the problem?
Comment 9 Jesse McConnell CLA 2011-01-12 16:50:33 EST
closing since there has been no response in 3-4 months

reopen if this remains an issue