HttpNegotiateServer.java revision 1300
1266N/A/*
1266N/A * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
1266N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1266N/A *
1266N/A * This code is free software; you can redistribute it and/or modify it
1266N/A * under the terms of the GNU General Public License version 2 only, as
1266N/A * published by the Free Software Foundation.
1266N/A *
1266N/A * This code is distributed in the hope that it will be useful, but WITHOUT
1266N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1266N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1266N/A * version 2 for more details (a copy is included in the LICENSE file that
1266N/A * accompanied this code).
1266N/A *
1266N/A * You should have received a copy of the GNU General Public License version
1266N/A * 2 along with this work; if not, write to the Free Software Foundation,
1266N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1266N/A *
1266N/A * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
1266N/A * CA 95054 USA or visit www.sun.com if you need additional information or
1266N/A * have any questions.
1266N/A */
1266N/A
1266N/A/*
1266N/A * @test
1266N/A * @bug 6578647
1266N/A * @summary Undefined requesting URL in java.net.Authenticator.getPasswordAuthentication()
1266N/A */
1266N/A
1266N/Aimport com.sun.net.httpserver.Headers;
1266N/Aimport com.sun.net.httpserver.HttpContext;
1266N/Aimport com.sun.net.httpserver.HttpExchange;
1266N/Aimport com.sun.net.httpserver.HttpHandler;
1266N/Aimport com.sun.net.httpserver.HttpServer;
1266N/Aimport com.sun.net.httpserver.HttpPrincipal;
1266N/Aimport com.sun.security.auth.module.Krb5LoginModule;
1266N/Aimport java.io.BufferedReader;
1266N/Aimport java.io.IOException;
1266N/Aimport java.io.InputStreamReader;
1266N/Aimport java.net.HttpURLConnection;
1266N/Aimport java.net.InetSocketAddress;
1266N/Aimport java.net.PasswordAuthentication;
1266N/Aimport java.net.Proxy;
1266N/Aimport java.net.URL;
1266N/Aimport java.security.PrivilegedExceptionAction;
1266N/Aimport java.util.HashMap;
1266N/Aimport java.util.Map;
1266N/Aimport javax.security.auth.Subject;
1266N/Aimport org.ietf.jgss.GSSContext;
1266N/Aimport org.ietf.jgss.GSSCredential;
1266N/Aimport org.ietf.jgss.GSSManager;
1266N/Aimport sun.security.jgss.GSSUtil;
1266N/Aimport sun.security.krb5.Config;
1266N/A
1266N/A/**
1266N/A * Basic JGSS/krb5 test with 3 parties: client, server, backend server. Each
1266N/A * party uses JAAS login to get subjects and executes JGSS calls using
1266N/A * Subject.doAs.
1266N/A */
1300N/Apublic class HttpNegotiateServer {
1266N/A
1266N/A // Two realm, web server in one, proxy server in another
1266N/A final static String REALM_WEB = "WEB.DOMAIN";
1266N/A final static String REALM_PROXY = "PROXY.DOMAIN";
1266N/A final static String KRB5_CONF = "web.conf";
1266N/A final static String KRB5_TAB = "web.ktab";
1266N/A
1266N/A // user principals
1266N/A final static String WEB_USER = "web";
1266N/A final static char[] WEB_PASS = "webby".toCharArray();
1266N/A final static String PROXY_USER = "pro";
1266N/A final static char[] PROXY_PASS = "proxy".toCharArray();
1266N/A final static int WEB_PORT = 17840;
1266N/A
1266N/A final static String WEB_HOST = "host.web.domain";
1266N/A final static String PROXY_HOST = "host.proxy.domain";
1266N/A final static int PROXY_PORT = 17841;
1266N/A
1266N/A // web page content
1266N/A final static String CONTENT = "Hello, World!";
1266N/A
1266N/A // URLs for web test, proxy test. The proxy server is not a real proxy
1266N/A // since it fakes the same content for any URL. :)
1266N/A final static URL webUrl, proxyUrl;
1266N/A static {
1266N/A URL u1 = null, u2 = null;
1266N/A try {
1266N/A u1 = new URL("http://" + WEB_HOST +":" + WEB_PORT + "/a/b/c");
1266N/A u2 = new URL("http://nosuchplace/a/b/c");
1266N/A } catch (Exception e) {
1266N/A }
1266N/A webUrl = u1; proxyUrl = u2;
1266N/A }
1266N/A
1266N/A /**
1266N/A * This Authenticator checks everything:
1266N/A * scheme, protocol, requestor type, host, port, and url
1266N/A */
1266N/A static class KnowAllAuthenticator extends java.net.Authenticator {
1266N/A public PasswordAuthentication getPasswordAuthentication () {
1266N/A if (!getRequestingScheme().equalsIgnoreCase("Negotiate")) {
1266N/A throw new RuntimeException("Bad scheme");
1266N/A }
1266N/A if (!getRequestingProtocol().equalsIgnoreCase("HTTP")) {
1266N/A throw new RuntimeException("Bad protocol");
1266N/A }
1266N/A if (getRequestorType() == RequestorType.SERVER) {
1266N/A if (!this.getRequestingHost().equalsIgnoreCase(webUrl.getHost())) {
1266N/A throw new RuntimeException("Bad host");
1266N/A }
1266N/A if (this.getRequestingPort() != webUrl.getPort()) {
1266N/A throw new RuntimeException("Bad port");
1266N/A }
1266N/A if (!this.getRequestingURL().equals(webUrl)) {
1266N/A throw new RuntimeException("Bad url");
1266N/A }
1266N/A return new PasswordAuthentication(
1266N/A WEB_USER+"@"+REALM_WEB, WEB_PASS);
1266N/A } else if (getRequestorType() == RequestorType.PROXY) {
1266N/A if (!this.getRequestingHost().equalsIgnoreCase(PROXY_HOST)) {
1266N/A throw new RuntimeException("Bad host");
1266N/A }
1266N/A if (this.getRequestingPort() != PROXY_PORT) {
1266N/A throw new RuntimeException("Bad port");
1266N/A }
1266N/A if (!this.getRequestingURL().equals(proxyUrl)) {
1266N/A throw new RuntimeException("Bad url");
1266N/A }
1266N/A return new PasswordAuthentication(
1266N/A PROXY_USER+"@"+REALM_PROXY, PROXY_PASS);
1266N/A } else {
1266N/A throw new RuntimeException("Bad requster type");
1266N/A }
1266N/A }
1266N/A }
1266N/A
1266N/A public static void main(String[] args)
1266N/A throws Exception {
1266N/A
1300N/A KDC kdcw = KDC.create(REALM_WEB);
1266N/A kdcw.addPrincipal(WEB_USER, WEB_PASS);
1266N/A kdcw.addPrincipalRandKey("krbtgt/" + REALM_WEB);
1266N/A kdcw.addPrincipalRandKey("HTTP/" + WEB_HOST);
1266N/A
1300N/A KDC kdcp = KDC.create(REALM_PROXY);
1266N/A kdcp.addPrincipal(PROXY_USER, PROXY_PASS);
1266N/A kdcp.addPrincipalRandKey("krbtgt/" + REALM_PROXY);
1266N/A kdcp.addPrincipalRandKey("HTTP/" + PROXY_HOST);
1266N/A
1266N/A KDC.writeMultiKtab(KRB5_TAB, kdcw, kdcp);
1266N/A KDC.saveConfig(KRB5_CONF, kdcw, kdcp,
1266N/A "default_keytab_name = " + KRB5_TAB,
1266N/A "[domain_realm]",
1266N/A "",
1266N/A ".web.domain="+REALM_WEB,
1266N/A ".proxy.domain="+REALM_PROXY);
1266N/A
1266N/A System.setProperty("java.security.krb5.conf", KRB5_CONF);
1266N/A Config.refresh();
1266N/A
1266N/A HttpServer h1 = httpd(WEB_PORT, "Negotiate", false,
1266N/A "HTTP/" + WEB_HOST + "@" + REALM_WEB, KRB5_TAB);
1266N/A HttpServer h2 = httpd(PROXY_PORT, "Negotiate", true,
1266N/A "HTTP/" + PROXY_HOST + "@" + REALM_PROXY, KRB5_TAB);
1266N/A
1266N/A try {
1266N/A
1266N/A BufferedReader reader;
1266N/A java.net.Authenticator.setDefault(new KnowAllAuthenticator());
1266N/A
1266N/A reader = new BufferedReader(new InputStreamReader(
1266N/A webUrl.openConnection().getInputStream()));
1266N/A if (!reader.readLine().equals(CONTENT)) {
1266N/A throw new RuntimeException("Bad content");
1266N/A }
1266N/A
1266N/A reader = new BufferedReader(new InputStreamReader(
1266N/A proxyUrl.openConnection(
1266N/A new Proxy(Proxy.Type.HTTP,
1266N/A new InetSocketAddress(PROXY_HOST, PROXY_PORT)))
1266N/A .getInputStream()));
1266N/A if (!reader.readLine().equals(CONTENT)) {
1266N/A throw new RuntimeException("Bad content");
1266N/A }
1266N/A } finally {
1266N/A // Must stop. Seems there's no HttpServer.startAsDaemon()
1266N/A if (h1 != null) h1.stop(0);
1266N/A if (h2 != null) h2.stop(0);
1266N/A }
1266N/A }
1266N/A
1266N/A /**
1266N/A * Creates and starts an HTTP or proxy server that requires
1266N/A * Negotiate authentication.
1266N/A * @param scheme "Negotiate" or "Kerberos"
1266N/A * @param principal the krb5 service principal the server runs with
1266N/A * @return the server
1266N/A */
1266N/A public static HttpServer httpd(int port, String scheme, boolean proxy,
1266N/A String principal, String ktab) throws Exception {
1266N/A MyHttpHandler h = new MyHttpHandler();
1266N/A HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
1266N/A HttpContext hc = server.createContext("/", h);
1266N/A hc.setAuthenticator(new MyServerAuthenticator(
1266N/A proxy, scheme, principal, ktab));
1266N/A server.start();
1266N/A return server;
1266N/A }
1266N/A
1266N/A static class MyHttpHandler implements HttpHandler {
1266N/A public void handle(HttpExchange t) throws IOException {
1266N/A t.sendResponseHeaders(200, 0);
1266N/A t.getResponseBody().write(CONTENT.getBytes());
1266N/A t.close();
1266N/A }
1266N/A }
1266N/A
1266N/A static class MyServerAuthenticator
1266N/A extends com.sun.net.httpserver.Authenticator {
1266N/A Subject s = new Subject();
1266N/A GSSManager m = null;
1266N/A GSSCredential cred = null;
1266N/A String scheme = null;
1266N/A String reqHdr = "WWW-Authenticate";
1266N/A String respHdr = "Authorization";
1266N/A int err = HttpURLConnection.HTTP_UNAUTHORIZED;
1266N/A
1266N/A public MyServerAuthenticator(boolean proxy, String scheme,
1266N/A String principal, String ktab) throws Exception {
1266N/A
1266N/A this.scheme = scheme;
1266N/A if (proxy) {
1266N/A reqHdr = "Proxy-Authenticate";
1266N/A respHdr = "Proxy-Authorization";
1266N/A err = HttpURLConnection.HTTP_PROXY_AUTH;
1266N/A }
1266N/A
1266N/A Krb5LoginModule krb5 = new Krb5LoginModule();
1266N/A Map<String, String> map = new HashMap<String, String>();
1266N/A Map<String, Object> shared = new HashMap<String, Object>();
1266N/A
1266N/A map.put("storeKey", "true");
1266N/A map.put("isInitiator", "false");
1266N/A map.put("useKeyTab", "true");
1266N/A map.put("keyTab", ktab);
1266N/A map.put("principal", principal);
1266N/A krb5.initialize(s, null, shared, map);
1266N/A krb5.login();
1266N/A krb5.commit();
1266N/A m = GSSManager.getInstance();
1266N/A cred = Subject.doAs(s, new PrivilegedExceptionAction<GSSCredential>() {
1266N/A @Override
1266N/A public GSSCredential run() throws Exception {
1266N/A System.err.println("Creating GSSCredential");
1266N/A return m.createCredential(
1266N/A null,
1266N/A GSSCredential.INDEFINITE_LIFETIME,
1266N/A MyServerAuthenticator.this.scheme.equalsIgnoreCase("Negotiate")?
1266N/A GSSUtil.GSS_SPNEGO_MECH_OID:
1266N/A GSSUtil.GSS_KRB5_MECH_OID,
1266N/A GSSCredential.ACCEPT_ONLY);
1266N/A }
1266N/A });
1266N/A }
1266N/A
1266N/A @Override
1266N/A public Result authenticate(HttpExchange exch) {
1266N/A // The GSContext is stored in an HttpContext attribute named
1266N/A // "GSSContext" and is created at the first request.
1266N/A GSSContext c = null;
1266N/A String auth = exch.getRequestHeaders().getFirst(respHdr);
1266N/A try {
1266N/A c = (GSSContext)exch.getHttpContext().getAttributes().get("GSSContext");
1266N/A if (auth == null) { // First request
1266N/A Headers map = exch.getResponseHeaders();
1266N/A map.set (reqHdr, scheme); // Challenge!
1266N/A c = Subject.doAs(s, new PrivilegedExceptionAction<GSSContext>() {
1266N/A @Override
1266N/A public GSSContext run() throws Exception {
1266N/A return m.createContext(cred);
1266N/A }
1266N/A });
1266N/A exch.getHttpContext().getAttributes().put("GSSContext", c);
1266N/A return new com.sun.net.httpserver.Authenticator.Retry(err);
1266N/A } else { // Later requests
1266N/A byte[] token = new sun.misc.BASE64Decoder()
1266N/A .decodeBuffer(auth.split(" ")[1]);
1266N/A token = c.acceptSecContext(token, 0, token.length);
1266N/A Headers map = exch.getResponseHeaders();
1266N/A map.set (reqHdr, scheme + " " + new sun.misc.BASE64Encoder()
1266N/A .encode(token).replaceAll("\\s", ""));
1266N/A if (c.isEstablished()) {
1266N/A return new com.sun.net.httpserver.Authenticator.Success(
1266N/A new HttpPrincipal(c.getSrcName().toString(), ""));
1266N/A } else {
1266N/A return new com.sun.net.httpserver.Authenticator.Retry(err);
1266N/A }
1266N/A }
1266N/A } catch (Exception e) {
1266N/A throw new RuntimeException(e);
1266N/A }
1266N/A }
1266N/A }
1266N/A}
1266N/A