234 lines
7.9 KiB
C#
234 lines
7.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Npgsql.Internal;
|
|
using Npgsql.Logging;
|
|
using Npgsql.Util;
|
|
|
|
namespace Npgsql.BackendMessages;
|
|
|
|
abstract class AuthenticationRequestMessage : IBackendMessage
|
|
{
|
|
public BackendMessageCode Code => BackendMessageCode.AuthenticationRequest;
|
|
internal abstract AuthenticationRequestType AuthRequestType { get; }
|
|
}
|
|
|
|
class AuthenticationOkMessage : AuthenticationRequestMessage
|
|
{
|
|
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationOk;
|
|
|
|
internal static readonly AuthenticationOkMessage Instance = new();
|
|
AuthenticationOkMessage() { }
|
|
}
|
|
|
|
class AuthenticationKerberosV5Message : AuthenticationRequestMessage
|
|
{
|
|
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationKerberosV5;
|
|
|
|
internal static readonly AuthenticationKerberosV5Message Instance = new();
|
|
AuthenticationKerberosV5Message() { }
|
|
}
|
|
|
|
class AuthenticationCleartextPasswordMessage : AuthenticationRequestMessage
|
|
{
|
|
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationCleartextPassword;
|
|
|
|
internal static readonly AuthenticationCleartextPasswordMessage Instance = new();
|
|
AuthenticationCleartextPasswordMessage() { }
|
|
}
|
|
|
|
class AuthenticationMD5PasswordMessage : AuthenticationRequestMessage
|
|
{
|
|
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationMD5Password;
|
|
|
|
internal byte[] Salt { get; private set; }
|
|
|
|
internal static AuthenticationMD5PasswordMessage Load(NpgsqlReadBuffer buf)
|
|
{
|
|
var salt = new byte[4];
|
|
buf.ReadBytes(salt, 0, 4);
|
|
return new AuthenticationMD5PasswordMessage(salt);
|
|
}
|
|
|
|
AuthenticationMD5PasswordMessage(byte[] salt)
|
|
{
|
|
Salt = salt;
|
|
}
|
|
}
|
|
|
|
class AuthenticationSCMCredentialMessage : AuthenticationRequestMessage
|
|
{
|
|
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationSCMCredential;
|
|
|
|
internal static readonly AuthenticationSCMCredentialMessage Instance = new();
|
|
AuthenticationSCMCredentialMessage() { }
|
|
}
|
|
|
|
class AuthenticationGSSMessage : AuthenticationRequestMessage
|
|
{
|
|
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationGSS;
|
|
|
|
internal static readonly AuthenticationGSSMessage Instance = new();
|
|
AuthenticationGSSMessage() { }
|
|
}
|
|
|
|
class AuthenticationGSSContinueMessage : AuthenticationRequestMessage
|
|
{
|
|
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationGSSContinue;
|
|
|
|
internal byte[] AuthenticationData { get; private set; }
|
|
|
|
internal static AuthenticationGSSContinueMessage Load(NpgsqlReadBuffer buf, int len)
|
|
{
|
|
len -= 4; // The AuthRequestType code
|
|
var authenticationData = new byte[len];
|
|
buf.ReadBytes(authenticationData, 0, len);
|
|
return new AuthenticationGSSContinueMessage(authenticationData);
|
|
}
|
|
|
|
AuthenticationGSSContinueMessage(byte[] authenticationData)
|
|
{
|
|
AuthenticationData = authenticationData;
|
|
}
|
|
}
|
|
|
|
class AuthenticationSSPIMessage : AuthenticationRequestMessage
|
|
{
|
|
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationSSPI;
|
|
|
|
internal static readonly AuthenticationSSPIMessage Instance = new();
|
|
AuthenticationSSPIMessage() { }
|
|
}
|
|
|
|
#region SASL
|
|
|
|
class AuthenticationSASLMessage : AuthenticationRequestMessage
|
|
{
|
|
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationSASL;
|
|
internal List<string> Mechanisms { get; } = new();
|
|
|
|
internal AuthenticationSASLMessage(NpgsqlReadBuffer buf)
|
|
{
|
|
while (buf.Buffer[buf.ReadPosition] != 0)
|
|
Mechanisms.Add(buf.ReadNullTerminatedString());
|
|
buf.ReadByte();
|
|
if (Mechanisms.Count == 0)
|
|
throw new NpgsqlException("Received AuthenticationSASL message with 0 mechanisms!");
|
|
}
|
|
}
|
|
|
|
class AuthenticationSASLContinueMessage : AuthenticationRequestMessage
|
|
{
|
|
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationSASLContinue;
|
|
internal byte[] Payload { get; }
|
|
|
|
internal AuthenticationSASLContinueMessage(NpgsqlReadBuffer buf, int len)
|
|
{
|
|
Payload = new byte[len];
|
|
buf.ReadBytes(Payload, 0, len);
|
|
}
|
|
}
|
|
|
|
class AuthenticationSCRAMServerFirstMessage
|
|
{
|
|
static readonly NpgsqlLogger Log = NpgsqlLogManager.CreateLogger(nameof(AuthenticationSCRAMServerFirstMessage));
|
|
|
|
internal string Nonce { get; }
|
|
internal string Salt { get; }
|
|
internal int Iteration { get; }
|
|
|
|
internal static AuthenticationSCRAMServerFirstMessage Load(byte[] bytes)
|
|
{
|
|
var data = PGUtil.UTF8Encoding.GetString(bytes);
|
|
string? nonce = null, salt = null;
|
|
var iteration = -1;
|
|
|
|
foreach (var part in data.Split(','))
|
|
{
|
|
if (part.StartsWith("r=", StringComparison.Ordinal))
|
|
nonce = part.Substring(2);
|
|
else if (part.StartsWith("s=", StringComparison.Ordinal))
|
|
salt = part.Substring(2);
|
|
else if (part.StartsWith("i=", StringComparison.Ordinal))
|
|
iteration = int.Parse(part.Substring(2));
|
|
else
|
|
Log.Debug("Unknown part in SCRAM server-first message:" + part);
|
|
}
|
|
|
|
if (nonce == null)
|
|
throw new NpgsqlException("Server nonce not received in SCRAM server-first message");
|
|
if (salt == null)
|
|
throw new NpgsqlException("Server salt not received in SCRAM server-first message");
|
|
if (iteration == -1)
|
|
throw new NpgsqlException("Server iterations not received in SCRAM server-first message");
|
|
|
|
return new AuthenticationSCRAMServerFirstMessage(nonce, salt, iteration);
|
|
}
|
|
|
|
AuthenticationSCRAMServerFirstMessage(string nonce, string salt, int iteration)
|
|
{
|
|
Nonce = nonce;
|
|
Salt = salt;
|
|
Iteration = iteration;
|
|
}
|
|
}
|
|
|
|
class AuthenticationSASLFinalMessage : AuthenticationRequestMessage
|
|
{
|
|
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationSASLFinal;
|
|
internal byte[] Payload { get; }
|
|
|
|
internal AuthenticationSASLFinalMessage(NpgsqlReadBuffer buf, int len)
|
|
{
|
|
Payload = new byte[len];
|
|
buf.ReadBytes(Payload, 0, len);
|
|
}
|
|
}
|
|
|
|
class AuthenticationSCRAMServerFinalMessage
|
|
{
|
|
static readonly NpgsqlLogger Log = NpgsqlLogManager.CreateLogger(nameof(AuthenticationSCRAMServerFinalMessage));
|
|
|
|
internal string ServerSignature { get; }
|
|
|
|
internal static AuthenticationSCRAMServerFinalMessage Load(byte[] bytes)
|
|
{
|
|
var data = PGUtil.UTF8Encoding.GetString(bytes);
|
|
string? serverSignature = null;
|
|
|
|
foreach (var part in data.Split(','))
|
|
{
|
|
if (part.StartsWith("v=", StringComparison.Ordinal))
|
|
serverSignature = part.Substring(2);
|
|
else
|
|
Log.Debug("Unknown part in SCRAM server-first message:" + part);
|
|
}
|
|
|
|
if (serverSignature == null)
|
|
throw new NpgsqlException("Server signature not received in SCRAM server-final message");
|
|
|
|
return new AuthenticationSCRAMServerFinalMessage(serverSignature);
|
|
}
|
|
|
|
internal AuthenticationSCRAMServerFinalMessage(string serverSignature)
|
|
=> ServerSignature = serverSignature;
|
|
}
|
|
|
|
#endregion SASL
|
|
|
|
// TODO: Remove Authentication prefix from everything
|
|
enum AuthenticationRequestType
|
|
{
|
|
AuthenticationOk = 0,
|
|
AuthenticationKerberosV4 = 1,
|
|
AuthenticationKerberosV5 = 2,
|
|
AuthenticationCleartextPassword = 3,
|
|
AuthenticationCryptPassword = 4,
|
|
AuthenticationMD5Password = 5,
|
|
AuthenticationSCMCredential = 6,
|
|
AuthenticationGSS = 7,
|
|
AuthenticationGSSContinue = 8,
|
|
AuthenticationSSPI = 9,
|
|
AuthenticationSASL = 10,
|
|
AuthenticationSASLContinue = 11,
|
|
AuthenticationSASLFinal = 12
|
|
} |