Skip to content

Commit

Permalink
fix: support waffle-jna 2.x and 3.x by using reflective approach for …
Browse files Browse the repository at this point in the history
…ManagedSecBufferDesc

This commit makes pgjdbc code compatible with both jna-platform 4.5 and jna-platform 5.x
The reason for the change is that SecBufferDesc was improperly typed in jna-platform 4.5,
so they suggested completely drop the old constructor.

Now we try both constructors, and use the newer one.

Inspired by bokken,
#2690 (comment)

Fixes #2690
  • Loading branch information
chrullrich authored and vlsi committed Nov 28, 2023
1 parent 30d4035 commit fddb94b
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 10 deletions.
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

0 comments on commit fddb94b

Please sign in to comment.