Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support waffle-jna 2.x and 3.x by using reflective approach for ManagedSecBufferDesc #2720

Merged
merged 1 commit into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 14 additions & 9 deletions docs/content/documentation/use.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,19 +296,24 @@ Permissible values are auto (default, see below), sspi (force SSPI) or gssapi (f
If this parameter is auto, SSPI is attempted if the server requests SSPI authentication, the JDBC client is running on Windows, and the Waffle libraries required for SSPI are on the CLASSPATH.
Otherwise Kerberos/GSSAPI via JSSE is used.

* **`gssResponseTimeout (`*Integer*`)`** *Default `5000`*\
Time in milliseconds to wait for a response after requesting a GSS encrypted connection from the server. If this is greater than the current connectTimeout then connectTimeout will be used.
> **Note**
>
> This behaviour does not exactly match that of libpq, which uses Windows' SSPI libraries for Kerberos (GSSAPI) requests by default when on Windows.
> **Note**
>
> This behaviour does not exactly match that of libpq, which uses Windows' SSPI libraries for Kerberos (GSSAPI) requests by default when on Windows.
gssapi mode forces JSSE's GSSAPI to be used even if SSPI is available, matching the pre-9.4 behaviour. On non-Windows platforms or where SSPI is unavailable, forcing sspi mode will fail with a PSQLException.

gssapi mode forces JSSE's GSSAPI to be used even if SSPI is available, matching the pre-9.4 behaviour.
To use SSPI with PgJDBC you must ensure that [the `waffle-jna` library](https://mvnrepository.com/artifact/com.github.waffle/waffle-jna/) and its dependencies are present on the `CLASSPATH`. pgJDBC does **not** bundle `waffle-jna` in the pgJDBC jar.

On non-Windows platforms or where SSPI is unavailable, forcing sspi mode will fail with a PSQLException.
To use SSPI with PgJDBC you must ensure that [the `waffle-jna` library](https://mvnrepository.com/artifact/com.github.waffle/waffle-jna/) and its dependencies are present on the `CLASSPATH` . pgJDBC does **not** bundle `waffle-jna` in the pgJDBC jar.
Compatibility matrix:

Since: 9.4
| pgjdbc | waffle-jna |
|-------------|------------|
| < 42.7.1 | < 2.0.0 |
| ≥ 42.7.1 | ≥ 1.0.0 |

* **`gssResponseTimeout (`*Integer*`)`** *Default `5000`*\
Time in milliseconds to wait for a response after requesting a GSS encrypted connection from the server. If this is greater than the current connectTimeout then connectTimeout will be used.
Since: 9.4

* **`sspiServiceClass (`*String*`)`** *Default `POSTGRES`*\
Specifies the name of the Windows SSPI service class that forms the service class part of the SPN. The default, POSTGRES, is almost always correct.
Expand Down
30 changes: 29 additions & 1 deletion pgjdbc/src/main/java/org/postgresql/sspi/SSPIClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import static org.postgresql.util.internal.Nullness.castNonNull;

import org.postgresql.core.PGStream;
import org.postgresql.util.GT;
import org.postgresql.util.HostSpec;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
Expand All @@ -24,6 +25,7 @@
import waffle.windows.auth.impl.WindowsSecurityContextImpl;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand All @@ -41,6 +43,7 @@ public class SSPIClient implements ISSPIClient {
public static final String SSPI_DEFAULT_SPN_SERVICE_CLASS = "POSTGRES";

private static final Logger LOGGER = Logger.getLogger(SSPIClient.class.getName());
private static final Constructor<? extends SecBufferDesc> SEC_BUFFER_DESC_FACTORY;
private final PGStream pgStream;
private final String spnServiceClass;
private final boolean enableNegotiate;
Expand All @@ -49,6 +52,23 @@ public class SSPIClient implements ISSPIClient {
private @Nullable WindowsSecurityContextImpl sspiContext;
private @Nullable String targetName;

static {
Class<? extends SecBufferDesc> klass;
try {
klass = Class.forName("com.sun.jna.platform.win32.SspiUtil$ManagedSecBufferDesc")
.asSubclass(SecBufferDesc.class);
} catch (ReflectiveOperationException ex) {
klass = SecBufferDesc.class;
}
try {
SEC_BUFFER_DESC_FACTORY = klass.getConstructor(int.class, byte[].class);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
GT.tr("Unable to instantiate SecBufferDesc, so SSPI is unavailable",
klass.getName()), e);
}
}

/**
* <p>Instantiate an SSPIClient for authentication of a connection.</p>
*
Expand Down Expand Up @@ -202,7 +222,15 @@ public void continueSSPI(int msgLength) throws SQLException, IOException {
/* Read the response token from the server */
byte[] receivedToken = pgStream.receive(msgLength);

SecBufferDesc continueToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, receivedToken);
SecBufferDesc continueToken;

try {
continueToken =
SEC_BUFFER_DESC_FACTORY.newInstance(Sspi.SECBUFFER_TOKEN, receivedToken);
} catch (ReflectiveOperationException ex) {
// A fallback just in case
continueToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, receivedToken);
}

sspiContext.initialize(sspiContext.getHandle(), continueToken, castNonNull(targetName));

Expand Down