improved the SASL authentication method, and added a simple accessor for
MS/AD authentication via GSS-SPNEGO.
This commit is contained in:
parent
de933da958
commit
7e32ba177c
110
lib/net/ldap.rb
110
lib/net/ldap.rb
|
@ -1052,6 +1052,7 @@ module Net
|
||||||
class Connection # :nodoc:
|
class Connection # :nodoc:
|
||||||
|
|
||||||
LdapVersion = 3
|
LdapVersion = 3
|
||||||
|
MaxSaslChallenges = 10
|
||||||
|
|
||||||
|
|
||||||
#--
|
#--
|
||||||
|
@ -1131,21 +1132,30 @@ module Net
|
||||||
# bind
|
# bind
|
||||||
#
|
#
|
||||||
def bind auth
|
def bind auth
|
||||||
|
|
||||||
meth = auth[:method]
|
meth = auth[:method]
|
||||||
user,psw = "",""
|
if [:simple, :anonymous, :anon].include?( meth )
|
||||||
|
bind_simple auth
|
||||||
if meth == :simple
|
|
||||||
user,psw = [auth[:username] || auth[:dn], auth[:password]]
|
|
||||||
elsif meth == :sasl
|
elsif meth == :sasl
|
||||||
return bind_sasl( auth ) # Note the early return.
|
bind_sasl( auth )
|
||||||
|
elsif meth == :gss_spnego
|
||||||
|
bind_gss_spnego( auth )
|
||||||
|
else
|
||||||
|
raise LdapError.new( "unsupported auth method (#{meth})" )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#--
|
||||||
|
# bind_simple
|
||||||
|
# Implements a simple user/psw authentication.
|
||||||
|
# Accessed by calling #bind with a method of :simple or :anonymous.
|
||||||
|
#
|
||||||
|
def bind_simple auth
|
||||||
|
user,psw = if auth[:method] == :simple
|
||||||
|
[auth[:username] || auth[:dn], auth[:password]]
|
||||||
|
else
|
||||||
|
["",""]
|
||||||
end
|
end
|
||||||
|
|
||||||
#user,psw = if auth[:method] == :anonymous
|
|
||||||
# ["",""]
|
|
||||||
#when :simple
|
|
||||||
# [auth[:username] || auth[:dn], auth[:password]]
|
|
||||||
#end
|
|
||||||
raise LdapError.new( "invalid binding information" ) unless (user && psw)
|
raise LdapError.new( "invalid binding information" ) unless (user && psw)
|
||||||
|
|
||||||
msgid = next_msgid.to_ber
|
msgid = next_msgid.to_ber
|
||||||
|
@ -1159,37 +1169,71 @@ module Net
|
||||||
|
|
||||||
#--
|
#--
|
||||||
# bind_sasl
|
# bind_sasl
|
||||||
|
# Required parameters: :mechanism, :initial_credential and :challenge_response
|
||||||
|
# Mechanism is a string value that will be passed in the SASL-packet's "mechanism" field.
|
||||||
|
# Initial credential is most likely a string. It's passed in the initial BindRequest
|
||||||
|
# that goes to the server. In some protocols, it may be empty.
|
||||||
|
# Challenge-response is a Ruby proc that takes a single parameter and returns an object
|
||||||
|
# that will typically be a string. The challenge-response block is called when the server
|
||||||
|
# returns a BindResponse with a result code of 14 (saslBindInProgress). The challenge-response
|
||||||
|
# block receives a parameter containing the data returned by the server in the saslServerCreds
|
||||||
|
# field of the LDAP BindResponse packet. The challenge-response block may be called multiple
|
||||||
|
# times during the course of a SASL authentication, and each time it must return a value
|
||||||
|
# that will be passed back to the server as the credential data in the next BindRequest packet.
|
||||||
|
#
|
||||||
|
def bind_sasl auth
|
||||||
|
mech,cred,chall = auth[:mechanism],auth[:initial_credential],auth[:challenge_response]
|
||||||
|
raise LdapError.new( "invalid binding information" ) unless (mech && cred && chall)
|
||||||
|
|
||||||
|
n = 0
|
||||||
|
loop {
|
||||||
|
msgid = next_msgid.to_ber
|
||||||
|
sasl = [mech.to_ber, cred.to_ber].to_ber_contextspecific(3)
|
||||||
|
request = [LdapVersion.to_ber, "".to_ber, sasl].to_ber_appsequence(0)
|
||||||
|
request_pkt = [msgid, request].to_ber_sequence
|
||||||
|
@conn.write request_pkt
|
||||||
|
|
||||||
|
(be = @conn.read_ber(AsnSyntax) and pdu = Net::LdapPdu.new( be )) or raise LdapError.new( "no bind result" )
|
||||||
|
return pdu.result_code unless pdu.result_code == 14 # saslBindInProgress
|
||||||
|
raise LdapError.new("sasl-challenge overflow") if ((n += 1) > MaxSaslChallenges)
|
||||||
|
|
||||||
|
cred = chall.call( pdu.result_server_sasl_creds )
|
||||||
|
}
|
||||||
|
|
||||||
|
raise LdapError.new( "why are we here?")
|
||||||
|
end
|
||||||
|
private :bind_sasl
|
||||||
|
|
||||||
|
#--
|
||||||
|
# bind_gss_spnego
|
||||||
# PROVISIONAL, only for testing SASL implementations. DON'T USE THIS YET.
|
# PROVISIONAL, only for testing SASL implementations. DON'T USE THIS YET.
|
||||||
# Uses Kohei Kajimoto's Ruby/NTLM. We have to find a clean way to integrate it without
|
# Uses Kohei Kajimoto's Ruby/NTLM. We have to find a clean way to integrate it without
|
||||||
# introducing an external dependency.
|
# introducing an external dependency.
|
||||||
# This is also wrong for another reason: we're assuming Microsoft GSSAPI negotiation.
|
# This authentication method is accessed by calling #bind with a :method parameter of
|
||||||
# Wee need to introduce some extra parameters to select that mode.
|
# :gss_spnego. It requires :username and :password attributes, just like the :simple
|
||||||
def bind_sasl auth
|
# authentication method. It performs a GSS-SPNEGO authentication with the server, which
|
||||||
|
# is presumed to be a Microsoft Active Directory.
|
||||||
|
#
|
||||||
|
def bind_gss_spnego auth
|
||||||
require 'ntlm.rb'
|
require 'ntlm.rb'
|
||||||
|
|
||||||
user,psw = [auth[:username] || auth[:dn], auth[:password]]
|
user,psw = [auth[:username] || auth[:dn], auth[:password]]
|
||||||
raise LdapError.new( "invalid binding information" ) unless (user && psw)
|
raise LdapError.new( "invalid binding information" ) unless (user && psw)
|
||||||
msgid = next_msgid.to_ber
|
|
||||||
sasl = ["GSS-SPNEGO".to_ber, NTLM::Message::Type1.new.serialize.to_ber].to_ber_contextspecific(3)
|
|
||||||
request = [LdapVersion.to_ber, "".to_ber, sasl].to_ber_appsequence(0)
|
|
||||||
request_pkt = [msgid, request].to_ber_sequence
|
|
||||||
@conn.write request_pkt
|
|
||||||
|
|
||||||
(be = @conn.read_ber(AsnSyntax) and pdu = Net::LdapPdu.new( be )) or raise LdapError.new( "no bind result" )
|
nego = proc {|challenge|
|
||||||
return pdu.result_code unless pdu.result_code == 14 # saslBindInProgress
|
t2_msg = NTLM::Message.parse( challenge )
|
||||||
|
t3_msg = t2_msg.response( {:user => user, :password => psw}, {:ntlmv2 => true} )
|
||||||
|
t3_msg.serialize
|
||||||
|
}
|
||||||
|
|
||||||
t2 = NTLM::Message.parse( pdu.result_server_sasl_creds ) # WARNING, can Kajimoto's code throw nasty errors?
|
bind_sasl( {
|
||||||
t3 = t2.response( {:user => user, :password => psw}, {:ntlmv2 => true} )
|
:method => :sasl,
|
||||||
|
:mechanism => "GSS-SPNEGO",
|
||||||
msgid = next_msgid.to_ber
|
:initial_credential => NTLM::Message::Type1.new.serialize,
|
||||||
sasl = ["GSS-SPNEGO".to_ber, t3.serialize.to_ber].to_ber_contextspecific(3)
|
:challenge_response => nego
|
||||||
request = [LdapVersion.to_ber, "".to_ber, sasl].to_ber_appsequence(0)
|
})
|
||||||
request_pkt = [msgid, request].to_ber_sequence
|
|
||||||
@conn.write request_pkt
|
|
||||||
|
|
||||||
(be = @conn.read_ber(AsnSyntax) and pdu = Net::LdapPdu.new( be )) or raise LdapError.new( "no bind result" )
|
|
||||||
pdu.result_code
|
|
||||||
end
|
end
|
||||||
private :bind_sasl
|
private :bind_gss_spnego
|
||||||
|
|
||||||
#--
|
#--
|
||||||
# search
|
# search
|
||||||
|
|
Loading…
Reference in a new issue