dap 0.0.9 → 0.0.10
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/lib/dap/filter/ldap.rb +3 -1
- data/lib/dap/proto/ldap.rb +147 -18
- data/lib/dap/version.rb +1 -1
- data/spec/dap/filter/ldap_filter_spec.rb +1 -0
- data/spec/dap/proto/ldap_proto_spec.rb +72 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b61714c9b3553759bb7c726aad187acbf2adddf
|
4
|
+
data.tar.gz: e5f0ea147aa9a6f9fbcbb09854221b3d247eb467
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53c0c68b19babf428a673063963122bb57f1e185a8205423f29c5e66ee9b4e2e3f2cfa5e905741a2bcf742358b2c5ddaef689edb862cb152a68ba453c7e8f592
|
7
|
+
data.tar.gz: fa2601b3d4bcaa99f0e3c4087e101e80f423a42228b95d53325c9bf31355aa8b186c3801f2a20b06a8cc1f4791ac6dfd1bc0b631fc829d4afa7e97b211fad25f
|
data/lib/dap/filter/ldap.rb
CHANGED
@@ -36,9 +36,11 @@ class FilterDecodeLdapSearchResult
|
|
36
36
|
begin
|
37
37
|
elem_decoded = OpenSSL::ASN1.decode(element)
|
38
38
|
rescue Exception => e
|
39
|
-
err_msg = 'FilterDecodeLdapSearchResult - Unable to decode
|
39
|
+
err_msg = 'FilterDecodeLdapSearchResult - Unable to decode ASN.1 element'
|
40
40
|
$stderr.puts "#{err_msg}: #{e}"
|
41
41
|
$stderr.puts e.backtrace
|
42
|
+
$stderr.puts "Element:\n\t#{element.inspect}"
|
43
|
+
$stderr.puts "Element hex:\n\t#{element.unpack('H*')}\n\n"
|
42
44
|
info['Error'] = { 'errorMessage' => err_msg }
|
43
45
|
next
|
44
46
|
end
|
data/lib/dap/proto/ldap.rb
CHANGED
@@ -2,6 +2,44 @@ module Dap
|
|
2
2
|
module Proto
|
3
3
|
class LDAP
|
4
4
|
|
5
|
+
# LDAPResult element resultCode lookup
|
6
|
+
# Reference: https://tools.ietf.org/html/rfc4511#section-4.1.9
|
7
|
+
# https://ldapwiki.willeke.com/wiki/LDAP%20Result%20Codes
|
8
|
+
RESULT_DESC = {
|
9
|
+
0 => 'success',
|
10
|
+
1 => 'operationsError',
|
11
|
+
2 => 'protocolError',
|
12
|
+
3 => 'timeLimitExceeded',
|
13
|
+
4 => 'sizeLimitExceeded',
|
14
|
+
5 => 'compareFalse',
|
15
|
+
6 => 'compareTrue',
|
16
|
+
7 => 'authMethodNotSupported',
|
17
|
+
8 => 'strongerAuthRequired',
|
18
|
+
9 => 'reserved',
|
19
|
+
10 => 'referral',
|
20
|
+
11 => 'adminLimitExceeded',
|
21
|
+
12 => 'unavailableCriticalExtension',
|
22
|
+
13 => 'confidentialityRequired',
|
23
|
+
14 => 'saslBindInProgress',
|
24
|
+
16 => 'noSuchAttribute',
|
25
|
+
17 => 'undefinedAttributeType',
|
26
|
+
18 => 'inappropriateMatching',
|
27
|
+
19 => 'constraintViolation',
|
28
|
+
20 => 'attributeOrValueExists',
|
29
|
+
21 => 'invalidAttributeSyntax',
|
30
|
+
32 => 'noSuchObject',
|
31
|
+
34 => 'invalidDNSyntax',
|
32
|
+
48 => 'inappropriateAuthentication',
|
33
|
+
49 => 'invalidCredentials',
|
34
|
+
50 => 'insufficientAccessRights',
|
35
|
+
51 => 'busy',
|
36
|
+
52 => 'unavailable',
|
37
|
+
53 => 'unwillingToPerform',
|
38
|
+
64 => 'namingViolation',
|
39
|
+
80 => 'other',
|
40
|
+
82 => 'localError (client response)',
|
41
|
+
94 => 'noResultsReturned (client response)',
|
42
|
+
}
|
5
43
|
|
6
44
|
#
|
7
45
|
# Parse ASN1 element and extract the length.
|
@@ -25,11 +63,15 @@ class LDAP
|
|
25
63
|
len_bytes = length - 128
|
26
64
|
return unless data.length > len_bytes + 2
|
27
65
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
66
|
+
# This shouldn't happen...
|
67
|
+
return unless len_bytes > 0
|
68
|
+
|
69
|
+
length = 0
|
70
|
+
len_bytes.times do |i|
|
71
|
+
temp_len = data.byteslice(2 + i).unpack('C')[0]
|
72
|
+
length = ( length << 8 ) + temp_len
|
32
73
|
end
|
74
|
+
|
33
75
|
elem_start += len_bytes
|
34
76
|
end
|
35
77
|
|
@@ -61,6 +103,49 @@ class LDAP
|
|
61
103
|
messages
|
62
104
|
end
|
63
105
|
|
106
|
+
#
|
107
|
+
# Parse an LDAPResult (not SearchResult) ASN.1 structure
|
108
|
+
# Reference: https://tools.ietf.org/html/rfc4511#section-4.1.9
|
109
|
+
#
|
110
|
+
# @param data [OpenSSL::ASN1::ASN1Data] LDAPResult structure
|
111
|
+
# @return [Hash] Hash containing decoded LDAP response
|
112
|
+
#
|
113
|
+
def self.parse_ldapresult(ldap_result)
|
114
|
+
results = {}
|
115
|
+
|
116
|
+
# Sanity check the result code element
|
117
|
+
if ldap_result.value[0] && ldap_result.value[0].value
|
118
|
+
code_elem = ldap_result.value[0]
|
119
|
+
return results unless code_elem.tag == 10 && code_elem.tag_class == :UNIVERSAL
|
120
|
+
results['resultCode'] = code_elem.value.to_i
|
121
|
+
end
|
122
|
+
|
123
|
+
# These are probably safe if the resultCode validates
|
124
|
+
results['resultDesc'] = RESULT_DESC[ results['resultCode'] ] if results['resultCode']
|
125
|
+
results['resultMatchedDN'] = ldap_result.value[1].value if ldap_result.value[1] && ldap_result.value[1].value
|
126
|
+
results['resultdiagMessage'] = ldap_result.value[2].value if ldap_result.value[2] && ldap_result.value[2].value
|
127
|
+
|
128
|
+
# Handle optional elements that may be returned by certain
|
129
|
+
# LDAP application messages
|
130
|
+
ldap_result.value.each do |element|
|
131
|
+
next unless element.tag_class && element.tag && element.value
|
132
|
+
next unless element.tag_class == :CONTEXT_SPECIFIC
|
133
|
+
|
134
|
+
case element.tag
|
135
|
+
when 3
|
136
|
+
results['referral'] = element.value
|
137
|
+
when 7
|
138
|
+
results['serverSaslCreds'] = element.value
|
139
|
+
when 10
|
140
|
+
results['responseName'] = element.value
|
141
|
+
when 11
|
142
|
+
results['responseValue'] = element.value
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
results
|
147
|
+
end
|
148
|
+
|
64
149
|
#
|
65
150
|
# Parse an LDAP SearchResult entry.
|
66
151
|
#
|
@@ -88,29 +173,73 @@ class LDAP
|
|
88
173
|
results['objectName'] = data.value[1].value[0].value
|
89
174
|
end
|
90
175
|
|
91
|
-
|
176
|
+
if data.value[1].value[1]
|
177
|
+
attrib_hash = {}
|
92
178
|
|
93
|
-
|
94
|
-
|
179
|
+
# Handle PartialAttributeValues
|
180
|
+
data.value[1].value[1].each do |partial_attrib|
|
95
181
|
|
96
|
-
|
97
|
-
|
182
|
+
value_array = []
|
183
|
+
attrib_type = partial_attrib.value[0].value
|
98
184
|
|
99
|
-
|
100
|
-
|
185
|
+
partial_attrib.value[1].each do |part_attrib_value|
|
186
|
+
value_array.push(part_attrib_value.value)
|
187
|
+
end
|
188
|
+
|
189
|
+
attrib_hash[attrib_type] = value_array
|
101
190
|
end
|
102
191
|
|
103
|
-
|
192
|
+
results['PartialAttributes'] = attrib_hash
|
104
193
|
end
|
105
194
|
|
106
|
-
|
107
|
-
|
108
|
-
elsif data.value[1].tag == 5
|
195
|
+
elsif data.value[1] && data.value[1].tag == 5
|
109
196
|
# SearchResultDone found..
|
110
197
|
result_type = 'SearchResultDone'
|
111
|
-
|
112
|
-
|
113
|
-
|
198
|
+
ldap_result = data.value[1]
|
199
|
+
|
200
|
+
if ldap_result.value[0] && ldap_result.value[0].class == OpenSSL::ASN1::Sequence
|
201
|
+
# Encoding of the SearchResultDone seems to vary, this is RFC format
|
202
|
+
# of an LDAPResult ASN.1 structure in which the data is contained in a
|
203
|
+
# Sequence
|
204
|
+
results = parse_ldapresult(ldap_result.value[0])
|
205
|
+
elsif ldap_result.value[0]
|
206
|
+
# LDAPResult w/o outer Sequence wrapper, used by MS Windows
|
207
|
+
results = parse_ldapresult(ldap_result)
|
208
|
+
end
|
209
|
+
if data.value[2] && data.value[2].tag == 10
|
210
|
+
# Unknown structure for providing a response, looks like LDAPResult
|
211
|
+
# but placed at a higher level in the response, salvage what we can..
|
212
|
+
results['resultCode'] = data.value[2].value.to_i if data.value[2].value
|
213
|
+
results['resultDesc'] = RESULT_DESC[ results['resultCode'] ] if results['resultCode']
|
214
|
+
results['resultMatchedDN'] = data.value[3].value if data.value[3] && data.value[3].value
|
215
|
+
results['resultdiagMessage'] = data.value[4].value if data.value[4] && data.value[4].value
|
216
|
+
end
|
217
|
+
|
218
|
+
elsif data.value[1] && data.value[1].tag == 1
|
219
|
+
result_type = 'BindResponse'
|
220
|
+
results = parse_ldapresult(data.value[1])
|
221
|
+
|
222
|
+
elsif data.value[1] && data.value[1].tag == 2
|
223
|
+
result_type = 'UnbindRequest'
|
224
|
+
|
225
|
+
elsif data.value[1] && data.value[1].tag == 3
|
226
|
+
# There is no legitimate use of application tag 3
|
227
|
+
# in this context per RFC 4511. Try to figure
|
228
|
+
# out what the intent is.
|
229
|
+
resp_data = data.value[1]
|
230
|
+
if resp_data.value[0].tag == 10 && resp_data.value[2].tag == 4
|
231
|
+
# Probably an incorrectly tagged BindResponse
|
232
|
+
result_type = 'BindResponse'
|
233
|
+
results = parse_ldapresult(resp_data)
|
234
|
+
else
|
235
|
+
result_type = 'UnhandledTag'
|
236
|
+
results['tagNumber'] = data.value[1].tag.to_i if data.value[1].tag
|
237
|
+
end
|
238
|
+
|
239
|
+
elsif data.value[1] && data.value[1].tag == 24
|
240
|
+
result_type = 'ExtendedResponse'
|
241
|
+
results = parse_ldapresult(data.value[1])
|
242
|
+
|
114
243
|
else
|
115
244
|
# Unhandled tag
|
116
245
|
result_type = 'UnhandledTag'
|
data/lib/dap/version.rb
CHANGED
@@ -4,6 +4,7 @@ describe Dap::Proto::LDAP do
|
|
4
4
|
describe '.decode_elem_length' do
|
5
5
|
context 'testing lengths shorter than 128 bits' do
|
6
6
|
data = ['301402'].pack('H*')
|
7
|
+
|
7
8
|
let(:decode_len) { subject.decode_elem_length(data) }
|
8
9
|
it 'returns a Fixnum' do
|
9
10
|
expect(decode_len.class).to eq(::Fixnum)
|
@@ -15,6 +16,7 @@ describe Dap::Proto::LDAP do
|
|
15
16
|
|
16
17
|
context 'testing lengths greater than 128 bits' do
|
17
18
|
data = ['308400000bc102010'].pack('H*')
|
19
|
+
|
18
20
|
let(:decode_len) { subject.decode_elem_length(data) }
|
19
21
|
it 'returns a Fixnum' do
|
20
22
|
expect(decode_len.class).to eq(::Fixnum)
|
@@ -24,8 +26,21 @@ describe Dap::Proto::LDAP do
|
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
29
|
+
context 'testing with 3 byte length' do
|
30
|
+
data = ['3083015e0802010764'].pack('H*')
|
31
|
+
|
32
|
+
let(:decode_len) { subject.decode_elem_length(data) }
|
33
|
+
it 'returns a Fixnum' do
|
34
|
+
expect(decode_len.class).to eq(::Fixnum)
|
35
|
+
end
|
36
|
+
it 'returns value correctly' do
|
37
|
+
expect(decode_len).to eq(89613)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
27
41
|
context 'testing invalid length' do
|
28
42
|
data = ['308400000bc1'].pack('H*')
|
43
|
+
|
29
44
|
let(:decode_len) { subject.decode_elem_length(data) }
|
30
45
|
it 'returns nil as expected' do
|
31
46
|
expect(decode_len).to eq(nil)
|
@@ -77,6 +92,44 @@ describe Dap::Proto::LDAP do
|
|
77
92
|
end
|
78
93
|
end
|
79
94
|
|
95
|
+
describe '.parse_ldapresult' do
|
96
|
+
|
97
|
+
context 'testing valid data' do
|
98
|
+
hex = ['300c02010765070a010004000400']
|
99
|
+
data = OpenSSL::ASN1.decode(hex.pack('H*'))
|
100
|
+
|
101
|
+
let(:parse_ldapresult) { subject.parse_ldapresult(data.value[1]) }
|
102
|
+
it 'returns Hash as expected' do
|
103
|
+
expect(parse_ldapresult.class).to eq(::Hash)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'returns results as expected' do
|
107
|
+
test_val = { 'resultCode' => 0,
|
108
|
+
'resultDesc' => 'success',
|
109
|
+
'resultMatchedDN' => '',
|
110
|
+
'resultdiagMessage' => ''
|
111
|
+
}
|
112
|
+
expect(parse_ldapresult).to eq(test_val)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'testing invalid data' do
|
117
|
+
hex = ['300702010765020400']
|
118
|
+
data = OpenSSL::ASN1.decode(hex.pack('H*'))
|
119
|
+
|
120
|
+
let(:parse_ldapresult) { subject.parse_ldapresult(data.value[1]) }
|
121
|
+
it 'returns Hash as expected' do
|
122
|
+
expect(parse_ldapresult.class).to eq(::Hash)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'returns empty Hash as expected' do
|
126
|
+
test_val = {}
|
127
|
+
expect(parse_ldapresult).to eq(test_val)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
80
133
|
describe '.parse_messages' do
|
81
134
|
|
82
135
|
context 'testing SearchResultEntry' do
|
@@ -115,6 +168,7 @@ describe Dap::Proto::LDAP do
|
|
115
168
|
it 'returns SearchResultDone value as expected' do
|
116
169
|
test_val = ['SearchResultDone', {
|
117
170
|
'resultCode' => 0,
|
171
|
+
'resultDesc' => 'success',
|
118
172
|
'resultMatchedDN' => '',
|
119
173
|
'resultdiagMessage' => ''
|
120
174
|
}]
|
@@ -122,6 +176,24 @@ describe Dap::Proto::LDAP do
|
|
122
176
|
end
|
123
177
|
end
|
124
178
|
|
179
|
+
context 'testing SearchResultDone - edge case #1' do
|
180
|
+
hex = ['300802010765000a0101']
|
181
|
+
data = OpenSSL::ASN1.decode(hex.pack('H*'))
|
182
|
+
|
183
|
+
let(:parse_message) { subject.parse_message(data) }
|
184
|
+
it 'returns Array as expected' do
|
185
|
+
expect(parse_message.class).to eq(::Array)
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'returns operationsError as expected' do
|
189
|
+
test_val = ['SearchResultDone', {
|
190
|
+
'resultCode' => 1,
|
191
|
+
'resultDesc' => 'operationsError'
|
192
|
+
}]
|
193
|
+
expect(parse_message).to eq(test_val)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
125
197
|
context 'testing UnhandledTag' do
|
126
198
|
hex = ['300c02010767070a010004000400']
|
127
199
|
data = OpenSSL::ASN1.decode(hex.pack('H*'))
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rapid7 Research
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -248,7 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
248
248
|
version: '0'
|
249
249
|
requirements: []
|
250
250
|
rubyforge_project:
|
251
|
-
rubygems_version: 2.5
|
251
|
+
rubygems_version: 2.2.5
|
252
252
|
signing_key:
|
253
253
|
specification_version: 4
|
254
254
|
summary: 'DAP: The Data Analysis Pipeline'
|
@@ -257,3 +257,4 @@ test_files:
|
|
257
257
|
- spec/dap/proto/ipmi_spec.rb
|
258
258
|
- spec/dap/proto/ldap_proto_spec.rb
|
259
259
|
- spec/spec_helper.rb
|
260
|
+
has_rdoc:
|