rubyntlm 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +2 -3
- data/README.md +9 -2
- data/Rakefile +8 -0
- data/examples/http.rb +1 -1
- data/lib/net/ntlm.rb +10 -9
- data/lib/net/ntlm/blob.rb +17 -6
- data/lib/net/ntlm/client.rb +61 -0
- data/lib/net/ntlm/client/session.rb +223 -0
- data/lib/net/ntlm/encode_util.rb +2 -2
- data/lib/net/ntlm/field_set.rb +9 -5
- data/lib/net/ntlm/message.rb +23 -9
- data/lib/net/ntlm/message/type1.rb +1 -26
- data/lib/net/ntlm/message/type2.rb +11 -37
- data/lib/net/ntlm/message/type3.rb +77 -14
- data/lib/net/ntlm/version.rb +1 -1
- data/rubyntlm.gemspec +3 -2
- data/spec/lib/net/ntlm/blob_spec.rb +1 -1
- data/spec/lib/net/ntlm/client/session_spec.rb +68 -0
- data/spec/lib/net/ntlm/client_spec.rb +64 -0
- data/spec/lib/net/ntlm/encode_util_spec.rb +3 -3
- data/spec/lib/net/ntlm/field_set_spec.rb +4 -4
- data/spec/lib/net/ntlm/field_spec.rb +5 -5
- data/spec/lib/net/ntlm/message/type1_spec.rb +99 -10
- data/spec/lib/net/ntlm/message/type2_spec.rb +68 -24
- data/spec/lib/net/ntlm/message/type3_spec.rb +207 -2
- data/spec/lib/net/ntlm/security_buffer_spec.rb +8 -8
- data/spec/lib/net/ntlm/string_spec.rb +14 -14
- data/spec/lib/net/ntlm/version_spec.rb +7 -6
- data/spec/lib/net/ntlm_spec.rb +20 -22
- data/spec/spec_helper.rb +1 -2
- data/spec/support/shared/examples/net/ntlm/field_shared.rb +3 -3
- data/spec/support/shared/examples/net/ntlm/fieldset_shared.rb +34 -34
- data/spec/support/shared/examples/net/ntlm/int_shared.rb +8 -8
- data/spec/support/shared/examples/net/ntlm/message_shared.rb +3 -3
- metadata +36 -16
data/lib/net/ntlm/encode_util.rb
CHANGED
@@ -24,8 +24,8 @@ module NTLM
|
|
24
24
|
end
|
25
25
|
else # Use native 1.9 string encoding functions
|
26
26
|
|
27
|
-
|
28
|
-
|
27
|
+
# Decode a UTF16 string to a ASCII string
|
28
|
+
# @param [String] str The string to convert
|
29
29
|
def self.decode_utf16le(str)
|
30
30
|
str.force_encoding(Encoding::UTF_16LE)
|
31
31
|
str.encode(Encoding::UTF_8, Encoding::UTF_16LE).force_encoding('UTF-8')
|
data/lib/net/ntlm/field_set.rb
CHANGED
@@ -88,12 +88,12 @@ module Net
|
|
88
88
|
@alist = self.class.prototypes.map{ |n, t, o| [n, t.new(o)] }
|
89
89
|
end
|
90
90
|
|
91
|
-
def
|
92
|
-
@alist.
|
91
|
+
def parse(str, offset=0)
|
92
|
+
@alist.inject(offset){|cur, a| cur += a[1].parse(str, cur)}
|
93
93
|
end
|
94
94
|
|
95
|
-
def
|
96
|
-
@alist.
|
95
|
+
def serialize
|
96
|
+
@alist.map{|n, f| f.serialize }.join
|
97
97
|
end
|
98
98
|
|
99
99
|
def size
|
@@ -119,7 +119,11 @@ module Net
|
|
119
119
|
def disable(name)
|
120
120
|
self[name].active = false
|
121
121
|
end
|
122
|
+
|
123
|
+
def has_disabled_fields?
|
124
|
+
@alist.any? { |name, field| !field.active }
|
125
|
+
end
|
122
126
|
end
|
123
127
|
|
124
128
|
end
|
125
|
-
end
|
129
|
+
end
|
data/lib/net/ntlm/message.rb
CHANGED
@@ -20,9 +20,10 @@ module NTLM
|
|
20
20
|
:LOCAL_CALL => 0x00004000,
|
21
21
|
:ALWAYS_SIGN => 0x00008000,
|
22
22
|
:TARGET_TYPE_DOMAIN => 0x00010000,
|
23
|
-
:TARGET_INFO => 0x00800000,
|
24
23
|
:NTLM2_KEY => 0x00080000,
|
24
|
+
:TARGET_INFO => 0x00800000,
|
25
25
|
:KEY128 => 0x20000000,
|
26
|
+
:KEY_EXCHANGE => 0x40000000,
|
26
27
|
:KEY56 => 0x80000000
|
27
28
|
}.freeze
|
28
29
|
|
@@ -42,14 +43,14 @@ module NTLM
|
|
42
43
|
m = Type0.new
|
43
44
|
m.parse(str)
|
44
45
|
case m.type
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
46
|
+
when 1
|
47
|
+
t = Type1.new.parse(str)
|
48
|
+
when 2
|
49
|
+
t = Type2.new.parse(str)
|
50
|
+
when 3
|
51
|
+
t = Type3.new.parse(str)
|
52
|
+
else
|
53
|
+
raise ArgumentError, "unknown type: #{m.type}"
|
53
54
|
end
|
54
55
|
t
|
55
56
|
end
|
@@ -59,6 +60,19 @@ module NTLM
|
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
63
|
+
# @return [self]
|
64
|
+
def parse(str)
|
65
|
+
super
|
66
|
+
|
67
|
+
while has_disabled_fields? && serialize.size < str.size
|
68
|
+
# enable the next disabled field
|
69
|
+
self.class.names.find { |name| !self[name].active && enable(name) }
|
70
|
+
super
|
71
|
+
end
|
72
|
+
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
62
76
|
def has_flag?(flag)
|
63
77
|
(self[:flag].value & FLAGS[flag]) == FLAGS[flag]
|
64
78
|
end
|
@@ -10,34 +10,9 @@ module Net
|
|
10
10
|
int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE1] }
|
11
11
|
security_buffer :domain, {:value => ""}
|
12
12
|
security_buffer :workstation, {:value => Socket.gethostname }
|
13
|
-
string :
|
13
|
+
string :os_version, {:size => 8, :value => "", :active => false }
|
14
14
|
|
15
|
-
class << Type1
|
16
|
-
# Parses a Type 1 Message
|
17
|
-
# @param [String] str A string containing Type 1 data
|
18
|
-
# @return [Type1] The parsed Type 1 message
|
19
|
-
def parse(str)
|
20
|
-
t = new
|
21
|
-
t.parse(str)
|
22
|
-
t
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# @!visibility private
|
27
|
-
def parse(str)
|
28
|
-
super(str)
|
29
|
-
enable(:domain) if has_flag?(:DOMAIN_SUPPLIED)
|
30
|
-
enable(:workstation) if has_flag?(:WORKSTATION_SUPPLIED)
|
31
|
-
super(str)
|
32
|
-
if ( (len = data_edge - head_size) > 0)
|
33
|
-
self.padding = "\0" * len
|
34
|
-
super(str)
|
35
|
-
end
|
36
|
-
end
|
37
15
|
end
|
38
|
-
|
39
16
|
end
|
40
17
|
end
|
41
18
|
end
|
42
|
-
|
43
|
-
|
@@ -5,39 +5,14 @@ module Net
|
|
5
5
|
# @private false
|
6
6
|
class Type2 < Message
|
7
7
|
|
8
|
-
string
|
9
|
-
int32LE
|
10
|
-
security_buffer
|
11
|
-
int32LE
|
12
|
-
int64LE
|
13
|
-
int64LE
|
14
|
-
security_buffer
|
15
|
-
string
|
16
|
-
|
17
|
-
class << Type2
|
18
|
-
# Parse a Type 2 packet
|
19
|
-
# @param [String] str A string containing Type 2 data
|
20
|
-
# @return [Type2]
|
21
|
-
def parse(str)
|
22
|
-
t = new
|
23
|
-
t.parse(str)
|
24
|
-
t
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# @!visibility private
|
29
|
-
def parse(str)
|
30
|
-
super(str)
|
31
|
-
if has_flag?(:TARGET_INFO)
|
32
|
-
enable(:context)
|
33
|
-
enable(:target_info)
|
34
|
-
super(str)
|
35
|
-
end
|
36
|
-
if ( (len = data_edge - head_size) > 0)
|
37
|
-
self.padding = "\0" * len
|
38
|
-
super(str)
|
39
|
-
end
|
40
|
-
end
|
8
|
+
string :sign, { :size => 8, :value => SSP_SIGN }
|
9
|
+
int32LE :type, { :value => 2 }
|
10
|
+
security_buffer :target_name, { :size => 0, :value => "" }
|
11
|
+
int32LE :flag, { :value => DEFAULT_FLAGS[:TYPE2] }
|
12
|
+
int64LE :challenge, { :value => 0}
|
13
|
+
int64LE :context, { :value => 0, :active => false }
|
14
|
+
security_buffer :target_info, { :value => "", :active => false }
|
15
|
+
string :os_version, { :size => 8, :value => "", :active => false }
|
41
16
|
|
42
17
|
# Generates a Type 3 response based on the Type 2 Information
|
43
18
|
# @return [Type3]
|
@@ -45,9 +20,9 @@ module Net
|
|
45
20
|
# @option arg [String] :password The user's password
|
46
21
|
# @option arg [String] :domain ('') The domain to authenticate to
|
47
22
|
# @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation
|
48
|
-
# @option opt [Boolean] :use_default_target (
|
23
|
+
# @option opt [Boolean] :use_default_target (false) Use the domain supplied by the server in the Type 2 packet
|
49
24
|
# @note An empty :domain option authenticates to the local machine.
|
50
|
-
# @note The :use_default_target has
|
25
|
+
# @note The :use_default_target has precedence over the :domain option
|
51
26
|
def response(arg, opt = {})
|
52
27
|
usr = arg[:user]
|
53
28
|
pwd = arg[:password]
|
@@ -115,9 +90,8 @@ module Net
|
|
115
90
|
:flag => self.flag
|
116
91
|
})
|
117
92
|
end
|
118
|
-
end
|
119
|
-
|
120
93
|
|
94
|
+
end
|
121
95
|
|
122
96
|
end
|
123
97
|
end
|
@@ -13,18 +13,10 @@ module Net
|
|
13
13
|
security_buffer :user, {:value => ""}
|
14
14
|
security_buffer :workstation, {:value => ""}
|
15
15
|
security_buffer :session_key, {:value => "", :active => false }
|
16
|
-
|
16
|
+
int32LE :flag, {:value => 0, :active => false }
|
17
|
+
string :os_version, {:size => 8, :active => false }
|
17
18
|
|
18
19
|
class << Type3
|
19
|
-
# Parse a Type 3 packet
|
20
|
-
# @param [String] str A string containing Type 3 data
|
21
|
-
# @return [Type2]
|
22
|
-
def parse(str)
|
23
|
-
t = new
|
24
|
-
t.parse(str)
|
25
|
-
t
|
26
|
-
end
|
27
|
-
|
28
20
|
# Builds a Type 3 packet
|
29
21
|
# @note All options must be properly encoded with either unicode or oem encoding
|
30
22
|
# @return [Type3]
|
@@ -47,7 +39,7 @@ module Net
|
|
47
39
|
|
48
40
|
if arg[:session_key]
|
49
41
|
t.enable(:session_key)
|
50
|
-
t.session_key = arg[session_key]
|
42
|
+
t.session_key = arg[:session_key]
|
51
43
|
end
|
52
44
|
|
53
45
|
if arg[:flag]
|
@@ -58,11 +50,82 @@ module Net
|
|
58
50
|
t
|
59
51
|
end
|
60
52
|
end
|
61
|
-
end
|
62
53
|
|
54
|
+
# @param server_challenge (see #password?)
|
55
|
+
def blank_password?(server_challenge)
|
56
|
+
password?('', server_challenge)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param password [String]
|
60
|
+
# @param server_challenge [String] The server's {Type2#challenge challenge} from the
|
61
|
+
# {Type2} message for which this object is a response.
|
62
|
+
# @return [true] if +password+ was the password used to generate this
|
63
|
+
# {Type3} message
|
64
|
+
# @return [false] otherwise
|
65
|
+
def password?(password, server_challenge)
|
66
|
+
case ntlm_version
|
67
|
+
when :ntlm2_session
|
68
|
+
ntlm2_session_password?(password, server_challenge)
|
69
|
+
when :ntlmv2
|
70
|
+
ntlmv2_password?(password, server_challenge)
|
71
|
+
else
|
72
|
+
raise
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Symbol]
|
77
|
+
def ntlm_version
|
78
|
+
if ntlm_response.size == 24 && lm_response[0,8] != "\x00"*8 && lm_response[8,16] == "\x00"*16
|
79
|
+
:ntlm2_session
|
80
|
+
elsif ntlm_response.size == 24
|
81
|
+
:ntlmv1
|
82
|
+
elsif ntlm_response.size > 24
|
83
|
+
:ntlmv2
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def ntlm2_session_password?(password, server_challenge)
|
90
|
+
hash = ntlm_response
|
91
|
+
_lm, empty_hash = NTLM.ntlm2_session(
|
92
|
+
{
|
93
|
+
:ntlm_hash => NTLM.ntlm_hash(password),
|
94
|
+
:challenge => server_challenge,
|
95
|
+
},
|
96
|
+
{
|
97
|
+
:client_challenge => lm_response[0,8]
|
98
|
+
}
|
99
|
+
)
|
100
|
+
hash == empty_hash
|
101
|
+
end
|
102
|
+
|
103
|
+
def ntlmv2_password?(password, server_challenge)
|
104
|
+
|
105
|
+
# The first 16 bytes of the ntlm_response are the HMAC of the blob
|
106
|
+
# that follows it.
|
107
|
+
blob = Blob.new
|
108
|
+
blob.parse(ntlm_response[16..-1])
|
63
109
|
|
110
|
+
empty_hash = NTLM.ntlmv2_response(
|
111
|
+
{
|
112
|
+
# user and domain came from the serialized data here, so
|
113
|
+
# they're already unicode
|
114
|
+
:ntlmv2_hash => NTLM.ntlmv2_hash(user, '', domain, :unicode => true),
|
115
|
+
:challenge => server_challenge,
|
116
|
+
:target_info => blob.target_info
|
117
|
+
},
|
118
|
+
{
|
119
|
+
:client_challenge => blob.challenge,
|
120
|
+
# The blob's timestamp is already in milliseconds since 1601,
|
121
|
+
# so convert it back to epoch time first
|
122
|
+
:timestamp => (blob.timestamp / 10_000_000) - NTLM::TIME_OFFSET,
|
123
|
+
}
|
124
|
+
)
|
125
|
+
|
126
|
+
empty_hash == ntlm_response
|
127
|
+
end
|
128
|
+
end
|
64
129
|
end
|
65
130
|
end
|
66
131
|
end
|
67
|
-
|
68
|
-
|
data/lib/net/ntlm/version.rb
CHANGED
data/rubyntlm.gemspec
CHANGED
@@ -18,10 +18,11 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
20
|
s.required_ruby_version = '>= 1.8.7'
|
21
|
-
|
21
|
+
|
22
22
|
s.license = 'MIT'
|
23
23
|
|
24
|
+
s.add_development_dependency "pry"
|
24
25
|
s.add_development_dependency "rake"
|
25
|
-
s.add_development_dependency "rspec"
|
26
|
+
s.add_development_dependency "rspec", ">= 2.11"
|
26
27
|
s.add_development_dependency "simplecov"
|
27
28
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Net::NTLM::Client::Session do
|
4
|
+
let(:t2_challenge) { Net::NTLM::Message.decode64 "TlRMTVNTUAACAAAADAAMADgAAAA1goriAAyk1DmJUnUAAAAAAAAAAFAAUABEAAAABgLwIwAAAA9TAEUAUgBWAEUAUgACAAwAUwBFAFIAVgBFAFIAAQAMAFMARQBSAFYARQBSAAQADABzAGUAcgB2AGUAcgADAAwAcwBlAHIAdgBlAHIABwAIADd7mrNaB9ABAAAAAA==" }
|
5
|
+
let(:inst) { Net::NTLM::Client::Session.new(nil, t2_challenge) }
|
6
|
+
let(:user_session_key) {["3c4918ff0b33e2603e5d7ceaf34bb7d5"].pack("H*")}
|
7
|
+
let(:client_sign_key) {["f7f97a82ec390f9c903dac4f6aceb132"].pack("H*")}
|
8
|
+
let(:client_seal_key) {["6f0d99535033951cbe499cd1914fe9ee"].pack("H*")}
|
9
|
+
let(:server_sign_key) {["f7f97a82ec390f9c903dac4f6aceb132"].pack("H*")}
|
10
|
+
let(:server_seal_key) {["6f0d99535033951cbe499cd1914fe9ee"].pack("H*")}
|
11
|
+
|
12
|
+
describe "#sign_message" do
|
13
|
+
|
14
|
+
it "signs a message and when KEY_EXCHANGE is true" do
|
15
|
+
expect(inst).to receive(:client_sign_key).and_return(client_sign_key)
|
16
|
+
expect(inst).to receive(:client_seal_key).and_return(client_seal_key)
|
17
|
+
expect(inst).to receive(:negotiate_key_exchange?).and_return(true)
|
18
|
+
sm = inst.sign_message("Test Message")
|
19
|
+
str = "01000000b35ccd60c110c52f00000000"
|
20
|
+
expect(sm.unpack("H*")[0]).to eq(str)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#verify_signature" do
|
26
|
+
|
27
|
+
it "verifies a message signature" do
|
28
|
+
expect(inst).to receive(:server_sign_key).and_return(server_sign_key)
|
29
|
+
expect(inst).to receive(:server_seal_key).and_return(server_seal_key)
|
30
|
+
expect(inst).to receive(:negotiate_key_exchange?).and_return(true)
|
31
|
+
sig = "01000000b35ccd60c110c52f00000000"
|
32
|
+
sm = inst.verify_signature([sig].pack("H*"), "Test Message")
|
33
|
+
expect(sm).to be true
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#seal_message" do
|
39
|
+
it "should seal the message" do
|
40
|
+
expect(inst).to receive(:client_seal_key).and_return(client_seal_key)
|
41
|
+
emsg = inst.seal_message("rubyntlm")
|
42
|
+
expect(emsg.unpack("H*")[0]).to eq("d7389b9604f6274f")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#unseal_message" do
|
47
|
+
it "should unseal the message" do
|
48
|
+
expect(inst).to receive(:server_seal_key).and_return(server_seal_key)
|
49
|
+
msg = inst.unseal_message(["d7389b9604f6274f"].pack("H*"))
|
50
|
+
expect(msg).to eq("rubyntlm")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#master_key" do
|
55
|
+
it "returns a random 16-byte key when negotiate_key_exchange? is true" do
|
56
|
+
expect(inst).to receive(:negotiate_key_exchange?).and_return(true)
|
57
|
+
expect(inst).not_to receive(:user_session_key)
|
58
|
+
inst.send :master_key
|
59
|
+
end
|
60
|
+
|
61
|
+
it "returns the user_session_key when negotiate_key_exchange? is false" do
|
62
|
+
expect(inst).to receive(:negotiate_key_exchange?).and_return(false)
|
63
|
+
expect(inst).to receive(:user_session_key).and_return(user_session_key)
|
64
|
+
inst.send :master_key
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Net::NTLM::Client do
|
4
|
+
let(:inst) { Net::NTLM::Client.new("test", "test01", :workstation => "testhost") }
|
5
|
+
let(:user_session_key) {["3c4918ff0b33e2603e5d7ceaf34bb7d5"].pack("H*")}
|
6
|
+
|
7
|
+
describe "#init_context" do
|
8
|
+
|
9
|
+
it "returns a default Type1 message" do
|
10
|
+
t1 = inst.init_context
|
11
|
+
expect(t1).to be_instance_of Net::NTLM::Message::Type1
|
12
|
+
expect(t1.domain).to eq("")
|
13
|
+
expect(t1.workstation).to eq("testhost")
|
14
|
+
expect(t1).to have_flag(:UNICODE)
|
15
|
+
expect(t1).to have_flag(:OEM)
|
16
|
+
expect(t1).to have_flag(:SIGN)
|
17
|
+
expect(t1).to have_flag(:SEAL)
|
18
|
+
expect(t1).to have_flag(:REQUEST_TARGET)
|
19
|
+
expect(t1).to have_flag(:NTLM)
|
20
|
+
expect(t1).to have_flag(:ALWAYS_SIGN)
|
21
|
+
expect(t1).to have_flag(:NTLM2_KEY)
|
22
|
+
expect(t1).to have_flag(:KEY128)
|
23
|
+
expect(t1).to have_flag(:KEY_EXCHANGE)
|
24
|
+
expect(t1).to have_flag(:KEY56)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "clears session variable on new init_context" do
|
28
|
+
inst.instance_variable_set :@session, "BADSESSION"
|
29
|
+
expect(inst.session).to eq("BADSESSION")
|
30
|
+
inst.init_context
|
31
|
+
expect(inst.session).to be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns a Type1 message with custom flags" do
|
35
|
+
flags = Net::NTLM::FLAGS[:UNICODE] | Net::NTLM::FLAGS[:REQUEST_TARGET] | Net::NTLM::FLAGS[:NTLM]
|
36
|
+
inst = Net::NTLM::Client.new("test", "test01", :workstation => "testhost", :flags => flags)
|
37
|
+
t1 = inst.init_context
|
38
|
+
expect(t1).to be_instance_of Net::NTLM::Message::Type1
|
39
|
+
expect(t1.domain).to eq("")
|
40
|
+
expect(t1.workstation).to eq("testhost")
|
41
|
+
expect(t1).to have_flag(:UNICODE)
|
42
|
+
expect(t1).not_to have_flag(:OEM)
|
43
|
+
expect(t1).not_to have_flag(:SIGN)
|
44
|
+
expect(t1).not_to have_flag(:SEAL)
|
45
|
+
expect(t1).to have_flag(:REQUEST_TARGET)
|
46
|
+
expect(t1).to have_flag(:NTLM)
|
47
|
+
expect(t1).not_to have_flag(:ALWAYS_SIGN)
|
48
|
+
expect(t1).not_to have_flag(:NTLM2_KEY)
|
49
|
+
expect(t1).not_to have_flag(:KEY128)
|
50
|
+
expect(t1).not_to have_flag(:KEY_EXCHANGE)
|
51
|
+
expect(t1).not_to have_flag(:KEY56)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "calls authenticate! when we receive a Challenge Message" do
|
55
|
+
t2_challenge = "TlRMTVNTUAACAAAADAAMADgAAAA1goriAAyk1DmJUnUAAAAAAAAAAFAAUABEAAAABgLwIwAAAA9TAEUAUgBWAEUAUgACAAwAUwBFAFIAVgBFAFIAAQAMAFMARQBSAFYARQBSAAQADABzAGUAcgB2AGUAcgADAAwAcwBlAHIAdgBlAHIABwAIADd7mrNaB9ABAAAAAA=="
|
56
|
+
session = double("session")
|
57
|
+
expect(session).to receive(:authenticate!)
|
58
|
+
expect(Net::NTLM::Client::Session).to receive(:new).with(inst, instance_of(Net::NTLM::Message::Type2)).and_return(session)
|
59
|
+
inst.init_context t2_challenge
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|