8293590: Some syntax checks performed by URL.openConnection() could be performed earlier, at URL construction

Reviewed-by: jpai, michaelm
This commit is contained in:
Daniel Fuchs 2022-10-18 14:30:51 +00:00
parent 78fed9d307
commit bd41428a56
4 changed files with 638 additions and 39 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 2022, 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
@ -504,10 +504,23 @@ public abstract class URLStreamHandler {
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);
"this handler");
}
// if early parsing, perform additional checks here rather than waiting
// for openConnection()
boolean earlyURLParsing = IPAddressUtil.earlyURLParsing();
boolean isBuiltInHandler = u.isBuiltinStreamHandler(this);
if (host != null && isBuiltInHandler) {
String errMsg = IPAddressUtil.checkHostString(host);
if (errMsg != null) throw new IllegalArgumentException(errMsg);
}
if (userInfo != null && isBuiltInHandler && earlyURLParsing) {
String errMsg = IPAddressUtil.checkUserInfo(userInfo);
if (errMsg != null) throw new IllegalArgumentException(errMsg);
}
if (authority != null && isBuiltInHandler && earlyURLParsing) {
String errMsg = IPAddressUtil.checkAuth(authority);
if (errMsg != null) throw new IllegalArgumentException(errMsg);
}
// ensure that no one can reset the protocol on a given URL.
u.set(u.getProtocol(), host, port, authority, userInfo, path, query, ref);

View file

@ -427,6 +427,16 @@ public class IPAddressUtil {
// All of the above
private static final long L_EXCLUDE = 0x84008008ffffffffL;
private static final long H_EXCLUDE = 0x8000000038000001L;
// excluded delims: "<>\" " - we don't include % and # here
private static final long L_EXCLUDED_DELIMS = 0x5000000500000000L;
private static final long H_EXCLUDED_DELIMS = 0x0L;
// unwise "{}|\\^[]`";
private static final long L_UNWISE = 0x0L;
private static final long H_UNWISE = 0x3800000178000000L;
private static final long L_FRAGMENT = 0x0000000800000000L;
private static final long H_FRAGMENT = 0x0L;
private static final long L_QUERY = 0x8000000000000000L;
private static final long H_QUERY = 0x0L;
private static final char[] OTHERS = {
8263,8264,8265,8448,8449,8453,8454,10868,
@ -479,10 +489,14 @@ public class IPAddressUtil {
return "'" + c + "'";
}
private static String checkUserInfo(String str) {
// Check user-info component.
// This method returns an error message if a problem
// is found. The caller is expected to use that message to
// throw an exception.
public static String checkUserInfo(String str) {
// colon is permitted in user info
int index = scan(str, L_EXCLUDE & ~L_COLON,
H_EXCLUDE & ~H_COLON);
int index = scan(str, MASKS.L_USERINFO_MASK,
MASKS.H_USERINFO_MASK);
if (index >= 0) {
return "Illegal character found in user-info: "
+ describeChar(str.charAt(index));
@ -498,8 +512,7 @@ public class IPAddressUtil {
index = str.indexOf('%');
if (index >= 0) {
index = scan(str = str.substring(index),
L_NON_PRINTABLE | L_IPV6_DELIMS,
H_NON_PRINTABLE | H_IPV6_DELIMS);
MASKS.L_SCOPE_MASK, MASKS.H_SCOPE_MASK);
if (index >= 0) {
return "Illegal character found in IPv6 scoped address: "
+ describeChar(str.charAt(index));
@ -509,7 +522,8 @@ public class IPAddressUtil {
}
return "Unrecognized IPv6 address format";
} else {
index = scan(str, L_EXCLUDE, H_EXCLUDE);
index = scan(str, L_EXCLUDE | MASKS.L_HOSTNAME_MASK,
H_EXCLUDE | MASKS.H_HOSTNAME_MASK, OTHERS);
if (index >= 0) {
return "Illegal character found in host: "
+ describeChar(str.charAt(index));
@ -518,7 +532,14 @@ public class IPAddressUtil {
return null;
}
private static String checkAuth(String str) {
// Simple checks for the authority component.
// Deeper checks on the various parts of a server-based
// authority component may be performed by calling
// #checkAuthority(URL url)
// This method returns an error message if a problem
// is found. The caller is expected to use that message to
// throw an exception.
public static String checkAuth(String str) {
int index = scan(str,
L_EXCLUDE & ~L_AUTH_DELIMS,
H_EXCLUDE & ~H_AUTH_DELIMS);
@ -529,8 +550,11 @@ public class IPAddressUtil {
return null;
}
// check authority of hierarchical URL. Appropriate for
// HTTP-like protocol handlers
// check authority of hierarchical (server based) URL.
// Appropriate for HTTP-like protocol handlers
// This method returns an error message if a problem
// is found. The caller is expected to use that message to
// throw an exception.
public static String checkAuthority(URL url) {
String s, u, h;
if (url == null) return null;
@ -546,33 +570,53 @@ public class IPAddressUtil {
return null;
}
// minimal syntax checks - deeper check may be performed
// by the appropriate protocol handler
// minimal syntax checks if delayed parsing is
// enabled - deeper check will be performed
// later by the appropriate protocol handler
// This method returns an error message if a problem
// is found. The caller is expected to use that message to
// throw an exception.
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));
boolean earlyURLParsing = earlyURLParsing();
String userInfo = url.getUserInfo();
if (earlyURLParsing) {
if ((s = checkUserInfo(userInfo)) != null) return s;
} else {
int index = scan(s = userInfo,
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) {
String host = url.getHost();
if ((s = checkHostString(host)) != null) {
return s;
}
return null;
}
// Check host component.
// This method returns an error message if a problem
// is found. The caller is expected to use that message to
// throw an exception.
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));
if (earlyURLParsing()) {
// also validate IPv6 literal format if present
return checkHost(host);
} else {
int index = scan(host,
MASKS.L_HOSTNAME_MASK,
MASKS.H_HOSTNAME_MASK,
OTHERS);
if (index >= 0) {
return "Illegal character found in host: "
+ describeChar(host.charAt(index));
}
}
return null;
}
@ -803,6 +847,14 @@ public class IPAddressUtil {
return parseAsciiDigit(c, DECIMAL);
}
public static boolean earlyURLParsing() {
return !MASKS.DELAY_URL_PARSING_SP_VALUE;
}
public static boolean delayURLParsing() {
return MASKS.DELAY_URL_PARSING_SP_VALUE;
}
// Supported radixes
private static final int HEXADECIMAL = 16;
private static final int DECIMAL = 10;
@ -817,4 +869,33 @@ public class IPAddressUtil {
private static final String ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP = "jdk.net.allowAmbiguousIPAddressLiterals";
private static final boolean ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP_VALUE = Boolean.valueOf(
GetPropertyAction.privilegedGetProperty(ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP, "false"));
private static class MASKS {
private static final String DELAY_URL_PARSING_SP = "jdk.net.url.delayParsing";
private static final boolean DELAY_URL_PARSING_SP_VALUE;
static final long L_USERINFO_MASK = L_EXCLUDE & ~L_COLON;
static final long H_USERINFO_MASK = H_EXCLUDE & ~H_COLON;
static final long L_HOSTNAME_MASK;
static final long H_HOSTNAME_MASK;
static final long L_SCOPE_MASK;
static final long H_SCOPE_MASK;
static {
var value = GetPropertyAction.privilegedGetProperty(
DELAY_URL_PARSING_SP, "false");
DELAY_URL_PARSING_SP_VALUE = value.isEmpty()
|| Boolean.parseBoolean(value);
if (DELAY_URL_PARSING_SP_VALUE) {
L_HOSTNAME_MASK = L_NON_PRINTABLE | L_SLASH;
H_HOSTNAME_MASK = H_NON_PRINTABLE | H_SLASH;
L_SCOPE_MASK = L_NON_PRINTABLE | L_IPV6_DELIMS;
H_SCOPE_MASK = H_NON_PRINTABLE | H_IPV6_DELIMS;
} else {
// the hostname mask can also forbid [ ] brackets, because IPv6 should be
// checked early before the mask is used when earlier parsing checks are performed
L_HOSTNAME_MASK = L_NON_PRINTABLE | L_SLASH | L_UNWISE | L_EXCLUDED_DELIMS;
H_HOSTNAME_MASK = H_NON_PRINTABLE | H_SLASH | H_UNWISE | H_EXCLUDED_DELIMS;
L_SCOPE_MASK = L_NON_PRINTABLE | L_IPV6_DELIMS | L_SLASH | L_BACKSLASH | L_FRAGMENT | L_QUERY;
H_SCOPE_MASK = H_NON_PRINTABLE | H_IPV6_DELIMS | H_SLASH | H_BACKSLASH | H_FRAGMENT | H_QUERY;
}
}
}
}

View file

@ -159,17 +159,15 @@ public class FtpURLConnection extends URLConnection {
}
}
static URL checkURL(URL u) throws IllegalArgumentException {
private static URL checkURL(URL u) throws MalformedURLException {
if (u != null) {
if (u.toExternalForm().indexOf('\n') > -1) {
Exception mfue = new MalformedURLException("Illegal character in URL");
throw new IllegalArgumentException(mfue.getMessage(), mfue);
throw new MalformedURLException("Illegal character in URL");
}
}
String s = IPAddressUtil.checkAuthority(u);
if (s != null) {
Exception mfue = new MalformedURLException(s);
throw new IllegalArgumentException(mfue.getMessage(), mfue);
String errMsg = IPAddressUtil.checkAuthority(u);
if (errMsg != null) {
throw new MalformedURLException(errMsg);
}
return u;
}
@ -179,14 +177,14 @@ public class FtpURLConnection extends URLConnection {
*
* @param url The {@code URL} to retrieve or store.
*/
public FtpURLConnection(URL url) {
public FtpURLConnection(URL url) throws MalformedURLException {
this(url, null);
}
/**
* Same as FtpURLconnection(URL) with a per connection proxy specified
*/
FtpURLConnection(URL url, Proxy p) {
FtpURLConnection(URL url, Proxy p) throws MalformedURLException {
super(checkURL(url));
instProxy = p;
host = url.getHost();