Skip to content

Commit 9602a75

Browse files
authored
Merge pull request #7 from ulvii/PR836
A different proposal for wildcard certificate matching
2 parents cb33eea + 7c07349 commit 9602a75

File tree

2 files changed

+50
-52
lines changed

2 files changed

+50
-52
lines changed

src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java

Lines changed: 45 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,70 +1403,63 @@ private String parseCommonName(String distinguishedName) {
14031403
private boolean validateServerName(String nameInCert) {
14041404
// Failed to get the common name from DN or empty CN
14051405
if (null == nameInCert) {
1406-
if (logger.isLoggable(Level.FINER))
1406+
if (logger.isLoggable(Level.FINER)) {
14071407
logger.finer(logContext + " Failed to parse the name from the certificate or name is empty.");
1408+
}
14081409
return false;
14091410
}
1410-
1411-
int wildcardIndex = nameInCert.indexOf("*");
1412-
1413-
// Respect wildcard. If wildcardIndex is larger than -1, then we have a wildcard.
1414-
if (wildcardIndex >= 0) {
1415-
// We do not allow wildcards to exist past the first period.
1416-
if (wildcardIndex > nameInCert.indexOf(".")) {
1417-
return false;
1418-
}
1419-
1420-
// We do not allow wildcards in IDNs.
1421-
if (nameInCert.startsWith("xn--")) {
1422-
return false;
1423-
}
1424-
1425-
/* We do not allow * plus a top-level domain.
1426-
* This if statement counts the number of .s in the nameInCert. If it's 1 or less, then reject it.
1427-
* This also catches cases where nameInCert is just *
1428-
*/
1429-
if ((nameInCert.length() - nameInCert.replace(".", "").length()) <= 1) {
1430-
return false;
1411+
// We do not allow wildcards in IDNs (xn--).
1412+
if (!nameInCert.startsWith("xn--") && nameInCert.contains("*")) {
1413+
int hostIndex = 0, certIndex = 0, match = 0, startIndex = -1, periodCount = 0;
1414+
while (hostIndex < hostName.length()) {
1415+
if ('.' == hostName.charAt(hostIndex)) {
1416+
periodCount++;
1417+
}
1418+
if (certIndex < nameInCert.length() && hostName.charAt(hostIndex) == nameInCert.charAt(certIndex)) {
1419+
hostIndex++;
1420+
certIndex++;
1421+
} else if (certIndex < nameInCert.length() && '*' == nameInCert.charAt(certIndex)) {
1422+
startIndex = certIndex;
1423+
match = hostIndex;
1424+
certIndex++;
1425+
} else if (startIndex != -1 && 0 == periodCount) {
1426+
certIndex = startIndex + 1;
1427+
match++;
1428+
hostIndex = match;
1429+
} else {
1430+
logFailMessage(nameInCert);
1431+
return false;
1432+
}
14311433
}
1432-
1433-
String certBeforeWildcard = nameInCert.substring(0, wildcardIndex);
1434-
int firstPeriodAfterWildcard = nameInCert.indexOf(".", wildcardIndex);
1435-
String certAfterWildcard;
1436-
1437-
if (firstPeriodAfterWildcard < 0) {
1438-
/* if we get something like peter.database.c*, then make certAfterWildcard empty so that we accept
1439-
* anything after *.
1440-
* both startsWith("") and endswith("") will always resolve to "true".
1441-
*/
1442-
certAfterWildcard = "";
1434+
if (nameInCert.length() == certIndex && periodCount > 1) {
1435+
logSuccessMessage(nameInCert);
1436+
return true;
14431437
} else {
1444-
certAfterWildcard = nameInCert.substring(firstPeriodAfterWildcard);
1445-
}
1446-
1447-
if (hostName.startsWith(certBeforeWildcard) && hostName.endsWith(certAfterWildcard)) {
1448-
// now, find the string that the wildcard covers. If it contains any periods, reject it.
1449-
int wildcardCoveredStringIndexStart = hostName.indexOf(certBeforeWildcard) + certBeforeWildcard.length();
1450-
int wildcardCoveredStringIndexEnd = hostName.lastIndexOf(certAfterWildcard);
1451-
if (!hostName.substring(wildcardCoveredStringIndexStart, wildcardCoveredStringIndexEnd).contains(".")) {
1452-
return true;
1453-
}
1438+
logFailMessage(nameInCert);
1439+
return false;
14541440
}
14551441
}
1456-
14571442
// Verify that the name in certificate matches exactly with the host name
14581443
if (!nameInCert.equals(hostName)) {
1459-
if (logger.isLoggable(Level.FINER))
1460-
logger.finer(logContext + " The name in certificate " + nameInCert
1461-
+ " does not match with the server name " + hostName + ".");
1444+
logFailMessage(nameInCert);
14621445
return false;
14631446
}
1447+
logSuccessMessage(nameInCert);
1448+
return true;
1449+
}
14641450

1465-
if (logger.isLoggable(Level.FINER))
1451+
private void logFailMessage(String nameInCert) {
1452+
if (logger.isLoggable(Level.FINER)) {
1453+
logger.finer(logContext + " The name in certificate " + nameInCert
1454+
+ " does not match with the server name " + hostName + ".");
1455+
}
1456+
}
1457+
1458+
private void logSuccessMessage(String nameInCert) {
1459+
if (logger.isLoggable(Level.FINER)) {
14661460
logger.finer(logContext + " The name in certificate:" + nameInCert + " validated against server name "
14671461
+ hostName + ".");
1468-
1469-
return true;
1462+
}
14701463
}
14711464

14721465
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
@@ -4637,8 +4630,8 @@ void writeTVPRows(TVP value) throws SQLServerException {
46374630
SQLServerError databaseError = new SQLServerError();
46384631
databaseError.setFromTDS(tdsReader);
46394632

4640-
SQLServerException.makeFromDatabaseError(con, null, databaseError.getErrorMessage(), databaseError,
4641-
false);
4633+
SQLServerException.makeFromDatabaseError(con, null, databaseError.getErrorMessage(),
4634+
databaseError, false);
46424635
}
46434636

46444637
command.setInterruptsEnabled(true);

src/test/java/com/microsoft/sqlserver/jdbc/SSLCertificateValidation.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ public void testValidateServerName() throws Exception {
4747
// Expected result: true
4848
assertTrue((boolean) method.invoke(hsoObject, "msjdbc.database.windows.net"));
4949

50+
// Server Name = msjdbc.database.windows.net
51+
// SAN = msjdbc***.database.windows.net
52+
// Expected result: true
53+
assertTrue((boolean) method.invoke(hsoObject, "msjdbc***.database.windows.net"));
54+
5055
// Server Name = msjdbc.database.windows.net
5156
// SAN = ms*bc.database.windows.net
5257
// Expected result: true

0 commit comments

Comments
 (0)