dap 0.0.9 → 0.0.10

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