View | Details | Raw Unified | Return to bug 55101
Collapse All | Expand All

(-)java/org/apache/catalina/authenticator/BasicAuthenticator.java (-38 / +147 lines)
Lines 44-51 Link Here
44
 * @version $Id$
44
 * @version $Id$
45
 */
45
 */
46
46
47
public class BasicAuthenticator
47
public class BasicAuthenticator extends AuthenticatorBase {
48
    extends AuthenticatorBase {
49
    private static final Log log = LogFactory.getLog(BasicAuthenticator.class);
48
    private static final Log log = LogFactory.getLog(BasicAuthenticator.class);
50
49
51
50
Lines 98-106 Link Here
98
        }
97
        }
99
98
100
        // Validate any credentials already included with this request
99
        // Validate any credentials already included with this request
101
        String username = null;
102
        String password = null;
103
104
        MessageBytes authorization =
100
        MessageBytes authorization =
105
            request.getCoyoteRequest().getMimeHeaders()
101
            request.getCoyoteRequest().getMimeHeaders()
106
            .getValue("authorization");
102
            .getValue("authorization");
Lines 108-151 Link Here
108
        if (authorization != null) {
104
        if (authorization != null) {
109
            authorization.toBytes();
105
            authorization.toBytes();
110
            ByteChunk authorizationBC = authorization.getByteChunk();
106
            ByteChunk authorizationBC = authorization.getByteChunk();
111
            if (authorizationBC.startsWithIgnoreCase("basic ", 0)) {
107
            BasicCredentials credentials = null;
112
                authorizationBC.setOffset(authorizationBC.getOffset() + 6);
108
            try {
109
                credentials = new BasicCredentials(authorizationBC);
110
                String username = credentials.getUsername();
111
                String password = credentials.getPassword();
113
112
114
                byte[] decoded = Base64.decodeBase64(
113
                principal = context.getRealm().authenticate(username, password);
115
                        authorizationBC.getBuffer(),
114
                if (principal != null) {
116
                        authorizationBC.getOffset(),
115
                    register(request, response, principal,
117
                        authorizationBC.getLength());
116
                        HttpServletRequest.BASIC_AUTH, username, password);
118
117
                    return (true);
119
                // Get username and password
120
                int colon = -1;
121
                for (int i = 0; i < decoded.length; i++) {
122
                    if (decoded[i] == ':') {
123
                        colon = i;
124
                        break;
125
                    }
126
                }
118
                }
127
119
            }
128
                if (colon < 0) {
120
            catch (IllegalArgumentException iae) {
129
                    username = new String(decoded, B2CConverter.ISO_8859_1);
121
                if (log.isDebugEnabled()) {
130
                } else {
122
                    log.debug("Invalid Authorization" + iae.getMessage());
131
                    username = new String(
132
                            decoded, 0, colon, B2CConverter.ISO_8859_1);
133
                    password = new String(
134
                            decoded, colon + 1, decoded.length - colon - 1,
135
                            B2CConverter.ISO_8859_1);
136
                }
123
                }
137
138
                authorizationBC.setOffset(authorizationBC.getOffset() - 6);
139
            }
124
            }
140
141
            principal = context.getRealm().authenticate(username, password);
142
            if (principal != null) {
143
                register(request, response, principal,
144
                        HttpServletRequest.BASIC_AUTH, username, password);
145
                return (true);
146
            }
147
        }
125
        }
148
126
127
        // the request could not be authenticated, so reissue the challenge
149
        StringBuilder value = new StringBuilder(16);
128
        StringBuilder value = new StringBuilder(16);
150
        value.append("Basic realm=\"");
129
        value.append("Basic realm=\"");
151
        value.append(getRealmName(context));
130
        value.append(getRealmName(context));
Lines 156-164 Link Here
156
135
157
    }
136
    }
158
137
159
160
    @Override
138
    @Override
161
    protected String getAuthMethod() {
139
    protected String getAuthMethod() {
162
        return HttpServletRequest.BASIC_AUTH;
140
        return HttpServletRequest.BASIC_AUTH;
163
    }
141
    }
142
143
144
    /**
145
     * Parser for an HTTP Authorization header for BASIC authentication
146
     * as per RFC 2617 section 2, and the Base64 encoded credentials as
147
     * per RFC 2045 section 6.8.
148
     */
149
    protected static class BasicCredentials {
150
151
        // the only authentication method supported by this parser
152
        // note: we include single white space as its delimiter
153
        private static final String METHOD = "basic ";
154
155
        private ByteChunk authorization;
156
        private int initialOffset;
157
        private int base64blobOffset;
158
        private int base64blobLength;
159
160
        private String username = null;
161
        private String password = null;
162
163
        /**
164
         * Parse the HTTP Authorization header for BASIC authentication
165
         * as per RFC 2617 section 2, and the Base64 encoded credentials
166
         * as per RFC 2045 section 6.8.
167
         *
168
         * @param input The header value to parse in-place
169
         *
170
         * @throws IllegalArgumentException If the header does not conform
171
         *                                  to RFC 2617
172
         */
173
        public BasicCredentials(ByteChunk input)
174
                throws IllegalArgumentException {
175
            authorization = input;
176
            initialOffset = input.getOffset();
177
            parseMethod();
178
            byte[] decoded = parseBase64();
179
            parseCredentials(decoded);
180
        }
181
182
        /**
183
         * Trivial accessor.
184
         *
185
         * @return  the decoded username token as a String, which is
186
         *          never be <code>null</code>, but can be empty.
187
         */
188
        public String getUsername() {
189
            return username;
190
        }
191
192
        /**
193
         * Trivial accessor.
194
         *
195
         * @return  the decoded password token as a String, or <code>null</code>
196
         *          if no password was found in the credentials.
197
         */
198
        public String getPassword() {
199
            return password;
200
        }
201
202
        /*
203
         * The authorization method string is case-insensitive and must
204
         * hae at least one space character as a delimiter.
205
         */
206
        private void parseMethod() throws IllegalArgumentException {
207
            if (authorization.startsWithIgnoreCase(METHOD, 0)) {
208
                // step past the auth method name
209
                base64blobOffset = initialOffset + METHOD.length();
210
                base64blobLength = authorization.getLength() - METHOD.length();
211
            }
212
            else {
213
                // is this possible, or permitted?
214
                throw new IllegalArgumentException(
215
                        "Authorization header method is not \"Basic\"");
216
            }
217
        }
218
        /*
219
         * Decode the base64-user-pass token, which RFC 2617 states
220
         * can be longer than the 76 characters per line limit defined
221
         * in RFC 2045. The base64 decoder will ignore embedded line
222
         * break characters as well as surplus surrounding white space.
223
         */
224
        private byte[] parseBase64() throws IllegalArgumentException {
225
            byte[] decoded = Base64.decodeBase64(
226
                        authorization.getBuffer(),
227
                        base64blobOffset, base64blobLength);
228
            //  restore original offset
229
            authorization.setOffset(initialOffset);
230
            if (decoded == null) {
231
                throw new IllegalArgumentException(
232
                        "Basic Authorization credentials are not Base64");
233
            }
234
            return decoded;
235
        }
236
237
        /*
238
         * Extract the mandatory username token and separate it from the
239
         * optional password token. Tolerate surplus surrounding white space.
240
         */
241
        private void parseCredentials(byte[] decoded)
242
                throws IllegalArgumentException {
243
244
            int colon = -1;
245
            for (int i = 0; i < decoded.length; i++) {
246
                if (decoded[i] == ':') {
247
                    colon = i;
248
                    break;
249
                }
250
            }
251
252
            if (colon < 0) {
253
                username = new String(decoded, B2CConverter.ISO_8859_1);
254
                // password will remain null!
255
            }
256
            else {
257
                username = new String(
258
                            decoded, 0, colon, B2CConverter.ISO_8859_1);
259
                password = new String(
260
                            decoded, colon + 1, decoded.length - colon - 1,
261
                            B2CConverter.ISO_8859_1);
262
                // tolerate surplus white space around credentials
263
                if (password.length() > 1) {
264
                    password = password.trim();
265
                }
266
            }
267
            // tolerate surplus white space around credentials
268
            if (username.length() > 1) {
269
                username = username.trim();
270
            }
271
        }
272
    }
164
}
273
}

Return to bug 55101