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

Support SSPI authentication for Windows clients #202

Closed
ringerc opened this issue Oct 9, 2014 · 2 comments
Closed

Support SSPI authentication for Windows clients #202

ringerc opened this issue Oct 9, 2014 · 2 comments
Assignees

Comments

@ringerc
Copy link
Member

ringerc commented Oct 9, 2014

PgJDBC currently responds to SSPI authentication requests by the PostgreSQL server by initiating Kerberos authentication via MakeGSS. This was added by Christian Ullrich in 6bd8784 (Jan 2012).

This approach can only succeed if the target server can accept Kerberos as an SSPI mechanism; it won't work for local loopback on a non-domain joined machine for example. It requires that a Kerberos principal be provided, and the undocumented useSpnego parameter must be set to true to allow the JGSS provider for JSSE to use the negotiation extensions for Kerberos. It's clumsy and it doesn't permit the Java client to use the cached credentials and trust relationship of the Windows host it's running on for single-sign-on.

It's theoretically possible to use the cached Kerberos credentials of the host via JGSS, but in practice it's not portable from OS to OS or even Windows version to Windows version, and has different issues on different JVM versions as well. It also requires that clients have direct access to the cached Kerberos credentials, which isn't ideal in security terms. Some details here; also kwin and Krb5LoginModule.

When we're running on a Windows host, we should use Windows's native SSPI API to delegate SSPI authentication to the host. This allows single-sign-on from Java applications that use PgJDBC.

Under the hood Windows handles the authentication with Kerberos (for networked domain authentication) or NTLM (for loopback on standalone hosts) but we don't have to care. If they add a new mechanism in future we'll support it transparently. We just broker the SSPI messaging between client and server until the Pg server confirms successful authentication.

The PostgreSQL server already supports SSPI on Windows, as do psqlODBC and libpq.

Ideally Java its self would support this via a JSSE mechanism - but at least as of Java 8, it does not. There's a patch floating around but no sign of if/when it'll hit a release version; see:

So we have to talk to the native platform APIs if we're going to do this.

@ringerc ringerc self-assigned this Oct 9, 2014
@ringerc
Copy link
Member Author

ringerc commented Oct 9, 2014

The Waffle project has JNA-based support for the server- and client-side parts of the SSPI APIs - as well as good unit tests for them.

This offers a much better option for native access as it doesn't require any native code in PgJDBC, unlike if we tried to do it with JNI.

I've prototyped an implementation of SSPI support based on Waffle's SSPI support, which you can grab from https://github.com/2ndquadrant/pgjdbc/tree/sia-jdbc-sspi . It's working very well.

Note the remaining work items mentioned in the commit:

https://github.com/2ndQuadrant/pgjdbc/commit/257b9458f892fc4dca84209d3afe69b9203a8774

@ringerc ringerc changed the title Support SSPI authenticatoin Support SSPI authentication for Windows hosts Oct 9, 2014
@ringerc ringerc changed the title Support SSPI authentication for Windows hosts Support SSPI authentication for Windows clients Oct 9, 2014
@ringerc
Copy link
Member Author

ringerc commented Oct 9, 2014

Backward compatibility

Right now, PgJDBC tries to treat SSPI authentication as GSSAPI. I think this can even work if you set the (undocumented) useSpnego parameter to true and set some other properties, but it's clumsy and I rather doubt it's used widely if at all.

If we're on Windows we should default to actual SSPI if we're asked for SSPI. This is a BC-affecting change for anyone using 'sspi' in pg_hba.conf with PgJDBC.

To mitigate that I want to switch back to using gssapi if we see that there's a kerberosServerName set in the JDBC URI. If that isn't set the GSSAPI couldn't succeed anyway, and it doesn't need to be set if SSPI is used.

Reasonable?

Using SSPI for GSSAPI too

On Windows we can actually delegate regular GSSAPI authentication to the host's SSPI implementation too. This lets PgJDBC authenticate to a 'nix PostgreSQL instance using GSSAPI authentication by using the Windows host's existing sign-on and trust relationship with the PostgreSQL server, instead of having to enter new credentials and do a separate login.

To support this, I'd like to use SSPI for GSSAPI auth requests too, if we're on Windows and no kerberosServerName is set.

This is consistent with how libpq does things.

Objections?

Dependency fetching

First, I need to pull in quite a large library. Waffle-jna its self is tiny (56kb) but it depends on the JNA library and its platform support library, which together are 1.5MB. Waffle also depends on Guava, which is 2MB, but I think I can simply omit that for the parts of Waffle that're used by PgJDBC.

I'd rather not commit a bunch of jars to source control and have to update them with Waffle releases. Since we have maven-ant-tasks in place, perhaps it'd be worth using the dependency resolver to fetch them at build-time? Any objections?

Alternately, I can provide ant properties for the download URL for each JAR and have Ant fetch them if they're not already in the lib/ directory. That's the more traditional Ant way of doing things, but still requires hardcoding versions as properties.

Whatever we do will need to work with the CI tools, too. Any advice for what's best there?

Conditional builds

I'd like to add Waffle as a new hard build dependency, but I'm aware that may not be popular for what's really a bit of a niche feature.

Does anyone feel strongly about making it conditional so you can turn it off with an ant property and elide the dependencies?

Note that even if it is left as a hard dependency it'll only be a build dependency. My final patch will handle Waffle/JNA being missing at runtime cleanly. That's why I want it to default to on, at least.

Platform detection, handling missing JNA/Waffle

There's no nice way to detect "Windows" on Java; you have to parse the os.name system property, and that sort of thing never works out well in the long run.

I'd like to just try to load Waffle-jni and initialize SSPI if we get an SSPI authentication request. If it fails we fall back to GSSAPI and debug-log the exception we got when trying to use Waffle along with an informative message, then document it.

Concerns?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant