mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-17 18:30:27 +00:00
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
Reviewed-by: vinnie
This commit is contained in:
parent
7c6bbdcd3e
commit
e96d7e3af4
@ -42,7 +42,7 @@ import sun.security.pkcs.PKCS9Attribute;
|
||||
* X.500 Attribute-Value-Assertion (AVA): an attribute, as identified by
|
||||
* some attribute ID, has some particular value. Values are as a rule ASN.1
|
||||
* printable strings. A conventional set of type IDs is recognized when
|
||||
* parsing (and generating) RFC 1779 or RFC 2253 syntax strings.
|
||||
* parsing (and generating) RFC 1779, 2253 or 4514 syntax strings.
|
||||
*
|
||||
* <P>AVAs are components of X.500 relative names. Think of them as being
|
||||
* individual fields of a database record. The attribute ID is how you
|
||||
@ -92,18 +92,20 @@ public class AVA implements DerEncoder {
|
||||
* Leading and trailing spaces, also multiple internal spaces, also
|
||||
* call for quoting the whole string.
|
||||
*/
|
||||
private static final String specialChars = ",+=\n<>#;";
|
||||
private static final String specialChars1779 = ",=\n+<>#;\\\"";
|
||||
|
||||
/*
|
||||
* In RFC2253, if the value has any of these characters in it, it
|
||||
* must be quoted by a preceding \.
|
||||
*/
|
||||
private static final String specialChars2253 = ",+\"\\<>;";
|
||||
private static final String specialChars2253 = ",=+<>#;\\\"";
|
||||
|
||||
/*
|
||||
* includes special chars from RFC1779 and RFC2253, as well as ' '
|
||||
* includes special chars from RFC1779 and RFC2253, as well as ' ' from
|
||||
* RFC 4514.
|
||||
*/
|
||||
private static final String specialCharsAll = ",=\n+<>#;\\\" ";
|
||||
private static final String specialCharsDefault = ",=\n+<>#;\\\" ";
|
||||
private static final String escapedDefault = ",+<>;\"";
|
||||
|
||||
/*
|
||||
* Values that aren't printable strings are emitted as BER-encoded
|
||||
@ -120,26 +122,26 @@ public class AVA implements DerEncoder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an RFC 1779 or RFC 2253 style AVA string: CN=fee fie foe fum
|
||||
* Parse an RFC 1779, 2253 or 4514 style AVA string: CN=fee fie foe fum
|
||||
* or perhaps with quotes. Not all defined AVA tags are supported;
|
||||
* of current note are X.400 related ones (PRMD, ADMD, etc).
|
||||
*
|
||||
* This terminates at unescaped AVA separators ("+") or RDN
|
||||
* separators (",", ";"), or DN terminators (">"), and removes
|
||||
* cosmetic whitespace at the end of values.
|
||||
* separators (",", ";"), and removes cosmetic whitespace at the end of
|
||||
* values.
|
||||
*/
|
||||
AVA(Reader in) throws IOException {
|
||||
this(in, DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an RFC 1779 or RFC 2253 style AVA string: CN=fee fie foe fum
|
||||
* Parse an RFC 1779, 2253 or 4514 style AVA string: CN=fee fie foe fum
|
||||
* or perhaps with quotes. Additional keywords can be specified in the
|
||||
* keyword/OID map.
|
||||
*
|
||||
* This terminates at unescaped AVA separators ("+") or RDN
|
||||
* separators (",", ";"), or DN terminators (">"), and removes
|
||||
* cosmetic whitespace at the end of values.
|
||||
* separators (",", ";"), and removes cosmetic whitespace at the end of
|
||||
* values.
|
||||
*/
|
||||
AVA(Reader in, Map<String, String> keywordMap) throws IOException {
|
||||
this(in, DEFAULT, keywordMap);
|
||||
@ -147,9 +149,6 @@ public class AVA implements DerEncoder {
|
||||
|
||||
/**
|
||||
* Parse an AVA string formatted according to format.
|
||||
*
|
||||
* XXX format RFC1779 should only allow RFC1779 syntax but is
|
||||
* actually DEFAULT with RFC1779 keywords.
|
||||
*/
|
||||
AVA(Reader in, int format) throws IOException {
|
||||
this(in, format, Collections.<String, String>emptyMap());
|
||||
@ -158,9 +157,6 @@ public class AVA implements DerEncoder {
|
||||
/**
|
||||
* Parse an AVA string formatted according to format.
|
||||
*
|
||||
* XXX format RFC1779 should only allow RFC1779 syntax but is
|
||||
* actually DEFAULT with RFC1779 keywords.
|
||||
*
|
||||
* @param in Reader containing AVA String
|
||||
* @param format parsing format
|
||||
* @param keywordMap a Map where a keyword String maps to a corresponding
|
||||
@ -168,11 +164,11 @@ public class AVA implements DerEncoder {
|
||||
* If an entry does not exist, it will fallback to the builtin
|
||||
* keyword/OID mapping.
|
||||
* @throws IOException if the AVA String is not valid in the specified
|
||||
* standard or an OID String from the keywordMap is improperly formatted
|
||||
* format or an OID String from the keywordMap is improperly formatted
|
||||
*/
|
||||
AVA(Reader in, int format, Map<String, String> keywordMap)
|
||||
throws IOException {
|
||||
// assume format is one of DEFAULT, RFC1779, RFC2253
|
||||
// assume format is one of DEFAULT or RFC2253
|
||||
|
||||
StringBuilder temp = new StringBuilder();
|
||||
int c;
|
||||
@ -193,7 +189,7 @@ public class AVA implements DerEncoder {
|
||||
|
||||
/*
|
||||
* Now parse the value. "#hex", a quoted string, or a string
|
||||
* terminated by "+", ",", ";", ">". Whitespace before or after
|
||||
* terminated by "+", ",", ";". Whitespace before or after
|
||||
* the value is stripped away unless format is RFC2253.
|
||||
*/
|
||||
temp.setLength(0);
|
||||
@ -202,7 +198,7 @@ public class AVA implements DerEncoder {
|
||||
c = in.read();
|
||||
if (c == ' ') {
|
||||
throw new IOException("Incorrect AVA RFC2253 format - " +
|
||||
"leading space must be escaped");
|
||||
"leading space must be escaped");
|
||||
}
|
||||
} else {
|
||||
// read next character skipping whitespace
|
||||
@ -331,8 +327,7 @@ public class AVA implements DerEncoder {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c != '\\' && c != '"' &&
|
||||
specialChars.indexOf((char)c) < 0) {
|
||||
if (specialChars1779.indexOf((char)c) < 0) {
|
||||
throw new IOException
|
||||
("Invalid escaped character in AVA: " +
|
||||
(char)c);
|
||||
@ -386,7 +381,7 @@ public class AVA implements DerEncoder {
|
||||
private DerValue parseString
|
||||
(Reader in, int c, int format, StringBuilder temp) throws IOException {
|
||||
|
||||
List<Byte> embeddedHex = new ArrayList<Byte>();
|
||||
List<Byte> embeddedHex = new ArrayList<>();
|
||||
boolean isPrintableString = true;
|
||||
boolean escape = false;
|
||||
boolean leadingChar = true;
|
||||
@ -413,24 +408,19 @@ public class AVA implements DerEncoder {
|
||||
}
|
||||
|
||||
// check if character was improperly escaped
|
||||
if ((format == DEFAULT &&
|
||||
specialCharsAll.indexOf((char)c) == -1) ||
|
||||
(format == RFC1779 &&
|
||||
specialChars.indexOf((char)c) == -1 &&
|
||||
c != '\\' && c != '\"')) {
|
||||
|
||||
if (format == DEFAULT &&
|
||||
specialCharsDefault.indexOf((char)c) == -1) {
|
||||
throw new IOException
|
||||
("Invalid escaped character in AVA: '" +
|
||||
(char)c + "'");
|
||||
|
||||
} else if (format == RFC2253) {
|
||||
if (c == ' ') {
|
||||
// only leading/trailing space can be escaped
|
||||
if (!leadingChar && !trailingSpace(in)) {
|
||||
throw new IOException
|
||||
("Invalid escaped space character " +
|
||||
"in AVA. Only a leading or trailing " +
|
||||
"space character can be escaped.");
|
||||
throw new IOException
|
||||
("Invalid escaped space character " +
|
||||
"in AVA. Only a leading or trailing " +
|
||||
"space character can be escaped.");
|
||||
}
|
||||
} else if (c == '#') {
|
||||
// only leading '#' can be escaped
|
||||
@ -443,18 +433,20 @@ public class AVA implements DerEncoder {
|
||||
throw new IOException
|
||||
("Invalid escaped character in AVA: '" +
|
||||
(char)c + "'");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// check if character should have been escaped
|
||||
if (format == RFC2253) {
|
||||
if (specialChars2253.indexOf((char)c) != -1) {
|
||||
throw new IOException
|
||||
("Character '" + (char)c +
|
||||
"' in AVA appears without escape");
|
||||
"' in AVA appears without escape");
|
||||
}
|
||||
} else if (escapedDefault.indexOf((char)c) != -1) {
|
||||
throw new IOException
|
||||
("Character '" + (char)c +
|
||||
"' in AVA appears without escape");
|
||||
}
|
||||
}
|
||||
|
||||
@ -551,7 +543,6 @@ public class AVA implements DerEncoder {
|
||||
case ',':
|
||||
return true;
|
||||
case ';':
|
||||
case '>':
|
||||
return format != RFC2253;
|
||||
default:
|
||||
return false;
|
||||
@ -1200,18 +1191,6 @@ class AVAKeyword {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object identifier representing the specified keyword (or
|
||||
* string encoded object identifier) in the given standard.
|
||||
*
|
||||
* @throws IOException If the keyword is not valid in the specified standard
|
||||
*/
|
||||
static ObjectIdentifier getOID(String keyword, int standard)
|
||||
throws IOException {
|
||||
return getOID
|
||||
(keyword, standard, Collections.<String, String>emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object identifier representing the specified keyword (or
|
||||
* string encoded object identifier) in the given standard.
|
||||
@ -1249,19 +1228,11 @@ class AVAKeyword {
|
||||
return new ObjectIdentifier(oidString);
|
||||
}
|
||||
|
||||
// no keyword found or not standard compliant, check if OID string
|
||||
|
||||
// RFC1779 requires, DEFAULT allows OID. prefix
|
||||
if (standard == AVA.RFC1779) {
|
||||
if (keyword.startsWith("OID.") == false) {
|
||||
throw new IOException("Invalid RFC1779 keyword: " + keyword);
|
||||
}
|
||||
// no keyword found, check if OID string
|
||||
if (standard == AVA.DEFAULT && keyword.startsWith("OID.")) {
|
||||
keyword = keyword.substring(4);
|
||||
} else if (standard == AVA.DEFAULT) {
|
||||
if (keyword.startsWith("OID.")) {
|
||||
keyword = keyword.substring(4);
|
||||
}
|
||||
}
|
||||
|
||||
boolean number = false;
|
||||
if (keyword.length() != 0) {
|
||||
char ch = keyword.charAt(0);
|
||||
|
||||
@ -142,9 +142,9 @@ public class X500Name implements GeneralNameInterface, Principal {
|
||||
/**
|
||||
* Constructs a name from a conventionally formatted string, such
|
||||
* as "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US".
|
||||
* (RFC 1779 or RFC 2253 style).
|
||||
* (RFC 1779, 2253, or 4514 style).
|
||||
*
|
||||
* @param DN X.500 Distinguished Name
|
||||
* @param dname the X.500 Distinguished Name
|
||||
*/
|
||||
public X500Name(String dname) throws IOException {
|
||||
this(dname, Collections.<String, String>emptyMap());
|
||||
@ -153,9 +153,9 @@ public class X500Name implements GeneralNameInterface, Principal {
|
||||
/**
|
||||
* Constructs a name from a conventionally formatted string, such
|
||||
* as "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US".
|
||||
* (RFC 1779 or RFC 2253 style).
|
||||
* (RFC 1779, 2253, or 4514 style).
|
||||
*
|
||||
* @param DN X.500 Distinguished Name
|
||||
* @param dname the X.500 Distinguished Name
|
||||
* @param keywordMap an additional keyword/OID map
|
||||
*/
|
||||
public X500Name(String dname, Map<String, String> keywordMap)
|
||||
@ -167,10 +167,11 @@ public class X500Name implements GeneralNameInterface, Principal {
|
||||
* Constructs a name from a string formatted according to format.
|
||||
* Currently, the formats DEFAULT and RFC2253 are supported.
|
||||
* DEFAULT is the default format used by the X500Name(String)
|
||||
* constructor. RFC2253 is format strictly according to RFC2253
|
||||
* constructor. RFC2253 is the format strictly according to RFC2253
|
||||
* without extensions.
|
||||
*
|
||||
* @param DN X.500 Distinguished Name
|
||||
* @param dname the X.500 Distinguished Name
|
||||
* @param format the specified format of the String DN
|
||||
*/
|
||||
public X500Name(String dname, String format) throws IOException {
|
||||
if (dname == null) {
|
||||
@ -865,8 +866,8 @@ public class X500Name implements GeneralNameInterface, Principal {
|
||||
* O="Sue, Grabbit and Runn" or
|
||||
* O=Sue\, Grabbit and Runn
|
||||
*
|
||||
* This method can parse 1779 or 2253 DNs and non-standard 3280 keywords.
|
||||
* Additional keywords can be specified in the keyword/OID map.
|
||||
* This method can parse RFC 1779, 2253 or 4514 DNs and non-standard 3280
|
||||
* keywords. Additional keywords can be specified in the keyword/OID map.
|
||||
*/
|
||||
private void parseDN(String input, Map<String, String> keywordMap)
|
||||
throws IOException {
|
||||
@ -875,7 +876,7 @@ public class X500Name implements GeneralNameInterface, Principal {
|
||||
return;
|
||||
}
|
||||
|
||||
List<RDN> dnVector = new ArrayList<RDN>();
|
||||
List<RDN> dnVector = new ArrayList<>();
|
||||
int dnOffset = 0;
|
||||
int rdnEnd;
|
||||
String rdnString;
|
||||
@ -945,52 +946,51 @@ public class X500Name implements GeneralNameInterface, Principal {
|
||||
if (dnString.length() == 0) {
|
||||
names = new RDN[0];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
List<RDN> dnVector = new ArrayList<RDN>();
|
||||
int dnOffset = 0;
|
||||
String rdnString;
|
||||
List<RDN> dnVector = new ArrayList<>();
|
||||
int dnOffset = 0;
|
||||
String rdnString;
|
||||
int searchOffset = 0;
|
||||
int rdnEnd = dnString.indexOf(',');
|
||||
while (rdnEnd >=0) {
|
||||
/*
|
||||
* We have encountered an RDN delimiter (comma).
|
||||
* If the comma in the RDN under consideration is
|
||||
* preceded by a backslash (escape), it
|
||||
* is part of the RDN. Otherwise, it is used as a separator, to
|
||||
* delimit the RDN under consideration from any subsequent RDNs.
|
||||
*/
|
||||
if (rdnEnd > 0 && !escaped(rdnEnd, searchOffset, dnString)) {
|
||||
|
||||
int searchOffset = 0;
|
||||
int rdnEnd = dnString.indexOf(',');
|
||||
while (rdnEnd >=0) {
|
||||
/*
|
||||
* We have encountered an RDN delimiter (comma).
|
||||
* If the comma in the RDN under consideration is
|
||||
* preceded by a backslash (escape), it
|
||||
* is part of the RDN. Otherwise, it is used as a separator, to
|
||||
* delimit the RDN under consideration from any subsequent RDNs.
|
||||
*/
|
||||
if (rdnEnd > 0 && !escaped(rdnEnd, searchOffset, dnString)) {
|
||||
/*
|
||||
* Comma is a separator
|
||||
*/
|
||||
rdnString = dnString.substring(dnOffset, rdnEnd);
|
||||
|
||||
/*
|
||||
* Comma is a separator
|
||||
*/
|
||||
rdnString = dnString.substring(dnOffset, rdnEnd);
|
||||
// Parse RDN, and store it in vector
|
||||
RDN rdn = new RDN(rdnString, "RFC2253");
|
||||
dnVector.add(rdn);
|
||||
|
||||
// Parse RDN, and store it in vector
|
||||
RDN rdn = new RDN(rdnString, "RFC2253");
|
||||
dnVector.add(rdn);
|
||||
// Increase the offset
|
||||
dnOffset = rdnEnd + 1;
|
||||
}
|
||||
|
||||
// Increase the offset
|
||||
dnOffset = rdnEnd + 1;
|
||||
}
|
||||
searchOffset = rdnEnd + 1;
|
||||
rdnEnd = dnString.indexOf(',', searchOffset);
|
||||
}
|
||||
|
||||
searchOffset = rdnEnd + 1;
|
||||
rdnEnd = dnString.indexOf(',', searchOffset);
|
||||
}
|
||||
// Parse last or only RDN, and store it in vector
|
||||
rdnString = dnString.substring(dnOffset);
|
||||
RDN rdn = new RDN(rdnString, "RFC2253");
|
||||
dnVector.add(rdn);
|
||||
|
||||
// Parse last or only RDN, and store it in vector
|
||||
rdnString = dnString.substring(dnOffset);
|
||||
RDN rdn = new RDN(rdnString, "RFC2253");
|
||||
dnVector.add(rdn);
|
||||
|
||||
/*
|
||||
* Store the vector elements as an array of RDNs
|
||||
* NOTE: It's only on output that little-endian ordering is used.
|
||||
*/
|
||||
Collections.reverse(dnVector);
|
||||
names = dnVector.toArray(new RDN[dnVector.size()]);
|
||||
/*
|
||||
* Store the vector elements as an array of RDNs
|
||||
* NOTE: It's only on output that little-endian ordering is used.
|
||||
*/
|
||||
Collections.reverse(dnVector);
|
||||
names = dnVector.toArray(new RDN[dnVector.size()]);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
73
jdk/test/javax/security/auth/x500/X500Principal/Parse.java
Normal file
73
jdk/test/javax/security/auth/x500/X500Principal/Parse.java
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 7024771
|
||||
* @summary various X500Principal DN parsing tests
|
||||
*/
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
public class Parse {
|
||||
|
||||
private static TestCase[] testCases = {
|
||||
new TestCase("CN=prefix\\<>suffix", false)
|
||||
};
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
for (int i = 0; i < testCases.length; i++) {
|
||||
testCases[i].run();
|
||||
}
|
||||
System.out.println("Test completed ok.");
|
||||
}
|
||||
}
|
||||
|
||||
class TestCase {
|
||||
|
||||
private String name;
|
||||
private boolean expectedResult;
|
||||
|
||||
TestCase(String name, boolean expectedResult) {
|
||||
this.name = name;
|
||||
this.expectedResult = expectedResult;
|
||||
}
|
||||
|
||||
void run() throws Exception {
|
||||
Exception f = null;
|
||||
try {
|
||||
System.out.println("Parsing: \"" + name + "\"");
|
||||
new X500Principal(name);
|
||||
if (expectedResult == false) {
|
||||
f = new Exception("Successfully parsed invalid name");
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (expectedResult == true) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
if (f != null) {
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user