rubyntlm 0.6.6 → 0.6.7
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +48 -48
- data/lib/net/ntlm/blob.rb +11 -10
- data/lib/net/ntlm/channel_binding.rb +13 -9
- data/lib/net/ntlm/client/session.rb +34 -141
- data/lib/net/ntlm/client/session_crypto.rb +126 -0
- data/lib/net/ntlm/client.rb +8 -9
- data/lib/net/ntlm/encode_util.rb +39 -38
- data/lib/net/ntlm/exceptions.rb +3 -0
- data/lib/net/ntlm/field.rb +29 -31
- data/lib/net/ntlm/field_set.rb +39 -18
- data/lib/net/ntlm/int16_le.rb +8 -8
- data/lib/net/ntlm/int32_le.rb +8 -7
- data/lib/net/ntlm/int64_le.rb +8 -7
- data/lib/net/ntlm/md4.rb +102 -64
- data/lib/net/ntlm/message/type0.rb +4 -7
- data/lib/net/ntlm/message/type1.rb +8 -9
- data/lib/net/ntlm/message/type2.rb +87 -67
- data/lib/net/ntlm/message/type3.rb +43 -41
- data/lib/net/ntlm/message.rb +116 -111
- data/lib/net/ntlm/rc4.rb +51 -44
- data/lib/net/ntlm/response.rb +112 -0
- data/lib/net/ntlm/security_buffer.rb +13 -15
- data/lib/net/ntlm/string.rb +8 -7
- data/lib/net/ntlm/target_info.rb +52 -44
- data/lib/net/ntlm/version.rb +3 -1
- data/lib/net/ntlm.rb +37 -125
- data/lib/rubyntlm.rb +3 -1
- metadata +5 -44
- data/.devcontainer/devcontainer.json +0 -22
- data/.github/dependabot.yml +0 -10
- data/.github/workflows/build.yml +0 -55
- data/.gitignore +0 -11
- data/.rspec +0 -3
- data/Gemfile +0 -20
- data/Rakefile +0 -37
- data/examples/http.rb +0 -86
- data/examples/imap.rb +0 -73
- data/examples/smtp.rb +0 -94
- data/rubyntlm.gemspec +0 -28
- data/spec/lib/net/ntlm/blob_spec.rb +0 -14
- data/spec/lib/net/ntlm/channel_binding_spec.rb +0 -15
- data/spec/lib/net/ntlm/client/session_spec.rb +0 -85
- data/spec/lib/net/ntlm/client_spec.rb +0 -62
- data/spec/lib/net/ntlm/encode_util_spec.rb +0 -14
- data/spec/lib/net/ntlm/field_set_spec.rb +0 -31
- data/spec/lib/net/ntlm/field_spec.rb +0 -32
- data/spec/lib/net/ntlm/int16_le_spec.rb +0 -16
- data/spec/lib/net/ntlm/int32_le_spec.rb +0 -17
- data/spec/lib/net/ntlm/int64_le_spec.rb +0 -17
- data/spec/lib/net/ntlm/md4_spec.rb +0 -16
- data/spec/lib/net/ntlm/message/type0_spec.rb +0 -19
- data/spec/lib/net/ntlm/message/type1_spec.rb +0 -129
- data/spec/lib/net/ntlm/message/type2_spec.rb +0 -131
- data/spec/lib/net/ntlm/message/type3_spec.rb +0 -336
- data/spec/lib/net/ntlm/message_spec.rb +0 -15
- data/spec/lib/net/ntlm/security_buffer_spec.rb +0 -62
- data/spec/lib/net/ntlm/string_spec.rb +0 -70
- data/spec/lib/net/ntlm/target_info_spec.rb +0 -74
- data/spec/lib/net/ntlm/version_spec.rb +0 -26
- data/spec/lib/net/ntlm_spec.rb +0 -133
- data/spec/spec_helper.rb +0 -51
- data/spec/support/certificates/sha_256_hash.pem +0 -19
- data/spec/support/shared/examples/net/ntlm/field_shared.rb +0 -25
- data/spec/support/shared/examples/net/ntlm/fieldset_shared.rb +0 -239
- data/spec/support/shared/examples/net/ntlm/int_shared.rb +0 -43
- data/spec/support/shared/examples/net/ntlm/message_shared.rb +0 -35
|
@@ -1,18 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Net
|
|
2
4
|
module NTLM
|
|
3
5
|
class Message
|
|
4
|
-
|
|
5
6
|
# @private false
|
|
6
7
|
class Type2 < Message
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
string :os_version, { :size => 8, :value => "", :active => false }
|
|
8
|
+
string :sign, { size: 8, value: SSP_SIGN }
|
|
9
|
+
int32_le :type, { value: 2 }
|
|
10
|
+
security_buffer :target_name, { size: 0, value: '' }
|
|
11
|
+
int32_le :flag, { value: DEFAULT_FLAGS[:TYPE2] }
|
|
12
|
+
int64_le :challenge, { value: 0 }
|
|
13
|
+
int64_le :context, { value: 0, active: false }
|
|
14
|
+
security_buffer :target_info, { value: '', active: false }
|
|
15
|
+
string :os_version, { size: 8, value: '', active: false }
|
|
16
16
|
|
|
17
17
|
# Generates a Type 3 response based on the Type 2 Information
|
|
18
18
|
# @return [Type3]
|
|
@@ -24,79 +24,99 @@ module Net
|
|
|
24
24
|
# @note An empty :domain option authenticates to the local machine.
|
|
25
25
|
# @note The :use_default_target has precedence over the :domain option
|
|
26
26
|
def response(arg, opt = {})
|
|
27
|
+
creds = response_credentials(arg, opt)
|
|
28
|
+
normalize_response_encoding(creds, opt)
|
|
29
|
+
creds[:domain] = target_name if opt[:use_default_target]
|
|
30
|
+
lm_res, ntlm_res = build_responses(creds, opt)
|
|
31
|
+
build_type3(creds, lm_res, ntlm_res)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def response_credentials(arg, opt)
|
|
27
37
|
usr = arg[:user]
|
|
28
38
|
pwd = arg[:password]
|
|
29
|
-
|
|
30
|
-
if usr.nil? or pwd.nil?
|
|
31
|
-
raise ArgumentError, "user and password have to be supplied"
|
|
32
|
-
end
|
|
39
|
+
raise ArgumentError, 'user and password have to be supplied' if usr.nil? || pwd.nil?
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
41
|
+
build_credentials(usr, pwd, arg, opt)
|
|
42
|
+
end
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
domain
|
|
53
|
-
opt[:
|
|
54
|
-
|
|
44
|
+
def build_credentials(usr, pwd, arg, opt)
|
|
45
|
+
{
|
|
46
|
+
user: usr,
|
|
47
|
+
password: pwd,
|
|
48
|
+
domain: arg[:domain] ? arg[:domain].upcase : '',
|
|
49
|
+
workstation: opt[:workstation] || Socket.gethostname,
|
|
50
|
+
client_challenge: normalize_client_challenge(opt)
|
|
51
|
+
}
|
|
52
|
+
end
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
opt[:unicode] = true
|
|
62
|
-
end
|
|
54
|
+
def normalize_client_challenge(opt)
|
|
55
|
+
cc = opt[:client_challenge] || rand(MAX64)
|
|
56
|
+
cc = NTLM.pack_int64le(cc) if cc.is_a?(Integer)
|
|
57
|
+
opt[:client_challenge] = cc
|
|
58
|
+
end
|
|
63
59
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
def normalize_response_encoding(creds, opt)
|
|
61
|
+
decode_response_strings(creds, opt) if flag?(:OEM) && opt[:unicode]
|
|
62
|
+
encode_response_strings(creds, opt) if flag?(:UNICODE) && !opt[:unicode]
|
|
63
|
+
end
|
|
67
64
|
|
|
68
|
-
|
|
65
|
+
def decode_response_strings(creds, opt)
|
|
66
|
+
creds[:user] = NTLM::EncodeUtil.decode_utf16le(creds[:user])
|
|
67
|
+
creds[:password] = NTLM::EncodeUtil.decode_utf16le(creds[:password])
|
|
68
|
+
creds[:workstation] = NTLM::EncodeUtil.decode_utf16le(creds[:workstation])
|
|
69
|
+
creds[:domain] = NTLM::EncodeUtil.decode_utf16le(creds[:domain])
|
|
70
|
+
opt[:unicode] = false
|
|
71
|
+
end
|
|
69
72
|
|
|
70
|
-
|
|
73
|
+
def encode_response_strings(creds, opt)
|
|
74
|
+
creds[:user] = NTLM::EncodeUtil.encode_utf16le(creds[:user])
|
|
75
|
+
creds[:password] = NTLM::EncodeUtil.encode_utf16le(creds[:password])
|
|
76
|
+
creds[:workstation] = NTLM::EncodeUtil.encode_utf16le(creds[:workstation])
|
|
77
|
+
creds[:domain] = NTLM::EncodeUtil.encode_utf16le(creds[:domain])
|
|
78
|
+
opt[:unicode] = true
|
|
79
|
+
end
|
|
71
80
|
|
|
81
|
+
def build_responses(creds, opt)
|
|
82
|
+
challenge = self[:challenge].serialize
|
|
72
83
|
if opt[:ntlmv2]
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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)
|
|
84
|
+
ntlmv2_responses(creds, challenge, opt)
|
|
85
|
+
elsif flag?(:NTLM2_KEY)
|
|
86
|
+
ntlm2_session_responses(creds, challenge, opt)
|
|
79
87
|
else
|
|
80
|
-
|
|
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)
|
|
88
|
+
v1_responses(creds, challenge, opt)
|
|
84
89
|
end
|
|
90
|
+
end
|
|
85
91
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
:user => usr,
|
|
91
|
-
:workstation => ws,
|
|
92
|
-
:flag => self.flag
|
|
93
|
-
})
|
|
92
|
+
def ntlmv2_responses(creds, challenge, opt)
|
|
93
|
+
ar = { ntlmv2_hash: NTLM.ntlmv2_hash(creds[:user], creds[:password], creds[:domain], opt),
|
|
94
|
+
challenge: challenge, target_info: target_info }
|
|
95
|
+
[NTLM.lmv2_response(ar, opt), NTLM.ntlmv2_response(ar, opt)]
|
|
94
96
|
end
|
|
95
97
|
|
|
96
|
-
|
|
98
|
+
def ntlm2_session_responses(creds, challenge, opt)
|
|
99
|
+
ar = { ntlm_hash: NTLM.ntlm_hash(creds[:password], opt), challenge: challenge }
|
|
100
|
+
NTLM.ntlm2_session(ar, opt)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def v1_responses(creds, challenge, opt)
|
|
104
|
+
lm_res = NTLM.lm_response({ lm_hash: NTLM.lm_hash(creds[:password]), challenge: challenge })
|
|
105
|
+
ntlm_res = NTLM.ntlm_response({ ntlm_hash: NTLM.ntlm_hash(creds[:password], opt), challenge: challenge })
|
|
106
|
+
[lm_res, ntlm_res]
|
|
107
|
+
end
|
|
97
108
|
|
|
109
|
+
def build_type3(creds, lm_res, ntlm_res)
|
|
110
|
+
Type3.create({
|
|
111
|
+
lm_response: lm_res,
|
|
112
|
+
ntlm_response: ntlm_res,
|
|
113
|
+
domain: creds[:domain],
|
|
114
|
+
user: creds[:user],
|
|
115
|
+
workstation: creds[:workstation],
|
|
116
|
+
flag: flag
|
|
117
|
+
})
|
|
118
|
+
end
|
|
119
|
+
end
|
|
98
120
|
end
|
|
99
121
|
end
|
|
100
122
|
end
|
|
101
|
-
|
|
102
|
-
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Net
|
|
2
4
|
module NTLM
|
|
3
5
|
class Message
|
|
4
|
-
|
|
5
6
|
# @private false
|
|
6
7
|
class Type3 < Message
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
security_buffer :
|
|
11
|
-
security_buffer :
|
|
12
|
-
security_buffer :
|
|
13
|
-
security_buffer :
|
|
14
|
-
security_buffer :
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
string :os_version, {:size => 8, :active => false }
|
|
8
|
+
string :sign, { size: 8, value: SSP_SIGN }
|
|
9
|
+
int32_le :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
|
+
int32_le :flag, { value: 0, active: false }
|
|
17
|
+
string :os_version, { size: 8, active: false }
|
|
18
18
|
|
|
19
19
|
class << Type3
|
|
20
20
|
# Builds a Type 3 packet
|
|
@@ -26,28 +26,33 @@ module Net
|
|
|
26
26
|
# @option arg [String] :workstation The name of the calling workstation
|
|
27
27
|
# @option arg [String] :session_key The session key
|
|
28
28
|
# @option arg [Integer] :flag Flags for the packet
|
|
29
|
-
def create(arg,
|
|
29
|
+
def create(arg, _opt = {})
|
|
30
30
|
t = new
|
|
31
31
|
t.lm_response = arg[:lm_response]
|
|
32
32
|
t.ntlm_response = arg[:ntlm_response]
|
|
33
33
|
t.domain = arg[:domain]
|
|
34
34
|
t.user = arg[:user]
|
|
35
|
+
t.workstation = arg[:workstation] if arg[:workstation]
|
|
36
|
+
enable_session_key(t, arg)
|
|
37
|
+
enable_flag(t, arg)
|
|
38
|
+
t
|
|
39
|
+
end
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
t.workstation = arg[:workstation]
|
|
38
|
-
end
|
|
41
|
+
private
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
t.session_key = arg[:session_key]
|
|
43
|
-
end
|
|
43
|
+
def enable_session_key(type3, arg)
|
|
44
|
+
return unless arg[:session_key]
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
type3.enable(:session_key)
|
|
47
|
+
type3.session_key = arg[:session_key]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def enable_flag(type3, arg)
|
|
51
|
+
return unless arg[:flag]
|
|
52
|
+
|
|
53
|
+
type3.enable(:session_key)
|
|
54
|
+
type3.enable(:flag)
|
|
55
|
+
type3.flag = arg[:flag]
|
|
51
56
|
end
|
|
52
57
|
end
|
|
53
58
|
|
|
@@ -75,7 +80,7 @@ module Net
|
|
|
75
80
|
|
|
76
81
|
# @return [Symbol]
|
|
77
82
|
def ntlm_version
|
|
78
|
-
if ntlm_response.size == 24 && lm_response[0,8] != "\x00"*8 && lm_response[8,16] == "\x00"*16
|
|
83
|
+
if ntlm_response.size == 24 && lm_response[0, 8] != "\x00" * 8 && lm_response[8, 16] == "\x00" * 16
|
|
79
84
|
:ntlm2_session
|
|
80
85
|
elsif ntlm_response.size == 24
|
|
81
86
|
:ntlmv1
|
|
@@ -87,31 +92,28 @@ module Net
|
|
|
87
92
|
private
|
|
88
93
|
|
|
89
94
|
def ntlm2_session_password?(password, server_challenge)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
)
|
|
100
|
-
hash == empty_hash
|
|
95
|
+
ntlm_response == ntlm2_session_empty_hash(password, server_challenge)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def ntlm2_session_empty_hash(password, server_challenge)
|
|
99
|
+
NTLM.ntlm2_session(
|
|
100
|
+
{ ntlm_hash: NTLM.ntlm_hash(password), challenge: server_challenge },
|
|
101
|
+
{ client_challenge: lm_response[0, 8] }
|
|
102
|
+
).last
|
|
101
103
|
end
|
|
102
104
|
|
|
103
105
|
def ntlmv2_password?(password, server_challenge)
|
|
104
106
|
# user and domain are already UTF-16LE from the wire; encode the
|
|
105
107
|
# supplied password to match before deriving the verification key.
|
|
106
|
-
key = NTLM.ntlmv2_hash(user, EncodeUtil.encode_utf16le(password), domain, :
|
|
108
|
+
key = NTLM.ntlmv2_hash(user, EncodeUtil.encode_utf16le(password), domain, unicode: true)
|
|
107
109
|
server_challenge = NTLM.pack_int64le(server_challenge) if server_challenge.is_a?(Integer)
|
|
108
110
|
|
|
109
111
|
# Authenticate the exact blob bytes. Rebuilding the blob truncates
|
|
110
112
|
# sub-second timestamps and rejects otherwise valid responses.
|
|
111
113
|
expected_proof = OpenSSL::HMAC.digest(
|
|
112
|
-
OpenSSL::Digest
|
|
114
|
+
OpenSSL::Digest.new('MD5'), key, server_challenge + ntlm_response[16..]
|
|
113
115
|
)
|
|
114
|
-
expected_proof == ntlm_response[0,16]
|
|
116
|
+
expected_proof == ntlm_response[0, 16]
|
|
115
117
|
end
|
|
116
118
|
end
|
|
117
119
|
end
|
data/lib/net/ntlm/message.rb
CHANGED
|
@@ -1,137 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Net
|
|
2
|
-
module NTLM
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
:
|
|
9
|
-
:
|
|
10
|
-
:
|
|
11
|
-
:
|
|
12
|
-
:
|
|
13
|
-
:
|
|
14
|
-
:
|
|
15
|
-
:
|
|
16
|
-
:
|
|
17
|
-
:
|
|
18
|
-
:
|
|
19
|
-
:
|
|
20
|
-
:
|
|
21
|
-
:
|
|
22
|
-
:
|
|
23
|
-
:
|
|
24
|
-
:
|
|
25
|
-
:
|
|
26
|
-
:
|
|
27
|
-
:
|
|
28
|
-
:
|
|
29
|
-
:KEY56 => 0x80000000,
|
|
4
|
+
module NTLM
|
|
5
|
+
SSP_SIGN = "NTLMSSP\0"
|
|
6
|
+
|
|
7
|
+
# See [2.2.2.5 NEGOTIATE](https://msdn.microsoft.com/en-us/library/cc236650.aspx)
|
|
8
|
+
FLAGS = {
|
|
9
|
+
UNICODE: 0x00000001,
|
|
10
|
+
OEM: 0x00000002,
|
|
11
|
+
REQUEST_TARGET: 0x00000004,
|
|
12
|
+
SIGN: 0x00000010,
|
|
13
|
+
SEAL: 0x00000020,
|
|
14
|
+
NEG_DATAGRAM: 0x00000040,
|
|
15
|
+
NEG_LM_KEY: 0x00000080,
|
|
16
|
+
NTLM: 0x00000200,
|
|
17
|
+
NEG_ANONYMOUS: 0x00000800,
|
|
18
|
+
DOMAIN_SUPPLIED: 0x00001000,
|
|
19
|
+
WORKSTATION_SUPPLIED: 0x00002000,
|
|
20
|
+
ALWAYS_SIGN: 0x00008000,
|
|
21
|
+
TARGET_TYPE_DOMAIN: 0x00010000,
|
|
22
|
+
TARGET_TYPE_SERVER: 0x00020000,
|
|
23
|
+
NTLM2_KEY: 0x00080000,
|
|
24
|
+
NEG_IDENTIFY: 0x00100000,
|
|
25
|
+
NON_NT_SESSION_KEY: 0x00400000,
|
|
26
|
+
TARGET_INFO: 0x00800000,
|
|
27
|
+
NEG_VERSION: 0x02000000,
|
|
28
|
+
KEY128: 0x20000000,
|
|
29
|
+
KEY_EXCHANGE: 0x40000000,
|
|
30
|
+
KEY56: 0x80000000,
|
|
30
31
|
# Undocumented flags:
|
|
31
|
-
:
|
|
32
|
-
:
|
|
33
|
-
:
|
|
34
|
-
:
|
|
35
|
-
:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
:
|
|
42
|
-
|
|
43
|
-
:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class
|
|
32
|
+
MBZ9: 0x00000008,
|
|
33
|
+
NETWARE: 0x00000100,
|
|
34
|
+
NEG_NT_ONLY: 0x00000400,
|
|
35
|
+
MBZ7: 0x00000800, # alias for :NEG_ANONYMOUS
|
|
36
|
+
LOCAL_CALL: 0x00004000
|
|
37
|
+
}.freeze
|
|
38
|
+
|
|
39
|
+
FLAG_KEYS = FLAGS.keys.sort { |a, b| FLAGS[a] <=> FLAGS[b] }.freeze
|
|
40
|
+
|
|
41
|
+
DEFAULT_FLAGS = {
|
|
42
|
+
TYPE1: FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] |
|
|
43
|
+
FLAGS[:NTLM2_KEY],
|
|
44
|
+
TYPE2: FLAGS[:UNICODE],
|
|
45
|
+
TYPE3: FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY]
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
# @private false
|
|
49
|
+
class Message < FieldSet
|
|
50
|
+
class << Message
|
|
51
|
+
def parse(str)
|
|
52
|
+
m = Type0.new
|
|
53
|
+
m.parse(str)
|
|
54
|
+
type_class = { 1 => Type1, 2 => Type2, 3 => Type3 }[m.type]
|
|
55
|
+
raise ArgumentError, "unknown type: #{m.type}" unless type_class
|
|
56
|
+
|
|
57
|
+
type_class.new.parse(str)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def decode64(str)
|
|
61
|
+
parse(Base64.decode64(str))
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @return [self]
|
|
49
66
|
def parse(str)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
when 2
|
|
56
|
-
t = Type2.new.parse(str)
|
|
57
|
-
when 3
|
|
58
|
-
t = Type3.new.parse(str)
|
|
59
|
-
else
|
|
60
|
-
raise ArgumentError, "unknown type: #{m.type}"
|
|
67
|
+
super
|
|
68
|
+
|
|
69
|
+
while disabled_fields? && serialize.size < str.size
|
|
70
|
+
enable_next_disabled_field
|
|
71
|
+
super
|
|
61
72
|
end
|
|
62
|
-
|
|
73
|
+
|
|
74
|
+
self
|
|
63
75
|
end
|
|
64
76
|
|
|
65
|
-
def
|
|
66
|
-
|
|
77
|
+
def flag?(flag)
|
|
78
|
+
(self[:flag].value & FLAGS[flag]) == FLAGS[flag]
|
|
67
79
|
end
|
|
68
|
-
end
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
def parse(str)
|
|
72
|
-
super
|
|
81
|
+
alias has_flag? flag?
|
|
73
82
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
self.class.names.find { |name| !self[name].active && enable(name) }
|
|
77
|
-
super
|
|
83
|
+
def flag=(flag)
|
|
84
|
+
self[:flag].value |= FLAGS[flag]
|
|
78
85
|
end
|
|
79
86
|
|
|
80
|
-
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def has_flag?(flag)
|
|
84
|
-
(self[:flag].value & FLAGS[flag]) == FLAGS[flag]
|
|
85
|
-
end
|
|
87
|
+
alias set_flag flag=
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
def dump_flags
|
|
90
|
+
FLAG_KEYS.each { |k| print(k, '=', flag?(k), "\n") }
|
|
91
|
+
end
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
def serialize
|
|
94
|
+
deflag
|
|
95
|
+
super + security_buffers.map { |_n, f|
|
|
96
|
+
f.value + (flag?(:UNICODE) ? "\x00".b * (f.value.length % 2) : '')
|
|
97
|
+
}.join
|
|
98
|
+
end
|
|
94
99
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
end
|
|
100
|
+
def encode64
|
|
101
|
+
Base64.encode64(serialize).gsub(/\n/, '')
|
|
102
|
+
end
|
|
99
103
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
def decode64(str)
|
|
105
|
+
parse(Base64.decode64(str))
|
|
106
|
+
end
|
|
103
107
|
|
|
104
|
-
|
|
105
|
-
parse(Base64.decode64(str))
|
|
106
|
-
end
|
|
108
|
+
alias head_size size
|
|
107
109
|
|
|
108
|
-
|
|
110
|
+
def data_size
|
|
111
|
+
security_buffers.inject(0) { |sum, a| sum + a[1].data_size }
|
|
112
|
+
end
|
|
109
113
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
def size
|
|
115
|
+
head_size + data_size
|
|
116
|
+
end
|
|
113
117
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
def security_buffers
|
|
119
|
+
@alist.find_all { |_n, f| f.instance_of?(SecurityBuffer) }
|
|
120
|
+
end
|
|
117
121
|
|
|
122
|
+
def deflag
|
|
123
|
+
security_buffers.inject(head_size) { |cur, a|
|
|
124
|
+
a[1].offset = cur
|
|
125
|
+
cur += a[1].data_size
|
|
126
|
+
flag?(:UNICODE) ? cur + cur % 2 : cur
|
|
127
|
+
}
|
|
128
|
+
end
|
|
118
129
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
130
|
+
def data_edge
|
|
131
|
+
security_buffers.map { |_n, f| f.active ? f.offset : size }.min
|
|
132
|
+
end
|
|
122
133
|
|
|
123
|
-
|
|
124
|
-
security_buffers.inject(head_size){|cur, a|
|
|
125
|
-
a[1].offset = cur
|
|
126
|
-
cur += a[1].data_size
|
|
127
|
-
has_flag?(:UNICODE) ? cur + cur % 2 : cur
|
|
128
|
-
}
|
|
129
|
-
end
|
|
134
|
+
private
|
|
130
135
|
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
# enable the next disabled field
|
|
137
|
+
def enable_next_disabled_field
|
|
138
|
+
self.class.names.find { |name| !self[name].active && enable(name) }
|
|
139
|
+
end
|
|
133
140
|
end
|
|
134
|
-
|
|
135
141
|
end
|
|
136
142
|
end
|
|
137
|
-
end
|