rubyntlm 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -3
  3. data/.rspec +2 -2
  4. data/.travis.yml +13 -12
  5. data/CHANGELOG.md +118 -6
  6. data/Gemfile +3 -3
  7. data/LICENSE +19 -19
  8. data/Rakefile +25 -22
  9. data/lib/net/ntlm.rb +266 -266
  10. data/lib/net/ntlm/blob.rb +28 -28
  11. data/lib/net/ntlm/channel_binding.rb +65 -65
  12. data/lib/net/ntlm/client.rb +65 -65
  13. data/lib/net/ntlm/client/session.rb +237 -237
  14. data/lib/net/ntlm/encode_util.rb +48 -48
  15. data/lib/net/ntlm/exceptions.rb +14 -14
  16. data/lib/net/ntlm/field.rb +34 -34
  17. data/lib/net/ntlm/field_set.rb +129 -129
  18. data/lib/net/ntlm/int16_le.rb +25 -25
  19. data/lib/net/ntlm/int32_le.rb +24 -24
  20. data/lib/net/ntlm/int64_le.rb +25 -25
  21. data/lib/net/ntlm/message.rb +129 -129
  22. data/lib/net/ntlm/message/type0.rb +16 -16
  23. data/lib/net/ntlm/message/type1.rb +18 -18
  24. data/lib/net/ntlm/message/type2.rb +102 -102
  25. data/lib/net/ntlm/message/type3.rb +131 -131
  26. data/lib/net/ntlm/security_buffer.rb +47 -47
  27. data/lib/net/ntlm/string.rb +34 -34
  28. data/lib/net/ntlm/target_info.rb +89 -89
  29. data/lib/net/ntlm/version.rb +11 -11
  30. data/rubyntlm.gemspec +29 -28
  31. data/spec/lib/net/ntlm/blob_spec.rb +16 -16
  32. data/spec/lib/net/ntlm/channel_binding_spec.rb +17 -17
  33. data/spec/lib/net/ntlm/client/session_spec.rb +68 -68
  34. data/spec/lib/net/ntlm/client_spec.rb +64 -64
  35. data/spec/lib/net/ntlm/encode_util_spec.rb +16 -16
  36. data/spec/lib/net/ntlm/field_set_spec.rb +33 -33
  37. data/spec/lib/net/ntlm/field_spec.rb +34 -34
  38. data/spec/lib/net/ntlm/int16_le_spec.rb +17 -17
  39. data/spec/lib/net/ntlm/int32_le_spec.rb +18 -18
  40. data/spec/lib/net/ntlm/int64_le_spec.rb +18 -18
  41. data/spec/lib/net/ntlm/message/type0_spec.rb +20 -20
  42. data/spec/lib/net/ntlm/message/type1_spec.rb +131 -131
  43. data/spec/lib/net/ntlm/message/type2_spec.rb +132 -132
  44. data/spec/lib/net/ntlm/message/type3_spec.rb +225 -225
  45. data/spec/lib/net/ntlm/message_spec.rb +16 -16
  46. data/spec/lib/net/ntlm/security_buffer_spec.rb +64 -64
  47. data/spec/lib/net/ntlm/string_spec.rb +72 -72
  48. data/spec/lib/net/ntlm/target_info_spec.rb +76 -76
  49. data/spec/lib/net/ntlm/version_spec.rb +27 -27
  50. data/spec/lib/net/ntlm_spec.rb +127 -127
  51. data/spec/spec_helper.rb +22 -22
  52. data/spec/support/certificates/sha_256_hash.pem +19 -19
  53. data/spec/support/shared/examples/net/ntlm/field_shared.rb +25 -25
  54. data/spec/support/shared/examples/net/ntlm/fieldset_shared.rb +239 -239
  55. data/spec/support/shared/examples/net/ntlm/int_shared.rb +43 -43
  56. data/spec/support/shared/examples/net/ntlm/message_shared.rb +35 -35
  57. metadata +17 -3
@@ -1,102 +1,102 @@
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 :os_version, { :size => 8, :value => "", :active => false }
16
-
17
- # Generates a Type 3 response based on the Type 2 Information
18
- # @return [Type3]
19
- # @option arg [String] :username The username to authenticate with
20
- # @option arg [String] :password The user's password
21
- # @option arg [String] :domain ('') The domain to authenticate to
22
- # @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation
23
- # @option opt [Boolean] :use_default_target (false) Use the domain supplied by the server in the Type 2 packet
24
- # @note An empty :domain option authenticates to the local machine.
25
- # @note The :use_default_target has precedence over the :domain option
26
- def response(arg, opt = {})
27
- usr = arg[:user]
28
- pwd = arg[:password]
29
- domain = arg[:domain] ? arg[:domain].upcase : ""
30
- if usr.nil? or pwd.nil?
31
- raise ArgumentError, "user and password have to be supplied"
32
- end
33
-
34
- if opt[:workstation]
35
- ws = opt[:workstation]
36
- else
37
- ws = Socket.gethostname
38
- end
39
-
40
- if opt[:client_challenge]
41
- cc = opt[:client_challenge]
42
- else
43
- cc = rand(MAX64)
44
- end
45
- cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
46
- opt[:client_challenge] = cc
47
-
48
- if has_flag?(:OEM) and opt[:unicode]
49
- usr = NTLM::EncodeUtil.decode_utf16le(usr)
50
- pwd = NTLM::EncodeUtil.decode_utf16le(pwd)
51
- ws = NTLM::EncodeUtil.decode_utf16le(ws)
52
- domain = NTLM::EncodeUtil.decode_utf16le(domain)
53
- opt[:unicode] = false
54
- end
55
-
56
- if has_flag?(:UNICODE) and !opt[:unicode]
57
- usr = NTLM::EncodeUtil.encode_utf16le(usr)
58
- pwd = NTLM::EncodeUtil.encode_utf16le(pwd)
59
- ws = NTLM::EncodeUtil.encode_utf16le(ws)
60
- domain = NTLM::EncodeUtil.encode_utf16le(domain)
61
- opt[:unicode] = true
62
- end
63
-
64
- if opt[:use_default_target]
65
- domain = self.target_name
66
- end
67
-
68
- ti = self.target_info
69
-
70
- chal = self[:challenge].serialize
71
-
72
- if opt[:ntlmv2]
73
- ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, domain, opt), :challenge => chal, :target_info => ti}
74
- lm_res = NTLM::lmv2_response(ar, opt)
75
- ntlm_res = NTLM::ntlmv2_response(ar, opt)
76
- elsif has_flag?(:NTLM2_KEY)
77
- ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
78
- lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
79
- else
80
- ar = {:lm_hash => NTLM::lm_hash(pwd), :challenge => chal}
81
- lm_res = NTLM::lm_response(ar)
82
- ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
83
- ntlm_res = NTLM::ntlm_response(ar)
84
- end
85
-
86
- Type3.create({
87
- :lm_response => lm_res,
88
- :ntlm_response => ntlm_res,
89
- :domain => domain,
90
- :user => usr,
91
- :workstation => ws,
92
- :flag => self.flag
93
- })
94
- end
95
-
96
- end
97
-
98
- end
99
- end
100
- end
101
-
102
-
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 :os_version, { :size => 8, :value => "", :active => false }
16
+
17
+ # Generates a Type 3 response based on the Type 2 Information
18
+ # @return [Type3]
19
+ # @option arg [String] :username The username to authenticate with
20
+ # @option arg [String] :password The user's password
21
+ # @option arg [String] :domain ('') The domain to authenticate to
22
+ # @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation
23
+ # @option opt [Boolean] :use_default_target (false) Use the domain supplied by the server in the Type 2 packet
24
+ # @note An empty :domain option authenticates to the local machine.
25
+ # @note The :use_default_target has precedence over the :domain option
26
+ def response(arg, opt = {})
27
+ usr = arg[:user]
28
+ pwd = arg[:password]
29
+ domain = arg[:domain] ? arg[:domain].upcase : ""
30
+ if usr.nil? or pwd.nil?
31
+ raise ArgumentError, "user and password have to be supplied"
32
+ end
33
+
34
+ if opt[:workstation]
35
+ ws = opt[:workstation]
36
+ else
37
+ ws = Socket.gethostname
38
+ end
39
+
40
+ if opt[:client_challenge]
41
+ cc = opt[:client_challenge]
42
+ else
43
+ cc = rand(MAX64)
44
+ end
45
+ cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
46
+ opt[:client_challenge] = cc
47
+
48
+ if has_flag?(:OEM) and opt[:unicode]
49
+ usr = NTLM::EncodeUtil.decode_utf16le(usr)
50
+ pwd = NTLM::EncodeUtil.decode_utf16le(pwd)
51
+ ws = NTLM::EncodeUtil.decode_utf16le(ws)
52
+ domain = NTLM::EncodeUtil.decode_utf16le(domain)
53
+ opt[:unicode] = false
54
+ end
55
+
56
+ if has_flag?(:UNICODE) and !opt[:unicode]
57
+ usr = NTLM::EncodeUtil.encode_utf16le(usr)
58
+ pwd = NTLM::EncodeUtil.encode_utf16le(pwd)
59
+ ws = NTLM::EncodeUtil.encode_utf16le(ws)
60
+ domain = NTLM::EncodeUtil.encode_utf16le(domain)
61
+ opt[:unicode] = true
62
+ end
63
+
64
+ if opt[:use_default_target]
65
+ domain = self.target_name
66
+ end
67
+
68
+ ti = self.target_info
69
+
70
+ chal = self[:challenge].serialize
71
+
72
+ if opt[:ntlmv2]
73
+ ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, domain, opt), :challenge => chal, :target_info => ti}
74
+ lm_res = NTLM::lmv2_response(ar, opt)
75
+ ntlm_res = NTLM::ntlmv2_response(ar, opt)
76
+ elsif has_flag?(:NTLM2_KEY)
77
+ ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
78
+ lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
79
+ else
80
+ ar = {:lm_hash => NTLM::lm_hash(pwd), :challenge => chal}
81
+ lm_res = NTLM::lm_response(ar)
82
+ ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
83
+ ntlm_res = NTLM::ntlm_response(ar)
84
+ end
85
+
86
+ Type3.create({
87
+ :lm_response => lm_res,
88
+ :ntlm_response => ntlm_res,
89
+ :domain => domain,
90
+ :user => usr,
91
+ :workstation => ws,
92
+ :flag => self.flag
93
+ })
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+ end
100
+ end
101
+
102
+
@@ -1,131 +1,131 @@
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
- int32LE :flag, {:value => 0, :active => false }
17
- string :os_version, {:size => 8, :active => false }
18
-
19
- class << Type3
20
- # Builds a Type 3 packet
21
- # @note All options must be properly encoded with either unicode or oem encoding
22
- # @return [Type3]
23
- # @option arg [String] :lm_response The LM hash
24
- # @option arg [String] :ntlm_response The NTLM hash
25
- # @option arg [String] :domain The domain to authenticate to
26
- # @option arg [String] :workstation The name of the calling workstation
27
- # @option arg [String] :session_key The session key
28
- # @option arg [Integer] :flag Flags for the packet
29
- def create(arg, opt ={})
30
- t = new
31
- t.lm_response = arg[:lm_response]
32
- t.ntlm_response = arg[:ntlm_response]
33
- t.domain = arg[:domain]
34
- t.user = arg[:user]
35
-
36
- if arg[:workstation]
37
- t.workstation = arg[:workstation]
38
- end
39
-
40
- if arg[:session_key]
41
- t.enable(:session_key)
42
- t.session_key = arg[:session_key]
43
- end
44
-
45
- if arg[:flag]
46
- t.enable(:session_key)
47
- t.enable(:flag)
48
- t.flag = arg[:flag]
49
- end
50
- t
51
- end
52
- end
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])
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
129
- end
130
- end
131
- end
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
+ int32LE :flag, {:value => 0, :active => false }
17
+ string :os_version, {:size => 8, :active => false }
18
+
19
+ class << Type3
20
+ # Builds a Type 3 packet
21
+ # @note All options must be properly encoded with either unicode or oem encoding
22
+ # @return [Type3]
23
+ # @option arg [String] :lm_response The LM hash
24
+ # @option arg [String] :ntlm_response The NTLM hash
25
+ # @option arg [String] :domain The domain to authenticate to
26
+ # @option arg [String] :workstation The name of the calling workstation
27
+ # @option arg [String] :session_key The session key
28
+ # @option arg [Integer] :flag Flags for the packet
29
+ def create(arg, opt ={})
30
+ t = new
31
+ t.lm_response = arg[:lm_response]
32
+ t.ntlm_response = arg[:ntlm_response]
33
+ t.domain = arg[:domain]
34
+ t.user = arg[:user]
35
+
36
+ if arg[:workstation]
37
+ t.workstation = arg[:workstation]
38
+ end
39
+
40
+ if arg[:session_key]
41
+ t.enable(:session_key)
42
+ t.session_key = arg[:session_key]
43
+ end
44
+
45
+ if arg[:flag]
46
+ t.enable(:session_key)
47
+ t.enable(:flag)
48
+ t.flag = arg[:flag]
49
+ end
50
+ t
51
+ end
52
+ end
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])
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
129
+ end
130
+ end
131
+ end