Skip to content

Commit f703ef8

Browse files
authored
Improve kerberos auth on windows (#6541)
Fixes #6529
1 parent 5f26d61 commit f703ef8

3 files changed

Lines changed: 18 additions & 8 deletions

File tree

src/Npgsql/Internal/IntegratedSecurityHandler.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class IntegratedSecurityHandler
1616
return new();
1717
}
1818

19-
public virtual ValueTask NegotiateAuthentication(bool async, NpgsqlConnector connector, CancellationToken cancellationToken)
19+
public virtual ValueTask NegotiateAuthentication(bool async, bool isKerberos, NpgsqlConnector connector, CancellationToken cancellationToken)
2020
=> throw new NotSupportedException(string.Format(NpgsqlStrings.IntegratedSecurityDisabled, nameof(NpgsqlSlimDataSourceBuilder.EnableIntegratedSecurity)));
2121

2222
public virtual ValueTask<GssEncryptionResult> GSSEncrypt(bool async, bool isRequired, NpgsqlConnector connector, CancellationToken cancellationToken)
@@ -30,8 +30,8 @@ sealed class RealIntegratedSecurityHandler : IntegratedSecurityHandler
3030
public override ValueTask<string?> GetUsername(bool async, bool includeRealm, ILogger connectionLogger, CancellationToken cancellationToken)
3131
=> KerberosUsernameProvider.GetUsername(async, includeRealm, connectionLogger, cancellationToken);
3232

33-
public override ValueTask NegotiateAuthentication(bool async, NpgsqlConnector connector, CancellationToken cancellationToken)
34-
=> connector.AuthenticateGSS(async, cancellationToken);
33+
public override ValueTask NegotiateAuthentication(bool async, bool isKerberos, NpgsqlConnector connector, CancellationToken cancellationToken)
34+
=> connector.AuthenticateGSS(async, isKerberos, cancellationToken);
3535

3636
public override ValueTask<GssEncryptionResult> GSSEncrypt(bool async, bool isRequired, NpgsqlConnector connector, CancellationToken cancellationToken)
3737
=> connector.GSSEncrypt(async, isRequired, cancellationToken);

src/Npgsql/Internal/NpgsqlConnector.Auth.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ await AuthenticateSASL(((AuthenticationSASLMessage)msg).Mechanisms, username, as
6161
case AuthenticationRequestType.GSS:
6262
case AuthenticationRequestType.SSPI:
6363
ThrowIfNotAllowed(requiredAuthModes, msg.AuthRequestType == AuthenticationRequestType.GSS ? RequireAuthMode.GSS : RequireAuthMode.SSPI);
64-
await DataSource.IntegratedSecurityHandler.NegotiateAuthentication(async, this, cancellationToken).ConfigureAwait(false);
64+
var isKerberos = msg.AuthRequestType == AuthenticationRequestType.GSS;
65+
await DataSource.IntegratedSecurityHandler.NegotiateAuthentication(async, isKerberos, this, cancellationToken).ConfigureAwait(false);
6566
return;
6667

6768
case AuthenticationRequestType.GSSContinue:
@@ -327,7 +328,7 @@ async Task AuthenticateMD5(string username, byte[] salt, bool async, Cancellatio
327328
await Flush(async, cancellationToken).ConfigureAwait(false);
328329
}
329330

330-
internal async ValueTask AuthenticateGSS(bool async, CancellationToken cancellationToken)
331+
internal async ValueTask AuthenticateGSS(bool async, bool isKerberos, CancellationToken cancellationToken)
331332
{
332333
var targetName = $"{KerberosServiceName}/{Host}";
333334
// See https://github.com/postgres/postgres/blob/a0dd0702e464f206b08c99a74cb58809c51aafa5/src/interfaces/libpq/fe-auth.c#L111-L123
@@ -337,15 +338,24 @@ internal async ValueTask AuthenticateGSS(bool async, CancellationToken cancellat
337338
TargetName = targetName,
338339
RequireMutualAuthentication = true
339340
};
341+
// If postgres requests GSS, we explicitly ask for Kerberos
342+
// Instead of relying on SSPI on windows to pick the correct protocol (Kerberos instead of NTLM)
343+
// Otherwise, leave Negotiate to allow SSPI to pick whatever it thinks is correct
344+
// This behavior differs from libpq, which prefers SSPI to pick the protocol
345+
// But mimics PGJDBC
346+
// On UNIX only Kerberos is supported, so no need to differentiate between OSes
347+
// TODO: PGJBC has a parameter to force SSPI. Not sure we need something like this.
348+
if (isKerberos)
349+
clientOptions.Package = "Kerberos";
340350

341351
NegotiateOptionsCallback?.Invoke(clientOptions);
342352

343353
using var authContext = new NegotiateAuthentication(clientOptions);
344354
var data = authContext.GetOutgoingBlob(ReadOnlySpan<byte>.Empty, out var statusCode)!;
345-
if (statusCode != NegotiateAuthenticationStatusCode.ContinueNeeded)
355+
if (statusCode is not NegotiateAuthenticationStatusCode.Completed and not NegotiateAuthenticationStatusCode.ContinueNeeded)
346356
{
347357
// Unable to retrieve credentials or some other issue
348-
throw new NpgsqlException($"Unable to authenticate with GSS: received {statusCode} instead of the expected ContinueNeeded");
358+
throw new NpgsqlException($"Unable to authenticate with GSS: received {statusCode} instead of the expected ContinueNeeded or Completed");
349359
}
350360
await WritePassword(data, 0, data.Length, async, cancellationToken).ConfigureAwait(false);
351361
await Flush(async, cancellationToken).ConfigureAwait(false);

src/Npgsql/Internal/NpgsqlConnector.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ internal async ValueTask<GssEncryptionResult> GSSEncrypt(bool async, bool isRequ
637637
return GssEncryptionResult.GetCredentialFailure;
638638
}
639639

640-
if (statusCode != NegotiateAuthenticationStatusCode.ContinueNeeded)
640+
if (statusCode is not NegotiateAuthenticationStatusCode.Completed and not NegotiateAuthenticationStatusCode.ContinueNeeded)
641641
{
642642
// Unable to retrieve credentials
643643
// If it's required, throw an appropriate exception

0 commit comments

Comments
 (0)