rubyntlm 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +3 -0
  3. data/.travis.yml +0 -1
  4. data/LICENSE +20 -0
  5. data/Rakefile +8 -15
  6. data/lib/net/ntlm.rb +22 -654
  7. data/lib/net/ntlm/blob.rb +17 -0
  8. data/lib/net/ntlm/encode_util.rb +49 -0
  9. data/lib/net/ntlm/field.rb +35 -0
  10. data/lib/net/ntlm/field_set.rb +125 -0
  11. data/lib/net/ntlm/int16_le.rb +26 -0
  12. data/lib/net/ntlm/int32_le.rb +25 -0
  13. data/lib/net/ntlm/int64_le.rb +26 -0
  14. data/lib/net/ntlm/message.rb +115 -0
  15. data/lib/net/ntlm/message/type0.rb +16 -0
  16. data/lib/net/ntlm/message/type1.rb +43 -0
  17. data/lib/net/ntlm/message/type2.rb +126 -0
  18. data/lib/net/ntlm/message/type3.rb +68 -0
  19. data/lib/net/ntlm/security_buffer.rb +48 -0
  20. data/lib/net/ntlm/string.rb +35 -0
  21. data/lib/net/ntlm/version.rb +11 -0
  22. data/rubyntlm.gemspec +4 -1
  23. data/spec/lib/net/ntlm/blob_spec.rb +16 -0
  24. data/spec/lib/net/ntlm/encode_util_spec.rb +16 -0
  25. data/spec/lib/net/ntlm/field_set_spec.rb +33 -0
  26. data/spec/lib/net/ntlm/field_spec.rb +34 -0
  27. data/spec/lib/net/ntlm/int16_le_spec.rb +18 -0
  28. data/spec/lib/net/ntlm/int32_le_spec.rb +19 -0
  29. data/spec/lib/net/ntlm/int64_le_spec.rb +19 -0
  30. data/spec/lib/net/ntlm/message/type0_spec.rb +21 -0
  31. data/spec/lib/net/ntlm/message/type1_spec.rb +42 -0
  32. data/spec/lib/net/ntlm/message/type2_spec.rb +88 -0
  33. data/spec/lib/net/ntlm/message/type3_spec.rb +20 -0
  34. data/spec/lib/net/ntlm/message_spec.rb +17 -0
  35. data/spec/lib/net/ntlm/security_buffer_spec.rb +64 -0
  36. data/spec/lib/net/ntlm/string_spec.rb +72 -0
  37. data/spec/lib/net/ntlm/version_spec.rb +26 -0
  38. data/spec/lib/net/ntlm_spec.rb +121 -0
  39. data/spec/spec_helper.rb +21 -0
  40. data/spec/support/shared/examples/net/ntlm/field_shared.rb +25 -0
  41. data/spec/support/shared/examples/net/ntlm/fieldset_shared.rb +239 -0
  42. data/spec/support/shared/examples/net/ntlm/int_shared.rb +43 -0
  43. data/spec/support/shared/examples/net/ntlm/message_shared.rb +35 -0
  44. metadata +77 -5
  45. 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
@@ -0,0 +1,11 @@
1
+ module Net
2
+ module NTLM
3
+ # @private
4
+ module VERSION
5
+ MAJOR = 0
6
+ MINOR = 4
7
+ TINY = 0
8
+ STRING = [MAJOR, MINOR, TINY].join('.')
9
+ end
10
+ end
11
+ end
@@ -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