Skip to content

Commit ab20ad2

Browse files
committed
Define GSS_SPNEGO AuthAdapter
1 parent 91db1ba commit ab20ad2

File tree

3 files changed

+63
-92
lines changed

3 files changed

+63
-92
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
require 'net/ldap/auth_adapter'
2+
require 'net/ldap/auth_adapters/sasl'
3+
4+
module Net
5+
class LDAP
6+
module AuthAdapers
7+
#--
8+
# PROVISIONAL, only for testing SASL implementations. DON'T USE THIS YET.
9+
# Uses Kohei Kajimoto's Ruby/NTLM. We have to find a clean way to
10+
# integrate it without introducing an external dependency.
11+
#
12+
# This authentication method is accessed by calling #bind with a :method
13+
# parameter of :gss_spnego. It requires :username and :password
14+
# attributes, just like the :simple authentication method. It performs a
15+
# GSS-SPNEGO authentication with the server, which is presumed to be a
16+
# Microsoft Active Directory.
17+
#++
18+
class GSS_SPNEGO < Net::LDAP::AuthAdapter
19+
def bind(auth)
20+
require 'ntlm'
21+
22+
user, psw = [auth[:username] || auth[:dn], auth[:password]]
23+
raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
24+
25+
nego = proc { |challenge|
26+
t2_msg = NTLM::Message.parse(challenge)
27+
t3_msg = t2_msg.response({ :user => user, :password => psw },
28+
{ :ntlmv2 => true })
29+
t3_msg.serialize
30+
}
31+
32+
Net::LDAP::AuthAdapter.new(@connection).
33+
bind(:method => :sasl, :mechanism => "GSS-SPNEGO",
34+
:initial_credential => NTLM::Message::Type1.new.serialize,
35+
:challenge_response => nego)
36+
end
37+
end
38+
end
39+
end
40+
end
41+
42+
Net::LDAP::Adapter.register(:gss_spnego, Net::LDAP::AuthAdapters::GSS_SPNEGO)

lib/net/ldap/auth_adapters/sasl.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,27 @@ module Net
44
class LDAP
55
module AuthAdapters
66
class Sasl < Net::LDAP::AuthAdapter
7+
#--
8+
# Required parameters: :mechanism, :initial_credential and
9+
# :challenge_response
10+
#
11+
# Mechanism is a string value that will be passed in the SASL-packet's
12+
# "mechanism" field.
13+
#
14+
# Initial credential is most likely a string. It's passed in the initial
15+
# BindRequest that goes to the server. In some protocols, it may be empty.
16+
#
17+
# Challenge-response is a Ruby proc that takes a single parameter and
18+
# returns an object that will typically be a string. The
19+
# challenge-response block is called when the server returns a
20+
# BindResponse with a result code of 14 (saslBindInProgress). The
21+
# challenge-response block receives a parameter containing the data
22+
# returned by the server in the saslServerCreds field of the LDAP
23+
# BindResponse packet. The challenge-response block may be called multiple
24+
# times during the course of a SASL authentication, and each time it must
25+
# return a value that will be passed back to the server as the credential
26+
# data in the next BindRequest packet.
27+
#++
728
def bind(auth)
829
mech, cred, chall = auth[:mechanism], auth[:initial_credential],
930
auth[:challenge_response]

lib/net/ldap/connection.rb

Lines changed: 0 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -253,101 +253,9 @@ def bind(auth)
253253
require "net/ldap/auth_adapters/#{meth}"
254254
adapter = Net::LDAP::AuthAdapter[meth]
255255
adapter.new(self).bind(auth)
256-
# if [:simple, :anonymous, :anon].include?(meth)
257-
# bind_simple auth
258-
# elsif meth == :sasl
259-
# bind_sasl(auth)
260-
# elsif meth == :gss_spnego
261-
# bind_gss_spnego(auth)
262-
# else
263-
# raise Net::LDAP::AuthMethodUnsupportedError, "Unsupported auth method (#{meth})"
264-
# end
265256
end
266257
end
267258

268-
#--
269-
# Required parameters: :mechanism, :initial_credential and
270-
# :challenge_response
271-
#
272-
# Mechanism is a string value that will be passed in the SASL-packet's
273-
# "mechanism" field.
274-
#
275-
# Initial credential is most likely a string. It's passed in the initial
276-
# BindRequest that goes to the server. In some protocols, it may be empty.
277-
#
278-
# Challenge-response is a Ruby proc that takes a single parameter and
279-
# returns an object that will typically be a string. The
280-
# challenge-response block is called when the server returns a
281-
# BindResponse with a result code of 14 (saslBindInProgress). The
282-
# challenge-response block receives a parameter containing the data
283-
# returned by the server in the saslServerCreds field of the LDAP
284-
# BindResponse packet. The challenge-response block may be called multiple
285-
# times during the course of a SASL authentication, and each time it must
286-
# return a value that will be passed back to the server as the credential
287-
# data in the next BindRequest packet.
288-
#++
289-
def bind_sasl(auth)
290-
mech, cred, chall = auth[:mechanism], auth[:initial_credential],
291-
auth[:challenge_response]
292-
raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (mech && cred && chall)
293-
294-
message_id = next_msgid
295-
296-
n = 0
297-
loop {
298-
sasl = [mech.to_ber, cred.to_ber].to_ber_contextspecific(3)
299-
request = [
300-
LdapVersion.to_ber, "".to_ber, sasl
301-
].to_ber_appsequence(Net::LDAP::PDU::BindRequest)
302-
303-
write(request, nil, message_id)
304-
pdu = queued_read(message_id)
305-
306-
if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult
307-
raise Net::LDAP::NoBindResultError, "no bind result"
308-
end
309-
310-
return pdu unless pdu.result_code == Net::LDAP::ResultCodeSaslBindInProgress
311-
raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
312-
313-
cred = chall.call(pdu.result_server_sasl_creds)
314-
}
315-
316-
raise Net::LDAP::SASLChallengeOverflowError, "why are we here?"
317-
end
318-
private :bind_sasl
319-
320-
#--
321-
# PROVISIONAL, only for testing SASL implementations. DON'T USE THIS YET.
322-
# Uses Kohei Kajimoto's Ruby/NTLM. We have to find a clean way to
323-
# integrate it without introducing an external dependency.
324-
#
325-
# This authentication method is accessed by calling #bind with a :method
326-
# parameter of :gss_spnego. It requires :username and :password
327-
# attributes, just like the :simple authentication method. It performs a
328-
# GSS-SPNEGO authentication with the server, which is presumed to be a
329-
# Microsoft Active Directory.
330-
#++
331-
def bind_gss_spnego(auth)
332-
require 'ntlm'
333-
334-
user, psw = [auth[:username] || auth[:dn], auth[:password]]
335-
raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
336-
337-
nego = proc { |challenge|
338-
t2_msg = NTLM::Message.parse(challenge)
339-
t3_msg = t2_msg.response({ :user => user, :password => psw },
340-
{ :ntlmv2 => true })
341-
t3_msg.serialize
342-
}
343-
344-
bind_sasl(:method => :sasl, :mechanism => "GSS-SPNEGO",
345-
:initial_credential => NTLM::Message::Type1.new.serialize,
346-
:challenge_response => nego)
347-
end
348-
private :bind_gss_spnego
349-
350-
351259
#--
352260
# Allow the caller to specify a sort control
353261
#

0 commit comments

Comments
 (0)