ldap-ber 0.1.0
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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +6 -0
- data/.rubocop.yml +20 -0
- data/.rubocop_todo.yml +44 -0
- data/Gemfile +17 -0
- data/LICENSE +25 -0
- data/README.md +9 -0
- data/Rakefile +16 -0
- data/bin/console +8 -0
- data/bin/setup +5 -0
- data/ldap-ber.gemspec +30 -0
- data/lib/ber.rb +76 -0
- data/lib/ber.yaml +199 -0
- data/lib/ber/function.rb +104 -0
- data/lib/ber/identified.rb +6 -0
- data/lib/ber/identified/array.rb +14 -0
- data/lib/ber/identified/null.rb +13 -0
- data/lib/ber/identified/oid.rb +27 -0
- data/lib/ber/identified/string.rb +20 -0
- data/lib/ber/refinements.rb +10 -0
- data/lib/ber/refinements/array.rb +59 -0
- data/lib/ber/refinements/false_class.rb +11 -0
- data/lib/ber/refinements/integer.rb +59 -0
- data/lib/ber/refinements/io.rb +19 -0
- data/lib/ber/refinements/ssl_socket.rb +21 -0
- data/lib/ber/refinements/string.rb +74 -0
- data/lib/ber/refinements/string_io.rb +27 -0
- data/lib/ber/refinements/true_class.rb +14 -0
- data/lib/ber/version.rb +5 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/unit/ber_spec.rb +50 -0
- data/spec/unit/identified/identified_string_spec.rb +46 -0
- data/spec/unit/refinements/array_spec.rb +32 -0
- data/spec/unit/refinements/boolean_spec.rb +16 -0
- data/spec/unit/refinements/integer_spec.rb +73 -0
- data/spec/unit/refinements/string_spec.rb +33 -0
- data/spec/unit/request_spec.rb +38 -0
- metadata +159 -0
data/lib/ber/function.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ber/identified'
|
4
|
+
|
5
|
+
module BER
|
6
|
+
# @see BER.function
|
7
|
+
#
|
8
|
+
# Used within refinements to identify...
|
9
|
+
#
|
10
|
+
class Function
|
11
|
+
def parse_ber_object(syntax, id, data)
|
12
|
+
object_type = (syntax && syntax[id]) || IDENTIFIED[id]
|
13
|
+
|
14
|
+
case object_type
|
15
|
+
when :string
|
16
|
+
s = BerIdentifiedString.new(data || EMPTY_STRING)
|
17
|
+
s.ber_identifier = id
|
18
|
+
s
|
19
|
+
|
20
|
+
when :integer
|
21
|
+
neg = !(data.unpack1('C') & 0x80).zero?
|
22
|
+
int = 0
|
23
|
+
|
24
|
+
data.each_byte do |b|
|
25
|
+
int = (int << 8) + (neg ? 255 - b : b)
|
26
|
+
end
|
27
|
+
|
28
|
+
if neg
|
29
|
+
(int + 1) * -1
|
30
|
+
else
|
31
|
+
int
|
32
|
+
end
|
33
|
+
|
34
|
+
when :oid
|
35
|
+
oid = data.unpack('w*')
|
36
|
+
f = oid.shift
|
37
|
+
g = if f < 40
|
38
|
+
[0, f]
|
39
|
+
elsif f < 80
|
40
|
+
[1, f - 40]
|
41
|
+
else
|
42
|
+
[2, f - 80]
|
43
|
+
end
|
44
|
+
oid.unshift g.last
|
45
|
+
oid.unshift g.first
|
46
|
+
BerIdentifiedOid.new(oid)
|
47
|
+
|
48
|
+
when :array
|
49
|
+
seq = BerIdentifiedArray.new
|
50
|
+
seq.ber_identifier = id
|
51
|
+
sio = StringIO.new(data || EMPTY_STRING)
|
52
|
+
|
53
|
+
until (e = read_ber(sio, syntax)).nil?
|
54
|
+
seq << e
|
55
|
+
end
|
56
|
+
seq
|
57
|
+
|
58
|
+
when :boolean
|
59
|
+
data != "\000"
|
60
|
+
|
61
|
+
when :null
|
62
|
+
n = BerIdentifiedNull.new
|
63
|
+
n.ber_identifier = id
|
64
|
+
n
|
65
|
+
|
66
|
+
else
|
67
|
+
raise Error, "Unsupported object type: id=#{id}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def read_ber_length(object)
|
72
|
+
n = object.getbyte
|
73
|
+
|
74
|
+
if n <= 0x7f
|
75
|
+
n
|
76
|
+
elsif n == 0x80
|
77
|
+
-1
|
78
|
+
elsif n == 0xff
|
79
|
+
raise Error, 'Invalid BER length 0xFF detected.'
|
80
|
+
else
|
81
|
+
v = 0
|
82
|
+
object.read(n & 0x7f).each_byte do |b|
|
83
|
+
v = (v << 8) + b
|
84
|
+
end
|
85
|
+
|
86
|
+
v
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def read_ber(object, syntax)
|
91
|
+
return unless (id = object.getbyte)
|
92
|
+
|
93
|
+
content_length = read_ber_length(object)
|
94
|
+
|
95
|
+
yield id, content_length if block_given?
|
96
|
+
|
97
|
+
raise Error, 'Indeterminite BER content length not implemented.' if content_length == -1
|
98
|
+
|
99
|
+
data = object.read(content_length)
|
100
|
+
|
101
|
+
parse_ber_object(syntax, id, data)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BER
|
4
|
+
class BerIdentifiedOid
|
5
|
+
attr_accessor :ber_identifier
|
6
|
+
|
7
|
+
def initialize(oid)
|
8
|
+
@value = oid.split(/\./).map(&:to_i) if oid.is_a?(String)
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_ber
|
12
|
+
to_ber_oid
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_ber_oid
|
16
|
+
@value.to_ber_oid
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
@value.join('.')
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_arr
|
24
|
+
@value.dup
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BER
|
4
|
+
# Wrapper around a String
|
5
|
+
#
|
6
|
+
# @see BER::Function.parse_ber_object
|
7
|
+
class BerIdentifiedString < String
|
8
|
+
attr_accessor :ber_identifier
|
9
|
+
|
10
|
+
def initialize(args)
|
11
|
+
super
|
12
|
+
|
13
|
+
return unless encoding == Encoding::BINARY
|
14
|
+
|
15
|
+
current_encoding = encoding
|
16
|
+
force_encoding('UTF-8')
|
17
|
+
force_encoding(current_encoding) unless valid_encoding?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ber/refinements/array'
|
4
|
+
require 'ber/refinements/false_class'
|
5
|
+
require 'ber/refinements/integer'
|
6
|
+
require 'ber/refinements/io'
|
7
|
+
require 'ber/refinements/ssl_socket'
|
8
|
+
require 'ber/refinements/string'
|
9
|
+
require 'ber/refinements/string_io'
|
10
|
+
require 'ber/refinements/true_class'
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Refine Array
|
4
|
+
#
|
5
|
+
module BER
|
6
|
+
refine ::Array do
|
7
|
+
# 48
|
8
|
+
#
|
9
|
+
def to_ber(id = 0)
|
10
|
+
to_ber_seq_internal(0x30 + id)
|
11
|
+
end
|
12
|
+
|
13
|
+
alias_method :to_ber_sequence, :to_ber
|
14
|
+
|
15
|
+
# 49
|
16
|
+
#
|
17
|
+
def to_ber_set(id = 0)
|
18
|
+
to_ber_seq_internal(0x31 + id)
|
19
|
+
end
|
20
|
+
|
21
|
+
# 96
|
22
|
+
#
|
23
|
+
def to_ber_appsequence(id = 0)
|
24
|
+
to_ber_seq_internal(0x60 + id)
|
25
|
+
end
|
26
|
+
|
27
|
+
# 160
|
28
|
+
#
|
29
|
+
def to_ber_contextspecific(id = 0)
|
30
|
+
to_ber_seq_internal(0xa0 + id)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_ber_oid
|
34
|
+
ary = dup
|
35
|
+
first = ary.shift
|
36
|
+
raise BER::Error, 'Invalid OID' unless [0, 1, 2].include?(first)
|
37
|
+
|
38
|
+
first = first * 40 + ary.shift
|
39
|
+
ary.unshift first
|
40
|
+
oid = ary.pack('w*')
|
41
|
+
[6, oid.length].pack('CC') + oid
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_ber_control
|
45
|
+
ary = self[0].is_a?(Array) ? self : [self]
|
46
|
+
ary = ary.collect do |control_sequence|
|
47
|
+
control_sequence.collect(&:to_ber).to_ber_sequence.reject_empty_ber_arrays
|
48
|
+
end
|
49
|
+
ary.to_ber_sequence.reject_empty_ber_arrays
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def to_ber_seq_internal(code)
|
55
|
+
s = join
|
56
|
+
[code].pack('C') + s.length.to_ber_length_encoding + s
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Refine Integer
|
4
|
+
#
|
5
|
+
module BER
|
6
|
+
refine ::Integer do
|
7
|
+
# @return [String]
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
def to_ber
|
11
|
+
"\002#{to_ber_internal}"
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
def to_ber_enumerated
|
18
|
+
"\012#{to_ber_internal}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_ber_length_encoding
|
22
|
+
if self <= 127
|
23
|
+
[self].pack('C')
|
24
|
+
else
|
25
|
+
i = [self].pack('N').sub(/^[\0]+/, ::BER::EMPTY_STRING)
|
26
|
+
[0x80 + i.length].pack('C') + i
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param tag [Integer]
|
31
|
+
#
|
32
|
+
# @return [String]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
def to_ber_application(tag)
|
36
|
+
[0x40 + tag].pack('C') + to_ber_internal
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def to_ber_internal
|
42
|
+
size = 1
|
43
|
+
size += 1 until ((negative? ? ~self : self) >> (size * 8)).zero?
|
44
|
+
|
45
|
+
size += 1 if positive? && (self & (0x80 << (size - 1) * 8)).positive?
|
46
|
+
|
47
|
+
size += 1 if negative? && (self & (0x80 << (size - 1) * 8)).zero?
|
48
|
+
|
49
|
+
result = [size]
|
50
|
+
|
51
|
+
while size.positive?
|
52
|
+
result << ((self >> ((size - 1) * 8)) & 0xff)
|
53
|
+
size -= 1
|
54
|
+
end
|
55
|
+
|
56
|
+
result.pack('C*')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Refine IO
|
4
|
+
#
|
5
|
+
module BER
|
6
|
+
refine ::IO do
|
7
|
+
def read_ber(syntax = ::BER::ASN_SYNTAX)
|
8
|
+
::BER.function.read_ber(self, syntax)
|
9
|
+
end
|
10
|
+
|
11
|
+
def read_ber_length
|
12
|
+
::BER.function.read_ber_length(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse_ber_object(syntax, id, data)
|
16
|
+
::BER.function.parse_ber_object(self, syntax, id, data)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Refine ::OpenSSL::SSL::SSLSocket
|
4
|
+
#
|
5
|
+
module BER
|
6
|
+
if defined? ::OpenSSL
|
7
|
+
refine ::OpenSSL::SSL::SSLSocket do
|
8
|
+
def read_ber(syntax = ::BER::ASN_SYNTAX)
|
9
|
+
::BER.function.read_ber(self, syntax)
|
10
|
+
end
|
11
|
+
|
12
|
+
def read_ber_length
|
13
|
+
::BER.function.read_ber_length(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse_ber_object(syntax, id, data)
|
17
|
+
::BER.function.parse_ber_object(self, syntax, id, data)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
# Refine String
|
6
|
+
#
|
7
|
+
module BER
|
8
|
+
refine ::String do
|
9
|
+
def read_ber(syntax = ::BER::ASN_SYNTAX)
|
10
|
+
::StringIO.new(self).read_ber(syntax)
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_ber!(syntax = ::BER::ASN_SYNTAX)
|
14
|
+
io = ::StringIO.new(self)
|
15
|
+
result = io.read_ber(syntax)
|
16
|
+
slice!(0...io.pos)
|
17
|
+
result
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_ber_length
|
21
|
+
::BER.function.read_ber_length(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_ber_object(syntax, id, data)
|
25
|
+
::BER.function.parse_ber_object(self, syntax, id, data)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param code [String] (0x04)
|
29
|
+
#
|
30
|
+
# @return [String]
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
def to_ber(code = 0x04)
|
34
|
+
raw_string = ascii_encoded
|
35
|
+
[code].pack('C') + raw_string.length.to_ber_length_encoding + raw_string
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param code [String] (0x04)
|
39
|
+
#
|
40
|
+
# @return [String]
|
41
|
+
#
|
42
|
+
# @api public
|
43
|
+
def to_ber_bin(code = 0x04)
|
44
|
+
[code].pack('C') + length.to_ber_length_encoding + self
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param code [String]
|
48
|
+
#
|
49
|
+
# @return [String]
|
50
|
+
#
|
51
|
+
# @api public
|
52
|
+
def to_ber_application_string(code)
|
53
|
+
to_ber(0x40 + code)
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_ber_contextspecific(code)
|
57
|
+
to_ber(0x80 + code)
|
58
|
+
end
|
59
|
+
|
60
|
+
def reject_empty_ber_arrays
|
61
|
+
gsub(/0\000/n, ::BER::EMPTY_STRING)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def ascii_encoded
|
67
|
+
encode('UTF-8').force_encoding('ASCII-8BIT')
|
68
|
+
rescue Encoding::UndefinedConversionError,
|
69
|
+
Encoding::ConverterNotFoundError,
|
70
|
+
Encoding::InvalidByteSequenceError
|
71
|
+
self
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|