Skip to content

Commit fc47abc

Browse files
authored
DEV-311 ldap-bind-timeout (#55)
1 parent 8cd376f commit fc47abc

File tree

11 files changed

+65
-37
lines changed

11 files changed

+65
-37
lines changed

MultiFactor.Radius.Adapter/Configuration/ClientConfiguration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,5 +239,10 @@ public bool ShouldLoadUserGroups()
239239
/// Overrides the root-level config.
240240
/// </summary>
241241
public RandomWaiterConfig InvalidCredentialDelay { get; internal set; }
242+
243+
/// <summary>
244+
/// Ldap connection timeout
245+
/// </summary>
246+
public TimeSpan LdapBindTimeout { get; set; } = new TimeSpan(0, 0, 30);
242247
}
243248
}

MultiFactor.Radius.Adapter/Configuration/ServiceConfiguration.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ public static ClientConfiguration LoadClientSettings(string name,
359359
var bypassSecondFactorWhenApiUnreachableSetting = appSettings.Settings["bypass-second-factor-when-api-unreachable"]?.Value;
360360
var multiFactorApiKeySetting = appSettings.Settings["multifactor-nas-identifier"]?.Value;
361361
var multiFactorApiSecretSetting = appSettings.Settings["multifactor-shared-secret"]?.Value;
362+
var ldapBindTimeoutSetting = appSettings.Settings["ldap-bind-timeout"]?.Value;
362363

363364
if (string.IsNullOrEmpty(firstFactorAuthenticationSourceSettings))
364365
{
@@ -496,6 +497,14 @@ public static ClientConfiguration LoadClientSettings(string name,
496497
{
497498
throw new Exception($"Configuration error: to enable pre-auth second factor for this client please set 'invalid-credential-delay' min value to 2 or more");
498499
}
500+
501+
if (TimeSpan.TryParseExact(ldapBindTimeoutSetting, @"hh\:mm\:ss", null, System.Globalization.TimeSpanStyles.None, out var ldapBindTimeout))
502+
{
503+
if (ldapBindTimeout > TimeSpan.Zero)
504+
{
505+
configuration.LdapBindTimeout = ldapBindTimeout;
506+
}
507+
}
499508

500509
return configuration;
501510
}

MultiFactor.Radius.Adapter/Server/FirstAuthFactorProcessing/AnonymousProcessor.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ private Dictionary<string, string[]> LoadRequiredAttributes(PendingRequest reque
8484
_logger.Debug($"Loading attributes for user '{{user:l}}' at {domainIdentity}", user.Name);
8585
using (var connection = _connectionFactory.CreateAsCurrentProcessUser(domain))
8686
{
87+
connection.Bind();
8788
var schema = _metadataCache.Get(
8889
request.Configuration.Name,
8990
domainIdentity,
@@ -95,18 +96,15 @@ private Dictionary<string, string[]> LoadRequiredAttributes(PendingRequest reque
9596
catch (UserDomainNotPermittedException ex)
9697
{
9798
_logger.Warning(ex.Message);
98-
continue;
9999
}
100100
catch (UserNameFormatException ex)
101101
{
102102
_logger.Warning(ex.Message);
103-
continue;
104103
}
105104
catch (Exception ex)
106105
{
107106
_logger.Error(ex, $"Loading attributes of user '{{user:l}}' at {domainIdentity} failed", request.UserName);
108107
_logger.Information("Run MultiFactor.Raduis.Adapter as user with domain read permissions (basically any domain user)");
109-
continue;
110108
}
111109
}
112110

MultiFactor.Radius.Adapter/Server/FirstAuthFactorProcessing/RadiusFirstAuthFactorProcessor.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ private Dictionary<string, string[]> LoadRequiredAttributes(PendingRequest reque
143143
_logger.Debug($"Loading attributes for user '{{user:l}}' at {domainIdentity}", user.Name);
144144
using (var connection = _connectionFactory.CreateAsCurrentProcessUser(domain))
145145
{
146+
connection.Bind();
146147
var schema = _metadataCache.Get(
147148
request.Configuration.Name,
148149
domainIdentity,
@@ -154,18 +155,15 @@ private Dictionary<string, string[]> LoadRequiredAttributes(PendingRequest reque
154155
catch (UserDomainNotPermittedException ex)
155156
{
156157
_logger.Warning(ex.Message);
157-
continue;
158158
}
159159
catch (UserNameFormatException ex)
160160
{
161161
_logger.Warning(ex.Message);
162-
continue;
163162
}
164163
catch (Exception ex)
165164
{
166165
_logger.Error(ex, $"Loading attributes of user '{{user:l}}' at {domainIdentity} failed", userName);
167166
_logger.Information("Run MultiFactor.Raduis.Adapter as user with domain read permissions (basically any domain user)");
168-
continue;
169167
}
170168
}
171169

MultiFactor.Radius.Adapter/Services/ActiveDirectory/ActiveDirectoryService.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ public bool ChangePassword(PendingRequest request, string currentPassword, out b
139139

140140
using (var connection = _connectionFactory.CreateAsCurrentProcessUser(_domain))
141141
{
142+
connection.Bind();
142143
var domain = LdapIdentity.FqdnToDn(_domain);
143144
var schema = _forestMetadataCache.Get(
144145
request.Configuration.Name,
@@ -196,6 +197,7 @@ private bool VerifyMembership(ClientConfiguration clientConfig, LdapIdentity use
196197

197198
using (var connection = _connectionFactory.CreateAsCurrentProcessUser(_domain))
198199
{
200+
connection.Bind();
199201
var forestSchema = _forestMetadataCache.Get(
200202
clientConfig.Name,
201203
domain,
@@ -271,10 +273,13 @@ private void VerifyCredential(LdapIdentity user, PendingRequest request)
271273
{
272274
_logger.Debug("Verifying user '{User:l}' credential and status at {Domain:l}", user, _domain);
273275

274-
using (_ = _connectionFactory.Create(_domain, user.Name, request.Passphrase.Password))
276+
using (var ldapConnection = _connectionFactory.Create(_domain, user.Name, request.Passphrase.Password, request.Configuration.LdapBindTimeout))
275277
{
276-
_logger.Information("User '{User:l}' credential and status verified successfully in {Domain:l}", user, _domain);
278+
_logger.Debug("Start bind to {Domain} as '{User}'", _domain, user.Name);
279+
ldapConnection.Bind();
277280
}
281+
282+
_logger.Information("User '{User:l}' credential and status verified successfully in {Domain:l}", user, _domain);
278283
}
279284

280285
private bool IsMemberOf(LdapProfile profile, string group)

MultiFactor.Radius.Adapter/Services/ActiveDirectory/MembershipVerification/ActiveDirectoryMembershipVerifier.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public ComplexMembershipVerificationResult VerifyMembership(PendingRequest reque
7070

7171
using (var connection = _connectionFactory.CreateAsCurrentProcessUser(domain))
7272
{
73+
connection.Bind();
7374
var schema = _metadataCache.Get(
7475
request.Configuration.Name,
7576
domainIdentity,
@@ -100,15 +101,13 @@ public ComplexMembershipVerificationResult VerifyMembership(PendingRequest reque
100101
result.AddDomainResult(MembershipVerificationResult.Create(domainIdentity)
101102
.SetSuccess(false)
102103
.Build());
103-
continue;
104104
}
105105
catch (UserNameFormatException ex)
106106
{
107107
_logger.Warning(ex.Message);
108108
result.AddDomainResult(MembershipVerificationResult.Create(domainIdentity)
109109
.SetSuccess(false)
110110
.Build());
111-
continue;
112111
}
113112
catch (Exception ex)
114113
{
@@ -117,7 +116,6 @@ public ComplexMembershipVerificationResult VerifyMembership(PendingRequest reque
117116
result.AddDomainResult(MembershipVerificationResult.Create(domainIdentity)
118117
.SetSuccess(false)
119118
.Build());
120-
continue;
121119
}
122120
}
123121

MultiFactor.Radius.Adapter/Services/Ldap/AdLdsService.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,11 @@ public bool VerifyCredentialAndMembership(PendingRequest request)
5454
{
5555
_logger.Debug($"Verifying user '{logonName}' credential and status at {ldapUrl}");
5656

57-
using (var connection = _connectionFactory.CreateForAdlds(ldapUrl, logonName, request.Passphrase.Password))
57+
using (var connection = _connectionFactory.CreateForAdlds(ldapUrl, logonName, request.Passphrase.Password, request.Configuration.LdapBindTimeout))
5858
{
59+
var isLdaps = ldapUrl.Scheme.ToLower() == "ldaps";
60+
_logger.Debug("Start bind to {Scheme}://{Server} as '{User}'", isLdaps ? "LDAPS" : "LDAP", ldapUrl.Authority, logonName);
61+
connection.Bind();
5962
_logger.Information($"User '{user.Name}' credential and status verified successfully at {ldapUrl}");
6063

6164
return true;

MultiFactor.Radius.Adapter/Services/Ldap/LdapConnectionFactory.cs

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,42 +11,44 @@ public class LdapConnectionFactory
1111
{
1212
private readonly ILogger _logger;
1313
private readonly PassedLineArguments _lineArguments;
14-
14+
1515
public LdapConnectionFactory(ILogger logger, PassedLineArguments lineArguments)
1616
{
1717
_logger = logger;
1818
_lineArguments = lineArguments;
1919
}
2020

2121
/// <summary>
22-
/// Creates new connection to a ldap domain, binds as a current process user credential using Negotiate auth type and returns it.
22+
/// Creates new connection to a ldap domain using a current process user credential with Negotiate auth type.
2323
/// </summary>
2424
/// <param name="domain">LDAP domain</param>
25+
/// <param name="connectionTimeout">Connection timeout.</param>
2526
/// <returns></returns>
2627
/// <exception cref="ArgumentException"></exception>
27-
public LdapConnection CreateAsCurrentProcessUser(string domain)
28+
public LdapConnection CreateAsCurrentProcessUser(string domain, TimeSpan? connectionTimeout = null)
2829
{
2930
if (string.IsNullOrWhiteSpace(domain))
3031
{
3132
throw new ArgumentException($"'{nameof(domain)}' cannot be null or whitespace.", nameof(domain));
3233
}
33-
34-
_logger.Debug("Start connection to {Domain}", domain);
34+
3535
var connection = new LdapConnection(domain);
3636
connection.SessionOptions.ProtocolVersion = 3;
37-
connection.SessionOptions.RootDseCache = true;
38-
37+
connection.SessionOptions.RootDseCache = true;
38+
if (connectionTimeout.HasValue)
39+
{
40+
connection.Timeout = connectionTimeout.Value;
41+
}
3942
if (_lineArguments.Has(KnownLineArg.ACT_AS_USER) && _lineArguments.Has(KnownLineArg.ACT_AS_USER_PWD))
4043
{
4144
var u = _lineArguments[KnownLineArg.ACT_AS_USER];
4245
var p = _lineArguments[KnownLineArg.ACT_AS_USER_PWD];
43-
_logger.Debug("Start bind to {Domain} with a passed user credential {u:l}:{p:l}", domain, u, HidePwd(p));
44-
connection.Bind(new NetworkCredential(u, p));
46+
_logger.Debug("Connection was created to {Domain} with a passed user credential {u:l}:{p:l}", domain, u, HidePwd(p));
47+
connection.Credential = new NetworkCredential(u, p);
4548
}
4649
else
4750
{
48-
_logger.Debug("Start bind to {Domain} as a process user", domain);
49-
connection.Bind();
51+
_logger.Debug("Connection was created to {Domain} with credential of a process user", domain);
5052
}
5153

5254
return connection;
@@ -63,15 +65,16 @@ private static string HidePwd(string pwd)
6365
}
6466

6567
/// <summary>
66-
/// Creates new connection to a ldap domain, binds with the specified credential using Negotiate auth type and returns it.
68+
/// Creates new connection to a ldap domain with the specified credential using Negotiate auth type.
6769
/// </summary>
6870
/// <param name="domain">LDAP domain.</param>
6971
/// <param name="userName">Username.</param>
7072
/// <param name="password">Password.</param>
73+
/// <param name="connectionTimeout">Connection timeout.</param>
7174
/// <returns></returns>
7275
/// <exception cref="ArgumentException"></exception>
7376
/// <exception cref="ArgumentNullException"></exception>
74-
public LdapConnection Create(string domain, string userName, string password)
77+
public LdapConnection Create(string domain, string userName, string password, TimeSpan? connectionTimeout = null)
7578
{
7679
if (string.IsNullOrWhiteSpace(domain))
7780
{
@@ -87,15 +90,15 @@ public LdapConnection Create(string domain, string userName, string password)
8790
{
8891
throw new ArgumentNullException(nameof(password));
8992
}
90-
91-
_logger.Debug("Start connection to {Domain}", domain);
93+
9294
var connection = new LdapConnection(domain);
9395
connection.Credential = new NetworkCredential(userName, password);
9496
connection.SessionOptions.ProtocolVersion = 3;
9597
connection.SessionOptions.RootDseCache = true;
96-
97-
_logger.Debug("Start bind to {Domain} as '{User}'", domain, userName);
98-
connection.Bind();
98+
if (connectionTimeout.HasValue)
99+
{
100+
connection.Timeout = connectionTimeout.Value;
101+
}
99102
return connection;
100103
}
101104

@@ -105,10 +108,11 @@ public LdapConnection Create(string domain, string userName, string password)
105108
/// <param name="ldapServer">LDAP uri.</param>
106109
/// <param name="logonName">Logon name.</param>
107110
/// <param name="password">Password.</param>
111+
/// <param name="connectionTimeout">Connection timeout.</param>
108112
/// <returns></returns>
109113
/// <exception cref="ArgumentNullException"></exception>
110114
/// <exception cref="ArgumentException"></exception>
111-
public LdapConnection CreateForAdlds(Uri ldapServer, string logonName, string password)
115+
public LdapConnection CreateForAdlds(Uri ldapServer, string logonName, string password, TimeSpan? connectionTimeout = null)
112116
{
113117
if (ldapServer is null)
114118
{
@@ -124,8 +128,7 @@ public LdapConnection CreateForAdlds(Uri ldapServer, string logonName, string pa
124128
{
125129
throw new ArgumentNullException(nameof(password));
126130
}
127-
128-
_logger.Debug("Start connection to {Server}", ldapServer.Authority);
131+
129132
var connection = new LdapConnection(ldapServer.Authority);
130133
connection.Credential = new NetworkCredential(logonName, password);
131134
connection.SessionOptions.RootDseCache = true;
@@ -136,10 +139,11 @@ public LdapConnection CreateForAdlds(Uri ldapServer, string logonName, string pa
136139
{
137140
connection.SessionOptions.SecureSocketLayer = true;
138141
}
142+
if (connectionTimeout.HasValue)
143+
{
144+
connection.Timeout = connectionTimeout.Value;
145+
}
139146

140-
_logger.Debug("Start bind to {Scheme}://{Server} as '{User}'",
141-
isLdaps ? "LDAPS" : "LDAP", ldapServer.Authority, logonName);
142-
connection.Bind();
143147
return connection;
144148
}
145149
}

MultiFactor.Radius.Adapter/Services/NetbiosService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ private string ResolveDomainByNetBios(ClientConfiguration clientConfig, string f
5959
_logger.Information("Degradation of the domain resolving method for {UserName:l}", fullUserName);
6060
using (var connection = _connectionFactory.CreateAsCurrentProcessUser(domain))
6161
{
62+
_logger.Debug("Start connection to {Domain}", domain);
63+
connection.Bind();
6264
var dnDomain = LdapIdentity.FqdnToDn(domain);
6365
var schema = _forestMetadataCache.Get(
6466
clientConfig.Name,

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ The component's parameters are stored in ```MultiFactor.Radius.Adapter.exe.confi
9494
<!--Timeout for requests in the Multifactor API, the minimum value is 65 seconds-->
9595
<add key="multifactor-api-timeout" value="00:01:05"/>
9696

97+
<!-- [Optional] Ldap bind request timeout. The default value is 30 seconds. -->
98+
<add key="ldap-bind-timeout" value="00:00:30"/>
99+
97100
<!-- Logging level: 'Debug', 'Info', 'Warn', 'Error' -->
98101
<add key="logging-level" value="Debug"/>
99102

0 commit comments

Comments
 (0)