mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-09 02:29:18 +00:00
7110803: SASL service for multiple hostnames
Reviewed-by: mullan
This commit is contained in:
parent
5412833e72
commit
a60d4fd720
@ -106,7 +106,7 @@ public abstract class Server extends NTLM {
|
||||
* 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 @@ public abstract class Server extends NTLM {
|
||||
throw new NTLMException(NTLMException.AUTH_FAILED,
|
||||
"None of LM and NTLM verified");
|
||||
}
|
||||
return new String[] {username, hostname};
|
||||
return new String[] {username, hostname, incomingDomain};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -58,14 +58,12 @@ final class CramMD5Server extends CramMD5Base implements SaslServer {
|
||||
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 {
|
||||
|
||||
@ -249,6 +249,8 @@ abstract class DigestMD5Base extends AbstractSaslImpl {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -141,8 +141,10 @@ final class DigestMD5Server extends DigestMD5Base implements SaslServer {
|
||||
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 @@ final class DigestMD5Server extends DigestMD5Base implements SaslServer {
|
||||
|
||||
// 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 @@ final class DigestMD5Server extends DigestMD5Base implements SaslServer {
|
||||
// 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 @@ final class DigestMD5Server extends DigestMD5Base implements SaslServer {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@ -67,9 +67,14 @@ final class GssKrb5Server extends GssKrb5Base implements SaslServer {
|
||||
|
||||
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 @@ final class GssKrb5Server extends GssKrb5Base implements SaslServer {
|
||||
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 @@ final class GssKrb5Server extends GssKrb5Base implements SaslServer {
|
||||
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 @@ final class GssKrb5Server extends GssKrb5Base implements SaslServer {
|
||||
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 @@ final class GssKrb5Server extends GssKrb5Base implements SaslServer {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,6 +99,7 @@ final class NTLMServer implements SaslServer {
|
||||
private String authzId;
|
||||
private final String mech;
|
||||
private String hostname;
|
||||
private String target;
|
||||
|
||||
/**
|
||||
* @param mech not null
|
||||
@ -180,6 +181,7 @@ final class NTLMServer implements SaslServer {
|
||||
String[] out = server.verify(response, nonce);
|
||||
authzId = out[0];
|
||||
hostname = out[1];
|
||||
target = out[2];
|
||||
return null;
|
||||
}
|
||||
} catch (NTLMException ex) {
|
||||
@ -220,6 +222,8 @@ final class NTLMServer implements SaslServer {
|
||||
switch (propName) {
|
||||
case Sasl.QOP:
|
||||
return "auth";
|
||||
case Sasl.BOUND_SERVER_NAME:
|
||||
return target;
|
||||
case NTLM_HOSTNAME:
|
||||
return hostname;
|
||||
default:
|
||||
|
||||
@ -149,7 +149,8 @@ public abstract class AbstractSaslImpl {
|
||||
|
||||
/**
|
||||
* 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 @@ public abstract class AbstractSaslImpl {
|
||||
*/
|
||||
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,
|
||||
|
||||
@ -118,6 +118,18 @@ public class Sasl {
|
||||
public static final String SERVER_AUTH =
|
||||
"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>.
|
||||
@ -449,7 +461,10 @@ public class Sasl {
|
||||
* 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.
|
||||
|
||||
@ -60,8 +60,10 @@ public abstract interface SaslServerFactory {
|
||||
* 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.
|
||||
|
||||
132
jdk/test/com/sun/security/sasl/digest/Unbound.java
Normal file
132
jdk/test/com/sun/security/sasl/digest/Unbound.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
97
jdk/test/sun/security/krb5/auto/SaslBasic.java
Normal file
97
jdk/test/sun/security/krb5/auto/SaslBasic.java
Normal file
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user