net-ldap 0.0.5
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.
Potentially problematic release.
This version of net-ldap might be problematic. Click here for more details.
- data/COPYING +272 -0
- data/History.txt +96 -0
- data/LICENSE +55 -0
- data/Manifest.txt +30 -0
- data/README.txt +62 -0
- data/Rakefile +18 -0
- data/Release-Announcement +95 -0
- data/lib/net/ber.rb +557 -0
- data/lib/net/ldap.rb +1613 -0
- data/lib/net/ldap/dataset.rb +108 -0
- data/lib/net/ldap/entry.rb +269 -0
- data/lib/net/ldap/filter.rb +499 -0
- data/lib/net/ldap/pdu.rb +258 -0
- data/lib/net/ldap/psw.rb +64 -0
- data/lib/net/ldif.rb +39 -0
- data/lib/net/snmp.rb +297 -0
- data/pre-setup.rb +45 -0
- data/setup.rb +1366 -0
- data/test/common.rb +7 -0
- data/test/test_ber.rb +100 -0
- data/test/test_entry.rb +7 -0
- data/test/test_filter.rb +83 -0
- data/test/test_ldif.rb +59 -0
- data/test/test_password.rb +17 -0
- data/test/test_snmp.rb +130 -0
- data/test/testdata.ldif +101 -0
- data/tests/NOTICE.txt +6 -0
- data/tests/testldap.rb +190 -0
- data/testserver/ldapserver.rb +229 -0
- data/testserver/testdata.ldif +101 -0
- metadata +105 -0
data/README.txt
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
= Net::LDAP for Ruby
|
2
|
+
|
3
|
+
* http://rubyforge.org/projects/net-ldap
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Pure Ruby LDAP library.
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
The Lightweight Directory Access Protocol (LDAP) is an Internet protocol
|
12
|
+
for accessing distributed directory services.
|
13
|
+
|
14
|
+
Net::LDAP is an LDAP support library written in pure Ruby. It supports
|
15
|
+
most LDAP client features and a subset of server features as well.
|
16
|
+
|
17
|
+
* Standards-based (going for RFC 4511)
|
18
|
+
* Portable: 100% Ruby
|
19
|
+
|
20
|
+
== SYNOPSIS:
|
21
|
+
|
22
|
+
See Net::LDAP for documentation and usage samples.
|
23
|
+
|
24
|
+
== REQUIREMENTS:
|
25
|
+
|
26
|
+
Net::LDAP requires Ruby 1.8.2 or better.
|
27
|
+
|
28
|
+
== INSTALL:
|
29
|
+
|
30
|
+
Net::LDAP is a pure Ruby library. It does not require any external
|
31
|
+
libraries.
|
32
|
+
|
33
|
+
You can install the RubyGems version of Net::LDAP available from the
|
34
|
+
usual sources.
|
35
|
+
|
36
|
+
* gem install net-ldap
|
37
|
+
|
38
|
+
If using the packaged (.tgz) version; it can be installed with:
|
39
|
+
|
40
|
+
* ruby setup.rb
|
41
|
+
|
42
|
+
== CREDITS:
|
43
|
+
|
44
|
+
Net::LDAP was originally developed by:
|
45
|
+
|
46
|
+
* Francis Cianfrocca <garbagecat10@gmail.com>
|
47
|
+
|
48
|
+
Contributions since:
|
49
|
+
|
50
|
+
* Austin Ziegler <halostatue@gmail.com>
|
51
|
+
* Emiel van de Laar <gemiel@gmail.com>
|
52
|
+
|
53
|
+
== LICENSE:
|
54
|
+
|
55
|
+
Copyright (C) 2006 by Francis Cianfrocca
|
56
|
+
|
57
|
+
Please read the file LICENSE for licensing restrictions on this library. In
|
58
|
+
the simplest terms, this library is available under the same terms as Ruby
|
59
|
+
itself.
|
60
|
+
|
61
|
+
Available under the same terms as Ruby. See LICENSE in the main
|
62
|
+
distribution for full licensing information.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
# Add 'lib' to load path.
|
7
|
+
$LOAD_PATH.unshift( "#{File.dirname(__FILE__)}/lib" )
|
8
|
+
|
9
|
+
# Pull in local 'net/ldap' as opposed to an installed version.
|
10
|
+
require 'net/ldap'
|
11
|
+
|
12
|
+
Hoe.new('net-ldap', Net::LDAP::VERSION) do |p|
|
13
|
+
p.rubyforge_name = 'net-ldap'
|
14
|
+
p.developer('Francis Cianfrocca', 'garbagecat10@gmail.com')
|
15
|
+
p.developer('Emiel van de Laar', 'gemiel@gmail.com')
|
16
|
+
end
|
17
|
+
|
18
|
+
# vim: syntax=Ruby
|
@@ -0,0 +1,95 @@
|
|
1
|
+
We're pleased to announce version 0.0.4 of Net::LDAP, the pure-Ruby LDAP library.
|
2
|
+
|
3
|
+
This version adds an implementation of Net::LDAP#bind_as, which allows
|
4
|
+
you to authenticate users who log into your applications using simple
|
5
|
+
identifiers like email addresses and simple usernames. Thanks to Simon Claret
|
6
|
+
for suggesting the original implementation in his page on the Rails-Wiki,
|
7
|
+
and for valuable comments and help with testing.
|
8
|
+
|
9
|
+
We have un-deprecated Net::LDAP#modify, which can be useful with
|
10
|
+
LDAP servers that observe non-standard transactional and concurrency
|
11
|
+
semantics in LDAP Modify operations. Note: this is a documentation change,
|
12
|
+
not a code change. Thanks to Justin Forder for providing the rationale
|
13
|
+
for this change.
|
14
|
+
|
15
|
+
We added a larger set of special characters which may appear in RFC-2254
|
16
|
+
standard search filters. Thanks to Andre Nathan for this patch.
|
17
|
+
|
18
|
+
We fixed a bug that was preventing Net::LDAP#open from being called more
|
19
|
+
than once on the same object.
|
20
|
+
|
21
|
+
|
22
|
+
Net::LDAP is a feature-complete LDAP client which can access as much as
|
23
|
+
possible of the functionality of the most-used LDAP server implementations.
|
24
|
+
This library does not wrap any existing native-code LDAP libraries, creates no
|
25
|
+
Ruby extensions, and has no dependencies external to Ruby.
|
26
|
+
|
27
|
+
If anyone wants to contribute suggestions, insights or (especially)
|
28
|
+
code, please email me at garbagecat10 .. .. gmail.com.
|
29
|
+
|
30
|
+
= What is Net::LDAP for Ruby?
|
31
|
+
This library provides a pure-Ruby implementation of an LDAP client.
|
32
|
+
It can be used to access any server which implements the LDAP protocol.
|
33
|
+
|
34
|
+
Net::LDAP is intended to provide full LDAP functionality while hiding
|
35
|
+
the more arcane aspects of the LDAP protocol itself, so as to make the
|
36
|
+
programming interface as Ruby-like as possible.
|
37
|
+
|
38
|
+
In particular, this means that there is no direct dependence on the
|
39
|
+
structure of the various "traditional" LDAP clients. This is a ground-up
|
40
|
+
rethinking of the LDAP API.
|
41
|
+
|
42
|
+
Net::LDAP is based on RFC-2251, which specifies the Lightweight Directory
|
43
|
+
Access Protocol, as amended and extended by subsequent RFCs and by the more
|
44
|
+
widely-used directory implementations.
|
45
|
+
|
46
|
+
Homepage:: http://rubyforge.org/projects/net-ldap/
|
47
|
+
Download:: http://rubyforge.org/frs/?group_id=143
|
48
|
+
Copyright:: 2006 by Francis Cianfrocca
|
49
|
+
|
50
|
+
== LICENCE NOTES
|
51
|
+
Please read the file LICENCE for licensing restrictions on this library. In
|
52
|
+
the simplest terms, this library is available under the same terms as Ruby
|
53
|
+
itself.
|
54
|
+
|
55
|
+
== Requirements and Installation
|
56
|
+
Net::LDAP requires Ruby 1.8.2 or better.
|
57
|
+
|
58
|
+
Net::LDAP can be installed with:
|
59
|
+
|
60
|
+
% ruby setup.rb
|
61
|
+
|
62
|
+
Alternatively, you can use the RubyGems version of Net::LDAP available
|
63
|
+
as ruby-net-ldap-0.0.2.gem from the usual sources.
|
64
|
+
|
65
|
+
== Whet your appetite:
|
66
|
+
require 'net/ldap'
|
67
|
+
|
68
|
+
ldap = Net::LDAP.new :host => server_ip_address,
|
69
|
+
:port => 389,
|
70
|
+
:auth => {
|
71
|
+
:method => :simple,
|
72
|
+
:username => "cn=manager,dc=example,dc=com",
|
73
|
+
:password => "opensesame"
|
74
|
+
}
|
75
|
+
|
76
|
+
filter = Net::LDAP::Filter.eq( "cn", "George*" )
|
77
|
+
treebase = "dc=example,dc=com"
|
78
|
+
|
79
|
+
ldap.search( :base => treebase, :filter => filter ) do |entry|
|
80
|
+
puts "DN: #{entry.dn}"
|
81
|
+
entry.each do |attribute, values|
|
82
|
+
puts " #{attribute}:"
|
83
|
+
values.each do |value|
|
84
|
+
puts " --->#{value}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
p ldap.get_operation_result
|
90
|
+
|
91
|
+
== Net::LDAP 0.0.2: May 3, 2006
|
92
|
+
* Fixed malformation in distro tarball and gem.
|
93
|
+
* Improved documentation.
|
94
|
+
* Supported "paged search control."
|
95
|
+
|
data/lib/net/ber.rb
ADDED
@@ -0,0 +1,557 @@
|
|
1
|
+
# $Id$
|
2
|
+
#
|
3
|
+
# NET::BER
|
4
|
+
# Mixes ASN.1/BER convenience methods into several standard classes.
|
5
|
+
# Also provides BER parsing functionality.
|
6
|
+
#
|
7
|
+
#----------------------------------------------------------------------------
|
8
|
+
#
|
9
|
+
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
10
|
+
#
|
11
|
+
# Gmail: garbagecat10
|
12
|
+
#
|
13
|
+
# This program is free software; you can redistribute it and/or modify
|
14
|
+
# it under the terms of the GNU General Public License as published by
|
15
|
+
# the Free Software Foundation; either version 2 of the License, or
|
16
|
+
# (at your option) any later version.
|
17
|
+
#
|
18
|
+
# This program is distributed in the hope that it will be useful,
|
19
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
20
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
21
|
+
# GNU General Public License for more details.
|
22
|
+
#
|
23
|
+
# You should have received a copy of the GNU General Public License
|
24
|
+
# along with this program; if not, write to the Free Software
|
25
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
26
|
+
#
|
27
|
+
#---------------------------------------------------------------------------
|
28
|
+
#
|
29
|
+
#
|
30
|
+
|
31
|
+
|
32
|
+
module Net
|
33
|
+
|
34
|
+
module BER
|
35
|
+
|
36
|
+
class BerError < StandardError; end
|
37
|
+
|
38
|
+
|
39
|
+
class BerIdentifiedString < String
|
40
|
+
attr_accessor :ber_identifier
|
41
|
+
def initialize args
|
42
|
+
super args
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class BerIdentifiedArray < Array
|
47
|
+
attr_accessor :ber_identifier
|
48
|
+
def initialize
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class BerIdentifiedNull
|
54
|
+
attr_accessor :ber_identifier
|
55
|
+
def to_ber
|
56
|
+
"\005\000"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class BerIdentifiedOid
|
61
|
+
attr_accessor :ber_identifier
|
62
|
+
def initialize oid
|
63
|
+
if oid.is_a?(String)
|
64
|
+
oid = oid.split(/\./).map {|s| s.to_i }
|
65
|
+
end
|
66
|
+
@value = oid
|
67
|
+
end
|
68
|
+
def to_ber
|
69
|
+
# Provisional implementation.
|
70
|
+
# We ASSUME that our incoming value is an array, and we
|
71
|
+
# use the Array#to_ber_oid method defined below.
|
72
|
+
# We probably should obsolete that method, actually, in
|
73
|
+
# and move the code here.
|
74
|
+
# WE ARE NOT CURRENTLY ENCODING THE BER-IDENTIFIER.
|
75
|
+
# This implementation currently hardcodes 6, the universal OID tag.
|
76
|
+
ary = @value.dup
|
77
|
+
first = ary.shift
|
78
|
+
raise Net::BER::BerError.new(" invalid OID" ) unless [0,1,2].include?(first)
|
79
|
+
first = first * 40 + ary.shift
|
80
|
+
ary.unshift first
|
81
|
+
oid = ary.pack("w*")
|
82
|
+
[6, oid.length].pack("CC") + oid
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
#--
|
87
|
+
# This condenses our nicely self-documenting ASN hashes down
|
88
|
+
# to an array for fast lookups.
|
89
|
+
# Scoped to be called as a module method, but not intended for
|
90
|
+
# user code to call.
|
91
|
+
#
|
92
|
+
def self.compile_syntax syn
|
93
|
+
out = [nil] * 256
|
94
|
+
syn.each {|tclass,tclasses|
|
95
|
+
tagclass = {:universal=>0, :application=>64, :context_specific=>128, :private=>192} [tclass]
|
96
|
+
tclasses.each {|codingtype,codings|
|
97
|
+
encoding = {:primitive=>0, :constructed=>32} [codingtype]
|
98
|
+
codings.each {|tag,objtype|
|
99
|
+
out[tagclass + encoding + tag] = objtype
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
out
|
104
|
+
end
|
105
|
+
|
106
|
+
# This module is for mixing into IO and IO-like objects.
|
107
|
+
module BERParser
|
108
|
+
|
109
|
+
# The order of these follows the class-codes in BER.
|
110
|
+
# Maybe this should have been a hash.
|
111
|
+
TagClasses = [:universal, :application, :context_specific, :private]
|
112
|
+
|
113
|
+
BuiltinSyntax = BER.compile_syntax( {
|
114
|
+
:universal => {
|
115
|
+
:primitive => {
|
116
|
+
1 => :boolean,
|
117
|
+
2 => :integer,
|
118
|
+
4 => :string,
|
119
|
+
5 => :null,
|
120
|
+
6 => :oid,
|
121
|
+
10 => :integer,
|
122
|
+
13 => :string # (relative OID)
|
123
|
+
},
|
124
|
+
:constructed => {
|
125
|
+
16 => :array,
|
126
|
+
17 => :array
|
127
|
+
}
|
128
|
+
},
|
129
|
+
:context_specific => {
|
130
|
+
:primitive => {
|
131
|
+
10 => :integer
|
132
|
+
}
|
133
|
+
}
|
134
|
+
})
|
135
|
+
|
136
|
+
#
|
137
|
+
# read_ber
|
138
|
+
# TODO: clean this up so it works properly with partial
|
139
|
+
# packets coming from streams that don't block when
|
140
|
+
# we ask for more data (like StringIOs). At it is,
|
141
|
+
# this can throw TypeErrors and other nasties.
|
142
|
+
#--
|
143
|
+
# BEWARE, this violates DRY and is largely equal in functionality to
|
144
|
+
# read_ber_from_string. Eventually that method may subsume the functionality
|
145
|
+
# of this one.
|
146
|
+
#
|
147
|
+
def read_ber syntax=nil
|
148
|
+
# don't bother with this line, since IO#getbyte by definition returns nil on eof.
|
149
|
+
#return nil if eof?
|
150
|
+
|
151
|
+
id = getbyte or return nil # don't trash this value, we'll use it later
|
152
|
+
#tag = id & 31
|
153
|
+
#tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" )
|
154
|
+
#tagclass = TagClasses[ id >> 6 ]
|
155
|
+
#encoding = (id & 0x20 != 0) ? :constructed : :primitive
|
156
|
+
|
157
|
+
n = getbyte
|
158
|
+
lengthlength,contentlength = if n <= 127
|
159
|
+
[1,n]
|
160
|
+
else
|
161
|
+
# Replaced the inject because it profiles hot.
|
162
|
+
#j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getbyte}
|
163
|
+
j = 0
|
164
|
+
read( n & 127 ).each_byte {|n1| j = (j << 8) + n1}
|
165
|
+
[1 + (n & 127), j]
|
166
|
+
end
|
167
|
+
|
168
|
+
newobj = read contentlength
|
169
|
+
|
170
|
+
# This exceptionally clever and clear bit of code is verrrry slow.
|
171
|
+
objtype = (syntax && syntax[id]) || BuiltinSyntax[id]
|
172
|
+
|
173
|
+
|
174
|
+
# == is expensive so sort this if/else so the common cases are at the top.
|
175
|
+
obj = if objtype == :string
|
176
|
+
#(newobj || "").dup
|
177
|
+
s = BerIdentifiedString.new( newobj || "" )
|
178
|
+
s.ber_identifier = id
|
179
|
+
s
|
180
|
+
elsif objtype == :integer
|
181
|
+
j = 0
|
182
|
+
newobj.each_byte {|b| j = (j << 8) + b}
|
183
|
+
j
|
184
|
+
elsif objtype == :oid
|
185
|
+
# cf X.690 pgh 8.19 for an explanation of this algorithm.
|
186
|
+
# Potentially not good enough. We may need a BerIdentifiedOid
|
187
|
+
# as a subclass of BerIdentifiedArray, to get the ber identifier
|
188
|
+
# and also a to_s method that produces the familiar dotted notation.
|
189
|
+
oid = newobj.unpack("w*")
|
190
|
+
f = oid.shift
|
191
|
+
g = if f < 40
|
192
|
+
[0, f]
|
193
|
+
elsif f < 80
|
194
|
+
[1, f-40]
|
195
|
+
else
|
196
|
+
[2, f-80] # f-80 can easily be > 80. What a weird optimization.
|
197
|
+
end
|
198
|
+
oid.unshift g.last
|
199
|
+
oid.unshift g.first
|
200
|
+
oid
|
201
|
+
elsif objtype == :array
|
202
|
+
#seq = []
|
203
|
+
seq = BerIdentifiedArray.new
|
204
|
+
seq.ber_identifier = id
|
205
|
+
sio = StringIO.new( newobj || "" )
|
206
|
+
# Interpret the subobject, but note how the loop
|
207
|
+
# is built: nil ends the loop, but false (a valid
|
208
|
+
# BER value) does not!
|
209
|
+
while (e = sio.read_ber(syntax)) != nil
|
210
|
+
seq << e
|
211
|
+
end
|
212
|
+
seq
|
213
|
+
elsif objtype == :boolean
|
214
|
+
newobj != "\000"
|
215
|
+
elsif objtype == :null
|
216
|
+
n = BerIdentifiedNull.new
|
217
|
+
n.ber_identifier = id
|
218
|
+
n
|
219
|
+
else
|
220
|
+
#raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
|
221
|
+
raise BerError.new( "unsupported object type: id=#{id}" )
|
222
|
+
end
|
223
|
+
|
224
|
+
# Add the identifier bits into the object if it's a String or an Array.
|
225
|
+
# We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway.
|
226
|
+
# Replaced this mechanism with subclasses because the instance_eval profiled too hot.
|
227
|
+
#obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end"
|
228
|
+
#obj.ber_identifier = id if obj.respond_to?(:ber_identifier)
|
229
|
+
obj
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
#--
|
234
|
+
# Violates DRY! This replicates the functionality of #read_ber.
|
235
|
+
# Eventually this method may replace that one.
|
236
|
+
# This version of #read_ber behaves properly in the face of incomplete
|
237
|
+
# data packets. If a full BER object is detected, we return an array containing
|
238
|
+
# the detected object and the number of bytes consumed from the string.
|
239
|
+
# If we don't detect a complete packet, return nil.
|
240
|
+
#
|
241
|
+
# Observe that weirdly we recursively call the original #read_ber in here.
|
242
|
+
# That needs to be fixed if we ever obsolete the original method in favor of this one.
|
243
|
+
def read_ber_from_string str, syntax=nil
|
244
|
+
id = str[0] or return nil
|
245
|
+
n = str[1] or return nil
|
246
|
+
n_consumed = 2
|
247
|
+
lengthlength,contentlength = if n <= 127
|
248
|
+
[1,n]
|
249
|
+
else
|
250
|
+
n1 = n & 127
|
251
|
+
return nil unless str.length >= (n_consumed + n1)
|
252
|
+
j = 0
|
253
|
+
n1.times {
|
254
|
+
j = (j << 8) + str[n_consumed]
|
255
|
+
n_consumed += 1
|
256
|
+
}
|
257
|
+
[1 + (n1), j]
|
258
|
+
end
|
259
|
+
|
260
|
+
return nil unless str.length >= (n_consumed + contentlength)
|
261
|
+
newobj = str[n_consumed...(n_consumed + contentlength)]
|
262
|
+
n_consumed += contentlength
|
263
|
+
|
264
|
+
objtype = (syntax && syntax[id]) || BuiltinSyntax[id]
|
265
|
+
|
266
|
+
# == is expensive so sort this if/else so the common cases are at the top.
|
267
|
+
obj = if objtype == :array
|
268
|
+
seq = BerIdentifiedArray.new
|
269
|
+
seq.ber_identifier = id
|
270
|
+
sio = StringIO.new( newobj || "" )
|
271
|
+
# Interpret the subobject, but note how the loop
|
272
|
+
# is built: nil ends the loop, but false (a valid
|
273
|
+
# BER value) does not!
|
274
|
+
# Also, we can use the standard read_ber method because
|
275
|
+
# we know for sure we have enough data. (Although this
|
276
|
+
# might be faster than the standard method.)
|
277
|
+
while (e = sio.read_ber(syntax)) != nil
|
278
|
+
seq << e
|
279
|
+
end
|
280
|
+
seq
|
281
|
+
elsif objtype == :string
|
282
|
+
s = BerIdentifiedString.new( newobj || "" )
|
283
|
+
s.ber_identifier = id
|
284
|
+
s
|
285
|
+
elsif objtype == :integer
|
286
|
+
j = 0
|
287
|
+
newobj.each_byte {|b| j = (j << 8) + b}
|
288
|
+
j
|
289
|
+
elsif objtype == :oid
|
290
|
+
# cf X.690 pgh 8.19 for an explanation of this algorithm.
|
291
|
+
# Potentially not good enough. We may need a BerIdentifiedOid
|
292
|
+
# as a subclass of BerIdentifiedArray, to get the ber identifier
|
293
|
+
# and also a to_s method that produces the familiar dotted notation.
|
294
|
+
oid = newobj.unpack("w*")
|
295
|
+
f = oid.shift
|
296
|
+
g = if f < 40
|
297
|
+
[0,f]
|
298
|
+
elsif f < 80
|
299
|
+
[1, f-40]
|
300
|
+
else
|
301
|
+
[2, f-80] # f-80 can easily be > 80. What a weird optimization.
|
302
|
+
end
|
303
|
+
oid.unshift g.last
|
304
|
+
oid.unshift g.first
|
305
|
+
oid
|
306
|
+
elsif objtype == :boolean
|
307
|
+
newobj != "\000"
|
308
|
+
elsif objtype == :null
|
309
|
+
n = BerIdentifiedNull.new
|
310
|
+
n.ber_identifier = id
|
311
|
+
n
|
312
|
+
else
|
313
|
+
raise BerError.new( "unsupported object type: id=#{id}" )
|
314
|
+
end
|
315
|
+
|
316
|
+
[obj, n_consumed]
|
317
|
+
end
|
318
|
+
|
319
|
+
end # module BERParser
|
320
|
+
end # module BER
|
321
|
+
|
322
|
+
end # module Net
|
323
|
+
|
324
|
+
|
325
|
+
class IO
|
326
|
+
include Net::BER::BERParser
|
327
|
+
end
|
328
|
+
|
329
|
+
require "stringio"
|
330
|
+
class StringIO
|
331
|
+
include Net::BER::BERParser
|
332
|
+
end
|
333
|
+
|
334
|
+
begin
|
335
|
+
require 'openssl'
|
336
|
+
class OpenSSL::SSL::SSLSocket
|
337
|
+
include Net::BER::BERParser
|
338
|
+
end
|
339
|
+
rescue LoadError
|
340
|
+
# Ignore LoadError.
|
341
|
+
# DON'T ignore NameError, which means the SSLSocket class
|
342
|
+
# is somehow unavailable on this implementation of Ruby's openssl.
|
343
|
+
# This may be WRONG, however, because we don't yet know how Ruby's
|
344
|
+
# openssl behaves on machines with no OpenSSL library. I suppose
|
345
|
+
# it's possible they do not fail to require 'openssl' but do not
|
346
|
+
# create the classes. So this code is provisional.
|
347
|
+
# Also, you might think that OpenSSL::SSL::SSLSocket inherits from
|
348
|
+
# IO so we'd pick it up above. But you'd be wrong.
|
349
|
+
end
|
350
|
+
|
351
|
+
|
352
|
+
|
353
|
+
class String
|
354
|
+
include Net::BER::BERParser
|
355
|
+
def read_ber syntax=nil
|
356
|
+
StringIO.new(self).read_ber(syntax)
|
357
|
+
end
|
358
|
+
def read_ber! syntax=nil
|
359
|
+
obj,n_consumed = read_ber_from_string(self, syntax)
|
360
|
+
if n_consumed
|
361
|
+
self.slice!(0...n_consumed)
|
362
|
+
obj
|
363
|
+
else
|
364
|
+
nil
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
#----------------------------------------------
|
370
|
+
|
371
|
+
|
372
|
+
class FalseClass
|
373
|
+
#
|
374
|
+
# to_ber
|
375
|
+
#
|
376
|
+
def to_ber
|
377
|
+
"\001\001\000"
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
|
382
|
+
class TrueClass
|
383
|
+
#
|
384
|
+
# to_ber
|
385
|
+
#
|
386
|
+
def to_ber
|
387
|
+
"\001\001\001"
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
|
392
|
+
|
393
|
+
class Fixnum
|
394
|
+
#
|
395
|
+
# to_ber
|
396
|
+
#
|
397
|
+
def to_ber
|
398
|
+
"\002" + to_ber_internal
|
399
|
+
end
|
400
|
+
|
401
|
+
#
|
402
|
+
# to_ber_enumerated
|
403
|
+
#
|
404
|
+
def to_ber_enumerated
|
405
|
+
"\012" + to_ber_internal
|
406
|
+
end
|
407
|
+
|
408
|
+
#
|
409
|
+
# to_ber_length_encoding
|
410
|
+
#
|
411
|
+
def to_ber_length_encoding
|
412
|
+
if self <= 127
|
413
|
+
[self].pack('C')
|
414
|
+
else
|
415
|
+
i = [self].pack('N').sub(/^[\0]+/,"")
|
416
|
+
[0x80 + i.length].pack('C') + i
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
# Generate a BER-encoding for an application-defined INTEGER.
|
421
|
+
# Example: SNMP's Counter, Gauge, and TimeTick types.
|
422
|
+
#
|
423
|
+
def to_ber_application tag
|
424
|
+
[0x40 + tag].pack("C") + to_ber_internal
|
425
|
+
end
|
426
|
+
|
427
|
+
#--
|
428
|
+
# Called internally to BER-encode the length and content bytes of a Fixnum.
|
429
|
+
# The caller will prepend the tag byte.
|
430
|
+
def to_ber_internal
|
431
|
+
# PLEASE optimize this code path. It's awfully ugly and probably slow.
|
432
|
+
# It also doesn't understand negative numbers yet.
|
433
|
+
raise Net::BER::BerError.new( "range error in fixnum" ) unless self >= 0
|
434
|
+
z = [self].pack("N")
|
435
|
+
zlen = if self < 0x80
|
436
|
+
1
|
437
|
+
elsif self < 0x8000
|
438
|
+
2
|
439
|
+
elsif self < 0x800000
|
440
|
+
3
|
441
|
+
else
|
442
|
+
4
|
443
|
+
end
|
444
|
+
[zlen].pack("C") + z[0-zlen,zlen]
|
445
|
+
end
|
446
|
+
private :to_ber_internal
|
447
|
+
|
448
|
+
end # class Fixnum
|
449
|
+
|
450
|
+
|
451
|
+
class Bignum
|
452
|
+
|
453
|
+
def to_ber
|
454
|
+
#i = [self].pack('w')
|
455
|
+
#i.length > 126 and raise Net::BER::BerError.new( "range error in bignum" )
|
456
|
+
#[2, i.length].pack("CC") + i
|
457
|
+
|
458
|
+
# Ruby represents Bignums as two's-complement numbers so we may actually be
|
459
|
+
# good as far as representing negatives goes.
|
460
|
+
# I'm sure this implementation can be improved performance-wise if necessary.
|
461
|
+
# Ruby's Bignum#size returns the number of bytes in the internal representation
|
462
|
+
# of the number, but it can and will include leading zero bytes on at least
|
463
|
+
# some implementations. Evidently Ruby stores these as sets of quadbytes.
|
464
|
+
# It's not illegal in BER to encode all of the leading zeroes but let's strip
|
465
|
+
# them out anyway.
|
466
|
+
#
|
467
|
+
sz = self.size
|
468
|
+
out = "\000" * sz
|
469
|
+
(sz*8).times {|bit|
|
470
|
+
if self[bit] == 1
|
471
|
+
out[bit/8] += (1 << (bit % 8))
|
472
|
+
end
|
473
|
+
}
|
474
|
+
|
475
|
+
while out.length > 1 and out[-1] == 0
|
476
|
+
out.slice!(-1,1)
|
477
|
+
end
|
478
|
+
|
479
|
+
[2, out.length].pack("CC") + out.reverse
|
480
|
+
end
|
481
|
+
|
482
|
+
end
|
483
|
+
|
484
|
+
|
485
|
+
|
486
|
+
class String
|
487
|
+
#
|
488
|
+
# to_ber
|
489
|
+
# A universal octet-string is tag number 4,
|
490
|
+
# but others are possible depending on the context, so we
|
491
|
+
# let the caller give us one.
|
492
|
+
# The preferred way to do this in user code is via to_ber_application_sring
|
493
|
+
# and to_ber_contextspecific.
|
494
|
+
#
|
495
|
+
def to_ber code = 4
|
496
|
+
[code].pack('C') + length.to_ber_length_encoding + self
|
497
|
+
end
|
498
|
+
|
499
|
+
#
|
500
|
+
# to_ber_application_string
|
501
|
+
#
|
502
|
+
def to_ber_application_string code
|
503
|
+
to_ber( 0x40 + code )
|
504
|
+
end
|
505
|
+
|
506
|
+
#
|
507
|
+
# to_ber_contextspecific
|
508
|
+
#
|
509
|
+
def to_ber_contextspecific code
|
510
|
+
to_ber( 0x80 + code )
|
511
|
+
end
|
512
|
+
|
513
|
+
end # class String
|
514
|
+
|
515
|
+
|
516
|
+
|
517
|
+
class Array
|
518
|
+
#
|
519
|
+
# to_ber_appsequence
|
520
|
+
# An application-specific sequence usually gets assigned
|
521
|
+
# a tag that is meaningful to the particular protocol being used.
|
522
|
+
# This is different from the universal sequence, which usually
|
523
|
+
# gets a tag value of 16.
|
524
|
+
# Now here's an interesting thing: We're adding the X.690
|
525
|
+
# "application constructed" code at the top of the tag byte (0x60),
|
526
|
+
# but some clients, notably ldapsearch, send "context-specific
|
527
|
+
# constructed" (0xA0). The latter would appear to violate RFC-1777,
|
528
|
+
# but what do I know? We may need to change this.
|
529
|
+
#
|
530
|
+
|
531
|
+
def to_ber id = 0; to_ber_seq_internal( 0x30 + id ); end
|
532
|
+
def to_ber_set id = 0; to_ber_seq_internal( 0x31 + id ); end
|
533
|
+
def to_ber_sequence id = 0; to_ber_seq_internal( 0x30 + id ); end
|
534
|
+
def to_ber_appsequence id = 0; to_ber_seq_internal( 0x60 + id ); end
|
535
|
+
def to_ber_contextspecific id = 0; to_ber_seq_internal( 0xA0 + id ); end
|
536
|
+
|
537
|
+
def to_ber_oid
|
538
|
+
ary = self.dup
|
539
|
+
first = ary.shift
|
540
|
+
raise Net::BER::BerError.new( "invalid OID" ) unless [0,1,2].include?(first)
|
541
|
+
first = first * 40 + ary.shift
|
542
|
+
ary.unshift first
|
543
|
+
oid = ary.pack("w*")
|
544
|
+
[6, oid.length].pack("CC") + oid
|
545
|
+
end
|
546
|
+
|
547
|
+
private
|
548
|
+
def to_ber_seq_internal code
|
549
|
+
s = ''
|
550
|
+
self.each{|x| s = s + x}
|
551
|
+
[code].pack('C') + s.length.to_ber_length_encoding + s
|
552
|
+
end
|
553
|
+
|
554
|
+
|
555
|
+
end # class Array
|
556
|
+
|
557
|
+
|