jcifs.smb.Kerb5Authenticator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcifs-ng Show documentation
Show all versions of jcifs-ng Show documentation
A pure-java CIFS/SMB client library
/*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package jcifs.smb;
import java.io.IOException;
import java.security.Principal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jcifs.CIFSContext;
import jcifs.CIFSException;
import jcifs.spnego.NegTokenInit;
/**
* Base kerberos authenticator
*
* Uses a subject that contains kerberos credentials for use in GSSAPI context establishment.
*
* Be advised that short/NetBIOS name usage is not supported with this authenticator. Always specify full FQDNs.
* This can be a problem if using DFS in it's default configuration as they still return referrals in short form.
* See KB-244380 for compatible server configuration.
* See {@link jcifs.Configuration#isDfsConvertToFQDN()} for a workaround.
*/
public class Kerb5Authenticator extends NtlmPasswordAuthenticator {
private static final long serialVersionUID = 1999400043787454432L;
private static final Logger log = LoggerFactory.getLogger(Kerb5Authenticator.class);
private static final String DEFAULT_SERVICE = "cifs";
private static final Set PREFERRED_MECHS = new HashSet<>();
private Subject subject = null;
private String user = null;
private String realm = null;
private String service = DEFAULT_SERVICE;
private int userLifetime = GSSCredential.DEFAULT_LIFETIME;
private int contextLifetime = GSSContext.DEFAULT_LIFETIME;
private boolean canFallback = false;
private boolean forceFallback;
static {
PREFERRED_MECHS.add(new ASN1ObjectIdentifier("1.2.840.113554.1.2.2"));
PREFERRED_MECHS.add(new ASN1ObjectIdentifier("1.2.840.48018.1.2.2"));
}
/**
* Construct a Kerb5Authenticator
object with Subject
* which hold TGT retrieved from KDC. If multiple TGT are contained, the
* first one will be used to retrieve user principal.
*
* @param subject
* represents the user who perform Kerberos authentication.
* It contains tickets retrieve from KDC.
*/
public Kerb5Authenticator ( Subject subject ) {
this.subject = subject;
}
/**
* Construct a Kerb5Authenticator
object with Subject
and
* potential NTLM fallback (if the server does not support kerberos).
*
* @param subject
* represents the user who perform Kerberos authentication. Should at least contain a TGT for the user.
* @param domain
* domain for NTLM fallback
* @param username
* user for NTLM fallback
* @param password
* password for NTLM fallback
*/
public Kerb5Authenticator ( Subject subject, String domain, String username, String password ) {
super(domain, username, password);
this.canFallback = true;
this.subject = subject;
}
/**
* Testing only: force fallback to NTLM
*
* @param forceFallback
* the forceFallback to set
*/
public void setForceFallback ( boolean forceFallback ) {
this.forceFallback = forceFallback;
}
/**
*
* {@inheritDoc}
*
* @see jcifs.smb.NtlmPasswordAuthenticator#createContext(jcifs.CIFSContext, java.lang.String, java.lang.String,
* byte[], boolean)
*/
@Override
public SSPContext createContext ( CIFSContext tc, String targetDomain, String host, byte[] initialToken, boolean doSigning ) throws SmbException {
if ( host.indexOf('.') < 0 && host.toUpperCase(Locale.ROOT).equals(host) ) {
// this is not too good, probably should better pass the address and check that it is a netbios one.
// While we could look up the domain controller/KDC we cannot really make the java kerberos implementation
// use a KDC of our choice.
// A potential workaround would be to try to get the server FQDN by reverse lookup, but this might have
// security implications and also is not how Microsoft does it.
throw new SmbUnsupportedOperationException("Cannot use netbios/short names with kerberos authentication, have " + host);
}
try {
NegTokenInit tok = new NegTokenInit(initialToken);
if ( log.isDebugEnabled() ) {
log.debug("Have initial token " + tok);
}
if ( tok.getMechanisms() != null ) {
Set mechs = new HashSet<>(Arrays.asList(tok.getMechanisms()));
boolean foundKerberos = false;
for ( ASN1ObjectIdentifier mech : Kerb5Context.SUPPORTED_MECHS ) {
foundKerberos |= mechs.contains(mech);
}
if ( ( !foundKerberos || this.forceFallback ) && this.canFallback && tc.getConfig().isAllowNTLMFallback() ) {
log.debug("Falling back to NTLM authentication");
return super.createContext(tc, targetDomain, host, initialToken, doSigning);
}
else if ( !foundKerberos ) {
throw new SmbUnsupportedOperationException("Server does not support kerberos authentication");
}
}
}
catch ( SmbException e ) {
throw e;
}
catch ( IOException e1 ) {
log.debug("Ignoring invalid initial token", e1);
}
try {
return createContext(tc, targetDomain, host);
}
catch ( GSSException e ) {
throw new SmbException("Context setup failed", e);
}
}
/**
* @param subject
* the subject to set
*/
protected void setSubject ( Subject subject ) {
this.subject = subject;
}
@Override
public void refresh () throws CIFSException {
// custom Kerb5Authenticators need to override this method for support
throw new SmbUnsupportedOperationException("Refreshing credentials is not supported by this authenticator");
}
@Override
public Kerb5Authenticator clone () {
Kerb5Authenticator auth = new Kerb5Authenticator(getSubject());
cloneInternal(auth, this);
return auth;
}
/**
* Clone the context
*
* @param to
* @param from
*/
public static void cloneInternal ( Kerb5Authenticator to, Kerb5Authenticator from ) {
NtlmPasswordAuthenticator.cloneInternal(to, from);
to.setUser(from.getUser());
to.setRealm(from.getRealm());
to.setService(from.getService());
to.setLifeTime(from.getLifeTime());
to.setUserLifeTime(from.getUserLifeTime());
to.canFallback = from.canFallback;
to.forceFallback = from.forceFallback;
}
/**
* Set the user name which is used to setup GSSContext
. If null
* is set, the default user will be used which is retrieved from the first
* TGT found in Subject
.
*
* @param name
* the user name used to setup GSSContext
*/
public void setUser ( String name ) {
this.user = name;
}
/**
* @param realm
* the realm to set
*/
public void setRealm ( String realm ) {
this.realm = realm;
}
/**
*
* @return the kerberos realm
*/
public String getRealm () {
return this.realm;
}
/**
* Get the Subject
object.
*
* @return Subject represents the user who perform Kerberos authentication.
* It contains the tickets retrieve from KDC.
*/
@Override
public Subject getSubject () {
return this.subject;
}
/**
* Get the user name which authenticate against to. If the default user
* is used, Null will be returned.
*
* @return user name
*/
public String getUser () {
return this.user;
}
/**
* Set the service name which is used to setup GSSContext
.
* Program will use this name to require service ticket from KDC.
*
* @param name
* the service name used to require service ticket from KDC.
*/
public void setService ( String name ) {
this.service = name;
}
/**
* Get the service name.
*
* @return the service name used to require service ticket from KDC
*/
public String getService () {
return this.service;
}
/**
* Get lifetime of current user.
*
* @return the remaining lifetime in seconds. If the default lifetime is
* used, this value have no meaning.
*
*/
public int getUserLifeTime () {
return this.userLifetime;
}
/**
* Set lifetime of current user.
*
* @param time
* the lifetime in seconds
*
*/
public void setUserLifeTime ( int time ) {
this.userLifetime = time;
}
/**
* Get lifetime of this context.
*
* @return the remaining lifetime in seconds. If the default lifetime is
* used, this value have no meaning.
*/
public int getLifeTime () {
return this.contextLifetime;
}
/**
* Set the lifetime for this context.
*
* @param time
* the lifetime in seconds
*/
public void setLifeTime ( int time ) {
this.contextLifetime = time;
}
/**
* {@inheritDoc}
*
* @see jcifs.smb.CredentialsInternal#isAnonymous()
*/
@Override
public boolean isAnonymous () {
return this.getSubject() == null && super.isAnonymous();
}
/**
* {@inheritDoc}
*
* @see jcifs.smb.NtlmPasswordAuthenticator#isPreferredMech(org.bouncycastle.asn1.ASN1ObjectIdentifier)
*/
@Override
public boolean isPreferredMech ( ASN1ObjectIdentifier mechanism ) {
if ( isAnonymous() ) {
return super.isPreferredMech(mechanism);
}
return PREFERRED_MECHS.contains(mechanism);
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString () {
return "Kerb5Authenticatior[subject=" + ( this.getSubject() != null ? this.getSubject().getPrincipals() : null ) + ",user=" + this.user
+ ",realm=" + this.realm + "]";
}
private SpnegoContext createContext ( CIFSContext tc, String targetDomain, String host ) throws GSSException {
return new SpnegoContext(
tc.getConfig(),
new Kerb5Context(
host,
this.service,
this.user,
this.userLifetime,
this.contextLifetime,
targetDomain != null ? targetDomain.toUpperCase(Locale.ROOT) : null));
}
/**
*
* {@inheritDoc}
*
* @see jcifs.smb.NtlmPasswordAuthenticator#equals(java.lang.Object)
*/
@Override
public boolean equals ( Object other ) {
// this method is called from SmbSession
if ( other != null && other instanceof Kerb5Authenticator )
return Objects.equals(this.getSubject(), ( (Kerb5Authenticator) other ).getSubject());
return false;
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode () {
return super.hashCode();
}
@Override
public String getUserDomain () {
if ( this.realm == null && this.getSubject() != null ) {
Set pr = this.getSubject().getPrincipals();
for ( Iterator ite = pr.iterator(); ite.hasNext(); ) {
try {
KerberosPrincipal entry = (KerberosPrincipal) ite.next();
return entry.getRealm();
}
catch ( Exception e ) {
continue;
}
}
}
if ( this.realm != null ) {
return this.realm;
}
return super.getUserDomain();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy