Open sukeerthiadiga opened 4 years ago
Hi! I just stumbled upon this. Any update or progress on this topic :)
We've been trying to make this work as well. We use GSS-SPNEGO and code that looks like this:
def sasl_gss_spnego(user, password, domain=nil)
raise Net::LDAP::Error, "Invalid binding information" unless user && password
challenge_response = proc do |challenge|
challenge.force_encoding(Encoding::BINARY)
t2_msg = Net::NTLM::Message.parse(challenge)
auth_params = {:user => user, :password => password}
auth_params[:domain] = domain unless domain.blank?
t3_msg = t2_msg.response(auth_params, {:ntlmv2 => true})
t3_msg.user.force_encoding(Encoding::BINARY)
t3_msg.serialize
end
{
:mechanism => "GSS-SPNEGO",
:initial_credential => Net::NTLM::Message::Type1.new.serialize,
:challenge_response => challenge_response,
:method => :sasl
}
end
Using the default Type1 flags (Net::NTLM::Message::Type1.new), it does not work. If I manipulate the flags, I can get it to be successful when I ask for sign and seal, but when watching it on wireshark, I don't see how it is any different than the default flags.
I have yet to require channel binding and test it however: https://support.microsoft.com/en-us/help/4034879/how-to-add-the-ldapenforcechannelbinding-registry-entry
I will also try your code.
Microsoft enforce channel binding or ldaps with the KB of march 2020.
I have the same issue AcceptSecurityContext error
:cry:
According the documentation here : https://ldap.com/ldap-result-code-reference-core-ldapv3-result-codes/#rc-invalidCredentials
invalidCredentials (49) Applicable operation types: bind The invalidCredentials result code indicates that the client attempted to bind with a set of credentials that cannot be used to authenticate. Some of the potential reasons that this result code might be returned are:
- The bind request targeted a user that does not exist.
- The client tried to authenticate with an incorrect password.
- The client tried to authenticate with a SASL bind request that included non-password credentials that could not be successfully verified.
- The bind request targeted a user that is not permitted to authenticate for some reason (for example, because the account has been locked, the user’s password has expired, etc.).
I have done the implementation of the net-ldap-gss-spnego
gem.
It's work for me with default AD settings.
To handle the channel binding you can use Net::NTLM::Client, it creates type3 message using challenge(type2) and channel binding. For creating channel binding you need server certificate that can be fetched from the type1 response or @connection
ntlm_client = Net::NTLM::Client.new(user, pass, {domain: "XYZ"}) binding = @connection.peer_cert.nil? ? nil : Net::NTLM::ChannelBinding.create(@connection.peer_cert) type3 = ntlm_client.init_context(Base64.encode64(type2_challenge), binding)
@raj-sharan could you please "beautify" your code example?
it's a bit hard to understand where exactly this defined type3
message needs to be added :slightly_smiling_face:
I tried ruby-net-ldap-gss-spnego
gem, but it does not fully work for me:
If on LDAP server-side, configuration flag EnforceChannelBinding
is set to 1
-
authentication using "ruby-net-ldap-gss-spnego" adapter works fine.
But, according to Microsoft recommendations EnforceChannelBinding
value should be changed to 2
. In this case, authentication is broken: LDAP server returns "LDAP Error-code: 49 (Invalid Credentials)" without more details. (username and password are 100% correct, tested multiple times with multiple users)
BTW, Is SSL/TLS config mandatory for ChannelBinding? Should it work without SSL certificates? Using Ldap::Client these encryption options etc:
encryption: {
method: :simple_tls,
tls_options: {verify_mode: OpenSSL::SSL::VERIFY_NONE}
}
I tried
ruby-net-ldap-gss-spnego
gem, but it does not fully work for me:If on LDAP server-side, configuration flag
EnforceChannelBinding
is set to1
- authentication using "ruby-net-ldap-gss-spnego" adapter works fine.But, according to Microsoft recommendations
EnforceChannelBinding
value should be changed to2
. In this case, authentication is broken: LDAP server returns "LDAP Error-code: 49 (Invalid Credentials)" without more details. (username and password are 100% correct, tested multiple times with multiple users)
Thanks for this comment, I don't have time this week to fix the ruby-net-ldap-gss-spnego
but I open for any PR to solve this issue.
Channel binding is an SSL/TLS concept only.
LDAP signing and sealing is a non-secure only concept too.
On Thu, Jun 18, 2020 at 3:26 AM Bohdan Malets notifications@github.com wrote:
I tried ruby-net-ldap-gss-spnego gem, but it does not fully work for me:
If on LDAP server-side, configuration flag EnforceChannelBinding is set to 1 - authentication using "ruby-net-ldap-gss-spnego" adapter works fine.
But, according to Microsoft recommendations https://support.microsoft.com/en-us/help/4034879/how-to-add-the-ldapenforcechannelbinding-registry-entry EnforceChannelBinding value should be changed to 2. In this case, authentication is broken: LDAP server returns "LDAP Error-code: 49 (Invalid Credentials)" without more details
BTW, Is SSL/TLS config mandatory for ChannelBinding? Should it work without SSL certificates? Using Ldap::Client these encryption options etc:
encryption: { method: :timple_tls, tls_options: {verify_mode: OpenSSL::SSL::VERIFY_NONE} }
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ruby-ldap/ruby-net-ldap/issues/339#issuecomment-645929293, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACXAZGT23B2NCDTPH7QLRTRXHTTRANCNFSM4J33PZEA .
As Channel binding is an SSL/TLS concept only - I added SSL/TLS settings to my LDAP configurations. Unfortunately, I am not "MS guru" to understand and even get more verbose logs from LDAP server side :( But here is a more detailed example when signing and binding don't want to work together:
require 'net/ldap'
require 'net/ldap/auth_adapter/gss_spnego'
ldap_options = {
hosts: [
['ldap_hostname.lan', 636]
],
base: 'DC=ldap_hostname,DC=lan',
encryption: {
method: :simple_tls,
tls_options: {
ca_file: '/full/path/to/cert.pem',
ssl_version: 'TLSv1_2'
}
},
auth: {
auth_method: 'GSS-SPNEGO',
username: 'bmalets', # username, not DN
password: 'password'
}
}
ldap = Net::LDAP.new(ldap_options)
# When LDAP server registry settings are:
# LDAPServerIntegrity = 2 (Always sign (Level 2))
# LdapEnforceChannelBinding = 1 (DWORD value: 1 indicates enabled, when supported. All clients that are running on a version of Windows that has been updated to support channel binding tokens (CBT) must provide channel binding information to the server. Clients that are running a version of Windows that has not been updated to support CBT do not have to do so. This is an intermediate option that allows for application compatibility.)
ldap.bind
ldap.get_operation_result # => 0. Success
# When LDAP server registry settings are:
# LDAPServerIntegrity = 2 (Always sign (Level 2))
# LdapEnforceChannelBinding = 2 (DWORD value: 2 indicates enabled, always. All clients must provide channel binding information. The server rejects authentication requests from clients that do not do so.)
ldap.bind
ldap.get_operation_result # => Error-code: 49. Invalid Credentials
@raj-sharan could you please "beautify" your code example? it's a bit hard to understand where exactly this defined
type3
message needs to be added 🙂
@bmalets, You should try this It should work with LdapEnforceChannelBinding = 2
require 'net/ldap'
require 'net/ntlm'
require 'net/ldap/auth_adapter/gss_spnego'
#Patch bind method to share the peer_cert
module Net
class LDAP
class AuthAdapter
class Sasl
# Patch this method
def bind(auth)
...........
...........
n = 0
loop do
..........
..........
# Instead
# cred = chall.call(pdu.result_server_sasl_creds)
# Add following code
cred =
if @connection.socket.respond_to?(:peer_cert)
chall.call(pdu.result_server_sasl_creds, @connection.socket.peer_cert)
else
chall.call(pdu.result_server_sasl_creds)
end
end
raise Net::LDAP::SASLChallengeOverflowError, "why are we here?"
end
end
end
end
end
def sasl_gss_spnego(credential)
challenge_response = proc do |challenge, peer_cert|
challenge.force_encoding(Encoding::BINARY)
ntlm_client = Net::NTLM::Client.new(credential[:user], credential[:password], {domain: credential[:domain]})
encoded_challenge = Base64.encode64(challenge)
channel_binding = peer_cert ? Net::NTLM::ChannelBinding.create(peer_cert) : nil
t3_msg = ntlm_client.init_context(encoded_challenge, channel_binding)
t3_msg.user.force_encoding(Encoding::BINARY)
t3_msg.serialize
end
{
:mechanism => "GSS-SPNEGO",
:initial_credential => Net::NTLM::Message::Type1.new.serialize,
:challenge_response => challenge_response,
:method => :sasl
}
end
ldap_options = {
hosts: [
['ldap_hostname.lan', 636]
],
base: 'DC=ldap_hostname,DC=lan',
encryption: {
method: :simple_tls,
tls_options: {
ca_file: '/full/path/to/cert.pem',
ssl_version: 'TLSv1_2'
}
},
auth: sasl_gss_spnego({:user => "bmalets", :password => "password", :domain => 'DOMAIN_NAME'})
}
ldap = Net::LDAP.new(ldap_options)
ldap.bind
ldap.get_operation_result
LDAPServerIntegrity and LdapEnforceChannelBinding are mutually exclusive. The reason your example didn't work is you changed LdapEnforceChannelBinding from 1 to 2. If you repeat that test with only changing LDAPServerIntegrity from 1 to 2 but leaving LdapEnforceChannelBinding at 1, it will work and prove that LDAPServerIntegrity doesn't matter when using SSL/TLS (which is what MS says is the case).
On Mon, Jun 22, 2020 at 3:52 AM Bohdan Malets notifications@github.com wrote:
More detailed example when signing and binding don't want to work together:
require 'net/ldap'require 'net/ldap/auth_adapter/gss_spnego' ldap_options = { hosts: [ ['ldap_hostname.lan', 636] ], base: 'DC=ldap_hostname,DC=lan', encryption: { method: :simple_tls, tls_options: { ca_file: '/full/path/to/cert.pem', ssl_version: 'TLSv1_2' } }, auth: { auth_method: :gss_spnego, username: 'bmalets' # username, not DN password: 'password' }} ldap = Net::LDAP.new(ldap_options)
When LDAP server registry settings are:# LDAPServerIntegrity = 2 (Always sign (Level 2))# LdapEnforceChannelBinding = 1 (DWORD value: 1 indicates enabled, when supported. All clients that are running on a version of Windows that has been updated to support channel binding tokens (CBT) must provide channel binding information to the server. Clients that are running a version of Windows that has not been updated to support CBT do not have to do so. This is an intermediate option that allows for application compatibility.)
ldap.bindldap.get_operation_result # => 0. Success
When LDAP server registry settings are:# LDAPServerIntegrity = 2 (Always sign (Level 2))# LdapEnforceChannelBinding = 2 (DWORD value: 2 indicates enabled, always. All clients must provide channel binding information. The server rejects authentication requests from clients that do not do so.)
ldap.bindldap.get_operation_result # => Error-code: 49. Invalid Credentials
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ruby-ldap/ruby-net-ldap/issues/339#issuecomment-647441592, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACXAZH7V2CLWTXSMJWWVYDRX4ZYLANCNFSM4J33PZEA .
@smlsml, thank you for your answer. Did not find any info in Windows Server documentation that LDAPServerIntegrity and LdapEnforceChannelBinding are mutually exclusive... Could you please share some links about this if you have one?
I got only one page about MS update recommendations with this message:
To maximize compatibility with older operating system versions (Windows Server 2008 and earlier versions), we recommend that you enable this setting with a value of 1.
But LDAP server that I am trying to connect is MS 2012, so it definitely does not affect my case.
So, "49. Invalid Credentials" response is an expected behavior when LDAPServerIntegrity = 2
and LdapEnforceChannelBinding = 2
.
And there are only two ways to make it work:
LDAPServerIntegrity = 1
and LdapEnforceChannelBinding = 2
LDAPServerIntegrity = 2
and LdapEnforceChannelBinding = 1
Is it correct? :thinking:
Trying to connect to AD by enforcing the LDAP Channel Binding ()
Ending up with the below error