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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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 String username;
private char[] password; 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; private final HttpCallerInfo hci;
public NegotiateCallbackHandler(HttpCallerInfo hci) { public NegotiateCallbackHandler(HttpCallerInfo hci) {
this.hci = 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 public void handle(Callback[] callbacks) throws
UnsupportedCallbackException, IOException { UnsupportedCallbackException, IOException {
for (int i=0; i<callbacks.length; i++) { for (int i=0; i<callbacks.length; i++) {
Callback callBack = callbacks[i]; Callback callBack = callbacks[i];
if (callBack instanceof NameCallback) { if (callBack instanceof NameCallback) {
if (username == null) { getAnswer();
PasswordAuthentication passAuth = ((NameCallback)callBack).setName(username);
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);
} else if (callBack instanceof PasswordCallback) { } else if (callBack instanceof PasswordCallback) {
PasswordCallback passwordCallback = getAnswer();
(PasswordCallback)callBack; ((PasswordCallback)callBack).setPassword(password);
if (password == null) { if (password != null) Arrays.fill(password, ' ');
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, ' ');
} else { } else {
throw new UnsupportedCallbackException(callBack, throw new UnsupportedCallbackException(callBack,
"Call back not supported"); "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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,8 +23,9 @@
/* /*
* @test * @test
* @bug 6578647 * @bug 6578647 6829283
* @summary Undefined requesting URL in java.net.Authenticator.getPasswordAuthentication() * @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; 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.net.httpserver.HttpPrincipal;
import com.sun.security.auth.module.Krb5LoginModule; import com.sun.security.auth.module.Krb5LoginModule;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
@ -79,6 +82,9 @@ public class HttpNegotiateServer {
// web page content // web page content
final static String CONTENT = "Hello, World!"; 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 // URLs for web test, proxy test. The proxy server is not a real proxy
// since it fakes the same content for any URL. :) // since it fakes the same content for any URL. :)
final static URL webUrl, proxyUrl; 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) public static void main(String[] args)
throws Exception { throws Exception {
@ -147,7 +164,6 @@ public class HttpNegotiateServer {
kdcp.addPrincipalRandKey("krbtgt/" + REALM_PROXY); kdcp.addPrincipalRandKey("krbtgt/" + REALM_PROXY);
kdcp.addPrincipalRandKey("HTTP/" + PROXY_HOST); kdcp.addPrincipalRandKey("HTTP/" + PROXY_HOST);
KDC.writeMultiKtab(KRB5_TAB, kdcw, kdcp);
KDC.saveConfig(KRB5_CONF, kdcw, kdcp, KDC.saveConfig(KRB5_CONF, kdcw, kdcp,
"default_keytab_name = " + KRB5_TAB, "default_keytab_name = " + KRB5_TAB,
"[domain_realm]", "[domain_realm]",
@ -157,6 +173,19 @@ public class HttpNegotiateServer {
System.setProperty("java.security.krb5.conf", KRB5_CONF); System.setProperty("java.security.krb5.conf", KRB5_CONF);
Config.refresh(); 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, HttpServer h1 = httpd(WEB_PORT, "Negotiate", false,
"HTTP/" + WEB_HOST + "@" + REALM_WEB, KRB5_TAB); "HTTP/" + WEB_HOST + "@" + REALM_WEB, KRB5_TAB);
@ -164,7 +193,30 @@ public class HttpNegotiateServer {
"HTTP/" + PROXY_HOST + "@" + REALM_PROXY, KRB5_TAB); "HTTP/" + PROXY_HOST + "@" + REALM_PROXY, KRB5_TAB);
try { try {
Exception e1 = null, e2 = null;
try {
test6578647();
} catch (Exception e) {
e1 = e;
e.printStackTrace();
}
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()
if (h1 != null) h1.stop(0);
if (h2 != null) h2.stop(0);
}
}
static void test6578647() throws Exception {
BufferedReader reader; BufferedReader reader;
java.net.Authenticator.setDefault(new KnowAllAuthenticator()); java.net.Authenticator.setDefault(new KnowAllAuthenticator());
@ -182,10 +234,19 @@ public class HttpNegotiateServer {
if (!reader.readLine().equals(CONTENT)) { if (!reader.readLine().equals(CONTENT)) {
throw new RuntimeException("Bad content"); throw new RuntimeException("Bad content");
} }
} finally { }
// Must stop. Seems there's no HttpServer.startAsDaemon()
if (h1 != null) h1.stop(0); static void test6829283() throws Exception {
if (h2 != null) h2.stop(0); 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");
} }
} }