Skip to content

Commit b6d9fbe

Browse files
author
Rory O'Connell
committed
Merge pull request #33 from DavidJLee/master
New filter type to allow searching of binary data
2 parents 51597ea + 9fa0b98 commit b6d9fbe

File tree

5 files changed

+50
-2
lines changed

5 files changed

+50
-2
lines changed

Contributors.rdoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ Contributions since:
1919
* Derek Harmel (derekharmel)
2020
* Erik Hetzner (egh)
2121
* nowhereman
22+
* David J. Lee (DavidJLee)

lib/net/ber/core_ext/string.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ def to_ber(code = 0x04)
1616
[code].pack('C') + raw_string.length.to_ber_length_encoding + raw_string
1717
end
1818

19+
##
20+
# Converts a string to a BER string but does *not* encode to UTF-8 first.
21+
# This is required for proper representation of binary data for Microsoft
22+
# Active Directory
23+
def to_ber_bin(code = 0x04)
24+
[code].pack('C') + length.to_ber_length_encoding + self
25+
end
26+
1927
def raw_utf8_encoded
2028
if self.respond_to?(:encode)
2129
# Strings should be UTF-8 encoded according to LDAP.

lib/net/ldap.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ class LdapError < StandardError; end
308308
DefaultPort = 389
309309
DefaultAuth = { :method => :anonymous }
310310
DefaultTreebase = "dc=com"
311+
DefaultForceNoPage = false
311312

312313
StartTlsOid = "1.3.6.1.4.1.1466.20037"
313314

@@ -373,6 +374,8 @@ def self.result2string(code) #:nodoc:
373374
# specifying the Hash {:method => :simple_tls}. There is a fairly large
374375
# range of potential values that may be given for this parameter. See
375376
# #encryption for details.
377+
# * :force_no_page => Set to true to prevent paged results even if your
378+
# server says it supports them. This is a fix for MS Active Directory
376379
#
377380
# Instantiating a Net::LDAP object does <i>not</i> result in network
378381
# traffic to the LDAP server. It simply stores the connection and binding
@@ -383,6 +386,7 @@ def initialize(args = {})
383386
@verbose = false # Make this configurable with a switch on the class.
384387
@auth = args[:auth] || DefaultAuth
385388
@base = args[:base] || DefaultTreebase
389+
@force_no_page = args[:force_no_page] || DefaultForceNoPage
386390
encryption args[:encryption] # may be nil
387391

388392
if pr = @auth[:password] and pr.respond_to?(:call)
@@ -1108,6 +1112,10 @@ def search_subschema_entry
11081112
# MUST refactor the root_dse call out.
11091113
#++
11101114
def paged_searches_supported?
1115+
# active directory returns that it supports paged results. However
1116+
# it returns binary data in the rfc2696_cookie which throws an
1117+
# encoding exception breaking searching.
1118+
return false if @force_no_page
11111119
@server_caps ||= search_root_dse
11121120
@server_caps[:supportedcontrol].include?(Net::LDAP::LDAPControls::PAGED_RESULTS)
11131121
end
@@ -1433,6 +1441,10 @@ def search(args = {})
14331441
search_attributes.to_ber_sequence
14341442
].to_ber_appsequence(3)
14351443

1444+
# rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
1445+
# this breaks when calling to_ber. (Can't force binary data to UTF-8)
1446+
# we have to disable paging (even though server supports it) to get around this...
1447+
14361448
controls = []
14371449
controls <<
14381450
[
@@ -1582,7 +1594,7 @@ def add(args)
15821594
#--
15831595
# TODO: need to support a time limit, in case the server fails to respond.
15841596
#++
1585-
def rename args
1597+
def rename(args)
15861598
old_dn = args[:olddn] or raise "Unable to rename empty DN"
15871599
new_rdn = args[:newrdn] or raise "Unable to rename to empty RDN"
15881600
delete_attrs = args[:delete_attributes] ? true : false

lib/net/ldap/filter.rb

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
class Net::LDAP::Filter
2424
##
2525
# Known filter types.
26-
FilterTypes = [ :ne, :eq, :ge, :le, :and, :or, :not, :ex ]
26+
FilterTypes = [ :ne, :eq, :ge, :le, :and, :or, :not, :ex, :bineq ]
2727

2828
def initialize(op, left, right) #:nodoc:
2929
unless FilterTypes.include?(op)
@@ -65,6 +65,23 @@ def eq(attribute, value)
6565
new(:eq, attribute, value)
6666
end
6767

68+
##
69+
# Creates a Filter object indicating a binary comparison.
70+
# this prevents the search data from being forced into a UTF-8 string.
71+
#
72+
# This is primarily used for Microsoft Active Directory to compare
73+
# GUID values.
74+
#
75+
# # for guid represented as hex charecters
76+
# guid = "6a31b4a12aa27a41aca9603f27dd5116"
77+
# guid_bin = [guid].pack("H*")
78+
# f = Net::LDAP::Filter.bineq("objectGUID", guid_bin)
79+
#
80+
# This filter does not perform any escaping.
81+
def bineq(attribute, value)
82+
new(:bineq, attribute, value)
83+
end
84+
6885
##
6986
# Creates a Filter object indicating extensible comparison. This Filter
7087
# object is currently considered EXPERIMENTAL.
@@ -399,6 +416,8 @@ def to_raw_rfc2254
399416
"!(#{@left}=#{@right})"
400417
when :eq
401418
"#{@left}=#{@right}"
419+
when :bineq
420+
"#{@left}=#{@right}"
402421
when :ex
403422
"#{@left}:=#{@right}"
404423
when :ge
@@ -508,6 +527,9 @@ def to_ber
508527
else # equality
509528
[@left.to_s.to_ber, unescape(@right).to_ber].to_ber_contextspecific(3)
510529
end
530+
when :bineq
531+
# make sure data is not forced to UTF-8
532+
[@left.to_s.to_ber, unescape(@right).to_ber_bin].to_ber_contextspecific(3)
511533
when :ex
512534
seq = []
513535

spec/unit/ber/ber_spec.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@
8484
it "should properly encode strings encodable as UTF-8" do
8585
"teststring".encode("US-ASCII").to_ber.should == "\x04\nteststring"
8686
end
87+
it "should properly encode binary data strings using to_ber_bin" do
88+
# This is used for searching for GUIDs in Active Directory
89+
["6a31b4a12aa27a41aca9603f27dd5116"].pack("H*").to_ber_bin.should ==
90+
"\x04\x10" + "j1\xB4\xA1*\xA2zA\xAC\xA9`?'\xDDQ\x16"
91+
end
8792
it "should fail on strings that can not be converted to UTF-8" do
8893
error = Encoding::UndefinedConversionError
8994
lambda {"\x81".to_ber }.should raise_exception(error)

0 commit comments

Comments
 (0)