Skip to content

Commit a822560

Browse files
committed
Merge branch 'master' into true-ber
2 parents ab7b60c + 0b73377 commit a822560

File tree

12 files changed

+477
-113
lines changed

12 files changed

+477
-113
lines changed

lib/net/ldap.rb

Lines changed: 103 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -321,29 +321,103 @@ class LdapError < StandardError; end
321321

322322
StartTlsOid = "1.3.6.1.4.1.1466.20037"
323323

324+
# https://tools.ietf.org/html/rfc4511#section-4.1.9
325+
# https://tools.ietf.org/html/rfc4511#appendix-A
326+
ResultCodeSuccess = 0
327+
ResultCodeOperationsError = 1
328+
ResultCodeProtocolError = 2
329+
ResultCodeTimeLimitExceeded = 3
330+
ResultCodeSizeLimitExceeded = 4
331+
ResultCodeCompareFalse = 5
332+
ResultCodeCompareTrue = 6
333+
ResultCodeAuthMethodNotSupported = 7
334+
ResultCodeStrongerAuthRequired = 8
335+
ResultCodeReferral = 10
336+
ResultCodeAdminLimitExceeded = 11
337+
ResultCodeUnavailableCriticalExtension = 12
338+
ResultCodeConfidentialityRequired = 13
339+
ResultCodeSaslBindInProgress = 14
340+
ResultCodeNoSuchAttribute = 16
341+
ResultCodeUndefinedAttributeType = 17
342+
ResultCodeInappropriateMatching = 18
343+
ResultCodeConstraintViolation = 19
344+
ResultCodeAttributeOrValueExists = 20
345+
ResultCodeInvalidAttributeSyntax = 21
346+
ResultCodeNoSuchObject = 32
347+
ResultCodeAliasProblem = 33
348+
ResultCodeInvalidDNSyntax = 34
349+
ResultCodeAliasDereferencingProblem = 36
350+
ResultCodeInappropriateAuthentication = 48
351+
ResultCodeInvalidCredentials = 49
352+
ResultCodeInsufficientAccessRights = 50
353+
ResultCodeBusy = 51
354+
ResultCodeUnavailable = 52
355+
ResultCodeUnwillingToPerform = 53
356+
ResultCodeNamingViolation = 64
357+
ResultCodeObjectClassViolation = 65
358+
ResultCodeNotAllowedOnNonLeaf = 66
359+
ResultCodeNotAllowedOnRDN = 67
360+
ResultCodeEntryAlreadyExists = 68
361+
ResultCodeObjectClassModsProhibited = 69
362+
ResultCodeAffectsMultipleDSAs = 71
363+
ResultCodeOther = 80
364+
365+
# https://tools.ietf.org/html/rfc4511#appendix-A.1
366+
ResultCodesNonError = [
367+
ResultCodeSuccess,
368+
ResultCodeCompareFalse,
369+
ResultCodeCompareTrue,
370+
ResultCodeReferral,
371+
ResultCodeSaslBindInProgress
372+
]
373+
374+
# nonstandard list of "successful" result codes for searches
375+
ResultCodesSearchSuccess = [
376+
ResultCodeSuccess,
377+
ResultCodeTimeLimitExceeded,
378+
ResultCodeSizeLimitExceeded
379+
]
380+
381+
# map of result code to human message
324382
ResultStrings = {
325-
0 => "Success",
326-
1 => "Operations Error",
327-
2 => "Protocol Error",
328-
3 => "Time Limit Exceeded",
329-
4 => "Size Limit Exceeded",
330-
10 => "Referral",
331-
12 => "Unavailable crtical extension",
332-
14 => "saslBindInProgress",
333-
16 => "No Such Attribute",
334-
17 => "Undefined Attribute Type",
335-
19 => "Constraint Violation",
336-
20 => "Attribute or Value Exists",
337-
32 => "No Such Object",
338-
34 => "Invalid DN Syntax",
339-
48 => "Inappropriate Authentication",
340-
49 => "Invalid Credentials",
341-
50 => "Insufficient Access Rights",
342-
51 => "Busy",
343-
52 => "Unavailable",
344-
53 => "Unwilling to perform",
345-
65 => "Object Class Violation",
346-
68 => "Entry Already Exists"
383+
ResultCodeSuccess => "Success",
384+
ResultCodeOperationsError => "Operations Error",
385+
ResultCodeProtocolError => "Protocol Error",
386+
ResultCodeTimeLimitExceeded => "Time Limit Exceeded",
387+
ResultCodeSizeLimitExceeded => "Size Limit Exceeded",
388+
ResultCodeCompareFalse => "False Comparison",
389+
ResultCodeCompareTrue => "True Comparison",
390+
ResultCodeAuthMethodNotSupported => "Auth Method Not Supported",
391+
ResultCodeStrongerAuthRequired => "Stronger Auth Needed",
392+
ResultCodeReferral => "Referral",
393+
ResultCodeAdminLimitExceeded => "Admin Limit Exceeded",
394+
ResultCodeUnavailableCriticalExtension => "Unavailable crtical extension",
395+
ResultCodeConfidentialityRequired => "Confidentiality Required",
396+
ResultCodeSaslBindInProgress => "saslBindInProgress",
397+
ResultCodeNoSuchAttribute => "No Such Attribute",
398+
ResultCodeUndefinedAttributeType => "Undefined Attribute Type",
399+
ResultCodeInappropriateMatching => "Inappropriate Matching",
400+
ResultCodeConstraintViolation => "Constraint Violation",
401+
ResultCodeAttributeOrValueExists => "Attribute or Value Exists",
402+
ResultCodeInvalidAttributeSyntax => "Invalide Attribute Syntax",
403+
ResultCodeNoSuchObject => "No Such Object",
404+
ResultCodeAliasProblem => "Alias Problem",
405+
ResultCodeInvalidDNSyntax => "Invalid DN Syntax",
406+
ResultCodeAliasDereferencingProblem => "Alias Dereferencing Problem",
407+
ResultCodeInappropriateAuthentication => "Inappropriate Authentication",
408+
ResultCodeInvalidCredentials => "Invalid Credentials",
409+
ResultCodeInsufficientAccessRights => "Insufficient Access Rights",
410+
ResultCodeBusy => "Busy",
411+
ResultCodeUnavailable => "Unavailable",
412+
ResultCodeUnwillingToPerform => "Unwilling to perform",
413+
ResultCodeNamingViolation => "Naming Violation",
414+
ResultCodeObjectClassViolation => "Object Class Violation",
415+
ResultCodeNotAllowedOnNonLeaf => "Not Allowed On Non-Leaf",
416+
ResultCodeNotAllowedOnRDN => "Not Allowed On RDN",
417+
ResultCodeEntryAlreadyExists => "Entry Already Exists",
418+
ResultCodeObjectClassModsProhibited => "ObjectClass Modifications Prohibited",
419+
ResultCodeAffectsMultipleDSAs => "Affects Multiple DSAs",
420+
ResultCodeOther => "Other"
347421
}
348422

349423
module LDAPControls
@@ -549,7 +623,7 @@ def get_operation_result
549623
elsif result
550624
os.code = result
551625
else
552-
os.code = 0
626+
os.code = Net::LDAP::ResultCodeSuccess
553627
end
554628
os.message = Net::LDAP.result2string(os.code)
555629
os
@@ -667,7 +741,7 @@ def search(args = {})
667741
:port => @port,
668742
:encryption => @encryption,
669743
:instrumentation_service => @instrumentation_service
670-
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
744+
if (@result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
671745
@result = conn.search(args) { |entry|
672746
result_set << entry if result_set
673747
yield entry if block_given?
@@ -680,14 +754,7 @@ def search(args = {})
680754

681755
if return_result_set
682756
unless @result.nil?
683-
case @result.result_code
684-
when ResultStrings.key("Success")
685-
# everything good
686-
result_set
687-
when ResultStrings.key("Size Limit Exceeded"), ResultStrings.key("Time Limit Exceeded")
688-
# LDAP: Size/Time limit exceeded
689-
# This happens when we use size option and results are truncated
690-
# Still we need to return user results
757+
if ResultCodesSearchSuccess.include?(@result.result_code)
691758
result_set
692759
end
693760
end
@@ -873,7 +940,7 @@ def add(args)
873940
:port => @port,
874941
:encryption => @encryption,
875942
:instrumentation_service => @instrumentation_service
876-
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
943+
if (@result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
877944
@result = conn.add(args)
878945
end
879946
ensure
@@ -977,7 +1044,7 @@ def modify(args)
9771044
:port => @port,
9781045
:encryption => @encryption,
9791046
:instrumentation_service => @instrumentation_service
980-
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
1047+
if (@result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
9811048
@result = conn.modify(args)
9821049
end
9831050
ensure
@@ -1054,7 +1121,7 @@ def rename(args)
10541121
:port => @port,
10551122
:encryption => @encryption,
10561123
:instrumentation_service => @instrumentation_service
1057-
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
1124+
if (@result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
10581125
@result = conn.rename(args)
10591126
end
10601127
ensure
@@ -1087,7 +1154,7 @@ def delete(args)
10871154
:port => @port,
10881155
:encryption => @encryption,
10891156
:instrumentation_service => @instrumentation_service
1090-
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
1157+
if (@result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
10911158
@result = conn.delete(args)
10921159
end
10931160
ensure

lib/net/ldap/connection.rb

Lines changed: 61 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,18 @@ def setup_encryption(args)
8787
# additional branches requiring server validation and peer certs, etc.
8888
# go here.
8989
when :start_tls
90-
request = [Net::LDAP::StartTlsOid.to_ber_contextspecific(0)].to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
91-
write(request)
92-
pdu = read
93-
raise Net::LDAP::LdapError, "no start_tls result" if pdu.nil?
90+
message_id = next_msgid
91+
request = [
92+
Net::LDAP::StartTlsOid.to_ber_contextspecific(0)
93+
].to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
94+
95+
write(request, nil, message_id)
96+
pdu = queued_read(message_id)
97+
98+
if pdu.nil? || pdu.app_tag != Net::LDAP::PDU::ExtendedResponse
99+
raise Net::LDAP::LdapError, "no start_tls result"
100+
end
101+
94102
if pdu.result_code.zero?
95103
@conn = self.class.wrap_with_ssl(@conn)
96104
else
@@ -226,12 +234,18 @@ def bind_simple(auth)
226234

227235
raise Net::LDAP::LdapError, "Invalid binding information" unless (user && psw)
228236

229-
request = [LdapVersion.to_ber, user.to_ber,
230-
psw.to_ber_contextspecific(0)].to_ber_appsequence(0)
231-
write(request)
237+
message_id = next_msgid
238+
request = [
239+
LdapVersion.to_ber, user.to_ber,
240+
psw.to_ber_contextspecific(0)
241+
].to_ber_appsequence(Net::LDAP::PDU::BindRequest)
232242

233-
pdu = read
234-
raise Net::LDAP::LdapError, "no bind result" unless pdu
243+
write(request, nil, message_id)
244+
pdu = queued_read(message_id)
245+
246+
if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult
247+
raise Net::LDAP::LdapError, "no bind result"
248+
end
235249

236250
pdu
237251
end
@@ -262,16 +276,23 @@ def bind_sasl(auth)
262276
auth[:challenge_response]
263277
raise Net::LDAP::LdapError, "Invalid binding information" unless (mech && cred && chall)
264278

279+
message_id = next_msgid
280+
265281
n = 0
266282
loop {
267283
sasl = [mech.to_ber, cred.to_ber].to_ber_contextspecific(3)
268-
request = [LdapVersion.to_ber, "".to_ber, sasl].to_ber_appsequence(0)
269-
write(request)
284+
request = [
285+
LdapVersion.to_ber, "".to_ber, sasl
286+
].to_ber_appsequence(Net::LDAP::PDU::BindRequest)
270287

271-
pdu = read
272-
raise Net::LDAP::LdapError, "no bind result" unless pdu
288+
write(request, nil, message_id)
289+
pdu = queued_read(message_id)
273290

274-
return pdu unless pdu.result_code == 14 # saslBindInProgress
291+
if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult
292+
raise Net::LDAP::LdapError, "no bind result"
293+
end
294+
295+
return pdu unless pdu.result_code == Net::LDAP::ResultCodeSaslBindInProgress
275296
raise Net::LDAP::LdapError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
276297

277298
cred = chall.call(pdu.result_server_sasl_creds)
@@ -450,7 +471,7 @@ def search(args = nil)
450471
attrs_only.to_ber,
451472
filter.to_ber,
452473
ber_attrs.to_ber_sequence
453-
].to_ber_appsequence(3)
474+
].to_ber_appsequence(Net::LDAP::PDU::SearchRequest)
454475

455476
# rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
456477
# this breaks when calling to_ber. (Can't force binary data to UTF-8)
@@ -488,7 +509,7 @@ def search(args = nil)
488509
when Net::LDAP::PDU::SearchResult
489510
result_pdu = pdu
490511
controls = pdu.result_controls
491-
if refs && pdu.result_code == 10
512+
if refs && pdu.result_code == Net::LDAP::ResultCodeReferral
492513
if block_given?
493514
se = Net::LDAP::Entry.new
494515
se[:search_referrals] = (pdu.search_referrals || [])
@@ -516,7 +537,7 @@ def search(args = nil)
516537
# of type OCTET STRING, covered in the default syntax supported by
517538
# read_ber, so I guess we're ok.
518539
more_pages = false
519-
if result_pdu.result_code == 0 and controls
540+
if result_pdu.result_code == Net::LDAP::ResultCodeSuccess and controls
520541
controls.each do |c|
521542
if c.oid == Net::LDAP::LDAPControls::PAGED_RESULTS
522543
# just in case some bogus server sends us more than 1 of these.
@@ -538,7 +559,7 @@ def search(args = nil)
538559
# track total result count
539560
payload[:result_count] = n_results
540561

541-
result_pdu || OpenStruct.new(:status => :failure, :result_code => 1, :message => "Invalid search")
562+
result_pdu || OpenStruct.new(:status => :failure, :result_code => Net::LDAP::ResultCodeOperationsError, :message => "Invalid search")
542563
end # instrument
543564
ensure
544565
# clean up message queue for this search
@@ -583,11 +604,15 @@ def self.modify_ops(operations)
583604
def modify(args)
584605
modify_dn = args[:dn] or raise "Unable to modify empty DN"
585606
ops = self.class.modify_ops args[:operations]
586-
request = [ modify_dn.to_ber,
587-
ops.to_ber_sequence ].to_ber_appsequence(6)
588-
write(request)
589607

590-
pdu = read
608+
message_id = next_msgid
609+
request = [
610+
modify_dn.to_ber,
611+
ops.to_ber_sequence
612+
].to_ber_appsequence(Net::LDAP::PDU::ModifyRequest)
613+
614+
write(request, nil, message_id)
615+
pdu = queued_read(message_id)
591616

592617
if !pdu || pdu.app_tag != Net::LDAP::PDU::ModifyResponse
593618
raise Net::LDAP::LdapError, "response missing or invalid"
@@ -610,10 +635,11 @@ def add(args)
610635
add_attrs << [ k.to_s.to_ber, Array(v).map { |m| m.to_ber}.to_ber_set ].to_ber_sequence
611636
}
612637

613-
request = [add_dn.to_ber, add_attrs.to_ber_sequence].to_ber_appsequence(8)
614-
write(request)
638+
message_id = next_msgid
639+
request = [add_dn.to_ber, add_attrs.to_ber_sequence].to_ber_appsequence(Net::LDAP::PDU::AddRequest)
615640

616-
pdu = read
641+
write(request, nil, message_id)
642+
pdu = queued_read(message_id)
617643

618644
if !pdu || pdu.app_tag != Net::LDAP::PDU::AddResponse
619645
raise Net::LDAP::LdapError, "response missing or invalid"
@@ -631,12 +657,12 @@ def rename(args)
631657
delete_attrs = args[:delete_attributes] ? true : false
632658
new_superior = args[:new_superior]
633659

634-
request = [old_dn.to_ber, new_rdn.to_ber, delete_attrs.to_ber]
635-
request << new_superior.to_ber_contextspecific(0) unless new_superior == nil
636-
637-
write(request.to_ber_appsequence(12))
660+
message_id = next_msgid
661+
request = [old_dn.to_ber, new_rdn.to_ber, delete_attrs.to_ber]
662+
request << new_superior.to_ber_contextspecific(0) unless new_superior == nil
638663

639-
pdu = read
664+
write(request.to_ber_appsequence(Net::LDAP::PDU::ModifyRDNRequest), nil, message_id)
665+
pdu = queued_read(message_id)
640666

641667
if !pdu || pdu.app_tag != Net::LDAP::PDU::ModifyRDNResponse
642668
raise Net::LDAP::LdapError.new "response missing or invalid"
@@ -650,11 +676,12 @@ def rename(args)
650676
#++
651677
def delete(args)
652678
dn = args[:dn] or raise "Unable to delete empty DN"
653-
controls = args.include?(:control_codes) ? args[:control_codes].to_ber_control : nil #use nil so we can compact later
654-
request = dn.to_s.to_ber_application_string(10)
655-
write(request, controls)
679+
controls = args.include?(:control_codes) ? args[:control_codes].to_ber_control : nil #use nil so we can compact later
680+
message_id = next_msgid
681+
request = dn.to_s.to_ber_application_string(Net::LDAP::PDU::DeleteRequest)
656682

657-
pdu = read
683+
write(request, controls, message_id)
684+
pdu = queued_read(message_id)
658685

659686
if !pdu || pdu.app_tag != Net::LDAP::PDU::DeleteResponse
660687
raise Net::LDAP::LdapError, "response missing or invalid"

0 commit comments

Comments
 (0)