6829283: HTTP/Negotiate: Autheticator triggered again when user cancels the first one

Reviewed-by: chegar
This commit is contained in:
Weijun Wang 2010-03-18 18:26:37 +08:00
parent 69e5f8b791
commit 23a0fee518
2 changed files with 112 additions and 44 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2005-2009 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2005-2010 Sun Microsystems, Inc. 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
@ -45,43 +45,50 @@ public class NegotiateCallbackHandler implements CallbackHandler {
private String username;
private char[] password;
/**
* Authenticator asks for username and password in a single prompt,
* but CallbackHandler checks one by one. So, no matter which callback
* gets handled first, make sure Authenticator is only called once.
*/
private boolean answered;
private final HttpCallerInfo hci;
public NegotiateCallbackHandler(HttpCallerInfo hci) {
this.hci = hci;
}
private void getAnswer() {
if (!answered) {
answered = true;
PasswordAuthentication passAuth =
Authenticator.requestPasswordAuthentication(
hci.host, hci.addr, hci.port, hci.protocol,
hci.prompt, hci.scheme, hci.url, hci.authType);
/**
* To be compatible with existing callback handler implementations,
* when the underlying Authenticator is canceled, username and
* password are assigned null. No exception is thrown.
*/
if (passAuth != null) {
username = passAuth.getUserName();
password = passAuth.getPassword();
}
}
}
public void handle(Callback[] callbacks) throws
UnsupportedCallbackException, IOException {
for (int i=0; i<callbacks.length; i++) {
Callback callBack = callbacks[i];
if (callBack instanceof NameCallback) {
if (username == null) {
PasswordAuthentication passAuth =
Authenticator.requestPasswordAuthentication(
hci.host, hci.addr, hci.port, hci.protocol,
hci.prompt, hci.scheme, hci.url, hci.authType);
username = passAuth.getUserName();
password = passAuth.getPassword();
}
NameCallback nameCallback =
(NameCallback)callBack;
nameCallback.setName(username);
getAnswer();
((NameCallback)callBack).setName(username);
} else if (callBack instanceof PasswordCallback) {
PasswordCallback passwordCallback =
(PasswordCallback)callBack;
if (password == null) {
PasswordAuthentication passAuth =
Authenticator.requestPasswordAuthentication(
hci.host, hci.addr, hci.port, hci.protocol,
hci.prompt, hci.scheme, hci.url, hci.authType);
username = passAuth.getUserName();
password = passAuth.getPassword();
}
passwordCallback.setPassword(password);
Arrays.fill(password, ' ');
getAnswer();
((PasswordCallback)callBack).setPassword(password);
if (password != null) Arrays.fill(password, ' ');
} else {
throw new UnsupportedCallbackException(callBack,
"Call back not supported");

View File

@ -1,5 +1,5 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2009-2010 Sun Microsystems, Inc. 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
@ -23,8 +23,9 @@
/*
* @test
* @bug 6578647
* @bug 6578647 6829283
* @summary Undefined requesting URL in java.net.Authenticator.getPasswordAuthentication()
* @summary HTTP/Negotiate: Authenticator triggered again when user cancels the first one
*/
import com.sun.net.httpserver.Headers;
@ -35,6 +36,8 @@ import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpPrincipal;
import com.sun.security.auth.module.Krb5LoginModule;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
@ -79,6 +82,9 @@ public class HttpNegotiateServer {
// web page content
final static String CONTENT = "Hello, World!";
// For 6829283, count how many times the Authenticator is called.
static int count = 0;
// URLs for web test, proxy test. The proxy server is not a real proxy
// since it fakes the same content for any URL. :)
final static URL webUrl, proxyUrl;
@ -134,6 +140,17 @@ public class HttpNegotiateServer {
}
}
/**
* This Authenticator knows nothing
*/
static class KnowNothingAuthenticator extends java.net.Authenticator {
@Override
public PasswordAuthentication getPasswordAuthentication () {
HttpNegotiateServer.count++;
return null;
}
}
public static void main(String[] args)
throws Exception {
@ -147,7 +164,6 @@ public class HttpNegotiateServer {
kdcp.addPrincipalRandKey("krbtgt/" + REALM_PROXY);
kdcp.addPrincipalRandKey("HTTP/" + PROXY_HOST);
KDC.writeMultiKtab(KRB5_TAB, kdcw, kdcp);
KDC.saveConfig(KRB5_CONF, kdcw, kdcp,
"default_keytab_name = " + KRB5_TAB,
"[domain_realm]",
@ -157,6 +173,19 @@ public class HttpNegotiateServer {
System.setProperty("java.security.krb5.conf", KRB5_CONF);
Config.refresh();
KDC.writeMultiKtab(KRB5_TAB, kdcw, kdcp);
// Write a customized JAAS conf file, so that any kinit cache
// will be ignored.
System.setProperty("java.security.auth.login.config", OneKDC.JAAS_CONF);
File f = new File(OneKDC.JAAS_CONF);
FileOutputStream fos = new FileOutputStream(f);
fos.write((
"com.sun.security.jgss.krb5.initiate {\n" +
" com.sun.security.auth.module.Krb5LoginModule required;\n};\n"
).getBytes());
fos.close();
f.deleteOnExit();
HttpServer h1 = httpd(WEB_PORT, "Negotiate", false,
"HTTP/" + WEB_HOST + "@" + REALM_WEB, KRB5_TAB);
@ -164,23 +193,21 @@ public class HttpNegotiateServer {
"HTTP/" + PROXY_HOST + "@" + REALM_PROXY, KRB5_TAB);
try {
BufferedReader reader;
java.net.Authenticator.setDefault(new KnowAllAuthenticator());
reader = new BufferedReader(new InputStreamReader(
webUrl.openConnection().getInputStream()));
if (!reader.readLine().equals(CONTENT)) {
throw new RuntimeException("Bad content");
Exception e1 = null, e2 = null;
try {
test6578647();
} catch (Exception e) {
e1 = e;
e.printStackTrace();
}
reader = new BufferedReader(new InputStreamReader(
proxyUrl.openConnection(
new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(PROXY_HOST, PROXY_PORT)))
.getInputStream()));
if (!reader.readLine().equals(CONTENT)) {
throw new RuntimeException("Bad content");
try {
test6829283();
} catch (Exception e) {
e2 = e;
e.printStackTrace();
}
if (e1 != null || e2 != null) {
throw new RuntimeException("Test error");
}
} finally {
// Must stop. Seems there's no HttpServer.startAsDaemon()
@ -189,6 +216,40 @@ public class HttpNegotiateServer {
}
}
static void test6578647() throws Exception {
BufferedReader reader;
java.net.Authenticator.setDefault(new KnowAllAuthenticator());
reader = new BufferedReader(new InputStreamReader(
webUrl.openConnection().getInputStream()));
if (!reader.readLine().equals(CONTENT)) {
throw new RuntimeException("Bad content");
}
reader = new BufferedReader(new InputStreamReader(
proxyUrl.openConnection(
new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(PROXY_HOST, PROXY_PORT)))
.getInputStream()));
if (!reader.readLine().equals(CONTENT)) {
throw new RuntimeException("Bad content");
}
}
static void test6829283() throws Exception {
BufferedReader reader;
java.net.Authenticator.setDefault(new KnowNothingAuthenticator());
try {
new BufferedReader(new InputStreamReader(
webUrl.openConnection().getInputStream()));
} catch (IOException ioe) {
// Will fail since no username and password is provided.
}
if (count > 1) {
throw new RuntimeException("Authenticator called twice");
}
}
/**
* Creates and starts an HTTP or proxy server that requires
* Negotiate authentication.