提交 4dcf283e 编写于 作者: M mullan

7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect

Reviewed-by: vinnie
上级 dc5debbe
......@@ -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;
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);
// Parse RDN, and store it in vector
RDN rdn = new RDN(rdnString, "RFC2253");
dnVector.add(rdn);
// Increase the offset
dnOffset = rdnEnd + 1;
}
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);
/*
* 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()]);
}
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)) {
/*
* 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);
// Increase the offset
dnOffset = rdnEnd + 1;
}
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);
/*
* 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()]);
}
/*
......
/*
* 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;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册