1 /* 2 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.net.httpserver; 27 28 /** 29 * BasicAuthenticator provides an implementation of HTTP Basic 30 * authentication. It is an abstract class and must be extended 31 * to provide an implementation of {@link #checkCredentials(String,String)} 32 * which is called to verify each incoming request. 33 */ 34 public abstract class BasicAuthenticator extends Authenticator { 35 36 protected String realm; 37 38 /** 39 * Creates a BasicAuthenticator for the given HTTP realm 40 * @param realm The HTTP Basic authentication realm 41 * @throws NullPointerException if the realm is an empty string 42 */ 43 public BasicAuthenticator (String realm) { 44 this.realm = realm; 45 } 46 47 /** 48 * returns the realm this BasicAuthenticator was created with 49 * @return the authenticator's realm string. 50 */ 51 public String getRealm () { 52 return realm; 53 } 54 55 public Result authenticate (HttpExchange t) 56 { 57 Headers rmap = (Headers) t.getRequestHeaders(); 58 /* 59 * look for auth token 60 */ 61 String auth = rmap.getFirst ("Authorization"); 62 if (auth == null) { 63 Headers map = (Headers) t.getResponseHeaders(); 64 map.set ("WWW-Authenticate", "Basic realm=" + "\""+realm+"\""); 65 return new Authenticator.Retry (401); 66 } 67 int sp = auth.indexOf (' '); 68 if (sp == -1 || !auth.substring(0, sp).equals ("Basic")) { 69 return new Authenticator.Failure (401); 70 } 71 byte[] b = Base64.base64ToByteArray (auth.substring(sp+1)); 72 String userpass = new String (b); 73 int colon = userpass.indexOf (':'); 74 String uname = userpass.substring (0, colon); 75 String pass = userpass.substring (colon+1); 76 77 if (checkCredentials (uname, pass)) { 78 return new Authenticator.Success ( 79 new HttpPrincipal ( 80 uname, realm 81 ) 82 ); 83 } else { 84 /* reject the request again with 401 */ 85 86 Headers map = (Headers) t.getResponseHeaders(); 87 map.set ("WWW-Authenticate", "Basic realm=" + "\""+realm+"\""); 88 return new Authenticator.Failure(401); 89 } 90 } 91 92 /** 93 * called for each incoming request to verify the 94 * given name and password in the context of this 95 * Authenticator's realm. Any caching of credentials 96 * must be done by the implementation of this method 97 * @param username the username from the request 98 * @param password the password from the request 99 * @return <code>true</code> if the credentials are valid, 100 * <code>false</code> otherwise. 101 */ 102 public abstract boolean checkCredentials (String username, String password); 103 } 104 105 class Base64 { 106 107 /** 108 * Translates the specified byte array into a Base64 string as per 109 * Preferences.put(byte[]). 110 */ 111 static String byteArrayToBase64(byte[] a) { 112 return byteArrayToBase64(a, false); 113 } 114 115 /** 116 * Translates the specified byte array into an "aternate representation" 117 * Base64 string. This non-standard variant uses an alphabet that does 118 * not contain the uppercase alphabetic characters, which makes it 119 * suitable for use in situations where case-folding occurs. 120 */ 121 static String byteArrayToAltBase64(byte[] a) { 122 return byteArrayToBase64(a, true); 123 } 124 125 private static String byteArrayToBase64(byte[] a, boolean alternate) { 126 int aLen = a.length; 127 int numFullGroups = aLen/3; 128 int numBytesInPartialGroup = aLen - 3*numFullGroups; 129 int resultLen = 4*((aLen + 2)/3); 130 StringBuffer result = new StringBuffer(resultLen); 131 char[] intToAlpha = (alternate ? intToAltBase64 : intToBase64); 132 133 // Translate all full groups from byte array elements to Base64 134 int inCursor = 0; 135 for (int i=0; i<numFullGroups; i++) { 136 int byte0 = a[inCursor++] & 0xff; 137 int byte1 = a[inCursor++] & 0xff; 138 int byte2 = a[inCursor++] & 0xff; 139 result.append(intToAlpha[byte0 >> 2]); 140 result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]); 141 result.append(intToAlpha[(byte1 << 2)&0x3f | (byte2 >> 6)]); 142 result.append(intToAlpha[byte2 & 0x3f]); 143 } 144 145 // Translate partial group if present 146 if (numBytesInPartialGroup != 0) { 147 int byte0 = a[inCursor++] & 0xff; 148 result.append(intToAlpha[byte0 >> 2]); 149 if (numBytesInPartialGroup == 1) { 150 result.append(intToAlpha[(byte0 << 4) & 0x3f]); 151 result.append("=="); 152 } else { 153 // assert numBytesInPartialGroup == 2; 154 int byte1 = a[inCursor++] & 0xff; 155 result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]); 156 result.append(intToAlpha[(byte1 << 2)&0x3f]); 157 result.append('='); 158 } 159 } 160 // assert inCursor == a.length; 161 // assert result.length() == resultLen; 162 return result.toString(); 163 } 164 165 /** 166 * This array is a lookup table that translates 6-bit positive integer 167 * index values into their "Base64 Alphabet" equivalents as specified 168 * in Table 1 of RFC 2045. 169 */ 170 private static final char intToBase64[] = { 171 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 172 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 173 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 174 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 175 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' 176 }; 177 178 /** 179 * This array is a lookup table that translates 6-bit positive integer 180 * index values into their "Alternate Base64 Alphabet" equivalents. 181 * This is NOT the real Base64 Alphabet as per in Table 1 of RFC 2045. 182 * This alternate alphabet does not use the capital letters. It is 183 * designed for use in environments where "case folding" occurs. 184 */ 185 private static final char intToAltBase64[] = { 186 '!', '"', '#', '$', '%', '&', '\'', '(', ')', ',', '-', '.', ':', 187 ';', '<', '>', '@', '[', ']', '^', '`', '_', '{', '|', '}', '~', 188 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 189 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 190 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '?' 191 }; 192 193 /** 194 * Translates the specified Base64 string (as per Preferences.get(byte[])) 195 * into a byte array. 196 * 197 * @throw IllegalArgumentException if <tt>s</tt> is not a valid Base64 198 * string. 199 */ 200 static byte[] base64ToByteArray(String s) { 201 return base64ToByteArray(s, false); 202 } 203 204 /** 205 * Translates the specified "aternate representation" Base64 string 206 * into a byte array. 207 * 208 * @throw IllegalArgumentException or ArrayOutOfBoundsException 209 * if <tt>s</tt> is not a valid alternate representation 210 * Base64 string. 211 */ 212 static byte[] altBase64ToByteArray(String s) { 213 return base64ToByteArray(s, true); 214 } 215 216 private static byte[] base64ToByteArray(String s, boolean alternate) { 217 byte[] alphaToInt = (alternate ? altBase64ToInt : base64ToInt); 218 int sLen = s.length(); 219 int numGroups = sLen/4; 220 if (4*numGroups != sLen) 221 throw new IllegalArgumentException( 222 "String length must be a multiple of four."); 223 int missingBytesInLastGroup = 0; 224 int numFullGroups = numGroups; 225 if (sLen != 0) { 226 if (s.charAt(sLen-1) == '=') { 227 missingBytesInLastGroup++; 228 numFullGroups--; 229 } 230 if (s.charAt(sLen-2) == '=') 231 missingBytesInLastGroup++; 232 } 233 byte[] result = new byte[3*numGroups - missingBytesInLastGroup]; 234 235 // Translate all full groups from base64 to byte array elements 236 int inCursor = 0, outCursor = 0; 237 for (int i=0; i<numFullGroups; i++) { 238 int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt); 239 int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt); 240 int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt); 241 int ch3 = base64toInt(s.charAt(inCursor++), alphaToInt); 242 result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4)); 243 result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); 244 result[outCursor++] = (byte) ((ch2 << 6) | ch3); 245 } 246 247 // Translate partial group, if present 248 if (missingBytesInLastGroup != 0) { 249 int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt); 250 int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt); 251 result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4)); 252 253 if (missingBytesInLastGroup == 1) { 254 int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt); 255 result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); 256 } 257 } 258 // assert inCursor == s.length()-missingBytesInLastGroup; 259 // assert outCursor == result.length; 260 return result; 261 } 262 263 /** 264 * Translates the specified character, which is assumed to be in the 265 * "Base 64 Alphabet" into its equivalent 6-bit positive integer. 266 * 267 * @throw IllegalArgumentException or ArrayOutOfBoundsException if 268 * c is not in the Base64 Alphabet. 269 */ 270 private static int base64toInt(char c, byte[] alphaToInt) { 271 int result = alphaToInt[c]; 272 if (result < 0) 273 throw new IllegalArgumentException("Illegal character " + c); 274 return result; 275 } 276 277 /** 278 * This array is a lookup table that translates unicode characters 279 * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045) 280 * into their 6-bit positive integer equivalents. Characters that 281 * are not in the Base64 alphabet but fall within the bounds of the 282 * array are translated to -1. 283 */ 284 private static final byte base64ToInt[] = { 285 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 286 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 287 -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 288 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 289 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 290 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 291 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 292 }; 293 294 /** 295 * This array is the analogue of base64ToInt, but for the nonstandard 296 * variant that avoids the use of uppercase alphabetic characters. 297 */ 298 private static final byte altBase64ToInt[] = { 299 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 300 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 301 2, 3, 4, 5, 6, 7, 8, -1, 62, 9, 10, 11, -1 , 52, 53, 54, 55, 56, 57, 302 58, 59, 60, 61, 12, 13, 14, -1, 15, 63, 16, -1, -1, -1, -1, -1, -1, 303 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 304 -1, -1, -1, 17, -1, 18, 19, 21, 20, 26, 27, 28, 29, 30, 31, 32, 33, 305 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 306 51, 22, 23, 24, 25 307 }; 308 309 }