package com.mongodb.internal.connection;
import com.mongodb.MongoCredential;
import com.mongodb.MongoException;
import com.mongodb.MongoSecurityException;
import com.mongodb.ServerAddress;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import static com.mongodb.AuthenticationMechanism.GSSAPI;
import static com.mongodb.MongoCredential.CANONICALIZE_HOST_NAME_KEY;
import static com.mongodb.MongoCredential.JAVA_SASL_CLIENT_PROPERTIES_KEY;
import static com.mongodb.MongoCredential.SERVICE_NAME_KEY;
class GSSAPIAuthenticator extends SaslAuthenticator {
private static final String GSSAPI_MECHANISM_NAME = "GSSAPI";
private static final String GSSAPI_OID = "1.2.840.113554.1.2.2";
private static final String SERVICE_NAME_DEFAULT_VALUE = "mongodb";
private static final Boolean CANONICALIZE_HOST_NAME_DEFAULT_VALUE = false;
GSSAPIAuthenticator(final MongoCredentialWithCache credential) {
super(credential);
if (getMongoCredential().getAuthenticationMechanism() != GSSAPI) {
throw new MongoException("Incorrect mechanism: " + getMongoCredential().getMechanism());
}
}
@Override
public String getMechanismName() {
return GSSAPI_MECHANISM_NAME;
}
@Override
protected SaslClient createSaslClient(final ServerAddress serverAddress) {
MongoCredential credential = getMongoCredential();
try {
Map<String, Object> saslClientProperties = credential.getMechanismProperty(JAVA_SASL_CLIENT_PROPERTIES_KEY, null);
if (saslClientProperties == null) {
saslClientProperties = new HashMap<String, Object>();
saslClientProperties.put(Sasl.MAX_BUFFER, "0");
saslClientProperties.put(Sasl.CREDENTIALS, getGSSCredential(credential.getUserName()));
}
SaslClient saslClient = Sasl.createSaslClient(new String[]{GSSAPI.getMechanismName()}, credential.getUserName(),
credential.getMechanismProperty(SERVICE_NAME_KEY, SERVICE_NAME_DEFAULT_VALUE),
getHostName(serverAddress), saslClientProperties, null);
if (saslClient == null) {
throw new MongoSecurityException(credential, String.format("No platform support for %s mechanism", GSSAPI));
}
return saslClient;
} catch (SaslException e) {
throw new MongoSecurityException(credential, "Exception initializing SASL client", e);
} catch (GSSException e) {
throw new MongoSecurityException(credential, "Exception initializing GSSAPI credentials", e);
} catch (UnknownHostException e) {
throw new MongoSecurityException(credential, "Unable to canonicalize host name + " + serverAddress);
}
}
private GSSCredential getGSSCredential(final String userName) throws GSSException {
Oid krb5Mechanism = new Oid(GSSAPI_OID);
GSSManager manager = GSSManager.getInstance();
GSSName name = manager.createName(userName, GSSName.NT_USER_NAME);
return manager.createCredential(name, GSSCredential.INDEFINITE_LIFETIME, krb5Mechanism, GSSCredential.INITIATE_ONLY);
}
private String getHostName(final ServerAddress serverAddress) throws UnknownHostException {
return getNonNullMechanismProperty(CANONICALIZE_HOST_NAME_KEY, CANONICALIZE_HOST_NAME_DEFAULT_VALUE)
? InetAddress.getByName(serverAddress.getHost()).getCanonicalHostName()
: serverAddress.getHost();
}
}