From 5ef65f8cc77bd86d62d65bd8279fc0dcbec8ccfd Mon Sep 17 00:00:00 2001 From: igerasim Date: Wed, 10 Apr 2019 15:32:36 -0700 Subject: [PATCH] 8221518: Normalize normalization Reviewed-by: chegar, igerasim, ahgross, rhalade --- src/share/classes/java/net/URL.java | 22 ++- .../classes/java/net/URLStreamHandler.java | 7 +- .../classes/sun/net/util/IPAddressUtil.java | 181 ++++++++++++++++++ .../www/protocol/ftp/FtpURLConnection.java | 21 +- .../www/protocol/http/HttpURLConnection.java | 8 +- .../https/HttpsURLConnectionImpl.java | 7 +- 6 files changed, 237 insertions(+), 9 deletions(-) diff --git a/src/share/classes/java/net/URL.java b/src/share/classes/java/net/URL.java index 919825adb..be6c294d1 100644 --- a/src/share/classes/java/net/URL.java +++ b/src/share/classes/java/net/URL.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ import java.io.ObjectStreamField; import java.io.ObjectInputStream.GetField; import java.util.Hashtable; import java.util.StringTokenizer; +import sun.net.util.IPAddressUtil; import sun.security.util.SecurityConstants; /** @@ -414,13 +415,19 @@ public final class URL implements java.io.Serializable { } ref = parts.getRef(); - // Note: we don't do validation of the URL here. Too risky to change + // Note: we don't do full validation of the URL here. Too risky to change // right now, but worth considering for future reference. -br if (handler == null && (handler = getURLStreamHandler(protocol)) == null) { throw new MalformedURLException("unknown protocol: " + protocol); } this.handler = handler; + if (host != null && isBuiltinStreamHandler(handler)) { + String s = IPAddressUtil.checkExternalForm(this); + if (s != null) { + throw new MalformedURLException(s); + } + } } /** @@ -943,7 +950,12 @@ public final class URL implements java.io.Serializable { * @since 1.5 */ public URI toURI() throws URISyntaxException { - return new URI (toString()); + URI uri = new URI(toString()); + if (authority != null && isBuiltinStreamHandler(handler)) { + String s = IPAddressUtil.checkAuthority(this); + if (s != null) throw new URISyntaxException(authority, s); + } + return uri; } /** @@ -1400,6 +1412,10 @@ public final class URL implements java.io.Serializable { return replacementURL; } + boolean isBuiltinStreamHandler(URLStreamHandler handler) { + return isBuiltinStreamHandler(handler.getClass().getName()); + } + private boolean isBuiltinStreamHandler(String handlerClassName) { return (handlerClassName.startsWith(BUILTIN_HANDLERS_PREFIX)); } diff --git a/src/share/classes/java/net/URLStreamHandler.java b/src/share/classes/java/net/URLStreamHandler.java index 513055982..cbdf02a5e 100644 --- a/src/share/classes/java/net/URLStreamHandler.java +++ b/src/share/classes/java/net/URLStreamHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -532,12 +532,15 @@ public abstract class URLStreamHandler { * @see java.net.URL#set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String) * @since 1.3 */ - protected void setURL(URL u, String protocol, String host, int port, + protected void setURL(URL u, String protocol, String host, int port, String authority, String userInfo, String path, String query, String ref) { if (this != u.handler) { throw new SecurityException("handler for url different from " + "this handler"); + } else if (host != null && u.isBuiltinStreamHandler(this)) { + String s = IPAddressUtil.checkHostString(host); + if (s != null) throw new IllegalArgumentException(s); } // ensure that no one can reset the protocol on a given URL. u.set(u.getProtocol(), host, port, authority, userInfo, path, query, ref); diff --git a/src/share/classes/sun/net/util/IPAddressUtil.java b/src/share/classes/sun/net/util/IPAddressUtil.java index 6aa98c8f8..2bcd2c078 100644 --- a/src/share/classes/sun/net/util/IPAddressUtil.java +++ b/src/share/classes/sun/net/util/IPAddressUtil.java @@ -25,6 +25,9 @@ package sun.net.util; +import java.net.URL; +import java.util.Arrays; + public class IPAddressUtil { private final static int INADDR4SZ = 4; private final static int INADDR16SZ = 16; @@ -287,4 +290,182 @@ public class IPAddressUtil { } return false; } + + // See java.net.URI for more details on how to generate these + // masks. + // + // square brackets + private static final long L_IPV6_DELIMS = 0x0L; // "[]" + private static final long H_IPV6_DELIMS = 0x28000000L; // "[]" + // RFC 3986 gen-delims + private static final long L_GEN_DELIMS = 0x8400800800000000L; // ":/?#[]@" + private static final long H_GEN_DELIMS = 0x28000001L; // ":/?#[]@" + // These gen-delims can appear in authority + private static final long L_AUTH_DELIMS = 0x400000000000000L; // "@[]:" + private static final long H_AUTH_DELIMS = 0x28000001L; // "@[]:" + // colon is allowed in userinfo + private static final long L_COLON = 0x400000000000000L; // ":" + private static final long H_COLON = 0x0L; // ":" + // slash should be encoded in authority + private static final long L_SLASH = 0x800000000000L; // "/" + private static final long H_SLASH = 0x0L; // "/" + // backslash should always be encoded + private static final long L_BACKSLASH = 0x0L; // "\" + private static final long H_BACKSLASH = 0x10000000L; // "\" + // ASCII chars 0-31 + 127 - various controls + CRLF + TAB + private static final long L_NON_PRINTABLE = 0xffffffffL; + private static final long H_NON_PRINTABLE = 0x8000000000000000L; + // All of the above + private static final long L_EXCLUDE = 0x84008008ffffffffL; + private static final long H_EXCLUDE = 0x8000000038000001L; + + private static final char[] OTHERS = { + 8263,8264,8265,8448,8449,8453,8454,10868, + 65109,65110,65119,65131,65283,65295,65306,65311,65312 + }; + + // Tell whether the given character is found by the given mask pair + public static boolean match(char c, long lowMask, long highMask) { + if (c < 64) + return ((1L << c) & lowMask) != 0; + if (c < 128) + return ((1L << (c - 64)) & highMask) != 0; + return false; // other non ASCII characters are not filtered + } + + // returns -1 if the string doesn't contain any characters + // from the mask, the index of the first such character found + // otherwise. + public static int scan(String s, long lowMask, long highMask) { + int i = -1, len; + if (s == null || (len = s.length()) == 0) return -1; + boolean match = false; + while (++i < len && !(match = match(s.charAt(i), lowMask, highMask))); + if (match) return i; + return -1; + } + + public static int scan(String s, long lowMask, long highMask, char[] others) { + int i = -1, len; + if (s == null || (len = s.length()) == 0) return -1; + boolean match = false; + char c, c0 = others[0]; + while (++i < len && !(match = match((c=s.charAt(i)), lowMask, highMask))) { + if (c >= c0 && (Arrays.binarySearch(others, c) > -1)) { + match = true; break; + } + } + if (match) return i; + + return -1; + } + + private static String describeChar(char c) { + if (c < 32 || c == 127) { + if (c == '\n') return "LF"; + if (c == '\r') return "CR"; + return "control char (code=" + (int)c + ")"; + } + if (c == '\\') return "'\\'"; + return "'" + c + "'"; + } + + private static String checkUserInfo(String str) { + // colon is permitted in user info + int index = scan(str, L_EXCLUDE & ~L_COLON, + H_EXCLUDE & ~H_COLON); + if (index >= 0) { + return "Illegal character found in user-info: " + + describeChar(str.charAt(index)); + } + return null; + } + + private static String checkHost(String str) { + int index; + if (str.startsWith("[") && str.endsWith("]")) { + str = str.substring(1, str.length() - 1); + if (isIPv6LiteralAddress(str)) { + index = str.indexOf('%'); + if (index >= 0) { + index = scan(str = str.substring(index), + L_NON_PRINTABLE | L_IPV6_DELIMS, + H_NON_PRINTABLE | H_IPV6_DELIMS); + if (index >= 0) { + return "Illegal character found in IPv6 scoped address: " + + describeChar(str.charAt(index)); + } + } + return null; + } + return "Unrecognized IPv6 address format"; + } else { + index = scan(str, L_EXCLUDE, H_EXCLUDE); + if (index >= 0) { + return "Illegal character found in host: " + + describeChar(str.charAt(index)); + } + } + return null; + } + + private static String checkAuth(String str) { + int index = scan(str, + L_EXCLUDE & ~L_AUTH_DELIMS, + H_EXCLUDE & ~H_AUTH_DELIMS); + if (index >= 0) { + return "Illegal character found in authority: " + + describeChar(str.charAt(index)); + } + return null; + } + + // check authority of hierarchical URL. Appropriate for + // HTTP-like protocol handlers + public static String checkAuthority(URL url) { + String s, u, h; + if (url == null) return null; + if ((s = checkUserInfo(u = url.getUserInfo())) != null) { + return s; + } + if ((s = checkHost(h = url.getHost())) != null) { + return s; + } + if (h == null && u == null) { + return checkAuth(url.getAuthority()); + } + return null; + } + + // minimal syntax checks - deeper check may be performed + // by the appropriate protocol handler + public static String checkExternalForm(URL url) { + String s; + if (url == null) return null; + int index = scan(s = url.getUserInfo(), + L_NON_PRINTABLE | L_SLASH, + H_NON_PRINTABLE | H_SLASH); + if (index >= 0) { + return "Illegal character found in authority: " + + describeChar(s.charAt(index)); + } + if ((s = checkHostString(url.getHost())) != null) { + return s; + } + return null; + } + + public static String checkHostString(String host) { + if (host == null) return null; + int index = scan(host, + L_NON_PRINTABLE | L_SLASH, + H_NON_PRINTABLE | H_SLASH, + OTHERS); + if (index >= 0) { + return "Illegal character found in host: " + + describeChar(host.charAt(index)); + } + return null; + } + } diff --git a/src/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java b/src/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java index e560abf9c..582687ba4 100644 --- a/src/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java +++ b/src/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,7 @@ import java.io.BufferedInputStream; import java.io.FilterInputStream; import java.io.FilterOutputStream; import java.io.FileNotFoundException; +import java.net.MalformedURLException; import java.net.URL; import java.net.SocketPermission; import java.net.UnknownHostException; @@ -47,6 +48,7 @@ import java.util.StringTokenizer; import java.util.Iterator; import java.security.Permission; import sun.net.NetworkClient; +import sun.net.util.IPAddressUtil; import sun.net.www.MessageHeader; import sun.net.www.MeteredStream; import sun.net.www.URLConnection; @@ -155,6 +157,21 @@ public class FtpURLConnection extends URLConnection { } } + static URL checkURL(URL u) throws IllegalArgumentException { + if (u != null) { + if (u.toExternalForm().indexOf('\n') > -1) { + Exception mfue = new MalformedURLException("Illegal character in URL"); + throw new IllegalArgumentException(mfue.getMessage(), mfue); + } + } + String s = IPAddressUtil.checkAuthority(u); + if (s != null) { + Exception mfue = new MalformedURLException(s); + throw new IllegalArgumentException(mfue.getMessage(), mfue); + } + return u; + } + /** * Creates an FtpURLConnection from a URL. * @@ -168,7 +185,7 @@ public class FtpURLConnection extends URLConnection { * Same as FtpURLconnection(URL) with a per connection proxy specified */ FtpURLConnection(URL url, Proxy p) { - super(url); + super(checkURL(url)); instProxy = p; host = url.getHost(); port = url.getPort(); diff --git a/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 4c5ec78fc..6adc9f65b 100644 --- a/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,6 +66,7 @@ import java.util.HashSet; import java.util.HashMap; import java.util.Set; import sun.net.*; +import sun.net.util.IPAddressUtil; import sun.net.www.*; import sun.net.www.http.HttpClient; import sun.net.www.http.PosterOutputStream; @@ -850,8 +851,13 @@ public class HttpURLConnection extends java.net.HttpURLConnection { throw new MalformedURLException("Illegal character in URL"); } } + String s = IPAddressUtil.checkAuthority(u); + if (s != null) { + throw new MalformedURLException(s); + } return u; } + protected HttpURLConnection(URL u, Proxy p, Handler handler) throws IOException { super(checkURL(u)); diff --git a/src/share/classes/sun/net/www/protocol/https/HttpsURLConnectionImpl.java b/src/share/classes/sun/net/www/protocol/https/HttpsURLConnectionImpl.java index e43a36310..0186e2472 100644 --- a/src/share/classes/sun/net/www/protocol/https/HttpsURLConnectionImpl.java +++ b/src/share/classes/sun/net/www/protocol/https/HttpsURLConnectionImpl.java @@ -45,6 +45,7 @@ import java.security.Permission; import java.security.Principal; import java.util.Map; import java.util.List; +import sun.net.util.IPAddressUtil; import sun.net.www.http.HttpClient; /** @@ -86,6 +87,10 @@ public class HttpsURLConnectionImpl throw new MalformedURLException("Illegal character in URL"); } } + String s = IPAddressUtil.checkAuthority(u); + if (s != null) { + throw new MalformedURLException(s); + } return u; } // For both copies of the file, uncomment one line and comment the other @@ -333,7 +338,7 @@ public class HttpsURLConnectionImpl * @param key the keyword by which the request is known * (e.g., "accept"). * @param value the value associated with it. - * @see #getRequestProperties(java.lang.String) + * @see #getRequestProperty(java.lang.String) * @since 1.4 */ public void addRequestProperty(String key, String value) { -- GitLab