Path: blob/master/test/jdk/sun/security/krb5/auto/HttpNegotiateServer.java
41152 views
/*1* Copyright (c) 2009, 2018, 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 6829283 8171340 819448626* @modules java.base/sun.security.util27* java.security.jgss/sun.security.krb5.internal:+open28* java.security.jgss/sun.security.jgss29* java.security.jgss/sun.security.krb5:+open30* java.security.jgss/sun.security.jgss.krb531* java.security.jgss/sun.security.krb5.internal.ccache32* java.security.jgss/sun.security.krb5.internal.crypto33* java.security.jgss/sun.security.krb5.internal.ktab34* jdk.security.auth35* jdk.security.jgss36* jdk.httpserver37* @summary Undefined requesting URL in java.net.Authenticator38* .getPasswordAuthentication()39* @summary HTTP/Negotiate: Authenticator triggered again when40* user cancels the first one41* @library /test/lib42* @run main jdk.test.lib.FileInstaller TestHosts TestHosts43* @run main/othervm -Djava.security.manager=allow -Djdk.net.hosts.file=TestHosts HttpNegotiateServer44*/4546import com.sun.net.httpserver.Headers;47import com.sun.net.httpserver.HttpContext;48import com.sun.net.httpserver.HttpExchange;49import com.sun.net.httpserver.HttpHandler;50import com.sun.net.httpserver.HttpServer;51import com.sun.net.httpserver.HttpPrincipal;52import com.sun.security.auth.module.Krb5LoginModule;53import java.io.BufferedReader;54import java.io.File;55import java.io.FileOutputStream;56import java.io.IOException;57import java.io.InputStream;58import java.io.InputStreamReader;59import java.net.HttpURLConnection;60import java.net.InetSocketAddress;61import java.net.PasswordAuthentication;62import java.net.Proxy;63import java.net.URL;64import java.net.URLConnection;65import java.security.*;66import java.util.HashMap;67import java.util.Map;68import javax.security.auth.Subject;69import javax.security.auth.callback.Callback;70import javax.security.auth.callback.CallbackHandler;71import javax.security.auth.callback.NameCallback;72import javax.security.auth.callback.PasswordCallback;73import javax.security.auth.callback.UnsupportedCallbackException;74import javax.security.auth.login.AppConfigurationEntry;75import javax.security.auth.login.Configuration;76import javax.security.auth.login.LoginContext;77import javax.security.auth.login.LoginException;78import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;79import org.ietf.jgss.GSSContext;80import org.ietf.jgss.GSSCredential;81import org.ietf.jgss.GSSManager;82import sun.security.jgss.GSSUtil;83import sun.security.krb5.Config;84import java.util.Base64;8586/**87* Basic JGSS/krb5 test with 3 parties: client, server, backend server. Each88* party uses JAAS login to get subjects and executes JGSS calls using89* Subject.doAs.90*/91public class HttpNegotiateServer {9293// Two realm, web server in one, proxy server in another94final static String REALM_WEB = "WEB.DOMAIN";95final static String REALM_PROXY = "PROXY.DOMAIN";96final static String KRB5_CONF = "web.conf";97final static String KRB5_TAB = "web.ktab";9899// user principals100final static String WEB_USER = "web";101final static char[] WEB_PASS = "webby".toCharArray();102final static String PROXY_USER = "pro";103final static char[] PROXY_PASS = "proxy".toCharArray();104105106final static String WEB_HOST = "host.web.domain";107final static String PROXY_HOST = "host.proxy.domain";108109// web page content110final static String CONTENT = "Hello, World!";111112// For 6829283, count how many times the Authenticator is called.113static int count = 0;114115static int webPort, proxyPort;116117// URLs for web test, proxy test. The proxy server is not a real proxy118// since it fakes the same content for any URL. :)119static URL webUrl, proxyUrl;120121/**122* This Authenticator checks everything:123* scheme, protocol, requestor type, host, port, and url124*/125static class KnowAllAuthenticator extends java.net.Authenticator {126public PasswordAuthentication getPasswordAuthentication () {127if (!getRequestingScheme().equalsIgnoreCase("Negotiate")) {128throw new RuntimeException("Bad scheme");129}130if (!getRequestingProtocol().equalsIgnoreCase("HTTP")) {131throw new RuntimeException("Bad protocol");132}133if (getRequestorType() == RequestorType.SERVER) {134if (!this.getRequestingHost().equalsIgnoreCase(webUrl.getHost())) {135throw new RuntimeException("Bad host");136}137if (this.getRequestingPort() != webUrl.getPort()) {138throw new RuntimeException("Bad port");139}140if (!this.getRequestingURL().equals(webUrl)) {141throw new RuntimeException("Bad url");142}143return new PasswordAuthentication(144WEB_USER+"@"+REALM_WEB, WEB_PASS);145} else if (getRequestorType() == RequestorType.PROXY) {146if (!this.getRequestingHost().equalsIgnoreCase(PROXY_HOST)) {147throw new RuntimeException("Bad host");148}149if (this.getRequestingPort() != proxyPort) {150throw new RuntimeException("Bad port");151}152if (!this.getRequestingURL().equals(proxyUrl)) {153throw new RuntimeException("Bad url");154}155return new PasswordAuthentication(156PROXY_USER+"@"+REALM_PROXY, PROXY_PASS);157} else {158throw new RuntimeException("Bad requster type");159}160}161}162163/**164* This Authenticator knows nothing165*/166static class KnowNothingAuthenticator extends java.net.Authenticator {167@Override168public PasswordAuthentication getPasswordAuthentication () {169HttpNegotiateServer.count++;170return null;171}172}173174public static void main(String[] args)175throws Exception {176177System.setProperty("sun.security.krb5.debug", "true");178179KDC kdcw = KDC.create(REALM_WEB);180kdcw.addPrincipal(WEB_USER, WEB_PASS);181kdcw.addPrincipalRandKey("krbtgt/" + REALM_WEB);182kdcw.addPrincipalRandKey("HTTP/" + WEB_HOST);183184KDC kdcp = KDC.create(REALM_PROXY);185kdcp.addPrincipal(PROXY_USER, PROXY_PASS);186kdcp.addPrincipalRandKey("krbtgt/" + REALM_PROXY);187kdcp.addPrincipalRandKey("HTTP/" + PROXY_HOST);188189KDC.saveConfig(KRB5_CONF, kdcw, kdcp,190"default_keytab_name = " + KRB5_TAB,191"[domain_realm]",192"",193".web.domain="+REALM_WEB,194".proxy.domain="+REALM_PROXY);195196System.setProperty("java.security.krb5.conf", KRB5_CONF);197Config.refresh();198KDC.writeMultiKtab(KRB5_TAB, kdcw, kdcp);199200// Write a customized JAAS conf file, so that any kinit cache201// will be ignored.202System.setProperty("java.security.auth.login.config", OneKDC.JAAS_CONF);203File f = new File(OneKDC.JAAS_CONF);204FileOutputStream fos = new FileOutputStream(f);205fos.write((206"com.sun.security.jgss.krb5.initiate {\n" +207" com.sun.security.auth.module.Krb5LoginModule required;\n};\n"208).getBytes());209fos.close();210211HttpServer h1 = httpd("Negotiate", false,212"HTTP/" + WEB_HOST + "@" + REALM_WEB, KRB5_TAB);213webPort = h1.getAddress().getPort();214HttpServer h2 = httpd("Negotiate", true,215"HTTP/" + PROXY_HOST + "@" + REALM_PROXY, KRB5_TAB);216proxyPort = h2.getAddress().getPort();217218webUrl = new URL("http://" + WEB_HOST +":" + webPort + "/a/b/c");219proxyUrl = new URL("http://nosuchplace/a/b/c");220221try {222Exception e1 = null, e2 = null, e3 = null;223try {224test6578647();225} catch (Exception e) {226e1 = e;227e.printStackTrace();228}229try {230test6829283();231} catch (Exception e) {232e2 = e;233e.printStackTrace();234}235try {236test8077155();237} catch (Exception e) {238e3 = e;239e.printStackTrace();240}241242if (e1 != null || e2 != null || e3 != null) {243throw new RuntimeException("Test error");244}245} finally {246// Must stop. Seems there's no HttpServer.startAsDaemon()247if (h1 != null) h1.stop(0);248if (h2 != null) h2.stop(0);249}250}251252static void test6578647() throws Exception {253BufferedReader reader;254java.net.Authenticator.setDefault(new KnowAllAuthenticator());255256reader = new BufferedReader(new InputStreamReader(257webUrl.openConnection(Proxy.NO_PROXY).getInputStream()));258if (!reader.readLine().equals(CONTENT)) {259throw new RuntimeException("Bad content");260}261262reader = new BufferedReader(new InputStreamReader(263proxyUrl.openConnection(new Proxy(Proxy.Type.HTTP,264new InetSocketAddress(PROXY_HOST, proxyPort)))265.getInputStream()));266if (!reader.readLine().equals(CONTENT)) {267throw new RuntimeException("Bad content");268}269}270271static void test6829283() throws Exception {272BufferedReader reader;273java.net.Authenticator.setDefault(new KnowNothingAuthenticator());274try {275new BufferedReader(new InputStreamReader(276webUrl.openConnection(Proxy.NO_PROXY).getInputStream()));277} catch (IOException ioe) {278// Will fail since no username and password is provided.279}280if (count > 1) {281throw new RuntimeException("Authenticator called twice");282}283}284285static void testConnect() {286InputStream inputStream = null;287try {288URL url = webUrl;289290URLConnection conn = url.openConnection(Proxy.NO_PROXY);291conn.connect();292inputStream = conn.getInputStream();293byte[] b = new byte[inputStream.available()];294for (int j = 0; j < b.length; j++) {295b[j] = (byte) inputStream.read();296}297String s = new String(b);298System.out.println("Length: " + s.length());299System.out.println(s);300} catch (Exception ex) {301throw new RuntimeException(ex);302} finally {303if (inputStream != null) {304try {305inputStream.close();306} catch (IOException e) {307e.printStackTrace();308}309}310}311}312313static void test8077155() throws Exception {314final String username = WEB_USER;315final char[] password = WEB_PASS;316317SecurityManager security = new SecurityManager();318Policy.setPolicy(new SecurityPolicy());319System.setSecurityManager(security);320321CallbackHandler callback = new CallbackHandler() {322@Override323public void handle(Callback[] pCallbacks)324throws IOException, UnsupportedCallbackException {325for (Callback cb : pCallbacks) {326if (cb instanceof NameCallback) {327NameCallback ncb = (NameCallback)cb;328ncb.setName(username);329330} else if (cb instanceof PasswordCallback) {331PasswordCallback pwdcb = (PasswordCallback) cb;332pwdcb.setPassword(password);333}334}335}336337};338339final String jaasConfigName = "oracle.test.kerberos.login";340final String krb5LoginModule341= "com.sun.security.auth.module.Krb5LoginModule";342343Configuration loginConfig = new Configuration() {344@Override345public AppConfigurationEntry[] getAppConfigurationEntry(String name) {346if (! jaasConfigName.equals(name)) {347return new AppConfigurationEntry[0];348}349350Map<String, String> options = new HashMap<String, String>();351options.put("useTicketCache", Boolean.FALSE.toString());352options.put("useKeyTab", Boolean.FALSE.toString());353354return new AppConfigurationEntry[] {355new AppConfigurationEntry(krb5LoginModule,356LoginModuleControlFlag.REQUIRED,357options)358};359}360};361362// oracle context/subject/login363LoginContext context = null;364try {365context = new LoginContext(366"oracle.test.kerberos.login", null, callback, loginConfig);367context.login();368369} catch (LoginException ex) {370ex.printStackTrace();371throw new RuntimeException(ex);372}373374375Subject subject = context.getSubject();376377final PrivilegedExceptionAction<Object> test_action378= new PrivilegedExceptionAction<Object>() {379public Object run() throws Exception {380testConnect();381return null;382}383};384385System.err.println("\n\nExpecting to succeed when executing " +386"with the the logged in subject.");387388try {389Subject.doAs(subject, test_action);390System.err.println("\n\nConnection succeed when executing " +391"with the the logged in subject.");392} catch (PrivilegedActionException e) {393System.err.println("\n\nFailure unexpected when executing " +394"with the the logged in subject.");395e.printStackTrace();396throw new RuntimeException("Failed to login as subject");397}398399try {400System.err.println("\n\nExpecting to fail when running " +401"with the current user's login.");402testConnect();403} catch (Exception ex) {404System.err.println("\nConnect failed when running " +405"with the current user's login:\n" + ex.getMessage());406}407}408409/**410* Creates and starts an HTTP or proxy server that requires411* Negotiate authentication.412* @param scheme "Negotiate" or "Kerberos"413* @param principal the krb5 service principal the server runs with414* @return the server415*/416public static HttpServer httpd(String scheme, boolean proxy,417String principal, String ktab) throws Exception {418MyHttpHandler h = new MyHttpHandler();419HttpServer server = HttpServer.create(new InetSocketAddress(0), 0);420HttpContext hc = server.createContext("/", h);421hc.setAuthenticator(new MyServerAuthenticator(422proxy, scheme, principal, ktab));423server.start();424return server;425}426427static class MyHttpHandler implements HttpHandler {428public void handle(HttpExchange t) throws IOException {429t.sendResponseHeaders(200, 0);430t.getResponseBody().write(CONTENT.getBytes());431t.close();432}433}434435static class MyServerAuthenticator436extends com.sun.net.httpserver.Authenticator {437Subject s = new Subject();438GSSManager m = null;439GSSCredential cred = null;440String scheme = null;441String reqHdr = "WWW-Authenticate";442String respHdr = "Authorization";443int err = HttpURLConnection.HTTP_UNAUTHORIZED;444445public MyServerAuthenticator(boolean proxy, String scheme,446String principal, String ktab) throws Exception {447448this.scheme = scheme;449if (proxy) {450reqHdr = "Proxy-Authenticate";451respHdr = "Proxy-Authorization";452err = HttpURLConnection.HTTP_PROXY_AUTH;453}454455Krb5LoginModule krb5 = new Krb5LoginModule();456Map<String, String> map = new HashMap<>();457Map<String, Object> shared = new HashMap<>();458459map.put("storeKey", "true");460map.put("isInitiator", "false");461map.put("useKeyTab", "true");462map.put("keyTab", ktab);463map.put("principal", principal);464krb5.initialize(s, null, shared, map);465krb5.login();466krb5.commit();467m = GSSManager.getInstance();468cred = Subject.doAs(s, new PrivilegedExceptionAction<GSSCredential>() {469@Override470public GSSCredential run() throws Exception {471System.err.println("Creating GSSCredential");472return m.createCredential(473null,474GSSCredential.INDEFINITE_LIFETIME,475MyServerAuthenticator.this.scheme476.equalsIgnoreCase("Negotiate") ?477GSSUtil.GSS_SPNEGO_MECH_OID :478GSSUtil.GSS_KRB5_MECH_OID,479GSSCredential.ACCEPT_ONLY);480}481});482}483484@Override485public Result authenticate(HttpExchange exch) {486// The GSContext is stored in an HttpContext attribute named487// "GSSContext" and is created at the first request.488GSSContext c = null;489String auth = exch.getRequestHeaders().getFirst(respHdr);490try {491c = (GSSContext)exch.getHttpContext()492.getAttributes().get("GSSContext");493if (auth == null) { // First request494Headers map = exch.getResponseHeaders();495map.set (reqHdr, scheme); // Challenge!496c = Subject.doAs(s, new PrivilegedExceptionAction<GSSContext>() {497@Override498public GSSContext run() throws Exception {499return m.createContext(cred);500}501});502exch.getHttpContext().getAttributes().put("GSSContext", c);503return new com.sun.net.httpserver.Authenticator.Retry(err);504} else { // Later requests505byte[] token = Base64.getMimeDecoder()506.decode(auth.split(" ")[1]);507token = c.acceptSecContext(token, 0, token.length);508Headers map = exch.getResponseHeaders();509map.set (reqHdr, scheme + " " + Base64.getMimeEncoder()510.encodeToString(token).replaceAll("\\s", ""));511if (c.isEstablished()) {512return new com.sun.net.httpserver.Authenticator.Success(513new HttpPrincipal(c.getSrcName().toString(), ""));514} else {515return new com.sun.net.httpserver.Authenticator.Retry(err);516}517}518} catch (Exception e) {519throw new RuntimeException(e);520}521}522}523}524525class SecurityPolicy extends Policy {526527private static Permissions perms;528529public SecurityPolicy() {530super();531if (perms == null) {532perms = new Permissions();533perms.add(new AllPermission());534}535}536537@Override538public PermissionCollection getPermissions(CodeSource codesource) {539return perms;540}541542}543544545