1 /* 2 * Copyright (c) 1997, 2017, 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.istack.internal.tools; 27 28 import java.io.BufferedReader; 29 import java.io.File; 30 import java.io.FileInputStream; 31 import java.io.FileNotFoundException; 32 import java.io.IOException; 33 import java.io.InputStreamReader; 34 import java.io.UnsupportedEncodingException; 35 import java.lang.reflect.Field; 36 import java.lang.reflect.Method; 37 import java.net.Authenticator; 38 import java.net.Authenticator.RequestorType; 39 import java.net.MalformedURLException; 40 import java.net.PasswordAuthentication; 41 import java.net.URL; 42 import java.net.URLDecoder; 43 import java.net.URLEncoder; 44 import java.security.AccessController; 45 import java.security.PrivilegedAction; 46 import java.security.PrivilegedActionException; 47 import java.security.PrivilegedExceptionAction; 48 import java.util.ArrayList; 49 import java.util.List; 50 import java.util.logging.Level; 51 import java.util.logging.Logger; 52 import java.util.regex.Pattern; 53 import org.xml.sax.Locator; 54 import org.xml.sax.helpers.LocatorImpl; 55 56 /** 57 * @author Vivek Pandey 58 * @author Lukas Jungmann 59 */ 60 public class DefaultAuthenticator extends Authenticator { 61 62 private static final Logger LOGGER = Logger.getLogger(DefaultAuthenticator.class.getName()); 63 private static DefaultAuthenticator instance; 64 private static Authenticator systemAuthenticator = getCurrentAuthenticator(); 65 private String proxyUser; 66 private String proxyPasswd; 67 private final List<AuthInfo> authInfo = new ArrayList<>(); 68 private static int counter = 0; 69 70 DefaultAuthenticator() { 71 //try undocumented but often used properties 72 if (System.getProperty("http.proxyUser") != null) { 73 proxyUser = System.getProperty("http.proxyUser"); 74 } else { 75 proxyUser = System.getProperty("proxyUser"); 76 } 77 if (System.getProperty("http.proxyPassword") != null) { 78 proxyPasswd = System.getProperty("http.proxyPassword"); 79 } else { 80 proxyPasswd = System.getProperty("proxyPassword"); 81 } 82 } 83 84 public static synchronized DefaultAuthenticator getAuthenticator() { 85 if (instance == null) { 86 instance = new DefaultAuthenticator(); 87 Authenticator.setDefault(instance); 88 } 89 counter++; 90 return instance; 91 } 92 93 public static synchronized void reset() { 94 --counter; 95 if (instance != null && counter == 0) { 96 Authenticator.setDefault(systemAuthenticator); 97 } 98 } 99 100 @Override 101 protected PasswordAuthentication getPasswordAuthentication() { 102 //If user sets proxy user and passwd and the RequestType is from proxy server then create 103 // PasswordAuthentication using proxyUser and proxyPasswd; 104 if ((getRequestorType() == RequestorType.PROXY) && proxyUser != null && proxyPasswd != null) { 105 return new PasswordAuthentication(proxyUser, proxyPasswd.toCharArray()); 106 } 107 for (AuthInfo auth : authInfo) { 108 if (auth.matchingHost(getRequestingURL())) { 109 return new PasswordAuthentication(auth.getUser(), auth.getPassword().toCharArray()); 110 } 111 } 112 return null; 113 } 114 115 /** 116 * Proxy authorization string in form of username:password. 117 * 118 * @param proxyAuth 119 */ 120 public void setProxyAuth(String proxyAuth) { 121 if (proxyAuth == null) { 122 this.proxyUser = null; 123 this.proxyPasswd = null; 124 } else { 125 int i = proxyAuth.indexOf(':'); 126 if (i < 0) { 127 this.proxyUser = proxyAuth; 128 this.proxyPasswd = ""; 129 } else if (i == 0) { 130 this.proxyUser = ""; 131 this.proxyPasswd = proxyAuth.substring(1); 132 } else { 133 this.proxyUser = proxyAuth.substring(0, i); 134 this.proxyPasswd = proxyAuth.substring(i + 1); 135 } 136 } 137 } 138 139 public void setAuth(File f, Receiver l) { 140 Receiver listener = l == null ? new DefaultRImpl() : l; 141 BufferedReader in = null; 142 FileInputStream fi = null; 143 InputStreamReader is = null; 144 try { 145 String text; 146 LocatorImpl locator = new LocatorImpl(); 147 locator.setSystemId(f.getAbsolutePath()); 148 try { 149 fi = new FileInputStream(f); 150 is = new InputStreamReader(fi, "UTF-8"); 151 in = new BufferedReader(is); 152 } catch (UnsupportedEncodingException | FileNotFoundException e) { 153 listener.onError(e, locator); 154 return; 155 } 156 try { 157 int lineno = 1; 158 locator.setSystemId(f.getCanonicalPath()); 159 while ((text = in.readLine()) != null) { 160 locator.setLineNumber(lineno++); 161 //ignore empty lines and treat those starting with '#' as comments 162 if ("".equals(text.trim()) || text.startsWith("#")) { 163 continue; 164 } 165 try { 166 AuthInfo ai = parseLine(text); 167 authInfo.add(ai); 168 } catch (Exception e) { 169 listener.onParsingError(text, locator); 170 } 171 } 172 } catch (IOException e) { 173 listener.onError(e, locator); 174 LOGGER.log(Level.SEVERE, e.getMessage(), e); 175 } 176 } finally { 177 try { 178 if (in != null) { 179 in.close(); 180 } 181 if (is != null) { 182 is.close(); 183 } 184 if (fi != null) { 185 fi.close(); 186 } 187 } catch (IOException ex) { 188 LOGGER.log(Level.SEVERE, null, ex); 189 } 190 } 191 } 192 193 private AuthInfo parseLine(String text) throws Exception { 194 URL url; 195 try { 196 url = new URL(text); 197 } catch (MalformedURLException mue) { 198 //possible cause of this can be that password contains 199 //character which has to be encoded in URL, 200 //such as '@', ')', '#' and few others 201 //so try to recreate the URL with encoded string 202 //between 2nd ':' and last '@' 203 int i = text.indexOf(':', text.indexOf(':') + 1) + 1; 204 int j = text.lastIndexOf('@'); 205 String encodedUrl = 206 text.substring(0, i) 207 + URLEncoder.encode(text.substring(i, j), "UTF-8") 208 + text.substring(j); 209 url = new URL(encodedUrl); 210 } 211 212 String authinfo = url.getUserInfo(); 213 214 if (authinfo != null) { 215 int i = authinfo.indexOf(':'); 216 217 if (i >= 0) { 218 String user = authinfo.substring(0, i); 219 String password = authinfo.substring(i + 1); 220 return new AuthInfo( 221 new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile()), 222 user, URLDecoder.decode(password, "UTF-8")); 223 } 224 } 225 throw new Exception(); 226 } 227 228 static Authenticator getCurrentAuthenticator() { 229 try { 230 return AccessController.doPrivileged(new PrivilegedExceptionAction<Authenticator>() { 231 @Override 232 public Authenticator run() throws Exception { 233 Method method = Authenticator.class.getMethod("getDefault"); 234 return (Authenticator) method.invoke(null); 235 } 236 237 }); 238 } catch (PrivilegedActionException pae) { 239 if (LOGGER.isLoggable(Level.FINE)) { 240 LOGGER.log(Level.FINE, null, pae); 241 } 242 Exception ex = pae.getException(); 243 if (!(ex instanceof NoSuchMethodException)) { 244 // if Authenticator.getDefault has not been found, 245 // we likely didn't get through sec, so return null 246 // and don't care about JDK version we're on 247 return null; 248 } 249 // or we're on JDK <9, so let's continue the old way... 250 } 251 252 final Field f = getTheAuthenticator(); 253 if (f == null) { 254 return null; 255 } 256 257 try { 258 AccessController.doPrivileged(new PrivilegedAction<Void>() { 259 @Override 260 public Void run() { 261 f.setAccessible(true); 262 return null; 263 } 264 }); 265 return (Authenticator) f.get(null); 266 } catch (IllegalAccessException | IllegalArgumentException ex) { 267 return null; 268 } finally { 269 AccessController.doPrivileged(new PrivilegedAction<Void>() { 270 @Override 271 public Void run() { 272 f.setAccessible(false); 273 return null; 274 } 275 }); 276 } 277 } 278 279 private static Field getTheAuthenticator() { 280 try { 281 return Authenticator.class.getDeclaredField("theAuthenticator"); 282 } catch (NoSuchFieldException | SecurityException ex) { 283 return null; 284 } 285 } 286 287 public static interface Receiver { 288 289 void onParsingError(String line, Locator loc); 290 291 void onError(Exception e, Locator loc); 292 } 293 294 private static class DefaultRImpl implements Receiver { 295 296 @Override 297 public void onParsingError(String line, Locator loc) { 298 System.err.println(getLocationString(loc) + ": " + line); 299 } 300 301 @Override 302 public void onError(Exception e, Locator loc) { 303 System.err.println(getLocationString(loc) + ": " + e.getMessage()); 304 LOGGER.log(Level.SEVERE, e.getMessage(), e); 305 } 306 307 private String getLocationString(Locator l) { 308 return "[" + l.getSystemId() + "#" + l.getLineNumber() + "]"; 309 } 310 } 311 312 /** 313 * Represents authorization information needed by 314 * {@link DefaultAuthenticator} to authenticate access to remote resources. 315 * 316 * @author Vivek Pandey 317 * @author Lukas Jungmann 318 */ 319 final static class AuthInfo { 320 321 private final String user; 322 private final String password; 323 private final Pattern urlPattern; 324 325 public AuthInfo(URL url, String user, String password) { 326 String u = url.toExternalForm().replaceFirst("\\?", "\\\\?"); 327 this.urlPattern = Pattern.compile(u.replace("*", ".*"), Pattern.CASE_INSENSITIVE); 328 this.user = user; 329 this.password = password; 330 } 331 332 public String getUser() { 333 return user; 334 } 335 336 public String getPassword() { 337 return password; 338 } 339 340 /** 341 * Returns if the requesting host and port are associated with this 342 * {@link AuthInfo} 343 */ 344 public boolean matchingHost(URL requestingURL) { 345 return urlPattern.matcher(requestingURL.toExternalForm()).matches(); 346 } 347 } 348 }