Skip to content

Commit 30cfc34

Browse files
committed
Init implement GSS API Authentication (gssapi-with-mic)
1 parent b199467 commit 30cfc34

13 files changed

Lines changed: 969 additions & 1 deletion
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#if NET
2+
using System;
3+
using System.Buffers;
4+
using System.Net;
5+
using System.Net.Security;
6+
#if NET8_0
7+
using System.Runtime.CompilerServices;
8+
#endif
9+
using System.Security.Principal;
10+
11+
namespace Renci.SshNet
12+
{
13+
public partial class GssApiAuthenticationMethod
14+
{
15+
private sealed class NegotiateContext : IAuthenticationContext
16+
{
17+
#if NET8_0
18+
// This API was made public in .NET 9 through ComputeIntegrityCheck.
19+
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "GetMIC")]
20+
private static extern void GetMICMethod(NegotiateAuthentication context, ReadOnlySpan<byte> data, IBufferWriter<byte> writer);
21+
#endif
22+
private readonly NegotiateAuthentication _negotiateAuthentication;
23+
public NegotiateContext(bool delegateCredential, NetworkCredential credential, string targetName)
24+
{
25+
var negotiateOptions = new NegotiateAuthenticationClientOptions()
26+
{
27+
AllowedImpersonationLevel = delegateCredential ? TokenImpersonationLevel.Delegation : TokenImpersonationLevel.Impersonation,
28+
Credential = credential,
29+
Package = "Kerberos",
30+
31+
#if NET10_0_OR_GREATER
32+
RequiredProtectionLevel = ProtectionLevel.Sign,
33+
#else
34+
// While only Sign is needed we need to set EncryptAndSign for
35+
// Windows client support. Sign only will pass in SECQOP_WRAP_NO_ENCRYPT
36+
// to MakeSignature which fails.
37+
// https://github.com/dotnet/runtime/issues/103461
38+
RequiredProtectionLevel = ProtectionLevel.EncryptAndSign,
39+
#endif
40+
41+
// While RFC states this should be set to "false", Win32-OpenSSH
42+
// fails if it's not true. I'm unsure if openssh-portable on Linux
43+
// will fail in the same way or not.
44+
RequireMutualAuthentication = true,
45+
TargetName = targetName
46+
};
47+
48+
_negotiateAuthentication = new NegotiateAuthentication(negotiateOptions);
49+
}
50+
51+
public bool IsSigned
52+
{
53+
get
54+
{
55+
return _negotiateAuthentication.IsSigned;
56+
}
57+
}
58+
59+
public byte[] ComputeIntegrityCheck(ReadOnlySpan<byte> message)
60+
{
61+
var signatureWriter = new ArrayBufferWriter<byte>();
62+
#if NET8_0
63+
GetMICMethod(
64+
_negotiateAuthentication,
65+
message,
66+
signatureWriter);
67+
#else
68+
_negotiateAuthentication.ComputeIntegrityCheck(
69+
message,
70+
signatureWriter);
71+
#endif
72+
73+
return signatureWriter.WrittenSpan.ToArray();
74+
}
75+
76+
public void Dispose()
77+
{
78+
_negotiateAuthentication.Dispose();
79+
}
80+
81+
public byte[] GetOutgoingBlob(ReadOnlySpan<byte> incomingBlob, out NegotiateStatusCode statusCode)
82+
{
83+
var outgoingBlob = _negotiateAuthentication.GetOutgoingBlob(incomingBlob, out var code);
84+
85+
#pragma warning disable IDE0010 // Add missing cases
86+
switch (code)
87+
{
88+
case NegotiateAuthenticationStatusCode.ContinueNeeded:
89+
statusCode = NegotiateStatusCode.ContinueNeeded;
90+
break;
91+
case NegotiateAuthenticationStatusCode.Completed:
92+
statusCode = NegotiateStatusCode.Completed;
93+
break;
94+
default:
95+
statusCode = NegotiateStatusCode.Other;
96+
break;
97+
}
98+
#pragma warning restore IDE0010 // Add missing cases
99+
100+
return outgoingBlob;
101+
}
102+
}
103+
}
104+
}
105+
#endif
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#if !NET
2+
#nullable enable
3+
using System;
4+
using System.Net;
5+
6+
namespace Renci.SshNet
7+
{
8+
public partial class GssApiAuthenticationMethod
9+
{
10+
private sealed class ReflectedNegotiateContext : IAuthenticationContext
11+
{
12+
13+
#pragma warning disable IDE0060 // Remove unused parameter
14+
public ReflectedNegotiateContext(bool delegateCredential, NetworkCredential credential, string targetName)
15+
#pragma warning restore IDE0060 // Remove unused parameter
16+
{
17+
}
18+
19+
public bool IsSigned
20+
{
21+
get
22+
{
23+
throw new NotImplementedException();
24+
}
25+
}
26+
27+
public byte[] ComputeIntegrityCheck(ReadOnlySpan<byte> message)
28+
{
29+
throw new NotImplementedException();
30+
}
31+
32+
public void Dispose()
33+
{
34+
}
35+
36+
public byte[] GetOutgoingBlob(ReadOnlySpan<byte> incomingBlob, out NegotiateStatusCode statusCode)
37+
{
38+
throw new NotImplementedException();
39+
}
40+
}
41+
}
42+
}
43+
#endif

0 commit comments

Comments
 (0)