-
-
Notifications
You must be signed in to change notification settings - Fork 163
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Example for the SSL factory implementation
Included a fixed version of GnuSslStream (only required on Windows...)
- Loading branch information
1 parent
86da869
commit 27d8bec
Showing
7 changed files
with
574 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// <copyright file="CustomSslStreamWrapperFactory.cs" company="Fubar Development Junker"> | ||
// Copyright (c) Fubar Development Junker. All rights reserved. | ||
// </copyright> | ||
|
||
using System.IO; | ||
using System.Net.Security; | ||
using System.Security.Cryptography.X509Certificates; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
using FubarDev.FtpServer.Authentication; | ||
|
||
namespace TestFtpServer | ||
{ | ||
public class CustomSslStreamWrapperFactory : ISslStreamWrapperFactory | ||
{ | ||
/// <inheritdoc /> | ||
public async Task<Stream> WrapStreamAsync( | ||
Stream unencryptedStream, | ||
bool keepOpen, | ||
X509Certificate certificate, | ||
CancellationToken cancellationToken) | ||
{ | ||
var sslStream = new GnuSslStream(unencryptedStream, keepOpen); | ||
try | ||
{ | ||
await sslStream.AuthenticateAsServerAsync(certificate) | ||
.ConfigureAwait(false); | ||
} | ||
catch | ||
{ | ||
sslStream.Dispose(); | ||
throw; | ||
} | ||
|
||
return sslStream; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Task CloseStreamAsync(Stream sslStream, CancellationToken cancellationToken) | ||
{ | ||
if (sslStream is GnuSslStream s) | ||
{ | ||
s.Close(); | ||
} | ||
|
||
return Task.CompletedTask; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
using System.IO; | ||
|
||
/* About the annoying BUG in SslStream: | ||
* .NET's SslStream class does not send the close_notify alert before closing the connection, | ||
* which is needed by GnuTLS. If we don't send this signal, GnuTLS will throw an error like this: | ||
* [GnuTLS error -110: The TLS connection was non-properly terminated.] | ||
* So we have to fix this issue by ourselves.(Microsoft refused to fix this, see:https://connect.microsoft.com/VisualStudio/feedback/details/788752/sslstream-does-not-properly-send-the-close-notify-alert) | ||
* The Following solution was provided by Neco(Nikolay Uvaliyev) at http://stackoverflow.com/questions/237807/net-sslstream-doesnt-close-tls-connection-properly . | ||
* Much Thanks to him! | ||
* P.S. SslStream in Mono works correctly. | ||
* by Ulysses , 2014 | ||
*/ | ||
/* 关于SslStream的BUG: | ||
* .NET的SslStream在关闭之前不会发送close_notify信号,而在很多TLS库(除了微软)之外是需要这个信号的。 | ||
* 如果不发这个信号,GnuTLS就会报错:服务器没有正常的关闭 TLS 连接。微软表示现阶段不会修复这个问题(网址见上)。 | ||
* 这个解决方法是StackOverflow的Neco(Nikolay Uvaliyev)提供的(这位老外的昵称难道是Neko(猫)吗……网址见上)。非常感谢他! | ||
* 经验证,Mono中的Sslstream无此问题! | ||
* by Ulysses , 2014 | ||
*/ | ||
|
||
|
||
namespace System.Net.Security | ||
{ | ||
public class GnuSslStream : SslStream | ||
{ | ||
public GnuSslStream(Stream innerStream) | ||
: base(innerStream) | ||
{ | ||
} | ||
public GnuSslStream(Stream innerStream, bool leaveInnerStreamOpen) | ||
: base(innerStream, leaveInnerStreamOpen) | ||
{ | ||
} | ||
public GnuSslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback) | ||
: base(innerStream, leaveInnerStreamOpen, userCertificateValidationCallback) | ||
{ | ||
} | ||
public GnuSslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback, LocalCertificateSelectionCallback userCertificateSelectionCallback) | ||
: base(innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, userCertificateSelectionCallback) | ||
{ | ||
} | ||
//Mono不支持此重载构造函数 索性取消 | ||
#if WINDOWS | ||
public GnuSslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback, LocalCertificateSelectionCallback userCertificateSelectionCallback, EncryptionPolicy encryptionPolicy) | ||
: base(innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, userCertificateSelectionCallback, encryptionPolicy) | ||
{ | ||
} | ||
#endif | ||
public override void Close() | ||
{ | ||
//MARK: SslStream in Mono works correctly. | ||
if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix) | ||
{ | ||
base.Close(); | ||
return; | ||
} | ||
try | ||
{ | ||
SslDirectCall.CloseNotify(this); | ||
base.Flush(); | ||
} | ||
finally | ||
{ | ||
base.Close(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
using System.Runtime.ConstrainedExecution; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace System.Net.Security | ||
{ | ||
public unsafe static class NativeApi | ||
{ | ||
internal enum BufferType | ||
{ | ||
Empty, | ||
Data, | ||
Token, | ||
Parameters, | ||
Missing, | ||
Extra, | ||
Trailer, | ||
Header, | ||
Padding = 9, | ||
Stream, | ||
ChannelBindings = 14, | ||
TargetHost = 16, | ||
ReadOnlyFlag = -2147483648, | ||
ReadOnlyWithChecksum = 268435456 | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential, Pack = 1)] | ||
internal struct SSPIHandle | ||
{ | ||
public IntPtr HandleHi; | ||
public IntPtr HandleLo; | ||
public bool IsZero | ||
{ | ||
get | ||
{ | ||
return this.HandleHi == IntPtr.Zero && this.HandleLo == IntPtr.Zero; | ||
} | ||
} | ||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] | ||
internal void SetToInvalid() | ||
{ | ||
this.HandleHi = IntPtr.Zero; | ||
this.HandleLo = IntPtr.Zero; | ||
} | ||
public override string ToString() | ||
{ | ||
return this.HandleHi.ToString("x") + ":" + this.HandleLo.ToString("x"); | ||
} | ||
} | ||
[StructLayout(LayoutKind.Sequential)] | ||
internal class SecurityBufferDescriptor | ||
{ | ||
public readonly int Version; | ||
public readonly int Count; | ||
public unsafe void* UnmanagedPointer; | ||
public SecurityBufferDescriptor(int count) | ||
{ | ||
this.Version = 0; | ||
this.Count = count; | ||
this.UnmanagedPointer = null; | ||
} | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
internal struct SecurityBufferStruct | ||
{ | ||
public int count; | ||
public BufferType type; | ||
public IntPtr token; | ||
public static readonly int Size = sizeof(SecurityBufferStruct); | ||
} | ||
|
||
internal enum SecurityStatus | ||
{ | ||
OK, | ||
ContinueNeeded = 590610, | ||
CompleteNeeded, | ||
CompAndContinue, | ||
ContextExpired = 590615, | ||
CredentialsNeeded = 590624, | ||
Renegotiate, | ||
OutOfMemory = -2146893056, | ||
InvalidHandle, | ||
Unsupported, | ||
TargetUnknown, | ||
InternalError, | ||
PackageNotFound, | ||
NotOwner, | ||
CannotInstall, | ||
InvalidToken, | ||
CannotPack, | ||
QopNotSupported, | ||
NoImpersonation, | ||
LogonDenied, | ||
UnknownCredentials, | ||
NoCredentials, | ||
MessageAltered, | ||
OutOfSequence, | ||
NoAuthenticatingAuthority, | ||
IncompleteMessage = -2146893032, | ||
IncompleteCredentials = -2146893024, | ||
BufferNotEnough, | ||
WrongPrincipal, | ||
TimeSkew = -2146893020, | ||
UntrustedRoot, | ||
IllegalMessage, | ||
CertUnknown, | ||
CertExpired, | ||
AlgorithmMismatch = -2146893007, | ||
SecurityQosFailed, | ||
SmartcardLogonRequired = -2146892994, | ||
UnsupportedPreauth = -2146892989, | ||
BadBinding = -2146892986 | ||
} | ||
[Flags] | ||
internal enum ContextFlags | ||
{ | ||
Zero = 0, | ||
Delegate = 1, | ||
MutualAuth = 2, | ||
ReplayDetect = 4, | ||
SequenceDetect = 8, | ||
Confidentiality = 16, | ||
UseSessionKey = 32, | ||
AllocateMemory = 256, | ||
Connection = 2048, | ||
InitExtendedError = 16384, | ||
AcceptExtendedError = 32768, | ||
InitStream = 32768, | ||
AcceptStream = 65536, | ||
InitIntegrity = 65536, | ||
AcceptIntegrity = 131072, | ||
InitManualCredValidation = 524288, | ||
InitUseSuppliedCreds = 128, | ||
InitIdentify = 131072, | ||
AcceptIdentify = 524288, | ||
ProxyBindings = 67108864, | ||
AllowMissingBindings = 268435456, | ||
UnverifiedTargetName = 536870912 | ||
} | ||
internal enum Endianness | ||
{ | ||
Network, | ||
Native = 16 | ||
} | ||
|
||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] | ||
[DllImport("secur32.dll", ExactSpelling = true, SetLastError = true)] | ||
internal static extern int ApplyControlToken(ref SSPIHandle contextHandle, [In] [Out] SecurityBufferDescriptor outputBuffer); | ||
|
||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] | ||
[DllImport("secur32.dll", ExactSpelling = true, SetLastError = true)] | ||
internal unsafe static extern int AcceptSecurityContext(ref SSPIHandle credentialHandle, ref SSPIHandle contextHandle, [In] SecurityBufferDescriptor inputBuffer, [In] ContextFlags inFlags, [In] Endianness endianness, ref SSPIHandle outContextPtr, [In] [Out] SecurityBufferDescriptor outputBuffer, [In] [Out] ref ContextFlags attributes, out long timeStamp); | ||
|
||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] | ||
[DllImport("secur32.dll", ExactSpelling = true, SetLastError = true)] | ||
internal unsafe static extern int InitializeSecurityContextW(ref SSPIHandle credentialHandle, ref SSPIHandle contextHandle, [In] byte* targetName, [In] ContextFlags inFlags, [In] int reservedI, [In] Endianness endianness, [In] SecurityBufferDescriptor inputBuffer, [In] int reservedII, ref SSPIHandle outContextPtr, [In] [Out] SecurityBufferDescriptor outputBuffer, [In] [Out] ref ContextFlags attributes, out long timeStamp); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
|
||
namespace System.Net.Security | ||
{ | ||
public static class ReflectUtil | ||
{ | ||
public static object GetField(object obj, string fieldName) | ||
{ | ||
var tp = obj.GetType(); | ||
var info = GetAllFields(tp) | ||
.Where(f => f.Name == fieldName).SingleOrDefault(); | ||
return info?.GetValue(obj); | ||
} | ||
|
||
public static object GetProperty(object obj, string propertyName) | ||
{ | ||
var tp = obj.GetType(); | ||
var info = GetAllProperties(tp) | ||
.Where(f => f.Name == propertyName).Single(); | ||
return info.GetValue(obj, null); | ||
} | ||
public static object CallMethod(object obj, string methodName, params object[] prm) | ||
{ | ||
var tp = obj.GetType(); | ||
var info = GetAllMethods(tp) | ||
.Where(f => f.Name == methodName && f.GetParameters().Length == prm.Length).Single(); | ||
object rez = info.Invoke(obj, prm); | ||
return rez; | ||
} | ||
public static object NewInstance(Assembly assembly, string typeName, params object[] prm) | ||
{ | ||
var tp = assembly.GetType(typeName); | ||
var info = tp.GetConstructors() | ||
.Where(f => f.GetParameters().Length == prm.Length).Single(); | ||
object rez = info.Invoke(prm); | ||
return rez; | ||
} | ||
public static object InvokeStaticMethod(Assembly assembly, string typeName, string methodName, params object[] prm) | ||
{ | ||
var tp = assembly.GetType(typeName); | ||
var info = GetAllMethods(tp) | ||
.Where(f => f.IsStatic) | ||
.Where(f => f.Name == methodName && f.GetParameters().Length == prm.Length).Single(); | ||
object rez = info.Invoke(null, prm); | ||
return rez; | ||
} | ||
public static object GetEnumValue(Assembly assembly, string typeName, int value) | ||
{ | ||
var tp = assembly.GetType(typeName); | ||
object rez = Enum.ToObject(tp, value); | ||
return rez; | ||
} | ||
|
||
private static IEnumerable<FieldInfo> GetAllFields(Type t) | ||
{ | ||
if (t == null) | ||
return Enumerable.Empty<FieldInfo>(); | ||
|
||
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | | ||
BindingFlags.Static | BindingFlags.Instance | | ||
BindingFlags.DeclaredOnly; | ||
return t.GetFields(flags).Concat(GetAllFields(t.BaseType)); | ||
} | ||
private static IEnumerable<PropertyInfo> GetAllProperties(Type t) | ||
{ | ||
if (t == null) | ||
return Enumerable.Empty<PropertyInfo>(); | ||
|
||
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | | ||
BindingFlags.Static | BindingFlags.Instance | | ||
BindingFlags.DeclaredOnly; | ||
return t.GetProperties(flags).Concat(GetAllProperties(t.BaseType)); | ||
} | ||
private static IEnumerable<MethodInfo> GetAllMethods(Type t) | ||
{ | ||
if (t == null) | ||
return Enumerable.Empty<MethodInfo>(); | ||
|
||
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | | ||
BindingFlags.Static | BindingFlags.Instance | | ||
BindingFlags.DeclaredOnly; | ||
return t.GetMethods(flags).Concat(GetAllMethods(t.BaseType)); | ||
} | ||
} | ||
} |
Oops, something went wrong.