Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/sun/security/krb5/auto/HttpNegotiateServer.java
38853 views
/*1* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 6578647 682928326* @run main/othervm -Dsun.net.spi.nameservice.provider.1=ns,mock HttpNegotiateServer27* @summary Undefined requesting URL in java.net.Authenticator.getPasswordAuthentication()28* @summary HTTP/Negotiate: Authenticator triggered again when user cancels the first one29*/3031import com.sun.net.httpserver.Headers;32import com.sun.net.httpserver.HttpContext;33import com.sun.net.httpserver.HttpExchange;34import com.sun.net.httpserver.HttpHandler;35import com.sun.net.httpserver.HttpServer;36import com.sun.net.httpserver.HttpPrincipal;37import com.sun.security.auth.module.Krb5LoginModule;38import java.io.BufferedReader;39import java.io.File;40import java.io.FileOutputStream;41import java.io.IOException;42import java.io.InputStream;43import java.io.InputStreamReader;44import java.net.HttpURLConnection;45import java.net.InetSocketAddress;46import java.net.PasswordAuthentication;47import java.net.Proxy;48import java.net.URL;49import java.net.URLConnection;50import java.security.*;51import java.util.HashMap;52import java.util.Map;53import javax.security.auth.Subject;54import javax.security.auth.callback.Callback;55import javax.security.auth.callback.CallbackHandler;56import javax.security.auth.callback.NameCallback;57import javax.security.auth.callback.PasswordCallback;58import javax.security.auth.callback.UnsupportedCallbackException;59import javax.security.auth.login.AppConfigurationEntry;60import javax.security.auth.login.Configuration;61import javax.security.auth.login.LoginContext;62import javax.security.auth.login.LoginException;63import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;64import org.ietf.jgss.GSSContext;65import org.ietf.jgss.GSSCredential;66import org.ietf.jgss.GSSManager;67import sun.security.jgss.GSSUtil;68import sun.security.krb5.Config;69import java.util.Base64;7071/**72* Basic JGSS/krb5 test with 3 parties: client, server, backend server. Each73* party uses JAAS login to get subjects and executes JGSS calls using74* Subject.doAs.75*/76public class HttpNegotiateServer {7778// Two realm, web server in one, proxy server in another79final static String REALM_WEB = "WEB.DOMAIN";80final static String REALM_PROXY = "PROXY.DOMAIN";81final static String KRB5_CONF = "web.conf";82final static String KRB5_TAB = "web.ktab";8384// user principals85final static String WEB_USER = "web";86final static char[] WEB_PASS = "webby".toCharArray();87final static String PROXY_USER = "pro";88final static char[] PROXY_PASS = "proxy".toCharArray();899091final static String WEB_HOST = "host.web.domain";92final static String PROXY_HOST = "host.proxy.domain";9394// web page content95final static String CONTENT = "Hello, World!";9697// For 6829283, count how many times the Authenticator is called.98static int count = 0;99100static int webPort, proxyPort;101102// URLs for web test, proxy test. The proxy server is not a real proxy103// since it fakes the same content for any URL. :)104static URL webUrl, proxyUrl;105106/**107* This Authenticator checks everything:108* scheme, protocol, requestor type, host, port, and url109*/110static class KnowAllAuthenticator extends java.net.Authenticator {111public PasswordAuthentication getPasswordAuthentication () {112if (!getRequestingScheme().equalsIgnoreCase("Negotiate")) {113throw new RuntimeException("Bad scheme");114}115if (!getRequestingProtocol().equalsIgnoreCase("HTTP")) {116throw new RuntimeException("Bad protocol");117}118if (getRequestorType() == RequestorType.SERVER) {119if (!this.getRequestingHost().equalsIgnoreCase(webUrl.getHost())) {120throw new RuntimeException("Bad host");121}122if (this.getRequestingPort() != webUrl.getPort()) {123throw new RuntimeException("Bad port");124}125if (!this.getRequestingURL().equals(webUrl)) {126throw new RuntimeException("Bad url");127}128return new PasswordAuthentication(129WEB_USER+"@"+REALM_WEB, WEB_PASS);130} else if (getRequestorType() == RequestorType.PROXY) {131if (!this.getRequestingHost().equalsIgnoreCase(PROXY_HOST)) {132throw new RuntimeException("Bad host");133}134if (this.getRequestingPort() != proxyPort) {135throw new RuntimeException("Bad port");136}137if (!this.getRequestingURL().equals(proxyUrl)) {138throw new RuntimeException("Bad url");139}140return new PasswordAuthentication(141PROXY_USER+"@"+REALM_PROXY, PROXY_PASS);142} else {143throw new RuntimeException("Bad requster type");144}145}146}147148/**149* This Authenticator knows nothing150*/151static class KnowNothingAuthenticator extends java.net.Authenticator {152@Override153public PasswordAuthentication getPasswordAuthentication () {154HttpNegotiateServer.count++;155return null;156}157}158159public static void main(String[] args)160throws Exception {161162KDC kdcw = KDC.create(REALM_WEB);163kdcw.addPrincipal(WEB_USER, WEB_PASS);164kdcw.addPrincipalRandKey("krbtgt/" + REALM_WEB);165kdcw.addPrincipalRandKey("HTTP/" + WEB_HOST);166167KDC kdcp = KDC.create(REALM_PROXY);168kdcp.addPrincipal(PROXY_USER, PROXY_PASS);169kdcp.addPrincipalRandKey("krbtgt/" + REALM_PROXY);170kdcp.addPrincipalRandKey("HTTP/" + PROXY_HOST);171172KDC.saveConfig(KRB5_CONF, kdcw, kdcp,173"default_keytab_name = " + KRB5_TAB,174"[domain_realm]",175"",176".web.domain="+REALM_WEB,177".proxy.domain="+REALM_PROXY);178179System.setProperty("java.security.krb5.conf", KRB5_CONF);180Config.refresh();181KDC.writeMultiKtab(KRB5_TAB, kdcw, kdcp);182183// Write a customized JAAS conf file, so that any kinit cache184// will be ignored.185System.setProperty("java.security.auth.login.config", OneKDC.JAAS_CONF);186File f = new File(OneKDC.JAAS_CONF);187FileOutputStream fos = new FileOutputStream(f);188fos.write((189"com.sun.security.jgss.krb5.initiate {\n" +190" com.sun.security.auth.module.Krb5LoginModule required;\n};\n"191).getBytes());192fos.close();193194HttpServer h1 = httpd("Negotiate", false,195"HTTP/" + WEB_HOST + "@" + REALM_WEB, KRB5_TAB);196webPort = h1.getAddress().getPort();197HttpServer h2 = httpd("Negotiate", true,198"HTTP/" + PROXY_HOST + "@" + REALM_PROXY, KRB5_TAB);199proxyPort = h2.getAddress().getPort();200201webUrl = new URL("http://" + WEB_HOST +":" + webPort + "/a/b/c");202proxyUrl = new URL("http://nosuchplace/a/b/c");203204try {205Exception e1 = null, e2 = null, e3 = null;206try {207test6578647();208} catch (Exception e) {209e1 = e;210e.printStackTrace();211}212try {213test6829283();214} catch (Exception e) {215e2 = e;216e.printStackTrace();217}218try {219test8077155();220} catch (Exception e) {221e3 = e;222e.printStackTrace();223}224225if (e1 != null || e2 != null || e3 != null) {226throw new RuntimeException("Test error");227}228} finally {229// Must stop. Seems there's no HttpServer.startAsDaemon()230if (h1 != null) h1.stop(0);231if (h2 != null) h2.stop(0);232}233}234235static void test6578647() throws Exception {236BufferedReader reader;237java.net.Authenticator.setDefault(new KnowAllAuthenticator());238239reader = new BufferedReader(new InputStreamReader(240webUrl.openConnection().getInputStream()));241if (!reader.readLine().equals(CONTENT)) {242throw new RuntimeException("Bad content");243}244245reader = new BufferedReader(new InputStreamReader(246proxyUrl.openConnection(247new Proxy(Proxy.Type.HTTP,248new InetSocketAddress(PROXY_HOST, proxyPort)))249.getInputStream()));250if (!reader.readLine().equals(CONTENT)) {251throw new RuntimeException("Bad content");252}253}254255static void test6829283() throws Exception {256BufferedReader reader;257java.net.Authenticator.setDefault(new KnowNothingAuthenticator());258try {259new BufferedReader(new InputStreamReader(260webUrl.openConnection().getInputStream()));261} catch (IOException ioe) {262// Will fail since no username and password is provided.263}264if (count > 1) {265throw new RuntimeException("Authenticator called twice");266}267}268269static void testConnect() {270InputStream inputStream = null;271try {272URL url = webUrl;273274URLConnection conn = url.openConnection();275conn.connect();276inputStream = conn.getInputStream();277byte[] b = new byte[inputStream.available()];278for (int j = 0; j < b.length; j++) {279b[j] = (byte) inputStream.read();280}281String s = new String(b);282System.out.println("Length: " + s.length());283System.out.println(s);284} catch (Exception ex) {285throw new RuntimeException(ex);286} finally {287if (inputStream != null) {288try {289inputStream.close();290} catch (IOException e) {291e.printStackTrace();292}293}294}295}296297static void test8077155() throws Exception {298final String username = WEB_USER;299final char[] password = WEB_PASS;300301SecurityManager security = new SecurityManager();302Policy.setPolicy(new SecurityPolicy());303System.setSecurityManager(security);304305CallbackHandler callback = new CallbackHandler() {306@Override307public void handle(Callback[] pCallbacks) throws IOException, UnsupportedCallbackException {308for (Callback cb : pCallbacks) {309if (cb instanceof NameCallback) {310NameCallback ncb = (NameCallback)cb;311ncb.setName(username);312313} else if (cb instanceof PasswordCallback) {314PasswordCallback pwdcb = (PasswordCallback) cb;315pwdcb.setPassword(password);316}317}318}319320};321322final String jaasConfigName = "oracle.test.kerberos.login";323final String krb5LoginModule = "com.sun.security.auth.module.Krb5LoginModule";324325Configuration loginConfig = new Configuration() {326@Override327public AppConfigurationEntry[] getAppConfigurationEntry(String name) {328if (! jaasConfigName.equals(name)) {329return new AppConfigurationEntry[0];330}331332Map<String, String> options = new HashMap<String, String>();333options.put("useTicketCache", Boolean.FALSE.toString());334options.put("useKeyTab", Boolean.FALSE.toString());335336return new AppConfigurationEntry[] {337new AppConfigurationEntry(krb5LoginModule,338LoginModuleControlFlag.REQUIRED,339options)340};341}342};343344// oracle context/subject/login345LoginContext context = null;346try {347context = new LoginContext("oracle.test.kerberos.login", null, callback, loginConfig);348context.login();349350} catch (LoginException ex) {351ex.printStackTrace();352throw new RuntimeException(ex);353}354355356Subject subject = context.getSubject();357358final PrivilegedExceptionAction<Object> test_action = new PrivilegedExceptionAction<Object>() {359public Object run() throws Exception {360testConnect();361return null;362}363};364365System.err.println("\n\nExpecting to succeed when executing with the the logged in subject.");366367try {368Subject.doAs(subject, test_action);369System.err.println("\n\nConnection succeed when executing with the the logged in subject.");370} catch (PrivilegedActionException e) {371System.err.println("\n\nFailure unexpected when executing with the the logged in subject.");372e.printStackTrace();373throw new RuntimeException("Failed to login as subject");374}375376try {377System.err.println("\n\nExpecting to fail when running with the current user's login.");378testConnect();379} catch (Exception ex) {380System.err.println("\nConnect failed when running with the current user's login:\n" + ex.getMessage());381}382}383384/**385* Creates and starts an HTTP or proxy server that requires386* Negotiate authentication.387* @param scheme "Negotiate" or "Kerberos"388* @param principal the krb5 service principal the server runs with389* @return the server390*/391public static HttpServer httpd(String scheme, boolean proxy,392String principal, String ktab) throws Exception {393MyHttpHandler h = new MyHttpHandler();394HttpServer server = HttpServer.create(new InetSocketAddress(0), 0);395HttpContext hc = server.createContext("/", h);396hc.setAuthenticator(new MyServerAuthenticator(397proxy, scheme, principal, ktab));398server.start();399return server;400}401402static class MyHttpHandler implements HttpHandler {403public void handle(HttpExchange t) throws IOException {404t.sendResponseHeaders(200, 0);405t.getResponseBody().write(CONTENT.getBytes());406t.close();407}408}409410static class MyServerAuthenticator411extends com.sun.net.httpserver.Authenticator {412Subject s = new Subject();413GSSManager m = null;414GSSCredential cred = null;415String scheme = null;416String reqHdr = "WWW-Authenticate";417String respHdr = "Authorization";418int err = HttpURLConnection.HTTP_UNAUTHORIZED;419420public MyServerAuthenticator(boolean proxy, String scheme,421String principal, String ktab) throws Exception {422423this.scheme = scheme;424if (proxy) {425reqHdr = "Proxy-Authenticate";426respHdr = "Proxy-Authorization";427err = HttpURLConnection.HTTP_PROXY_AUTH;428}429430Krb5LoginModule krb5 = new Krb5LoginModule();431Map<String, String> map = new HashMap<>();432Map<String, Object> shared = new HashMap<>();433434map.put("storeKey", "true");435map.put("isInitiator", "false");436map.put("useKeyTab", "true");437map.put("keyTab", ktab);438map.put("principal", principal);439krb5.initialize(s, null, shared, map);440krb5.login();441krb5.commit();442m = GSSManager.getInstance();443cred = Subject.doAs(s, new PrivilegedExceptionAction<GSSCredential>() {444@Override445public GSSCredential run() throws Exception {446System.err.println("Creating GSSCredential");447return m.createCredential(448null,449GSSCredential.INDEFINITE_LIFETIME,450MyServerAuthenticator.this.scheme.equalsIgnoreCase("Negotiate")?451GSSUtil.GSS_SPNEGO_MECH_OID:452GSSUtil.GSS_KRB5_MECH_OID,453GSSCredential.ACCEPT_ONLY);454}455});456}457458@Override459public Result authenticate(HttpExchange exch) {460// The GSContext is stored in an HttpContext attribute named461// "GSSContext" and is created at the first request.462GSSContext c = null;463String auth = exch.getRequestHeaders().getFirst(respHdr);464try {465c = (GSSContext)exch.getHttpContext().getAttributes().get("GSSContext");466if (auth == null) { // First request467Headers map = exch.getResponseHeaders();468map.set (reqHdr, scheme); // Challenge!469c = Subject.doAs(s, new PrivilegedExceptionAction<GSSContext>() {470@Override471public GSSContext run() throws Exception {472return m.createContext(cred);473}474});475exch.getHttpContext().getAttributes().put("GSSContext", c);476return new com.sun.net.httpserver.Authenticator.Retry(err);477} else { // Later requests478byte[] token = Base64.getMimeDecoder().decode(auth.split(" ")[1]);479token = c.acceptSecContext(token, 0, token.length);480Headers map = exch.getResponseHeaders();481map.set (reqHdr, scheme + " " + Base64.getMimeEncoder()482.encodeToString(token).replaceAll("\\s", ""));483if (c.isEstablished()) {484return new com.sun.net.httpserver.Authenticator.Success(485new HttpPrincipal(c.getSrcName().toString(), ""));486} else {487return new com.sun.net.httpserver.Authenticator.Retry(err);488}489}490} catch (Exception e) {491throw new RuntimeException(e);492}493}494}495}496497class SecurityPolicy extends Policy {498499private static Permissions perms;500501public SecurityPolicy() {502super();503if (perms == null) {504perms = new Permissions();505perms.add(new AllPermission());506}507}508509@Override510public PermissionCollection getPermissions(CodeSource codesource) {511return perms;512}513514}515516517