--- a/jdk/src/share/classes/com/sun/security/ntlm/Server.java Thu Nov 01 18:09:43 2012 -0400
+++ b/jdk/src/share/classes/com/sun/security/ntlm/Server.java Fri Nov 02 10:48:04 2012 +0800
@@ -106,7 +106,7 @@
* various negotiated information.
* @param type3 the incoming Type3 message from client, must not be null
* @param nonce the same nonce provided in {@link #type2}, must not be null
- * @return username and hostname of the client in a byte array
+ * @return client username, client hostname, and the request target
* @throws NTLMException if the incoming message is invalid, or
* {@code nonce} is null.
*/
@@ -194,7 +194,7 @@
throw new NTLMException(NTLMException.AUTH_FAILED,
"None of LM and NTLM verified");
}
- return new String[] {username, hostname};
+ return new String[] {username, hostname, incomingDomain};
}
/**
--- a/jdk/src/share/classes/com/sun/security/sasl/CramMD5Server.java Thu Nov 01 18:09:43 2012 -0400
+++ b/jdk/src/share/classes/com/sun/security/sasl/CramMD5Server.java Fri Nov 02 10:48:04 2012 +0800
@@ -58,14 +58,12 @@
private CallbackHandler cbh;
/**
- * Creates a SASL mechanism with client credentials that it needs
- * to participate in CRAM-MD5 authentication exchange with the server.
+ * Creates a CRAM-MD5 SASL server.
*
- * @param authID A non-null string representing the principal
- * being authenticated.
- *
- * @param pw A non-null String or byte[]
- * containing the password. If it is an array, it is first cloned.
+ * @param protocol ignored in CRAM-MD5
+ * @param serverFqdn non-null, used in generating a challenge
+ * @param props ignored in CRAM-MD5
+ * @param cbh find password, authorize user
*/
CramMD5Server(String protocol, String serverFqdn, Map<String, ?> props,
CallbackHandler cbh) throws SaslException {
--- a/jdk/src/share/classes/com/sun/security/sasl/digest/DigestMD5Base.java Thu Nov 01 18:09:43 2012 -0400
+++ b/jdk/src/share/classes/com/sun/security/sasl/digest/DigestMD5Base.java Fri Nov 02 10:48:04 2012 +0800
@@ -249,6 +249,8 @@
if (completed) {
if (propName.equals(Sasl.STRENGTH)) {
return negotiatedStrength;
+ } else if (propName.equals(Sasl.BOUND_SERVER_NAME)) {
+ return digestUri.substring(digestUri.indexOf('/') + 1);
} else {
return super.getNegotiatedProperty(propName);
}
--- a/jdk/src/share/classes/com/sun/security/sasl/digest/DigestMD5Server.java Thu Nov 01 18:09:43 2012 -0400
+++ b/jdk/src/share/classes/com/sun/security/sasl/digest/DigestMD5Server.java Fri Nov 02 10:48:04 2012 +0800
@@ -141,8 +141,10 @@
private List<String> serverRealms;
DigestMD5Server(String protocol, String serverName, Map<String, ?> props,
- CallbackHandler cbh) throws SaslException {
- super(props, MY_CLASS_NAME, 1, protocol + "/" + serverName, cbh);
+ CallbackHandler cbh) throws SaslException {
+ super(props, MY_CLASS_NAME, 1,
+ protocol + "/" + (serverName==null?"*":serverName),
+ cbh);
serverRealms = new ArrayList<String>();
@@ -173,7 +175,12 @@
// By default, use server name as realm
if (serverRealms.isEmpty()) {
- serverRealms.add(serverName);
+ if (serverName == null) {
+ throw new SaslException(
+ "A realm must be provided in props or serverName");
+ } else {
+ serverRealms.add(serverName);
+ }
}
}
@@ -539,7 +546,7 @@
// host should match one of service's configured service names
// Check against digest URI that mech was created with
- if (digestUri.equalsIgnoreCase(digestUriFromResponse)) {
+ if (uriMatches(digestUri, digestUriFromResponse)) {
digestUri = digestUriFromResponse; // account for case-sensitive diffs
} else {
throw new SaslException("DIGEST-MD5: digest response format " +
@@ -653,6 +660,21 @@
}
}
+ private static boolean uriMatches(String thisUri, String incomingUri) {
+ // Full match
+ if (thisUri.equalsIgnoreCase(incomingUri)) {
+ return true;
+ }
+ // Unbound match
+ if (thisUri.endsWith("/*")) {
+ int protoAndSlash = thisUri.length() - 1;
+ String thisProtoAndSlash = thisUri.substring(0, protoAndSlash);
+ String incomingProtoAndSlash = incomingUri.substring(0, protoAndSlash);
+ return thisProtoAndSlash.equalsIgnoreCase(incomingProtoAndSlash);
+ }
+ return false;
+ }
+
/**
* Server sends a message formatted as follows:
* response-auth = "rspauth" "=" response-value
--- a/jdk/src/share/classes/com/sun/security/sasl/gsskerb/GssKrb5Server.java Thu Nov 01 18:09:43 2012 -0400
+++ b/jdk/src/share/classes/com/sun/security/sasl/gsskerb/GssKrb5Server.java Fri Nov 02 10:48:04 2012 +0800
@@ -67,9 +67,14 @@
private int handshakeStage = 0;
private String peer;
+ private String me;
private String authzid;
private CallbackHandler cbh;
+ // When serverName is null, the server will be unbound. We need to save and
+ // check the protocol name after the context is established. This value
+ // will be null if serverName is not null.
+ private final String protocolSaved;
/**
* Creates a SASL mechanism with server credentials that it needs
* to participate in GSS-API/Kerberos v5 authentication exchange
@@ -81,7 +86,15 @@
super(props, MY_CLASS_NAME);
this.cbh = cbh;
- String service = protocol + "@" + serverName;
+
+ String service;
+ if (serverName == null) {
+ protocolSaved = protocol;
+ service = null;
+ } else {
+ protocolSaved = null;
+ service = protocol + "@" + serverName;
+ }
logger.log(Level.FINE, "KRB5SRV01:Using service name: {0}", service);
@@ -89,8 +102,8 @@
GSSManager mgr = GSSManager.getInstance();
// Create the name for the requested service entity for Krb5 mech
- GSSName serviceName = mgr.createName(service,
- GSSName.NT_HOSTBASED_SERVICE, KRB5_OID);
+ GSSName serviceName = service == null ? null:
+ mgr.createName(service, GSSName.NT_HOSTBASED_SERVICE, KRB5_OID);
GSSCredential cred = mgr.createCredential(serviceName,
GSSCredential.INDEFINITE_LIFETIME,
@@ -163,8 +176,18 @@
handshakeStage = 1;
peer = secCtx.getSrcName().toString();
+ me = secCtx.getTargName().toString();
- logger.log(Level.FINE, "KRB5SRV05:Peer name is : {0}", peer);
+ logger.log(Level.FINE,
+ "KRB5SRV05:Peer name is : {0}, my name is : {1}",
+ new Object[]{peer, me});
+
+ // me might take the form of proto@host or proto/host
+ if (protocolSaved != null &&
+ !protocolSaved.equalsIgnoreCase(me.split("[/@]")[0])) {
+ throw new SaslException(
+ "GSS context targ name protocol error: " + me);
+ }
if (gssOutToken == null) {
return doHandshake1(EMPTY);
@@ -319,4 +342,25 @@
throw new IllegalStateException("Authentication incomplete");
}
}
+
+ public Object getNegotiatedProperty(String propName) {
+ if (!completed) {
+ throw new IllegalStateException("Authentication incomplete");
+ }
+
+ Object result;
+ switch (propName) {
+ case Sasl.BOUND_SERVER_NAME:
+ try {
+ // me might take the form of proto@host or proto/host
+ result = me.split("[/@]")[1];
+ } catch (Exception e) {
+ result = null;
+ }
+ break;
+ default:
+ result = super.getNegotiatedProperty(propName);
+ }
+ return result;
+ }
}
--- a/jdk/src/share/classes/com/sun/security/sasl/ntlm/NTLMServer.java Thu Nov 01 18:09:43 2012 -0400
+++ b/jdk/src/share/classes/com/sun/security/sasl/ntlm/NTLMServer.java Fri Nov 02 10:48:04 2012 +0800
@@ -99,6 +99,7 @@
private String authzId;
private final String mech;
private String hostname;
+ private String target;
/**
* @param mech not null
@@ -180,6 +181,7 @@
String[] out = server.verify(response, nonce);
authzId = out[0];
hostname = out[1];
+ target = out[2];
return null;
}
} catch (NTLMException ex) {
@@ -220,6 +222,8 @@
switch (propName) {
case Sasl.QOP:
return "auth";
+ case Sasl.BOUND_SERVER_NAME:
+ return target;
case NTLM_HOSTNAME:
return hostname;
default:
--- a/jdk/src/share/classes/com/sun/security/sasl/util/AbstractSaslImpl.java Thu Nov 01 18:09:43 2012 -0400
+++ b/jdk/src/share/classes/com/sun/security/sasl/util/AbstractSaslImpl.java Fri Nov 02 10:48:04 2012 +0800
@@ -149,7 +149,8 @@
/**
* Retrieves the negotiated property.
- * @exception SaslException if this authentication exchange has not completed
+ * @exception IllegalStateException if this authentication exchange has
+ * not completed
*/
public Object getNegotiatedProperty(String propName) {
if (!completed) {
@@ -255,7 +256,9 @@
*/
protected static final void traceOutput(String srcClass, String srcMethod,
String traceTag, byte[] output) {
- traceOutput(srcClass, srcMethod, traceTag, output, 0, output.length);
+ if (output != null) {
+ traceOutput(srcClass, srcMethod, traceTag, output, 0, output.length);
+ }
}
protected static final void traceOutput(String srcClass, String srcMethod,
--- a/jdk/src/share/classes/javax/security/sasl/Sasl.java Thu Nov 01 18:09:43 2012 -0400
+++ b/jdk/src/share/classes/javax/security/sasl/Sasl.java Fri Nov 02 10:48:04 2012 +0800
@@ -119,6 +119,18 @@
"javax.security.sasl.server.authentication";
/**
+ * The name of a property that specifies the bound server name for
+ * an unbound server. A server is created as an unbound server by setting
+ * the {@code serverName} argument in {@link #createSaslServer} as null.
+ * The property contains the bound host name after the authentication
+ * exchange has completed. It is only available on the server side.
+ * <br>The value of this constant is
+ * <tt>"javax.security.sasl.bound.server.name"</tt>.
+ */
+ public static final String BOUND_SERVER_NAME =
+ "javax.security.sasl.bound.server.name";
+
+ /**
* The name of a property that specifies the maximum size of the receive
* buffer in bytes of <tt>SaslClient</tt>/<tt>SaslServer</tt>.
* The property contains the string representation of an integer.
@@ -449,7 +461,10 @@
* IANA-registered name of a SASL mechanism. (e.g. "GSSAPI", "CRAM-MD5").
* @param protocol The non-null string name of the protocol for which
* the authentication is being performed (e.g., "ldap").
- * @param serverName The non-null fully qualified host name of the server.
+ * @param serverName The fully qualified host name of the server, or null
+ * if the server is not bound to any specific host name. If the mechanism
+ * does not allow an unbound server, a <code>SaslException</code> will
+ * be thrown.
* @param props The possibly null set of properties used to
* select the SASL mechanism and to configure the authentication
* exchange of the selected mechanism.
--- a/jdk/src/share/classes/javax/security/sasl/SaslServerFactory.java Thu Nov 01 18:09:43 2012 -0400
+++ b/jdk/src/share/classes/javax/security/sasl/SaslServerFactory.java Fri Nov 02 10:48:04 2012 +0800
@@ -60,8 +60,10 @@
* IANA-registered name of a SASL mechanism. (e.g. "GSSAPI", "CRAM-MD5").
* @param protocol The non-null string name of the protocol for which
* the authentication is being performed (e.g., "ldap").
- * @param serverName The non-null fully qualified host name of the server
- * to authenticate to.
+ * @param serverName The fully qualified host name of the server to
+ * authenticate to, or null if the server is not bound to any specific host
+ * name. If the mechanism does not allow an unbound server, a
+ * <code>SaslException</code> will be thrown.
* @param props The possibly null set of properties used to select the SASL
* mechanism and to configure the authentication exchange of the selected
* mechanism. See the <tt>Sasl</tt> class for a list of standard properties.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/security/sasl/digest/Unbound.java Fri Nov 02 10:48:04 2012 +0800
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7110803
+ * @summary SASL service for multiple hostnames
+ * @run main Unbound jmx
+ * @run main/fail Unbound j
+ */
+import javax.security.sasl.*;
+import javax.security.auth.callback.*;
+import java.util.*;
+
+public class Unbound {
+ private static final String MECH = "DIGEST-MD5";
+ private static final String SERVER_FQDN = "machineX.imc.org";
+ private static final String PROTOCOL = "jmx";
+
+ private static final byte[] EMPTY = new byte[0];
+
+ private static String pwfile, namesfile, proxyfile;
+ private static boolean auto;
+ private static boolean verbose = false;
+
+ private static void init(String[] args) throws Exception {
+ if (args.length == 1) {
+ pwfile = "pw.properties";
+ namesfile = "names.properties";
+ auto = true;
+ } else {
+ int i = 1;
+ if (args[i].equals("-m")) {
+ i++;
+ auto = false;
+ }
+ if (args.length > i) {
+ pwfile = args[i++];
+
+ if (args.length > i) {
+ namesfile = args[i++];
+
+ if (args.length > i) {
+ proxyfile = args[i];
+ }
+ }
+ } else {
+ pwfile = "pw.properties";
+ namesfile = "names.properties";
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ init(args);
+
+ CallbackHandler clntCbh = new ClientCallbackHandler(auto);
+
+ CallbackHandler srvCbh =
+ new PropertiesFileCallbackHandler(pwfile, namesfile, proxyfile);
+
+ SaslClient clnt = Sasl.createSaslClient(
+ new String[]{MECH}, null, PROTOCOL, SERVER_FQDN, null, clntCbh);
+
+ Map props = System.getProperties();
+ props.put("com.sun.security.sasl.digest.realm", SERVER_FQDN);
+
+ SaslServer srv = Sasl.createSaslServer(MECH, args[0], null, props,
+ srvCbh);
+
+ if (clnt == null) {
+ throw new IllegalStateException(
+ "Unable to find client impl for " + MECH);
+ }
+ if (srv == null) {
+ throw new IllegalStateException(
+ "Unable to find server impl for " + MECH);
+ }
+
+ byte[] response = (clnt.hasInitialResponse()?
+ clnt.evaluateChallenge(EMPTY) : EMPTY);
+ byte[] challenge;
+
+ while (!clnt.isComplete() || !srv.isComplete()) {
+ challenge = srv.evaluateResponse(response);
+
+ if (challenge != null) {
+ response = clnt.evaluateChallenge(challenge);
+ }
+ }
+
+ if (clnt.isComplete() && srv.isComplete()) {
+ if (verbose) {
+ System.out.println("SUCCESS");
+ System.out.println("authzid is " + srv.getAuthorizationID());
+ }
+ } else {
+ throw new IllegalStateException(
+ "FAILURE: mismatched state:" +
+ " client complete? " + clnt.isComplete() +
+ " server complete? " + srv.isComplete());
+ }
+
+ if (!SERVER_FQDN.equalsIgnoreCase((String)
+ srv.getNegotiatedProperty(Sasl.BOUND_SERVER_NAME))) {
+ throw new Exception("Server side gets wrong requested server name");
+ }
+ clnt.dispose();
+ srv.dispose();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/SaslBasic.java Fri Nov 02 10:48:04 2012 +0800
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7110803
+ * @summary SASL service for multiple hostnames
+ * @compile -XDignore.symbol.file SaslBasic.java
+ * @run main/othervm SaslBasic bound
+ * @run main/othervm SaslBasic unbound
+ */
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Locale;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.sasl.*;
+
+// The basic krb5 test skeleton you can copy from
+public class SaslBasic {
+
+ public static void main(String[] args) throws Exception {
+
+ boolean bound = args[0].equals("bound");
+ String name = "host." + OneKDC.REALM.toLowerCase(Locale.US);
+
+ new OneKDC(null).writeJAASConf();
+ System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
+
+ HashMap clntprops = new HashMap();
+ clntprops.put(Sasl.QOP, "auth-conf");
+ SaslClient sc = Sasl.createSaslClient(
+ new String[]{"GSSAPI"}, null, "server",
+ name, clntprops, null);
+
+ final HashMap srvprops = new HashMap();
+ srvprops.put(Sasl.QOP, "auth,auth-int,auth-conf");
+ SaslServer ss = Sasl.createSaslServer("GSSAPI", "server",
+ bound? name: null, srvprops,
+ new CallbackHandler() {
+ public void handle(Callback[] callbacks)
+ throws IOException, UnsupportedCallbackException {
+ for (Callback cb : callbacks) {
+ if (cb instanceof RealmCallback) {
+ ((RealmCallback) cb).setText(OneKDC.REALM);
+ } else if (cb instanceof AuthorizeCallback) {
+ ((AuthorizeCallback) cb).setAuthorized(true);
+ }
+ }
+ }
+ });
+
+ byte[] token = new byte[0];
+ while (!sc.isComplete() || !ss.isComplete()) {
+ if (!sc.isComplete()) {
+ token = sc.evaluateChallenge(token);
+ }
+ if (!ss.isComplete()) {
+ token = ss.evaluateResponse(token);
+ }
+ }
+ if (!bound) {
+ String boundName = (String)ss.getNegotiatedProperty(Sasl.BOUND_SERVER_NAME);
+ if (!boundName.equals(name)) {
+ throw new Exception("Wrong bound server name");
+ }
+ }
+ byte[] hello = "hello".getBytes();
+ token = sc.wrap(hello, 0, hello.length);
+ token = ss.unwrap(token, 0, token.length);
+ if (!Arrays.equals(hello, token)) {
+ throw new Exception("Message altered");
+ }
+ }
+}