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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 487ec0ed128ac49a84730159ed2561cc0abe5a0b
4
- data.tar.gz: 4709517322ef8b40ea7527ff852b7f664a256d19
3
+ metadata.gz: 8b61714c9b3553759bb7c726aad187acbf2adddf
4
+ data.tar.gz: e5f0ea147aa9a6f9fbcbb09854221b3d247eb467
5
5
  SHA512:
6
- metadata.gz: 102e072962fc919016d7261e26c1b05c85c3017006810216761202dc0109b4d40ac72db361325058fa1e76798d27c2f38bd474aff386760621aebac5e306693e
7
- data.tar.gz: d7667a595feeb261c0109f94d304097f1bcad3b47e70d7dd655cdbf3efa829d806c209f5b4807f92a5f69bd762b41115c07b021321dc26830506d498e0998518
6
+ metadata.gz: 53c0c68b19babf428a673063963122bb57f1e185a8205423f29c5e66ee9b4e2e3f2cfa5e905741a2bcf742358b2c5ddaef689edb862cb152a68ba453c7e8f592
7
+ data.tar.gz: fa2601b3d4bcaa99f0e3c4087e101e80f423a42228b95d53325c9bf31355aa8b186c3801f2a20b06a8cc1f4791ac6dfd1bc0b631fc829d4afa7e97b211fad25f
@@ -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 ANS1 element'
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
@@ -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
- if len_bytes == 2
29
- length = data.byteslice(2, len_bytes).unpack('S>')[0]
30
- else
31
- length = data.byteslice(2, len_bytes).unpack('L>')[0]
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
- attrib_hash = {}
176
+ if data.value[1].value[1]
177
+ attrib_hash = {}
92
178
 
93
- # Handle PartialAttributeValues
94
- data.value[1].value[1].each do |partial_attrib|
179
+ # Handle PartialAttributeValues
180
+ data.value[1].value[1].each do |partial_attrib|
95
181
 
96
- value_array = []
97
- attrib_type = partial_attrib.value[0].value
182
+ value_array = []
183
+ attrib_type = partial_attrib.value[0].value
98
184
 
99
- partial_attrib.value[1].each do |part_attrib_value|
100
- value_array.push(part_attrib_value.value)
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
- attrib_hash[attrib_type] = value_array
192
+ results['PartialAttributes'] = attrib_hash
104
193
  end
105
194
 
106
- results['PartialAttributes'] = attrib_hash
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
- results['resultCode'] = data.value[1].value[0].value.to_i if data.value[1].value[0].value
112
- results['resultMatchedDN'] = data.value[1].value[1].value if data.value[1].value[1].value
113
- results['resultdiagMessage'] = data.value[1].value[2].value if data.value[1].value[2].value
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'
@@ -1,3 +1,3 @@
1
1
  module Dap
2
- VERSION = "0.0.9"
2
+ VERSION = "0.0.10"
3
3
  end
@@ -18,6 +18,7 @@ describe Dap::Filter::FilterDecodeLdapSearchResult do
18
18
  it 'returns expected value' do
19
19
  test_val = { 'SearchResultDone' => {
20
20
  'resultCode' => 0,
21
+ 'resultDesc' => 'success',
21
22
  'resultMatchedDN' => '',
22
23
  'resultdiagMessage' => ''
23
24
  },
@@ -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.9
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-07-27 00:00:00.000000000 Z
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.1
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: