rubyntlm 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +3 -0
- data/.travis.yml +0 -1
- data/LICENSE +20 -0
- data/Rakefile +8 -15
- data/lib/net/ntlm.rb +22 -654
- data/lib/net/ntlm/blob.rb +17 -0
- data/lib/net/ntlm/encode_util.rb +49 -0
- data/lib/net/ntlm/field.rb +35 -0
- data/lib/net/ntlm/field_set.rb +125 -0
- data/lib/net/ntlm/int16_le.rb +26 -0
- data/lib/net/ntlm/int32_le.rb +25 -0
- data/lib/net/ntlm/int64_le.rb +26 -0
- data/lib/net/ntlm/message.rb +115 -0
- data/lib/net/ntlm/message/type0.rb +16 -0
- data/lib/net/ntlm/message/type1.rb +43 -0
- data/lib/net/ntlm/message/type2.rb +126 -0
- data/lib/net/ntlm/message/type3.rb +68 -0
- data/lib/net/ntlm/security_buffer.rb +48 -0
- data/lib/net/ntlm/string.rb +35 -0
- data/lib/net/ntlm/version.rb +11 -0
- data/rubyntlm.gemspec +4 -1
- data/spec/lib/net/ntlm/blob_spec.rb +16 -0
- data/spec/lib/net/ntlm/encode_util_spec.rb +16 -0
- data/spec/lib/net/ntlm/field_set_spec.rb +33 -0
- data/spec/lib/net/ntlm/field_spec.rb +34 -0
- data/spec/lib/net/ntlm/int16_le_spec.rb +18 -0
- data/spec/lib/net/ntlm/int32_le_spec.rb +19 -0
- data/spec/lib/net/ntlm/int64_le_spec.rb +19 -0
- data/spec/lib/net/ntlm/message/type0_spec.rb +21 -0
- data/spec/lib/net/ntlm/message/type1_spec.rb +42 -0
- data/spec/lib/net/ntlm/message/type2_spec.rb +88 -0
- data/spec/lib/net/ntlm/message/type3_spec.rb +20 -0
- data/spec/lib/net/ntlm/message_spec.rb +17 -0
- data/spec/lib/net/ntlm/security_buffer_spec.rb +64 -0
- data/spec/lib/net/ntlm/string_spec.rb +72 -0
- data/spec/lib/net/ntlm/version_spec.rb +26 -0
- data/spec/lib/net/ntlm_spec.rb +121 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/shared/examples/net/ntlm/field_shared.rb +25 -0
- data/spec/support/shared/examples/net/ntlm/fieldset_shared.rb +239 -0
- data/spec/support/shared/examples/net/ntlm/int_shared.rb +43 -0
- data/spec/support/shared/examples/net/ntlm/message_shared.rb +35 -0
- metadata +77 -5
- data/spec/unit/ntlm_spec.rb +0 -183
@@ -0,0 +1,126 @@
|
|
1
|
+
module Net
|
2
|
+
module NTLM
|
3
|
+
class Message
|
4
|
+
|
5
|
+
# @private false
|
6
|
+
class Type2 < Message
|
7
|
+
|
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 :padding, {:size => 0, :value => "", :active => false }
|
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
|
41
|
+
|
42
|
+
# Generates a Type 3 response based on the Type 2 Information
|
43
|
+
# @return [Type3]
|
44
|
+
# @option arg [String] :username The username to authenticate with
|
45
|
+
# @option arg [String] :password The user's password
|
46
|
+
# @option arg [String] :domain ('') The domain to authenticate to
|
47
|
+
# @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation
|
48
|
+
# @option opt [Boolean] :use_default_target (False) Use the domain supplied by the server in the Type 2 packet
|
49
|
+
# @note An empty :domain option authenticates to the local machine.
|
50
|
+
# @note The :use_default_target has presidence over the :domain option
|
51
|
+
def response(arg, opt = {})
|
52
|
+
usr = arg[:user]
|
53
|
+
pwd = arg[:password]
|
54
|
+
domain = arg[:domain] ? arg[:domain].upcase : ""
|
55
|
+
if usr.nil? or pwd.nil?
|
56
|
+
raise ArgumentError, "user and password have to be supplied"
|
57
|
+
end
|
58
|
+
|
59
|
+
if opt[:workstation]
|
60
|
+
ws = opt[:workstation]
|
61
|
+
else
|
62
|
+
ws = Socket.gethostname
|
63
|
+
end
|
64
|
+
|
65
|
+
if opt[:client_challenge]
|
66
|
+
cc = opt[:client_challenge]
|
67
|
+
else
|
68
|
+
cc = rand(MAX64)
|
69
|
+
end
|
70
|
+
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
|
71
|
+
opt[:client_challenge] = cc
|
72
|
+
|
73
|
+
if has_flag?(:OEM) and opt[:unicode]
|
74
|
+
usr = NTLM::EncodeUtil.decode_utf16le(usr)
|
75
|
+
pwd = NTLM::EncodeUtil.decode_utf16le(pwd)
|
76
|
+
ws = NTLM::EncodeUtil.decode_utf16le(ws)
|
77
|
+
domain = NTLM::EncodeUtil.decode_utf16le(domain)
|
78
|
+
opt[:unicode] = false
|
79
|
+
end
|
80
|
+
|
81
|
+
if has_flag?(:UNICODE) and !opt[:unicode]
|
82
|
+
usr = NTLM::EncodeUtil.encode_utf16le(usr)
|
83
|
+
pwd = NTLM::EncodeUtil.encode_utf16le(pwd)
|
84
|
+
ws = NTLM::EncodeUtil.encode_utf16le(ws)
|
85
|
+
domain = NTLM::EncodeUtil.encode_utf16le(domain)
|
86
|
+
opt[:unicode] = true
|
87
|
+
end
|
88
|
+
|
89
|
+
if opt[:use_default_target]
|
90
|
+
domain = self.target_name
|
91
|
+
end
|
92
|
+
|
93
|
+
ti = self.target_info
|
94
|
+
|
95
|
+
chal = self[:challenge].serialize
|
96
|
+
|
97
|
+
if opt[:ntlmv2]
|
98
|
+
ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, domain, opt), :challenge => chal, :target_info => ti}
|
99
|
+
lm_res = NTLM::lmv2_response(ar, opt)
|
100
|
+
ntlm_res = NTLM::ntlmv2_response(ar, opt)
|
101
|
+
elsif has_flag?(:NTLM2_KEY)
|
102
|
+
ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
|
103
|
+
lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
|
104
|
+
else
|
105
|
+
lm_res = NTLM::lm_response(pwd, chal)
|
106
|
+
ntlm_res = NTLM::ntlm_response(pwd, chal)
|
107
|
+
end
|
108
|
+
|
109
|
+
Type3.create({
|
110
|
+
:lm_response => lm_res,
|
111
|
+
:ntlm_response => ntlm_res,
|
112
|
+
:domain => domain,
|
113
|
+
:user => usr,
|
114
|
+
:workstation => ws,
|
115
|
+
:flag => self.flag
|
116
|
+
})
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Net
|
2
|
+
module NTLM
|
3
|
+
class Message
|
4
|
+
|
5
|
+
# @private false
|
6
|
+
class Type3 < Message
|
7
|
+
|
8
|
+
string :sign, {:size => 8, :value => SSP_SIGN}
|
9
|
+
int32LE :type, {:value => 3}
|
10
|
+
security_buffer :lm_response, {:value => ""}
|
11
|
+
security_buffer :ntlm_response, {:value => ""}
|
12
|
+
security_buffer :domain, {:value => ""}
|
13
|
+
security_buffer :user, {:value => ""}
|
14
|
+
security_buffer :workstation, {:value => ""}
|
15
|
+
security_buffer :session_key, {:value => "", :active => false }
|
16
|
+
int64LE :flag, {:value => 0, :active => false }
|
17
|
+
|
18
|
+
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
|
+
# Builds a Type 3 packet
|
29
|
+
# @note All options must be properly encoded with either unicode or oem encoding
|
30
|
+
# @return [Type3]
|
31
|
+
# @option arg [String] :lm_response The LM hash
|
32
|
+
# @option arg [String] :ntlm_response The NTLM hash
|
33
|
+
# @option arg [String] :domain The domain to authenticate to
|
34
|
+
# @option arg [String] :workstation The name of the calling workstation
|
35
|
+
# @option arg [String] :session_key The session key
|
36
|
+
# @option arg [Integer] :flag Flags for the packet
|
37
|
+
def create(arg, opt ={})
|
38
|
+
t = new
|
39
|
+
t.lm_response = arg[:lm_response]
|
40
|
+
t.ntlm_response = arg[:ntlm_response]
|
41
|
+
t.domain = arg[:domain]
|
42
|
+
t.user = arg[:user]
|
43
|
+
|
44
|
+
if arg[:workstation]
|
45
|
+
t.workstation = arg[:workstation]
|
46
|
+
end
|
47
|
+
|
48
|
+
if arg[:session_key]
|
49
|
+
t.enable(:session_key)
|
50
|
+
t.session_key = arg[session_key]
|
51
|
+
end
|
52
|
+
|
53
|
+
if arg[:flag]
|
54
|
+
t.enable(:session_key)
|
55
|
+
t.enable(:flag)
|
56
|
+
t.flag = arg[:flag]
|
57
|
+
end
|
58
|
+
t
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
|
2
|
+
module Net
|
3
|
+
module NTLM
|
4
|
+
|
5
|
+
class SecurityBuffer < FieldSet
|
6
|
+
|
7
|
+
int16LE :length, {:value => 0}
|
8
|
+
int16LE :allocated, {:value => 0}
|
9
|
+
int32LE :offset, {:value => 0}
|
10
|
+
|
11
|
+
attr_accessor :active
|
12
|
+
def initialize(opts={})
|
13
|
+
super()
|
14
|
+
@value = opts[:value]
|
15
|
+
@active = opts[:active].nil? ? true : opts[:active]
|
16
|
+
@size = 8
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse(str, offset=0)
|
20
|
+
if @active and str.size >= offset + @size
|
21
|
+
super(str, offset)
|
22
|
+
@value = str[self.offset, self.length]
|
23
|
+
@size
|
24
|
+
else
|
25
|
+
0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def serialize
|
30
|
+
super if @active
|
31
|
+
end
|
32
|
+
|
33
|
+
def value
|
34
|
+
@value
|
35
|
+
end
|
36
|
+
|
37
|
+
def value=(val)
|
38
|
+
@value = val
|
39
|
+
self.length = self.allocated = val.size
|
40
|
+
end
|
41
|
+
|
42
|
+
def data_size
|
43
|
+
@active ? @value.size : 0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Net
|
2
|
+
module NTLM
|
3
|
+
|
4
|
+
class String < Field
|
5
|
+
def initialize(opts)
|
6
|
+
super(opts)
|
7
|
+
@size = opts[:size]
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(str, offset=0)
|
11
|
+
if @active and str.size >= offset + @size
|
12
|
+
@value = str[offset, @size]
|
13
|
+
@size
|
14
|
+
else
|
15
|
+
0
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def serialize
|
20
|
+
if @active
|
21
|
+
@value.to_s
|
22
|
+
else
|
23
|
+
""
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def value=(val)
|
28
|
+
@value = val
|
29
|
+
@size = @value.nil? ? 0 : @value.size
|
30
|
+
@active = (@size > 0)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
data/rubyntlm.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'lib', 'net', 'ntlm')
|
1
|
+
require File.join(File.dirname(__FILE__), 'lib', 'net', 'ntlm', 'version')
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.platform = Gem::Platform::RUBY
|
@@ -18,7 +18,10 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
20
|
s.required_ruby_version = '>= 1.8.7'
|
21
|
+
|
22
|
+
s.license = 'MIT'
|
21
23
|
|
22
24
|
s.add_development_dependency "rake"
|
23
25
|
s.add_development_dependency "rspec"
|
26
|
+
s.add_development_dependency "simplecov"
|
24
27
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Net::NTLM::Blob do
|
4
|
+
|
5
|
+
fields = [
|
6
|
+
{ :name => :blob_signature, :class => Net::NTLM::Int32LE, :value => 257, :active => true },
|
7
|
+
{ :name => :reserved, :class => Net::NTLM::Int32LE, :value => 0, :active => true },
|
8
|
+
{ :name => :timestamp, :class => Net::NTLM::Int64LE, :value => 0, :active => true },
|
9
|
+
{ :name => :challenge, :class => Net::NTLM::String, :value => '', :active => true },
|
10
|
+
{ :name => :unknown1, :class => Net::NTLM::Int32LE, :value => 0, :active => true },
|
11
|
+
{ :name => :target_info, :class => Net::NTLM::String, :value => '', :active => true },
|
12
|
+
{ :name => :unknown2, :class => Net::NTLM::Int32LE, :value => 0, :active => true },
|
13
|
+
]
|
14
|
+
|
15
|
+
it_behaves_like 'a fieldset', fields
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Net::NTLM::EncodeUtil do
|
4
|
+
|
5
|
+
context '#encode_utf16le' do
|
6
|
+
it 'should convert an ASCII string to UTF' do
|
7
|
+
Net::NTLM::EncodeUtil.encode_utf16le('Test').should == "T\x00e\x00s\x00t\x00"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context '#decode_utf16le' do
|
12
|
+
it 'should convert a UTF string to ASCII' do
|
13
|
+
Net::NTLM::EncodeUtil.decode_utf16le("T\x00e\x00s\x00t\x00").should == 'Test'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Net::NTLM::FieldSet do
|
4
|
+
|
5
|
+
fields = []
|
6
|
+
|
7
|
+
it_behaves_like 'a fieldset', fields
|
8
|
+
|
9
|
+
subject(:fieldset_class) do
|
10
|
+
Class.new(Net::NTLM::FieldSet)
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'an instance' do
|
14
|
+
subject(:fieldset_object) do
|
15
|
+
fieldset_class.string(:test_string, { :value => 'Test', :active => true, :size => 4})
|
16
|
+
fieldset_class.string(:test_string2, { :value => 'Foo', :active => true, :size => 3})
|
17
|
+
fieldset_class.new
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should serialize all the fields' do
|
21
|
+
fieldset_object.serialize.should == 'TestFoo'
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should parse a string across the fields' do
|
25
|
+
fieldset_object.parse('FooBarBaz')
|
26
|
+
fieldset_object.serialize.should == 'FooBarB'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should return an aggregate size of all the fields' do
|
30
|
+
fieldset_object.size.should == 7
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Net::NTLM::Field do
|
4
|
+
|
5
|
+
it_behaves_like 'a field', 'Foo', false
|
6
|
+
|
7
|
+
context 'with no size specified' do
|
8
|
+
let (:field_without_size) { Net::NTLM::Field.new({ :value => 'Foo', :active => true }) }
|
9
|
+
it 'should set size to 0 if not active' do
|
10
|
+
field_without_size.size.should == 0
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should return 0 if active but no size specified' do
|
14
|
+
field_without_size.active = true
|
15
|
+
field_without_size.size.should == 0
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with a size specified' do
|
20
|
+
let (:field_with_size) { Net::NTLM::Field.new({ :value => 'Foo', :active => true, :size => 100 }) }
|
21
|
+
|
22
|
+
it 'should return the size provided in the initialize options if active' do
|
23
|
+
field_with_size.size.should == 100
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should still return 0 if not active' do
|
27
|
+
field_with_size.active = false
|
28
|
+
field_with_size.size.should == 0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Net::NTLM::Int16LE do
|
4
|
+
|
5
|
+
int_values = {
|
6
|
+
:default => 15,
|
7
|
+
:default_hex => "\x0F\x00",
|
8
|
+
:alt => 14,
|
9
|
+
:alt_hex => "\x0E\x00",
|
10
|
+
:small => "\x0F",
|
11
|
+
:size => 2,
|
12
|
+
:bits => 16
|
13
|
+
}
|
14
|
+
|
15
|
+
it_behaves_like 'a field', 15, false
|
16
|
+
it_behaves_like 'an integer field', int_values
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Net::NTLM::Int32LE do
|
4
|
+
|
5
|
+
int_values = {
|
6
|
+
:default => 252716124,
|
7
|
+
:default_hex => "\x5C\x24\x10\x0f",
|
8
|
+
:alt => 235938908,
|
9
|
+
:alt_hex => "\x5C\x24\x10\x0e",
|
10
|
+
:small => "\x0F\x00",
|
11
|
+
:size => 4,
|
12
|
+
:bits => 32
|
13
|
+
}
|
14
|
+
|
15
|
+
|
16
|
+
it_behaves_like 'a field', 252716124, false
|
17
|
+
it_behaves_like 'an integer field', int_values
|
18
|
+
|
19
|
+
end
|