1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
# $Id: pdu.rb 126 2006-05-31 15:55:16Z blackhedd $
#
# LDAP PDU support classes
#
#
#----------------------------------------------------------------------------
#
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
#
# Gmail: garbagecat10
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
#---------------------------------------------------------------------------
#
module Net
class LdapPduError < Exception; end
class LdapPdu
BindResult = 1
SearchReturnedData = 4
SearchResult = 5
ModifyResponse = 7
AddResponse = 9
DeleteResponse = 11
ModifyRDNResponse = 13
SearchResultReferral = 19
attr_reader :msg_id, :app_tag
attr_reader :search_dn, :search_attributes, :search_entry
attr_reader :search_referrals
#
# initialize
# An LDAP PDU always looks like a BerSequence with
# at least two elements: an integer (message-id number), and
# an application-specific sequence.
# Some LDAPv3 packets also include an optional
# third element, which is a sequence of "controls"
# (See RFC 2251, section 4.1.12).
# The application-specific tag in the sequence tells
# us what kind of packet it is, and each kind has its
# own format, defined in RFC-1777.
# Observe that many clients (such as ldapsearch)
# do not necessarily enforce the expected application
# tags on received protocol packets. This implementation
# does interpret the RFC strictly in this regard, and
# it remains to be seen whether there are servers out
# there that will not work well with our approach.
#
# Added a controls-processor to SearchResult.
# Didn't add it everywhere because it just _feels_
# like it will need to be refactored.
#
def initialize ber_object
begin
@msg_id = ber_object[0].to_i
@app_tag = ber_object[1].ber_identifier - 0x60
rescue
# any error becomes a data-format error
raise LdapPduError.new( "ldap-pdu format error" )
end
case @app_tag
when BindResult
parse_ldap_result ber_object[1]
when SearchReturnedData
parse_search_return ber_object[1]
when SearchResultReferral
parse_search_referral ber_object[1]
when SearchResult
parse_ldap_result ber_object[1]
parse_controls(ber_object[2]) if ber_object[2]
when ModifyResponse
parse_ldap_result ber_object[1]
when AddResponse
parse_ldap_result ber_object[1]
when DeleteResponse
parse_ldap_result ber_object[1]
when ModifyRDNResponse
parse_ldap_result ber_object[1]
else
raise LdapPduError.new( "unknown pdu-type: #{@app_tag}" )
end
end
#
# result_code
# This returns an LDAP result code taken from the PDU,
# but it will be nil if there wasn't a result code.
# That can easily happen depending on the type of packet.
#
def result_code code = :resultCode
@ldap_result and @ldap_result[code]
end
# Return RFC-2251 Controls if any.
# Messy. Does this functionality belong somewhere else?
def result_controls
@ldap_controls || []
end
#
# parse_ldap_result
#
def parse_ldap_result sequence
sequence.length >= 3 or raise LdapPduError
@ldap_result = {:resultCode => sequence[0], :matchedDN => sequence[1], :errorMessage => sequence[2]}
end
private :parse_ldap_result
#
# parse_search_return
# Definition from RFC 1777 (we're handling application-4 here)
#
# Search Response ::=
# CHOICE {
# entry [APPLICATION 4] SEQUENCE {
# objectName LDAPDN,
# attributes SEQUENCE OF SEQUENCE {
# AttributeType,
# SET OF AttributeValue
# }
# },
# resultCode [APPLICATION 5] LDAPResult
# }
#
# We concoct a search response that is a hash of the returned attribute values.
# NOW OBSERVE CAREFULLY: WE ARE DOWNCASING THE RETURNED ATTRIBUTE NAMES.
# This is to make them more predictable for user programs, but it
# may not be a good idea. Maybe this should be configurable.
# ALTERNATE IMPLEMENTATION: In addition to @search_dn and @search_attributes,
# we also return @search_entry, which is an LDAP::Entry object.
# If that works out well, then we'll remove the first two.
#
# Provisionally removed obsolete search_attributes and search_dn, 04May06.
#
def parse_search_return sequence
sequence.length >= 2 or raise LdapPduError
@search_entry = LDAP::Entry.new( sequence[0] )
#@search_dn = sequence[0]
#@search_attributes = {}
sequence[1].each {|seq|
@search_entry[seq[0]] = seq[1]
#@search_attributes[seq[0].downcase.intern] = seq[1]
}
end
#
# A search referral is a sequence of one or more LDAP URIs.
# Any number of search-referral replies can be returned by the server, interspersed
# with normal replies in any order.
# Until I can think of a better way to do this, we'll return the referrals as an array.
# It'll be up to higher-level handlers to expose something reasonable to the client.
def parse_search_referral uris
@search_referrals = uris
end
# Per RFC 2251, an LDAP "control" is a sequence of tuples, each consisting
# of an OID, a boolean criticality flag defaulting FALSE, and an OPTIONAL
# Octet String. If only two fields are given, the second one may be
# either criticality or data, since criticality has a default value.
# Someday we may want to come back here and add support for some of
# more-widely used controls. RFC-2696 is a good example.
#
def parse_controls sequence
@ldap_controls = sequence.map do |control|
o = OpenStruct.new
o.oid,o.criticality,o.value = control[0],control[1],control[2]
if o.criticality and o.criticality.is_a?(String)
o.value = o.criticality
o.criticality = false
end
o
end
end
private :parse_controls
end
end # module Net
|