rubyntlm 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
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