blob: 0b32a1a3e06a4b3c101ae773051e05a5897d7a2b [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (c) 2012-2014 Monty Program Ab
// Copyright (c) 2015-2021 MariaDB Corporation Ab
package org.mariadb.jdbc.plugin.authentication.addon.gssapi;
import java.io.*;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.sql.SQLException;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.ietf.jgss.*;
import org.mariadb.jdbc.client.ReadableByteBuf;
import org.mariadb.jdbc.client.socket.Reader;
import org.mariadb.jdbc.client.socket.Writer;
/** Basic GSSAPI implementation if waffle is not on classpath */
public class StandardGssapiAuthentication implements GssapiAuth {
/**
* Process default GSS plugin authentication.
*
* @param out out stream
* @param in in stream
* @param servicePrincipalName service principal name
* @param mechanisms gssapi mechanism
* @throws IOException if socket error
* @throws SQLException in any Exception occur
*/
public void authenticate(
final Writer out, final Reader in, final String servicePrincipalName, String mechanisms)
throws SQLException, IOException {
if ("".equals(servicePrincipalName)) {
throw new SQLException(
"No principal name defined on server. Please set server variable"
+ " \"gssapi-principal-name\" or set option \"servicePrincipalName\"",
"28000");
}
if (System.getProperty("java.security.auth.login.config") == null) {
final File jaasConfFile;
try {
jaasConfFile = File.createTempFile("jaas.conf", null);
try (PrintStream bos = new PrintStream(new FileOutputStream(jaasConfFile))) {
bos.print(
"Krb5ConnectorContext {\n"
+ "com.sun.security.auth.module.Krb5LoginModule required "
+ "useTicketCache=true "
+ "debug=true "
+ "renewTGT=true "
+ "doNotPrompt=true; };");
}
jaasConfFile.deleteOnExit();
} catch (final IOException ex) {
throw new UncheckedIOException(ex);
}
System.setProperty("java.security.auth.login.config", jaasConfFile.getCanonicalPath());
}
try {
LoginContext loginContext = new LoginContext("Krb5ConnectorContext");
// attempt authentication
loginContext.login();
final Subject mySubject = loginContext.getSubject();
if (!mySubject.getPrincipals().isEmpty()) {
try {
PrivilegedExceptionAction<Void> action =
() -> {
try {
Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
GSSManager manager = GSSManager.getInstance();
GSSName peerName = manager.createName(servicePrincipalName, GSSName.NT_USER_NAME);
GSSContext context =
manager.createContext(
peerName, krb5Mechanism, null, GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(true);
byte[] inToken = new byte[0];
byte[] outToken;
while (true) {
outToken = context.initSecContext(inToken, 0, inToken.length);
// Send a token to the peer if one was generated by acceptSecContext
if (outToken != null) {
out.writeBytes(outToken);
out.flush();
}
if (context.isEstablished()) {
break;
}
ReadableByteBuf buf = in.readReusablePacket();
inToken = new byte[buf.readableBytes()];
buf.readBytes(inToken);
}
} catch (GSSException le) {
throw new SQLException("GSS-API authentication exception", "28000", 1045, le);
}
return null;
};
Subject.doAs(mySubject, action);
} catch (PrivilegedActionException exception) {
throw new SQLException("GSS-API authentication exception", "28000", 1045, exception);
}
} else {
throw new SQLException(
"GSS-API authentication exception : no credential cache not found.", "28000", 1045);
}
} catch (LoginException le) {
throw new SQLException("GSS-API authentication exception", "28000", 1045, le);
}
}
}