зеркало из https://github.com/mozilla/pjs.git
292 строки
12 KiB
Java
292 строки
12 KiB
Java
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1999 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
package netscape.ldap;
|
|
|
|
import java.util.*;
|
|
import netscape.ldap.client.*;
|
|
import netscape.ldap.client.opers.*;
|
|
import netscape.ldap.ber.stream.*;
|
|
import netscape.ldap.util.*;
|
|
import java.io.*;
|
|
import java.net.*;
|
|
//import javax.security.auth.callback.CallbackHandler;
|
|
|
|
/**
|
|
* Authenticates to a server using SASL
|
|
*/
|
|
public class LDAPSaslBind implements LDAPBind, java.io.Serializable {
|
|
|
|
static final long serialVersionUID = -7615315715163655443L;
|
|
|
|
/**
|
|
* Construct an object which can authenticate to an LDAP server
|
|
* using the specified name and a specified SASL mechanism.
|
|
*
|
|
* @param dn if non-null and non-empty, specifies that the connection and
|
|
* all operations through it should authenticate with dn as the
|
|
* distinguished name
|
|
* @param mechanisms array of mechanism names, e.g. { "GSSAPI", "SKEY" }
|
|
* @param props optional additional properties of the desired
|
|
* authentication mechanism, e.g. minimum security level
|
|
* @param cbh a class which may be called by the SASL framework to
|
|
* obtain additional required information
|
|
*/
|
|
public LDAPSaslBind( String dn,
|
|
String[] mechanisms,
|
|
String packageName,
|
|
Hashtable props,
|
|
/*CallbackHandler*/ Object cbh ) {
|
|
_dn = dn;
|
|
_mechanisms = mechanisms;
|
|
_packageName = packageName;
|
|
_props = props;
|
|
_cbh = cbh;
|
|
|
|
// 12-01-99 Disabled check for instanceof CallbackHandler so that
|
|
// there is no dependency on the extenal jaas.jar package. This is
|
|
// reqired for Communicator build where the ldap java package does not
|
|
// include any sasl classes.
|
|
|
|
/*if ( (cbh != null) &&
|
|
!(cbh instanceof javax.security.auth.callback.CallbackHandler) ) {
|
|
throw new IllegalArgumentException(
|
|
"Callback argument must implement " +
|
|
"javax.security.auth.callback.CallbackHandler" );
|
|
}*/
|
|
}
|
|
|
|
/**
|
|
* Authenticates to the LDAP server (that the object is currently
|
|
* connected to) using the parameter that were provided to the
|
|
* constructor. If the requested SASL mechanism is not
|
|
* available, an exception is thrown. If the object has been
|
|
* disconnected from an LDAP server, this method attempts to reconnect
|
|
* to the server. If the object had already authenticated, the old
|
|
* authentication is discarded.
|
|
*
|
|
* @param ldc an active connection to a server, which will have
|
|
* the new authentication state on return from the method
|
|
* @exception LDAPException Failed to authenticate to the LDAP server.
|
|
*/
|
|
public void bind( LDAPConnection ldc ) throws LDAPException {
|
|
if ( _props == null ) {
|
|
_props = new Hashtable();
|
|
}
|
|
if ( (!_props.containsKey( CLIENTPKGS )) &&
|
|
(System.getProperty( CLIENTPKGS ) == null) ) {
|
|
_props.put( CLIENTPKGS, ldc.DEFAULT_SASL_PACKAGE );
|
|
}
|
|
_saslClient = getClient( ldc, _packageName );
|
|
if ( _saslClient != null ) {
|
|
bind( ldc, true );
|
|
return;
|
|
} else {
|
|
ldc.printDebug( "LDAPSaslBind.bind: getClient " +
|
|
"returned null" );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a SaslClient object from the Sasl framework
|
|
*
|
|
* @param ldc contains the host name
|
|
* @param packageName package containing a ClientFactory
|
|
* @return a SaslClient supporting one of the mechanisms
|
|
* of the member variable _mechanisms.
|
|
* @exception LDAPException on error producing a client
|
|
*/
|
|
private Object getClient( LDAPConnection ldc, String packageName )
|
|
throws LDAPException {
|
|
try {
|
|
Object[] args = new Object[6];
|
|
args[0] = _mechanisms;
|
|
args[1] = _dn;
|
|
args[2] = "ldap";
|
|
args[3] = ldc.getHost();
|
|
args[4] = _props;
|
|
args[5] = _cbh;
|
|
String[] argNames = new String[6];
|
|
argNames[0] = "[Ljava.lang.String;";
|
|
argNames[1] = "java.lang.String";
|
|
argNames[2] = "java.lang.String";
|
|
argNames[3] = "java.lang.String";
|
|
argNames[4] = "java.util.Hashtable";
|
|
argNames[5] = CALLBACK_HANDLER;
|
|
|
|
// Get a mechanism driver
|
|
return DynamicInvoker.invokeMethod(null,
|
|
packageName+".Sasl",
|
|
"createSaslClient",
|
|
args, argNames);
|
|
|
|
} catch (Exception e) {
|
|
ldc.printDebug( "LDAPSaslBind.getClient: " +
|
|
packageName+".Sasl.createSaslClient: " +
|
|
e );
|
|
throw new LDAPException(e.toString(), LDAPException.OTHER);
|
|
}
|
|
}
|
|
|
|
void bind(LDAPConnection ldc, boolean rebind)
|
|
throws LDAPException {
|
|
|
|
if ((ldc.isConnected() && rebind) ||
|
|
!ldc.isConnected()) {
|
|
try {
|
|
// Get the initial request to start authentication
|
|
String className = _saslClient.getClass().getName();
|
|
ldc.printDebug( "LDAPSaslBind.bind: calling " +
|
|
className+".createInitialResponse" );
|
|
byte[] outVals =
|
|
(byte[])DynamicInvoker.invokeMethod(
|
|
_saslClient,
|
|
className,
|
|
"createInitialResponse", null, null);
|
|
|
|
String mechanismName =
|
|
(String)DynamicInvoker.invokeMethod(
|
|
_saslClient,
|
|
className,
|
|
"getMechanismName", null, null);
|
|
ldc.printDebug( "LDAPSaslBind.bind: mechanism " +
|
|
"name is " +
|
|
mechanismName );
|
|
boolean isExternal = isExternalMechanism(mechanismName);
|
|
int resultCode = LDAPException.SASL_BIND_IN_PROGRESS;
|
|
JDAPBindResponse response = null;
|
|
while (!checkForSASLBindCompletion(resultCode)) {
|
|
ldc.printDebug( "LDAPSaslBind.bind: calling " +
|
|
"saslBind" );
|
|
response = saslBind(ldc, mechanismName, outVals);
|
|
resultCode = response.getResultCode();
|
|
ldc.printDebug( "LDAPSaslBind.bind: saslBind " +
|
|
"returned " + resultCode );
|
|
if (isExternal) {
|
|
continue;
|
|
}
|
|
|
|
byte[] b = response.getCredentials();
|
|
|
|
Object[] args = {b};
|
|
String[] argNames = {"[B"}; // class name for byte array
|
|
|
|
outVals =
|
|
(byte[])DynamicInvoker.invokeMethod(
|
|
_saslClient,
|
|
className, "evaluateChallenge",
|
|
args, argNames);
|
|
}
|
|
|
|
// Make sure authentication REALLY is complete
|
|
Boolean bool =
|
|
(Boolean)DynamicInvoker.invokeMethod(
|
|
_saslClient,
|
|
className, "isComplete", null, null);
|
|
if (!bool.booleanValue()) {
|
|
// Authentication session hijacked!
|
|
throw new LDAPException("The server indicates that " +
|
|
"authentication is successful" +
|
|
", but the SASL driver " +
|
|
"indicates that authentication" +
|
|
" is not yet done.",
|
|
LDAPException.OTHER);
|
|
}
|
|
|
|
Object[] args = {ldc.getInputStream()};
|
|
String[] argNames = {"java.io.InputStream"};
|
|
InputStream is =
|
|
(InputStream)DynamicInvoker.invokeMethod(
|
|
_saslClient,
|
|
className, "getInputStream", args, argNames);
|
|
ldc.setInputStream(is);
|
|
args[0] = ldc.getOutputStream();
|
|
argNames[0] = "java.io.OutputStream";
|
|
OutputStream os =
|
|
(OutputStream)DynamicInvoker.invokeMethod(
|
|
_saslClient,
|
|
className, "getOutputStream", args, argNames);
|
|
ldc.setOutputStream(os);
|
|
ldc.markConnAsBound();
|
|
} catch (LDAPException e) {
|
|
throw e;
|
|
} catch (Exception e) {
|
|
throw new LDAPException(e.toString(), LDAPException.OTHER);
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean isExternalMechanism(String name) {
|
|
return name.equalsIgnoreCase( LDAPConnection.EXTERNAL_MECHANISM );
|
|
}
|
|
|
|
private boolean checkForSASLBindCompletion(int resultCode)
|
|
throws LDAPException{
|
|
|
|
if (resultCode == LDAPException.SUCCESS) {
|
|
return true;
|
|
} else if (resultCode == LDAPException.SASL_BIND_IN_PROGRESS) {
|
|
return false;
|
|
} else {
|
|
throw new LDAPException("Authentication failed", resultCode);
|
|
}
|
|
}
|
|
|
|
private JDAPBindResponse saslBind(LDAPConnection ldc,
|
|
String mechanismName,
|
|
byte[] credentials)
|
|
throws LDAPException {
|
|
|
|
LDAPResponseListener myListener = ldc.getResponseListener ();
|
|
|
|
try {
|
|
ldc.sendRequest(new JDAPBindRequest(3,
|
|
_dn,
|
|
mechanismName,
|
|
credentials),
|
|
myListener, ldc.getConstraints());
|
|
LDAPMessage response = myListener.getResponse();
|
|
|
|
JDAPProtocolOp protocolOp = response.getProtocolOp();
|
|
if (protocolOp instanceof JDAPBindResponse) {
|
|
return (JDAPBindResponse)protocolOp;
|
|
} else {
|
|
throw new LDAPException("Unknown response from the " +
|
|
"server during SASL bind",
|
|
LDAPException.OTHER);
|
|
}
|
|
} finally {
|
|
ldc.releaseResponseListener(myListener);
|
|
}
|
|
}
|
|
|
|
private static final String CALLBACK_HANDLER =
|
|
"javax.security.auth.callback.CallbackHandler";
|
|
private static final String CLIENTPKGS =
|
|
"javax.security.sasl.client.pkgs";
|
|
private String _dn;
|
|
private String[] _mechanisms;
|
|
private String _packageName;
|
|
private Hashtable _props;
|
|
private /*CallbackHandler*/ Object _cbh;
|
|
private Object _saslClient = null;
|
|
}
|