mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8372731: Detailed authentication failure messages
Reviewed-by: dfuchs, michaelm
This commit is contained in:
parent
5141e1a4f4
commit
895232fc65
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2025, 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
|
||||
@ -25,6 +25,7 @@
|
||||
|
||||
package sun.net.www.protocol.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
@ -428,9 +429,10 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
|
||||
* @param conn The connection to apply the header(s) to
|
||||
* @param p A source of header values for this connection, if needed.
|
||||
* @param raw The raw header field (if needed)
|
||||
* @return true if all goes well, false if no headers were set.
|
||||
* @throws IOException if no headers were set
|
||||
*/
|
||||
public abstract boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw);
|
||||
public abstract void setHeaders(HttpURLConnection conn, HeaderParser p, String raw)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Check if the header indicates that the current auth. parameters are stale.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2025, 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
|
||||
@ -125,15 +125,13 @@ final class BasicAuthentication extends AuthenticationInfo {
|
||||
* @param conn The connection to apply the header(s) to
|
||||
* @param p A source of header values for this connection, if needed.
|
||||
* @param raw The raw header values for this connection, if needed.
|
||||
* @return true if all goes well, false if no headers were set.
|
||||
*/
|
||||
@Override
|
||||
public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
public void setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
// no need to synchronize here:
|
||||
// already locked by s.n.w.p.h.HttpURLConnection
|
||||
assert conn.isLockHeldByCurrentThread();
|
||||
conn.setAuthenticationProperty(getHeaderName(), getHeaderValue(null,null));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2025, 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
|
||||
@ -321,7 +321,11 @@ final class DigestAuthentication extends AuthenticationInfo {
|
||||
*/
|
||||
@Override
|
||||
public String getHeaderValue(URL url, String method) {
|
||||
return getHeaderValueImpl(url.getFile(), method);
|
||||
try {
|
||||
return getHeaderValueImpl(url.getFile(), method);
|
||||
} catch (IOException _) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -339,7 +343,11 @@ final class DigestAuthentication extends AuthenticationInfo {
|
||||
* @return the value of the HTTP header this authentication wants set
|
||||
*/
|
||||
String getHeaderValue(String requestURI, String method) {
|
||||
return getHeaderValueImpl(requestURI, method);
|
||||
try {
|
||||
return getHeaderValueImpl(requestURI, method);
|
||||
} catch (IOException _) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -369,10 +377,11 @@ final class DigestAuthentication extends AuthenticationInfo {
|
||||
* @param conn The connection to apply the header(s) to
|
||||
* @param p A source of header values for this connection, if needed.
|
||||
* @param raw Raw header values for this connection, if needed.
|
||||
* @return true if all goes well, false if no headers were set.
|
||||
* @throws IOException if no headers were set
|
||||
*/
|
||||
@Override
|
||||
public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
public void setHeaders(HttpURLConnection conn, HeaderParser p, String raw)
|
||||
throws IOException {
|
||||
// no need to synchronize here:
|
||||
// already locked by s.n.w.p.h.HttpURLConnection
|
||||
assert conn.isLockHeldByCurrentThread();
|
||||
@ -380,14 +389,14 @@ final class DigestAuthentication extends AuthenticationInfo {
|
||||
params.setNonce (p.findValue("nonce"));
|
||||
params.setOpaque (p.findValue("opaque"));
|
||||
params.setQop (p.findValue("qop"));
|
||||
params.setUserhash (Boolean.valueOf(p.findValue("userhash")));
|
||||
params.setUserhash (Boolean.parseBoolean(p.findValue("userhash")));
|
||||
String charset = p.findValue("charset");
|
||||
if (charset == null) {
|
||||
charset = "ISO_8859_1";
|
||||
} else if (!charset.equalsIgnoreCase("UTF-8")) {
|
||||
// UTF-8 is only valid value. ISO_8859_1 represents default behavior
|
||||
// when the parameter is not set.
|
||||
return false;
|
||||
throw new IOException("Illegal charset in header");
|
||||
}
|
||||
params.setCharset(charset.toUpperCase(Locale.ROOT));
|
||||
|
||||
@ -405,7 +414,7 @@ final class DigestAuthentication extends AuthenticationInfo {
|
||||
}
|
||||
|
||||
if (params.nonce == null || authMethod == null || pw == null || realm == null) {
|
||||
return false;
|
||||
throw new IOException("Server challenge incomplete");
|
||||
}
|
||||
if (authMethod.length() >= 1) {
|
||||
// Method seems to get converted to all lower case elsewhere.
|
||||
@ -415,8 +424,7 @@ final class DigestAuthentication extends AuthenticationInfo {
|
||||
+ authMethod.substring(1).toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
if (!setAlgorithmNames(p, params))
|
||||
return false;
|
||||
setAlgorithmNames(p, params);
|
||||
|
||||
// If authQop is true, then the server is doing RFC2617 and
|
||||
// has offered qop=auth. We do not support any other modes
|
||||
@ -426,20 +434,17 @@ final class DigestAuthentication extends AuthenticationInfo {
|
||||
params.setNewCnonce();
|
||||
}
|
||||
|
||||
String value = getHeaderValueImpl (uri, method);
|
||||
if (value != null) {
|
||||
conn.setAuthenticationProperty(getHeaderName(), value);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
String value = getHeaderValueImpl(uri, method);
|
||||
assert value != null;
|
||||
conn.setAuthenticationProperty(getHeaderName(), value);
|
||||
}
|
||||
|
||||
// Algorithm name is stored in two separate fields (of Paramaeters)
|
||||
// This allows for variations in digest algorithm name (aliases)
|
||||
// and also allow for the -sess variant defined in HTTP Digest protocol
|
||||
// returns false if algorithm not supported
|
||||
private static boolean setAlgorithmNames(HeaderParser p, Parameters params) {
|
||||
// throws IOException if algorithm not supported
|
||||
private static void setAlgorithmNames(HeaderParser p, Parameters params)
|
||||
throws IOException {
|
||||
String algorithm = p.findValue("algorithm");
|
||||
String digestName = algorithm;
|
||||
if (algorithm == null || algorithm.isEmpty()) {
|
||||
@ -459,18 +464,17 @@ final class DigestAuthentication extends AuthenticationInfo {
|
||||
var oid = KnownOIDs.findMatch(digestName);
|
||||
if (oid == null) {
|
||||
log("unknown algorithm: " + algorithm);
|
||||
return false;
|
||||
throw new IOException("Unknown algorithm: " + algorithm);
|
||||
}
|
||||
digestName = oid.stdName();
|
||||
params.setAlgorithm (algorithm);
|
||||
params.setDigestName (digestName);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Calculate the Authorization header field given the request URI
|
||||
* and based on the authorization information in params
|
||||
*/
|
||||
private String getHeaderValueImpl (String uri, String method) {
|
||||
private String getHeaderValueImpl (String uri, String method) throws IOException {
|
||||
String response;
|
||||
char[] passwd = pw.getPassword();
|
||||
boolean qop = params.authQop();
|
||||
@ -479,11 +483,7 @@ final class DigestAuthentication extends AuthenticationInfo {
|
||||
String nonce = params.getNonce ();
|
||||
String algorithm = params.getAlgorithm ();
|
||||
String digest = params.getDigestName ();
|
||||
try {
|
||||
validateDigest(digest);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
validateDigest(digest);
|
||||
Charset charset = params.getCharset();
|
||||
boolean userhash = params.getUserhash ();
|
||||
params.incrementNC ();
|
||||
@ -505,7 +505,7 @@ final class DigestAuthentication extends AuthenticationInfo {
|
||||
digest, session, charset);
|
||||
} catch (CharacterCodingException | NoSuchAlgorithmException ex) {
|
||||
log(ex.getMessage());
|
||||
return null;
|
||||
throw new IOException("Failed to compute digest", ex);
|
||||
}
|
||||
|
||||
String ncfield = "\"";
|
||||
@ -534,7 +534,7 @@ final class DigestAuthentication extends AuthenticationInfo {
|
||||
}
|
||||
} catch (CharacterCodingException | NoSuchAlgorithmException ex) {
|
||||
log(ex.getMessage());
|
||||
return null;
|
||||
throw new IOException("Failed to compute user hash", ex);
|
||||
}
|
||||
|
||||
String value = authMethod
|
||||
|
||||
@ -61,6 +61,7 @@ import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import jdk.internal.access.JavaNetHttpCookieAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.util.Exceptions;
|
||||
import sun.net.NetProperties;
|
||||
import sun.net.NetworkClient;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
@ -1469,16 +1470,29 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
/* in this case, only one header field will be present */
|
||||
String raw = responses.findValue ("Proxy-Authenticate");
|
||||
reset ();
|
||||
if (!proxyAuthentication.setHeaders(this,
|
||||
authhdr.headerParser(), raw)) {
|
||||
try {
|
||||
proxyAuthentication.setHeaders(this,
|
||||
authhdr.headerParser(), raw);
|
||||
} catch (IOException ex) {
|
||||
disconnectInternal();
|
||||
throw new IOException ("Authentication failure");
|
||||
if (Exceptions.enhancedNonSocketExceptions()) {
|
||||
throw new IOException ("Authentication failure", ex);
|
||||
} else {
|
||||
throw new IOException ("Authentication failure");
|
||||
}
|
||||
}
|
||||
if (serverAuthentication != null && srvHdr != null &&
|
||||
!serverAuthentication.setHeaders(this,
|
||||
srvHdr.headerParser(), raw)) {
|
||||
disconnectInternal ();
|
||||
throw new IOException ("Authentication failure");
|
||||
if (serverAuthentication != null && srvHdr != null) {
|
||||
try {
|
||||
serverAuthentication.setHeaders(this,
|
||||
srvHdr.headerParser(), raw);
|
||||
} catch (IOException ex) {
|
||||
disconnectInternal();
|
||||
if (Exceptions.enhancedNonSocketExceptions()) {
|
||||
throw new IOException ("Authentication failure", ex);
|
||||
} else {
|
||||
throw new IOException ("Authentication failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
authObj = null;
|
||||
doingNTLMp2ndStage = false;
|
||||
@ -1557,9 +1571,15 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
} else {
|
||||
reset ();
|
||||
/* header not used for ntlm */
|
||||
if (!serverAuthentication.setHeaders(this, null, raw)) {
|
||||
try {
|
||||
serverAuthentication.setHeaders(this, null, raw);
|
||||
} catch (IOException ex) {
|
||||
disconnectWeb();
|
||||
throw new IOException ("Authentication failure");
|
||||
if (Exceptions.enhancedNonSocketExceptions()) {
|
||||
throw new IOException ("Authentication failure", ex);
|
||||
} else {
|
||||
throw new IOException ("Authentication failure");
|
||||
}
|
||||
}
|
||||
doingNTLM2ndStage = false;
|
||||
authObj = null;
|
||||
@ -1935,10 +1955,16 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
} else {
|
||||
String raw = responses.findValue ("Proxy-Authenticate");
|
||||
reset ();
|
||||
if (!proxyAuthentication.setHeaders(this,
|
||||
authhdr.headerParser(), raw)) {
|
||||
try {
|
||||
proxyAuthentication.setHeaders(this,
|
||||
authhdr.headerParser(), raw);
|
||||
} catch (IOException ex) {
|
||||
disconnectInternal();
|
||||
throw new IOException ("Authentication failure");
|
||||
if (Exceptions.enhancedNonSocketExceptions()) {
|
||||
throw new IOException ("Authentication failure", ex);
|
||||
} else {
|
||||
throw new IOException ("Authentication failure");
|
||||
}
|
||||
}
|
||||
authObj = null;
|
||||
doingNTLMp2ndStage = false;
|
||||
@ -2201,7 +2227,9 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
};
|
||||
}
|
||||
if (ret != null) {
|
||||
if (!ret.setHeaders(this, p, raw)) {
|
||||
try {
|
||||
ret.setHeaders(this, p, raw);
|
||||
} catch (IOException e) {
|
||||
ret.disposeContext();
|
||||
ret = null;
|
||||
}
|
||||
@ -2358,7 +2386,9 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
}
|
||||
}
|
||||
if (ret != null ) {
|
||||
if (!ret.setHeaders(this, p, raw)) {
|
||||
try {
|
||||
ret.setHeaders(this, p, raw);
|
||||
} catch (IOException e) {
|
||||
ret.disposeContext();
|
||||
ret = null;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2025, 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
|
||||
@ -168,29 +168,24 @@ final class NegotiateAuthentication extends AuthenticationInfo {
|
||||
* @param p A source of header values for this connection, not used because
|
||||
* HeaderParser converts the fields to lower case, use raw instead
|
||||
* @param raw The raw header field.
|
||||
* @return true if all goes well, false if no headers were set.
|
||||
* @throws IOException if no headers were set
|
||||
*/
|
||||
@Override
|
||||
public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
public void setHeaders(HttpURLConnection conn, HeaderParser p, String raw) throws IOException {
|
||||
// no need to synchronize here:
|
||||
// already locked by s.n.w.p.h.HttpURLConnection
|
||||
assert conn.isLockHeldByCurrentThread();
|
||||
|
||||
try {
|
||||
String response;
|
||||
byte[] incoming = null;
|
||||
String[] parts = raw.split("\\s+");
|
||||
if (parts.length > 1) {
|
||||
incoming = Base64.getDecoder().decode(parts[1]);
|
||||
}
|
||||
response = hci.scheme + " " + Base64.getEncoder().encodeToString(
|
||||
incoming==null?firstToken():nextToken(incoming));
|
||||
|
||||
conn.setAuthenticationProperty(getHeaderName(), response);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
String response;
|
||||
byte[] incoming = null;
|
||||
String[] parts = raw.split("\\s+");
|
||||
if (parts.length > 1) {
|
||||
incoming = Base64.getDecoder().decode(parts[1]);
|
||||
}
|
||||
response = hci.scheme + " " + Base64.getEncoder().encodeToString(
|
||||
incoming==null?firstToken():nextToken(incoming));
|
||||
|
||||
conn.setAuthenticationProperty(getHeaderName(), response);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2025, 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
|
||||
@ -35,7 +35,6 @@ import java.net.URL;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Base64;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
import sun.net.www.HeaderParser;
|
||||
import sun.net.www.protocol.http.AuthenticationInfo;
|
||||
@ -203,10 +202,10 @@ public final class NTLMAuthentication extends AuthenticationInfo {
|
||||
* @param p A source of header values for this connection, not used because
|
||||
* HeaderParser converts the fields to lower case, use raw instead
|
||||
* @param raw The raw header field.
|
||||
* @return true if all goes well, false if no headers were set.
|
||||
* @throws IOException if no headers were set
|
||||
*/
|
||||
@Override
|
||||
public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
public void setHeaders(HttpURLConnection conn, HeaderParser p, String raw) throws IOException {
|
||||
// no need to synchronize here:
|
||||
// already locked by s.n.w.p.h.HttpURLConnection
|
||||
assert conn.isLockHeldByCurrentThread();
|
||||
@ -220,9 +219,8 @@ public final class NTLMAuthentication extends AuthenticationInfo {
|
||||
response = buildType3Msg (msg);
|
||||
}
|
||||
conn.setAuthenticationProperty(getHeaderName(), response);
|
||||
return true;
|
||||
} catch (IOException | GeneralSecurityException e) {
|
||||
return false;
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,8 +230,7 @@ public final class NTLMAuthentication extends AuthenticationInfo {
|
||||
return result;
|
||||
}
|
||||
|
||||
private String buildType3Msg (String challenge) throws GeneralSecurityException,
|
||||
IOException {
|
||||
private String buildType3Msg (String challenge) throws GeneralSecurityException {
|
||||
/* First decode the type2 message to get the server nonce */
|
||||
/* nonce is located at type2[24] for 8 bytes */
|
||||
|
||||
|
||||
@ -91,6 +91,7 @@ public class NTLMAuthSequence {
|
||||
|
||||
private native long getCredentialsHandle (String user, String domain, String password);
|
||||
|
||||
private native byte[] getNextToken (long crdHandle, byte[] lastToken, Status returned);
|
||||
private native byte[] getNextToken (long crdHandle, byte[] lastToken, Status returned)
|
||||
throws IOException;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2025, 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
|
||||
@ -26,9 +26,7 @@
|
||||
package sun.net.www.protocol.http.ntlm;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.net.UnknownHostException;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
import sun.net.NetProperties;
|
||||
@ -204,10 +202,10 @@ public final class NTLMAuthentication extends AuthenticationInfo {
|
||||
* @param p A source of header values for this connection, not used because
|
||||
* HeaderParser converts the fields to lower case, use raw instead
|
||||
* @param raw The raw header field.
|
||||
* @return true if all goes well, false if no headers were set.
|
||||
* @throws IOException if no headers were set
|
||||
*/
|
||||
@Override
|
||||
public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
public void setHeaders(HttpURLConnection conn, HeaderParser p, String raw) throws IOException {
|
||||
|
||||
// no need to synchronize here:
|
||||
// already locked by s.n.w.p.h.HttpURLConnection
|
||||
@ -224,10 +222,9 @@ public final class NTLMAuthentication extends AuthenticationInfo {
|
||||
if (seq.isComplete()) {
|
||||
conn.authObj(null);
|
||||
}
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
conn.authObj(null);
|
||||
return false;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,6 +230,8 @@ JNIEXPORT jbyteArray JNICALL Java_sun_net_www_protocol_http_ntlm_NTLMAuthSequenc
|
||||
}
|
||||
|
||||
if (ss < 0) {
|
||||
SetLastError(ss);
|
||||
JNU_ThrowIOExceptionWithLastError(env, "InitializeSecurityContext");
|
||||
endSequence (pCred, pCtx, env, status);
|
||||
return 0;
|
||||
}
|
||||
@ -238,6 +240,8 @@ JNIEXPORT jbyteArray JNICALL Java_sun_net_www_protocol_http_ntlm_NTLMAuthSequenc
|
||||
ss = CompleteAuthToken( pCtx, &OutBuffDesc );
|
||||
|
||||
if (ss < 0) {
|
||||
SetLastError(ss);
|
||||
JNU_ThrowIOExceptionWithLastError(env, "CompleteAuthToken");
|
||||
endSequence (pCred, pCtx, env, status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
167
test/jdk/sun/net/www/protocol/http/NTLMFailTest.java
Normal file
167
test/jdk/sun/net/www/protocol/http/NTLMFailTest.java
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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 8372731
|
||||
* @library /test/lib
|
||||
* @run main/othervm NTLMFailTest
|
||||
* @run main/othervm -Djdk.includeInExceptions= NTLMFailTest
|
||||
* @summary check that the Authentication failure exception
|
||||
* honors the jdk.includeInExceptions setting
|
||||
*/
|
||||
|
||||
import jdk.test.lib.net.HttpHeaderParser;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Authenticator;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
|
||||
public class NTLMFailTest {
|
||||
|
||||
static final int BODY_LEN = 8192;
|
||||
|
||||
static final String RESP_SERVER_AUTH =
|
||||
"HTTP/1.1 401 Unauthorized\r\n" +
|
||||
"WWW-Authenticate: NTLM\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"Content-Length: " + BODY_LEN + "\r\n" +
|
||||
"\r\n";
|
||||
|
||||
static final String RESP_SERVER_NTLM =
|
||||
"HTTP/1.1 401 Unauthorized\r\n" +
|
||||
"WWW-Authenticate: NTLM InvalidChallenge\r\n" +
|
||||
"Connection: Keep-Alive\r\n" +
|
||||
"Content-Length: " + BODY_LEN + "\r\n" +
|
||||
"\r\n";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Authenticator.setDefault(new TestAuthenticator());
|
||||
try (NTLMServer server = startServer(new ServerSocket(0, 0, InetAddress.getLoopbackAddress()))) {
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(server.getLocalPort())
|
||||
.path("/")
|
||||
.toURLUnchecked();
|
||||
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
|
||||
uc.setRequestMethod("HEAD");
|
||||
uc.getInputStream().readAllBytes();
|
||||
throw new RuntimeException("Expected exception was not thrown");
|
||||
} catch (IOException e) {
|
||||
if (e.getMessage().contains("Authentication failure")) {
|
||||
System.err.println("Got expected exception:");
|
||||
e.printStackTrace();
|
||||
if (System.getProperty("jdk.includeInExceptions") == null) {
|
||||
// detailed message enabled by default
|
||||
if (e.getCause() == null) {
|
||||
throw new RuntimeException("Expected a detailed exception", e);
|
||||
}
|
||||
// no checks on the detailed message; it's platform-specific and may be translated
|
||||
} else {
|
||||
// detailed message disabled
|
||||
if (e.getCause() != null) {
|
||||
throw new RuntimeException("Unexpected detailed exception", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class NTLMServer extends Thread implements AutoCloseable {
|
||||
final ServerSocket ss;
|
||||
volatile boolean closed;
|
||||
|
||||
NTLMServer(ServerSocket serverSS) {
|
||||
super();
|
||||
setDaemon(true);
|
||||
this.ss = serverSS;
|
||||
}
|
||||
|
||||
int getLocalPort() { return ss.getLocalPort(); }
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean doing2ndStageNTLM = false;
|
||||
while (!closed) {
|
||||
try {
|
||||
Socket s = ss.accept();
|
||||
InputStream is = s.getInputStream();
|
||||
OutputStream os = s.getOutputStream();
|
||||
doServer(is, os, doing2ndStageNTLM);
|
||||
if (!doing2ndStageNTLM) {
|
||||
doing2ndStageNTLM = true;
|
||||
} else {
|
||||
os.close();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
if (!closed) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (closed) return;
|
||||
synchronized(this) {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
}
|
||||
try { ss.close(); } catch (IOException x) { };
|
||||
}
|
||||
}
|
||||
|
||||
static NTLMServer startServer(ServerSocket serverSS) {
|
||||
NTLMServer server = new NTLMServer(serverSS);
|
||||
server.start();
|
||||
return server;
|
||||
}
|
||||
|
||||
static void doServer(InputStream is, OutputStream os, boolean doing2ndStageNTLM) throws IOException {
|
||||
if (!doing2ndStageNTLM) {
|
||||
new HttpHeaderParser(is);
|
||||
os.write(RESP_SERVER_AUTH.getBytes("ASCII"));
|
||||
} else {
|
||||
new HttpHeaderParser(is);
|
||||
os.write(RESP_SERVER_NTLM.getBytes("ASCII"));
|
||||
}
|
||||
}
|
||||
|
||||
static class TestAuthenticator extends Authenticator {
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication("test", "secret".toCharArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user