To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / .svn / pristine / 60 / 60a7eaf3b71f4ea5a952f91807abdd540583522f.svn-base @ 1297:0a574315af3e

History | View | Annotate | Download (6.53 KB)

1
# $Id: pdu.rb 126 2006-05-31 15:55:16Z blackhedd $
2
#
3
# LDAP PDU support classes
4
#
5
#
6
#----------------------------------------------------------------------------
7
#
8
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
9
#
10
# Gmail: garbagecat10
11
#
12
# This program is free software; you can redistribute it and/or modify
13
# it under the terms of the GNU General Public License as published by
14
# the Free Software Foundation; either version 2 of the License, or
15
# (at your option) any later version.
16
#
17
# This program is distributed in the hope that it will be useful,
18
# but WITHOUT ANY WARRANTY; without even the implied warranty of
19
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
# GNU General Public License for more details.
21
#
22
# You should have received a copy of the GNU General Public License
23
# along with this program; if not, write to the Free Software
24
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
25
#
26
#---------------------------------------------------------------------------
27
#
28

    
29

    
30

    
31
module Net
32

    
33

    
34
class LdapPduError < Exception; end
35

    
36

    
37
class LdapPdu
38

    
39
  BindResult = 1
40
  SearchReturnedData = 4
41
  SearchResult = 5
42
  ModifyResponse = 7
43
  AddResponse = 9
44
  DeleteResponse = 11
45
  ModifyRDNResponse = 13
46
  SearchResultReferral = 19
47

    
48
  attr_reader :msg_id, :app_tag
49
  attr_reader :search_dn, :search_attributes, :search_entry
50
  attr_reader :search_referrals
51

    
52
  #
53
  # initialize
54
  # An LDAP PDU always looks like a BerSequence with
55
  # at least two elements: an integer (message-id number), and
56
  # an application-specific sequence.
57
  # Some LDAPv3 packets also include an optional
58
  # third element, which is a sequence of "controls"
59
  # (See RFC 2251, section 4.1.12).
60
  # The application-specific tag in the sequence tells
61
  # us what kind of packet it is, and each kind has its
62
  # own format, defined in RFC-1777.
63
  # Observe that many clients (such as ldapsearch)
64
  # do not necessarily enforce the expected application
65
  # tags on received protocol packets. This implementation
66
  # does interpret the RFC strictly in this regard, and
67
  # it remains to be seen whether there are servers out
68
  # there that will not work well with our approach.
69
  #
70
  # Added a controls-processor to SearchResult.
71
  # Didn't add it everywhere because it just _feels_
72
  # like it will need to be refactored.
73
  #
74
  def initialize ber_object
75
    begin
76
      @msg_id = ber_object[0].to_i
77
      @app_tag = ber_object[1].ber_identifier - 0x60
78
    rescue
79
      # any error becomes a data-format error
80
      raise LdapPduError.new( "ldap-pdu format error" )
81
    end
82

    
83
    case @app_tag
84
    when BindResult
85
      parse_ldap_result ber_object[1]
86
    when SearchReturnedData
87
      parse_search_return ber_object[1]
88
    when SearchResultReferral
89
      parse_search_referral ber_object[1]
90
    when SearchResult
91
      parse_ldap_result ber_object[1]
92
      parse_controls(ber_object[2]) if ber_object[2]
93
    when ModifyResponse
94
      parse_ldap_result ber_object[1]
95
    when AddResponse
96
      parse_ldap_result ber_object[1]
97
    when DeleteResponse
98
      parse_ldap_result ber_object[1]
99
    when ModifyRDNResponse
100
      parse_ldap_result ber_object[1]
101
    else
102
      raise LdapPduError.new( "unknown pdu-type: #{@app_tag}" )
103
    end
104
  end
105

    
106
  #
107
  # result_code
108
  # This returns an LDAP result code taken from the PDU,
109
  # but it will be nil if there wasn't a result code.
110
  # That can easily happen depending on the type of packet.
111
  #
112
  def result_code code = :resultCode
113
    @ldap_result and @ldap_result[code]
114
  end
115

    
116
  # Return RFC-2251 Controls if any.
117
  # Messy. Does this functionality belong somewhere else?
118
  def result_controls
119
    @ldap_controls || []
120
  end
121

    
122

    
123
  #
124
  # parse_ldap_result
125
  #
126
  def parse_ldap_result sequence
127
    sequence.length >= 3 or raise LdapPduError
128
    @ldap_result = {:resultCode => sequence[0], :matchedDN => sequence[1], :errorMessage => sequence[2]}
129
  end
130
  private :parse_ldap_result
131

    
132
  #
133
  # parse_search_return
134
  # Definition from RFC 1777 (we're handling application-4 here)
135
  #
136
  # Search Response ::=
137
  #    CHOICE {
138
  #         entry          [APPLICATION 4] SEQUENCE {
139
  #                             objectName     LDAPDN,
140
  #                             attributes     SEQUENCE OF SEQUENCE {
141
  #                                                 AttributeType,
142
  #                                                 SET OF AttributeValue
143
  #                                            }
144
  #                        },
145
  #         resultCode     [APPLICATION 5] LDAPResult
146
  #     }
147
  #
148
  # We concoct a search response that is a hash of the returned attribute values.
149
  # NOW OBSERVE CAREFULLY: WE ARE DOWNCASING THE RETURNED ATTRIBUTE NAMES.
150
  # This is to make them more predictable for user programs, but it
151
  # may not be a good idea. Maybe this should be configurable.
152
  # ALTERNATE IMPLEMENTATION: In addition to @search_dn and @search_attributes,
153
  # we also return @search_entry, which is an LDAP::Entry object.
154
  # If that works out well, then we'll remove the first two.
155
  #
156
  # Provisionally removed obsolete search_attributes and search_dn, 04May06.
157
  #
158
  def parse_search_return sequence
159
    sequence.length >= 2 or raise LdapPduError
160
    @search_entry = LDAP::Entry.new( sequence[0] )
161
    #@search_dn = sequence[0]
162
    #@search_attributes = {}
163
    sequence[1].each {|seq|
164
      @search_entry[seq[0]] = seq[1]
165
      #@search_attributes[seq[0].downcase.intern] = seq[1]
166
    }
167
  end
168

    
169
  #
170
  # A search referral is a sequence of one or more LDAP URIs.
171
  # Any number of search-referral replies can be returned by the server, interspersed
172
  # with normal replies in any order.
173
  # Until I can think of a better way to do this, we'll return the referrals as an array.
174
  # It'll be up to higher-level handlers to expose something reasonable to the client.
175
  def parse_search_referral uris
176
    @search_referrals = uris
177
  end
178

    
179

    
180
  # Per RFC 2251, an LDAP "control" is a sequence of tuples, each consisting
181
  # of an OID, a boolean criticality flag defaulting FALSE, and an OPTIONAL
182
  # Octet String. If only two fields are given, the second one may be
183
  # either criticality or data, since criticality has a default value.
184
  # Someday we may want to come back here and add support for some of
185
  # more-widely used controls. RFC-2696 is a good example.
186
  #
187
  def parse_controls sequence
188
    @ldap_controls = sequence.map do |control|
189
      o = OpenStruct.new
190
      o.oid,o.criticality,o.value = control[0],control[1],control[2]
191
      if o.criticality and o.criticality.is_a?(String)
192
        o.value = o.criticality
193
        o.criticality = false
194
      end
195
      o
196
    end
197
  end
198
  private :parse_controls
199

    
200

    
201
end
202

    
203

    
204
end # module Net
205