Closed mknet3 closed 4 years ago
You have two choices:
FtpLoginStateMachine
implementationImplicit TLS is usually supported by the FTP clients, but is still a non-standard feature.
You could use implicit TLS. You're basically enforcing a TLS control connection from the beginning. Take a look at the following snippet:
services.Decorate<IFtpServer>(
(ftpServer, serviceProvider) =>
{
if (options.Ftps.Implicit)
{
var authTlsOptions = serviceProvider.GetRequiredService<IOptions<AuthTlsOptions>>();
if (authTlsOptions.Value.ServerCertificate != null)
{
// Use an implicit SSL connection (without the AUTH TLS command)
ftpServer.ConfigureConnection += (s, e) =>
{
TlsEnableServerCommandHandler.EnableTlsAsync(
e.Connection,
authTlsOptions.Value.ServerCertificate,
serviceProvider.GetService<ILogger<TlsEnableServerCommandHandler>>(),
CancellationToken.None).Wait();
};
}
}
return ftpServer;
});
The snippet is called during the dependency injection configuration and does the following things:
TlsEnableServerCommandHandler.EnableTlsAsync
This enforces the usage of an encrypted FTP control connection and therefore automatically protects the login.
Be aware that the "implicit TLS" solution uses Task.Wait
, which will most likely create a deadlock when the code is used in a different environment than ASP.NET Core.
FtpLoginStateMachine
implementationFtpLoginStateMachine
ExecuteCommandAsync
method
ftpCommand.Name.Trim().ToUpperInvariant()
is USER
SelectedAuthenticationMechanism
has a null
valueservices.AddScoped<IFtpLoginStateMachine, YourFtpLoginStateMachine>();
EDIT: Some clarification
Sorry, but im trying to add implicit connection and this solution is not working to me:
HResult=0x80131500
Message=One or more errors occurred. (Status must be Running, Stopped, or Paused, but was ReadyToRun.)
Source=System.Private.CoreLib
StackTrace:
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at Ftps.FtpsConfiguration.<>c__DisplayClass2_1.<AddFtpsHostedService>b__5(Object s, ConnectionEventArgs e) in Ftps\FtpsConfiguration.cs:line 90
at FubarDev.FtpServer.FtpServer.<AddClientAsync>d__39.MoveNext()
Inner Exception 1:
InvalidOperationException: Status must be Running, Stopped, or Paused, but was ReadyToRun.
should work this solution in 3.0.1 version?
Yes, it should work. I'll take a look at it tomorrow.
3.0.2 is released and should fix the implicit TLS problem.
Thanks! It is working, but not with code above... work with next code found in samples:
services
.AddSingleton(new ImplicitFtpsControlConnectionStreamAdapterOptions(certificate))
.AddSingleton<IFtpControlStreamAdapter, ImplicitFtpsControlConnectionStreamAdapter>();
// Ensure that PROT and PBSZ commands are working.
services.Decorate<IFtpServer>(
(ftpServer, _) =>
{
ftpServer.ConfigureConnection += (s, e) =>
{
var serviceProvider = e.Connection.ConnectionServices;
var stateMachine = serviceProvider.GetRequiredService<IFtpLoginStateMachine>();
var authTlsMechanism = serviceProvider.GetRequiredService<IEnumerable<IAuthenticationMechanism>>()
.Single(x => x.CanHandle("TLS"));
stateMachine.Activate(authTlsMechanism);
};
return ftpServer;
});
return services;
}
private class ImplicitFtpsControlConnectionStreamAdapterOptions
{
public ImplicitFtpsControlConnectionStreamAdapterOptions(X509Certificate2 certificate)
{
Certificate = certificate;
}
public X509Certificate2 Certificate { get; }
}
private class ImplicitFtpsControlConnectionStreamAdapter : IFtpControlStreamAdapter
{
private readonly ImplicitFtpsControlConnectionStreamAdapterOptions _options;
private readonly ISslStreamWrapperFactory _sslStreamWrapperFactory;
public ImplicitFtpsControlConnectionStreamAdapter(
ImplicitFtpsControlConnectionStreamAdapterOptions options,
ISslStreamWrapperFactory sslStreamWrapperFactory)
{
_options = options;
_sslStreamWrapperFactory = sslStreamWrapperFactory;
}
/// <inheritdoc />
public Task<Stream> WrapAsync(Stream stream, CancellationToken cancellationToken)
{
return _sslStreamWrapperFactory.WrapStreamAsync(stream, false, _options.Certificate, cancellationToken);
}
}
}
That's strange, but it's good that you solved the problem.
EDIT: I somehow used old code as an example. I don't know why I mixed this up, sorry 😞
It is possible to block all connections without certificate before credentials are sent by the client?
Is this functionality supported?