From 7438a2ab45b6aabfbcb1b078fc72419d7b0442ba Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Sat, 16 Mar 2013 13:10:02 +0100 Subject: [PATCH 1/9] One LdapError-class => Specific errors for every exception. --- lib/net/ldap.rb | 73 +++++++++++++++++++++++++++------------- lib/net/ldap/entry.rb | 2 +- lib/net/ldap/filter.rb | 16 ++++----- lib/net/ldap/password.rb | 2 +- test/test_filter.rb | 4 +-- 5 files changed, 62 insertions(+), 35 deletions(-) diff --git a/lib/net/ldap.rb b/lib/net/ldap.rb index 680526da..559c9e3a 100644 --- a/lib/net/ldap.rb +++ b/lib/net/ldap.rb @@ -244,6 +244,33 @@ class Net::LDAP VERSION = "0.4.0" class LdapError < StandardError; end + class AlreadyOpenedError < LdapError; end + class SocketError < LdapError; end + class ConnectionRefusedError < LdapError; end + class NoOpenSSLError < LdapError; end + class NoStartTLSResultError < LdapError; end + class StartTLSError < LdapError; end + class EncryptionUnsupportedError < LdapError; end + class EncMethodUnsupportedError < LdapError; end + class AuthMethodUnsupportedError < LdapError; end + class BindingInformationInvalidError < LdapError; end + class NoBindResultError < LdapError; end + class SASLChallengeOverflowError < LdapError; end + class SearchSizeInvalidError < LdapError; end + class SearchScopeInvalidError < LdapError; end + class ResponseTypeInvalidError < LdapError; end + class ResponseMissingOrInvalidError < LdapError; end + class EmptyDNError < LdapError; end + class HashTypeUnsupportedError < LdapError; end + class OperatorError < LdapError; end + class SubstringFilterError < LdapError; end + class SearchFilterError < LdapError; end + class BERInvalidError < LdapError; end + class SearchFilterTypeUnknownError < LdapError; end + class BadAttributeError < LdapError; end + class FilterTypeUnknownError < LdapError; end + class FilterSyntaxInvalidError < LdapError; end + class EntryOverflowError < LdapError; end SearchScope_BaseObject = 0 SearchScope_SingleLevel = 1 @@ -563,7 +590,7 @@ def open # anything with the bind results. We then pass self to the caller's # block, where he will execute his LDAP operations. Of course they will # all generate auth failures if the bind was unsuccessful. - raise Net::LDAP::LdapError, "Open already in progress" if @open_connection + raise Net::LDAP::AlreadyOpenedError, "Open already in progress" if @open_connection begin @open_connection = Net::LDAP::Connection.new(:host => @host, @@ -1134,9 +1161,9 @@ def initialize(server) begin @conn = TCPSocket.new(server[:host], server[:port]) rescue SocketError - raise Net::LDAP::LdapError, "No such address or other socket error." + raise Net::LDAP::SocketError, "No such address or other socket error." rescue Errno::ECONNREFUSED - raise Net::LDAP::LdapError, "Server #{server[:host]} refused connection on port #{server[:port]}." + raise Net::LDAP::ConnectionRefusedError, "Server #{server[:host]} refused connection on port #{server[:port]}." end if server[:encryption] @@ -1153,7 +1180,7 @@ def getbyte end def self.wrap_with_ssl(io) - raise Net::LDAP::LdapError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL + raise Net::LDAP::NoOpenSSLError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL ctx = OpenSSL::SSL::SSLContext.new conn = OpenSSL::SSL::SSLSocket.new(io, ctx) conn.connect @@ -1202,16 +1229,16 @@ def setup_encryption(args) request_pkt = [msgid, request].to_ber_sequence @conn.write request_pkt be = @conn.read_ber(Net::LDAP::AsnSyntax) - raise Net::LDAP::LdapError, "no start_tls result" if be.nil? + raise Net::LDAP::NoStartTLSResultError, "no start_tls result" if be.nil? pdu = Net::LDAP::PDU.new(be) - raise Net::LDAP::LdapError, "no start_tls result" if pdu.nil? + raise Net::LDAP::NoStartTLSResultError, "no start_tls result" if pdu.nil? if pdu.result_code.zero? @conn = self.class.wrap_with_ssl(@conn) else - raise Net::LDAP::LdapError, "start_tls failed: #{pdu.result_code}" + raise Net::LDAP::StartTLSError, "start_tls failed: #{pdu.result_code}" end else - raise Net::LDAP::LdapError, "unsupported encryption method #{args[:method]}" + raise Net::LDAP::EncryptionUnsupportedError, "unsupported encryption method #{args[:method]}" end end @@ -1239,7 +1266,7 @@ def bind(auth) elsif meth == :gss_spnego bind_gss_spnego(auth) else - raise Net::LDAP::LdapError, "Unsupported auth method (#{meth})" + raise Net::LDAP::AuthMethodUnsupportedError, "Unsupported auth method (#{meth})" end end @@ -1254,7 +1281,7 @@ def bind_simple(auth) ["", ""] end - raise Net::LDAP::LdapError, "Invalid binding information" unless (user && psw) + raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw) msgid = next_msgid.to_ber request = [LdapVersion.to_ber, user.to_ber, @@ -1262,7 +1289,7 @@ def bind_simple(auth) request_pkt = [msgid, request].to_ber_sequence @conn.write request_pkt - (be = @conn.read_ber(Net::LDAP::AsnSyntax) and pdu = Net::LDAP::PDU.new(be)) or raise Net::LDAP::LdapError, "no bind result" + (be = @conn.read_ber(Net::LDAP::AsnSyntax) and pdu = Net::LDAP::PDU.new(be)) or raise Net::LDAP::NoBindResultError, "no bind result" pdu end @@ -1291,7 +1318,7 @@ def bind_simple(auth) def bind_sasl(auth) mech, cred, chall = auth[:mechanism], auth[:initial_credential], auth[:challenge_response] - raise Net::LDAP::LdapError, "Invalid binding information" unless (mech && cred && chall) + raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (mech && cred && chall) n = 0 loop { @@ -1301,9 +1328,9 @@ def bind_sasl(auth) request_pkt = [msgid, request].to_ber_sequence @conn.write request_pkt - (be = @conn.read_ber(Net::LDAP::AsnSyntax) and pdu = Net::LDAP::PDU.new(be)) or raise Net::LDAP::LdapError, "no bind result" + (be = @conn.read_ber(Net::LDAP::AsnSyntax) and pdu = Net::LDAP::PDU.new(be)) or raise Net::LDAP::NoBindResultError, "no bind result" return pdu unless pdu.result_code == 14 # saslBindInProgress - raise Net::LDAP::LdapError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges) + raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges) cred = chall.call(pdu.result_server_sasl_creds) } @@ -1327,7 +1354,7 @@ def bind_gss_spnego(auth) require 'ntlm' user, psw = [auth[:username] || auth[:dn], auth[:password]] - raise Net::LDAP::LdapError, "Invalid binding information" unless (user && psw) + raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw) nego = proc { |challenge| t2_msg = NTLM::Message.parse(challenge) @@ -1389,12 +1416,12 @@ def search(args = {}) search_attributes = ((args && args[:attributes]) || []).map { |attr| attr.to_s.to_ber} return_referrals = args && args[:return_referrals] == true sizelimit = (args && args[:size].to_i) || 0 - raise Net::LDAP::LdapError, "invalid search-size" unless sizelimit >= 0 + raise Net::LDAP::SearchSizeInvalidError, "invalid search-size" unless sizelimit >= 0 paged_searches_supported = (args && args[:paged_searches_supported]) attributes_only = (args and args[:attributes_only] == true) scope = args[:scope] || Net::LDAP::SearchScope_WholeSubtree - raise Net::LDAP::LdapError, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope) + raise Net::LDAP::SearchScopeInvalidError, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope) sort_control = encode_sort_controls(args.fetch(:sort_controls){ false }) # An interesting value for the size limit would be close to A/D's @@ -1490,7 +1517,7 @@ def search(args = {}) end break else - raise Net::LDAP::LdapError, "invalid response-type in search: #{pdu.app_tag}" + raise Net::LDAP::ResponseTypeInvalidError, "invalid response-type in search: #{pdu.app_tag}" end end @@ -1563,7 +1590,7 @@ def modify(args) pkt = [ next_msgid.to_ber, request ].to_ber_sequence @conn.write pkt - (be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == 7) or raise Net::LDAP::LdapError, "response missing or invalid" + (be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == 7) or raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid" pdu end @@ -1576,7 +1603,7 @@ def modify(args) # to the error message and the matched-DN returned by the server. #++ def add(args) - add_dn = args[:dn] or raise Net::LDAP::LdapError, "Unable to add empty DN" + add_dn = args[:dn] or raise Net::LDAP::EmptyDNError, "Unable to add empty DN" add_attrs = [] a = args[:attributes] and a.each { |k, v| add_attrs << [ k.to_s.to_ber, Array(v).map { |m| m.to_ber}.to_ber_set ].to_ber_sequence @@ -1589,7 +1616,7 @@ def add(args) (be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == 9) or - raise Net::LDAP::LdapError, "response missing or invalid" + raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid" pdu end @@ -1611,7 +1638,7 @@ def rename(args) (be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new( be )) && (pdu.app_tag == 13) or - raise Net::LDAP::LdapError.new( "response missing or invalid" ) + raise Net::LDAP::ResponseMissingOrInvalidError.new( "response missing or invalid" ) pdu end @@ -1626,7 +1653,7 @@ def delete(args) pkt = [next_msgid.to_ber, request, controls].compact.to_ber_sequence @conn.write pkt - (be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == 11) or raise Net::LDAP::LdapError, "response missing or invalid" + (be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == 11) or raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid" pdu end diff --git a/lib/net/ldap/entry.rb b/lib/net/ldap/entry.rb index 616ffe7f..18bfd053 100644 --- a/lib/net/ldap/entry.rb +++ b/lib/net/ldap/entry.rb @@ -71,7 +71,7 @@ def from_single_ldif_string(ldif) return nil if ds.empty? - raise Net::LDAP::LdapError, "Too many LDIF entries" unless ds.size == 1 + raise Net::LDAP::EntryOverflowError, "Too many LDIF entries" unless ds.size == 1 entry = ds.to_entries.first diff --git a/lib/net/ldap/filter.rb b/lib/net/ldap/filter.rb index f77c5e26..3f1ff6dd 100644 --- a/lib/net/ldap/filter.rb +++ b/lib/net/ldap/filter.rb @@ -27,7 +27,7 @@ class Net::LDAP::Filter def initialize(op, left, right) #:nodoc: unless FilterTypes.include?(op) - raise Net::LDAP::LdapError, "Invalid or unsupported operator #{op.inspect} in LDAP Filter." + raise Net::LDAP::OperatorError, "Invalid or unsupported operator #{op.inspect} in LDAP Filter." end @op = op @left = left @@ -290,7 +290,7 @@ def parse_ber(ber) ber.last.each { |b| case b.ber_identifier when 0x80 # context-specific primitive 0, SubstringFilter "initial" - raise Net::LDAP::LdapError, "Unrecognized substring filter; bad initial value." if str.length > 0 + raise Net::LDAP::SubstringFilterError, "Unrecognized substring filter; bad initial value." if str.length > 0 str += b when 0x81 # context-specific primitive 0, SubstringFilter "any" str += "*#{b}" @@ -309,7 +309,7 @@ def parse_ber(ber) # call to_s to get rid of the BER-identifiedness of the incoming string. present?(ber.to_s) when 0xa9 # context-specific constructed 9, "extensible comparison" - raise Net::LDAP::LdapError, "Invalid extensible search filter, should be at least two elements" if ber.size<2 + raise Net::LDAP::SearchFilterError, "Invalid extensible search filter, should be at least two elements" if ber.size<2 # Reassembles the extensible filter parts # (["sn", "2.4.6.8.10", "Barbara Jones", '1']) @@ -330,7 +330,7 @@ def parse_ber(ber) ex(attribute, value) else - raise Net::LDAP::LdapError, "Invalid BER tag-value (#{ber.ber_identifier}) in search filter." + raise Net::LDAP::BERInvalidError, "Invalid BER tag-value (#{ber.ber_identifier}) in search filter." end end @@ -357,7 +357,7 @@ def parse_ldap_filter(obj) when 0xa3 # equalityMatch. context-specific constructed 3. eq(obj[0], obj[1]) else - raise Net::LDAP::LdapError, "Unknown LDAP search-filter type: #{obj.ber_identifier}" + raise Net::LDAP::SearchFilterTypeUnknownError, "Unknown LDAP search-filter type: #{obj.ber_identifier}" end end end @@ -534,7 +534,7 @@ def to_ber seq = [] unless @left =~ /^([-;\w]*)(:dn)?(:(\w+|[.\w]+))?$/ - raise Net::LDAP::LdapError, "Bad attribute #{@left}" + raise Net::LDAP::BadAttributeError, "Bad attribute #{@left}" end type, dn, rule = $1, $2, $4 @@ -641,7 +641,7 @@ def match(entry) l = entry[@left] and l = Array(l) and l.index(@right) end else - raise Net::LDAP::LdapError, "Unknown filter type in match: #{@op}" + raise Net::LDAP::FilterTypeUnknownError, "Unknown filter type in match: #{@op}" end end @@ -674,7 +674,7 @@ def parse(ldap_filter_string) def initialize(str) require 'strscan' # Don't load strscan until we need it. @filter = parse(StringScanner.new(str)) - raise Net::LDAP::LdapError, "Invalid filter syntax." unless @filter + raise Net::LDAP::FilterSyntaxInvalidError, "Invalid filter syntax." unless @filter end ## diff --git a/lib/net/ldap/password.rb b/lib/net/ldap/password.rb index 9c873029..c7ca8e05 100644 --- a/lib/net/ldap/password.rb +++ b/lib/net/ldap/password.rb @@ -29,7 +29,7 @@ def generate(type, str) srand; salt = (rand * 1000).to_i.to_s attribute_value = '{SSHA}' + Base64.encode64(Digest::SHA1.digest(str + salt) + salt).chomp! else - raise Net::LDAP::LdapError, "Unsupported password-hash type (#{type})" + raise Net::LDAP::HashTypeUnsupportedError, "Unsupported password-hash type (#{type})" end return attribute_value end diff --git a/test/test_filter.rb b/test/test_filter.rb index 03436e03..1ac40ed8 100644 --- a/test/test_filter.rb +++ b/test/test_filter.rb @@ -9,11 +9,11 @@ def test_bug_7534_rfc2254 end def test_invalid_filter_string - assert_raises(Net::LDAP::LdapError) { Filter.from_rfc2254("") } + assert_raises(Net::LDAP::FilterSyntaxInvalidError) { Filter.from_rfc2254("") } end def test_invalid_filter - assert_raises(Net::LDAP::LdapError) { + assert_raises(Net::LDAP::OperatorError) { # This test exists to prove that our constructor blocks unknown filter # types. All filters must be constructed using helpers. Filter.__send__(:new, :xx, nil, nil) From a6d6ec8285f2548fa0c08169ecc65b112922c5bd Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Sun, 4 Jan 2015 15:28:35 +0900 Subject: [PATCH 2/9] Rename Net::LDAP::LdapError to Net::LDAP::Error --- lib/net/ldap.rb | 30 +----------------------------- lib/net/ldap/error.rb | 31 +++++++++++++++++++++++++++++++ test/test_ldap_connection.rb | 4 ++-- 3 files changed, 34 insertions(+), 31 deletions(-) create mode 100644 lib/net/ldap/error.rb diff --git a/lib/net/ldap.rb b/lib/net/ldap.rb index fa05ea78..75b463fb 100644 --- a/lib/net/ldap.rb +++ b/lib/net/ldap.rb @@ -26,6 +26,7 @@ class LDAP require 'net/ldap/instrumentation' require 'net/ldap/connection' require 'net/ldap/version' +require 'net/ldap/error' # == Quick-start for the Impatient # === Quick Example of a user-authentication against an LDAP directory: @@ -246,35 +247,6 @@ class LDAP class Net::LDAP include Net::LDAP::Instrumentation - class LdapError < StandardError; end - class AlreadyOpenedError < LdapError; end - class SocketError < LdapError; end - class ConnectionRefusedError < LdapError; end - class NoOpenSSLError < LdapError; end - class NoStartTLSResultError < LdapError; end - class StartTLSError < LdapError; end - class EncryptionUnsupportedError < LdapError; end - class EncMethodUnsupportedError < LdapError; end - class AuthMethodUnsupportedError < LdapError; end - class BindingInformationInvalidError < LdapError; end - class NoBindResultError < LdapError; end - class SASLChallengeOverflowError < LdapError; end - class SearchSizeInvalidError < LdapError; end - class SearchScopeInvalidError < LdapError; end - class ResponseTypeInvalidError < LdapError; end - class ResponseMissingOrInvalidError < LdapError; end - class EmptyDNError < LdapError; end - class HashTypeUnsupportedError < LdapError; end - class OperatorError < LdapError; end - class SubstringFilterError < LdapError; end - class SearchFilterError < LdapError; end - class BERInvalidError < LdapError; end - class SearchFilterTypeUnknownError < LdapError; end - class BadAttributeError < LdapError; end - class FilterTypeUnknownError < LdapError; end - class FilterSyntaxInvalidError < LdapError; end - class EntryOverflowError < LdapError; end - SearchScope_BaseObject = 0 SearchScope_SingleLevel = 1 SearchScope_WholeSubtree = 2 diff --git a/lib/net/ldap/error.rb b/lib/net/ldap/error.rb new file mode 100644 index 00000000..bd2c74ad --- /dev/null +++ b/lib/net/ldap/error.rb @@ -0,0 +1,31 @@ +class Net::LDAP + class Error < StandardError; end + + class AlreadyOpenedError < Error; end + class SocketError < Error; end + class ConnectionRefusedError < Error; end + class NoOpenSSLError < Error; end + class NoStartTLSResultError < Error; end + class StartTLSError < Error; end + class EncryptionUnsupportedError < Error; end + class EncMethodUnsupportedError < Error; end + class AuthMethodUnsupportedError < Error; end + class BindingInformationInvalidError < Error; end + class NoBindResultError < Error; end + class SASLChallengeOverflowError < Error; end + class SearchSizeInvalidError < Error; end + class SearchScopeInvalidError < Error; end + class ResponseTypeInvalidError < Error; end + class ResponseMissingOrInvalidError < Error; end + class EmptyDNError < Error; end + class HashTypeUnsupportedError < Error; end + class OperatorError < Error; end + class SubstringFilterError < Error; end + class SearchFilterError < Error; end + class BERInvalidError < Error; end + class SearchFilterTypeUnknownError < Error; end + class BadAttributeError < Error; end + class FilterTypeUnknownError < Error; end + class FilterSyntaxInvalidError < Error; end + class EntryOverflowError < Error; end +end diff --git a/test/test_ldap_connection.rb b/test/test_ldap_connection.rb index 424f6bb3..96b542ac 100644 --- a/test/test_ldap_connection.rb +++ b/test/test_ldap_connection.rb @@ -2,14 +2,14 @@ class TestLDAPConnection < Test::Unit::TestCase def test_unresponsive_host - assert_raise Net::LDAP::LdapError do + assert_raise Net::LDAP::Error do Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636) end end def test_blocked_port flexmock(TCPSocket).should_receive(:new).and_raise(SocketError) - assert_raise Net::LDAP::LdapError do + assert_raise Net::LDAP::Error do Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636) end end From beac837aa8a8b408b8a12f9cda1ec3bf7b6a836c Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Sun, 4 Jan 2015 21:27:43 +0900 Subject: [PATCH 3/9] Replace Net::LDAP::LdapError with Net::LDAP::Error in net/ldap/connection.rb --- lib/net/ldap/connection.rb | 54 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/net/ldap/connection.rb b/lib/net/ldap/connection.rb index 0e3d7e05..929415c8 100644 --- a/lib/net/ldap/connection.rb +++ b/lib/net/ldap/connection.rb @@ -12,13 +12,13 @@ def initialize(server) begin @conn = server[:socket] || TCPSocket.new(server[:host], server[:port]) rescue SocketError - raise Net::LDAP::LdapError, "No such address or other socket error." + raise Net::LDAP::Error, "No such address or other socket error." rescue Errno::ECONNREFUSED - raise Net::LDAP::LdapError, "Server #{server[:host]} refused connection on port #{server[:port]}." + raise Net::LDAP::Error, "Server #{server[:host]} refused connection on port #{server[:port]}." rescue Errno::EHOSTUNREACH => error - raise Net::LDAP::LdapError, "Host #{server[:host]} was unreachable (#{error.message})" + raise Net::LDAP::Error, "Host #{server[:host]} was unreachable (#{error.message})" rescue Errno::ETIMEDOUT - raise Net::LDAP::LdapError, "Connection to #{server[:host]} timed out." + raise Net::LDAP::Error, "Connection to #{server[:host]} timed out." end if server[:encryption] @@ -42,7 +42,7 @@ def close end def self.wrap_with_ssl(io, tls_options = {}) - raise Net::LDAP::LdapError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL + raise Net::LDAP::Error, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL ctx = OpenSSL::SSL::SSLContext.new @@ -67,7 +67,7 @@ def self.wrap_with_ssl(io, tls_options = {}) # successfully-opened @conn instance variable, which is a TCP connection. # Depending on the received arguments, we establish SSL, potentially # replacing the value of @conn accordingly. Don't generate any errors here - # if no encryption is requested. DO raise Net::LDAP::LdapError objects if encryption + # if no encryption is requested. DO raise Net::LDAP::Error objects if encryption # is requested and we have trouble setting it up. That includes if OpenSSL # is not set up on the machine. (Question: how does the Ruby OpenSSL # wrapper react in that case?) DO NOT filter exceptions raised by the @@ -105,16 +105,16 @@ def setup_encryption(args) pdu = queued_read(message_id) if pdu.nil? || pdu.app_tag != Net::LDAP::PDU::ExtendedResponse - raise Net::LDAP::LdapError, "no start_tls result" + raise Net::LDAP::Error, "no start_tls result" end if pdu.result_code.zero? @conn = self.class.wrap_with_ssl(@conn, args[:tls_options]) else - raise Net::LDAP::LdapError, "start_tls failed: #{pdu.result_code}" + raise Net::LDAP::Error, "start_tls failed: #{pdu.result_code}" end else - raise Net::LDAP::LdapError, "unsupported encryption method #{args[:method]}" + raise Net::LDAP::Error, "unsupported encryption method #{args[:method]}" end end @@ -225,7 +225,7 @@ def bind(auth) elsif meth == :gss_spnego bind_gss_spnego(auth) else - raise Net::LDAP::LdapError, "Unsupported auth method (#{meth})" + raise Net::LDAP::Error, "Unsupported auth method (#{meth})" end end end @@ -241,7 +241,7 @@ def bind_simple(auth) ["", ""] end - raise Net::LDAP::LdapError, "Invalid binding information" unless (user && psw) + raise Net::LDAP::Error, "Invalid binding information" unless (user && psw) message_id = next_msgid request = [ @@ -253,7 +253,7 @@ def bind_simple(auth) pdu = queued_read(message_id) if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult - raise Net::LDAP::LdapError, "no bind result" + raise Net::LDAP::Error, "no bind result" end pdu @@ -283,7 +283,7 @@ def bind_simple(auth) def bind_sasl(auth) mech, cred, chall = auth[:mechanism], auth[:initial_credential], auth[:challenge_response] - raise Net::LDAP::LdapError, "Invalid binding information" unless (mech && cred && chall) + raise Net::LDAP::Error, "Invalid binding information" unless (mech && cred && chall) message_id = next_msgid @@ -298,16 +298,16 @@ def bind_sasl(auth) pdu = queued_read(message_id) if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult - raise Net::LDAP::LdapError, "no bind result" + raise Net::LDAP::Error, "no bind result" end return pdu unless pdu.result_code == Net::LDAP::ResultCodeSaslBindInProgress - raise Net::LDAP::LdapError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges) + raise Net::LDAP::Error, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges) cred = chall.call(pdu.result_server_sasl_creds) } - raise Net::LDAP::LdapError, "why are we here?" + raise Net::LDAP::Error, "why are we here?" end private :bind_sasl @@ -326,7 +326,7 @@ def bind_gss_spnego(auth) require 'ntlm' user, psw = [auth[:username] || auth[:dn], auth[:password]] - raise Net::LDAP::LdapError, "Invalid binding information" unless (user && psw) + raise Net::LDAP::Error, "Invalid binding information" unless (user && psw) nego = proc { |challenge| t2_msg = NTLM::Message.parse(challenge) @@ -412,10 +412,10 @@ def search(args = nil) sort = args.fetch(:sort_controls, false) # arg validation - raise Net::LDAP::LdapError, "search base is required" unless base - raise Net::LDAP::LdapError, "invalid search-size" unless size >= 0 - raise Net::LDAP::LdapError, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope) - raise Net::LDAP::LdapError, "invalid alias dereferencing value" unless Net::LDAP::DerefAliasesArray.include?(deref) + raise Net::LDAP::Error, "search base is required" unless base + raise Net::LDAP::Error, "invalid search-size" unless size >= 0 + raise Net::LDAP::Error, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope) + raise Net::LDAP::Error, "invalid alias dereferencing value" unless Net::LDAP::DerefAliasesArray.include?(deref) # arg transforms filter = Net::LDAP::Filter.construct(filter) if filter.is_a?(String) @@ -527,7 +527,7 @@ def search(args = nil) end break else - raise Net::LDAP::LdapError, "invalid response-type in search: #{pdu.app_tag}" + raise Net::LDAP::Error, "invalid response-type in search: #{pdu.app_tag}" end end @@ -624,7 +624,7 @@ def modify(args) pdu = queued_read(message_id) if !pdu || pdu.app_tag != Net::LDAP::PDU::ModifyResponse - raise Net::LDAP::LdapError, "response missing or invalid" + raise Net::LDAP::Error, "response missing or invalid" end pdu @@ -638,7 +638,7 @@ def modify(args) # to the error message and the matched-DN returned by the server. #++ def add(args) - add_dn = args[:dn] or raise Net::LDAP::LdapError, "Unable to add empty DN" + add_dn = args[:dn] or raise Net::LDAP::Error, "Unable to add empty DN" add_attrs = [] a = args[:attributes] and a.each { |k, v| add_attrs << [ k.to_s.to_ber, Array(v).map { |m| m.to_ber}.to_ber_set ].to_ber_sequence @@ -651,7 +651,7 @@ def add(args) pdu = queued_read(message_id) if !pdu || pdu.app_tag != Net::LDAP::PDU::AddResponse - raise Net::LDAP::LdapError, "response missing or invalid" + raise Net::LDAP::Error, "response missing or invalid" end pdu @@ -674,7 +674,7 @@ def rename(args) pdu = queued_read(message_id) if !pdu || pdu.app_tag != Net::LDAP::PDU::ModifyRDNResponse - raise Net::LDAP::LdapError.new "response missing or invalid" + raise Net::LDAP::Error.new "response missing or invalid" end pdu @@ -693,7 +693,7 @@ def delete(args) pdu = queued_read(message_id) if !pdu || pdu.app_tag != Net::LDAP::PDU::DeleteResponse - raise Net::LDAP::LdapError, "response missing or invalid" + raise Net::LDAP::Error, "response missing or invalid" end pdu From b412ca05f6b430eaa1ce97ac95885b4cf187b04a Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Wed, 7 Jan 2015 08:43:48 +0900 Subject: [PATCH 4/9] Use SecureRandam to generate salt --- lib/net/ldap/password.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/net/ldap/password.rb b/lib/net/ldap/password.rb index 3669dfcc..729e17e3 100644 --- a/lib/net/ldap/password.rb +++ b/lib/net/ldap/password.rb @@ -27,7 +27,7 @@ def generate(type, str) when :sha attribute_value = '{SHA}' + Base64.encode64(Digest::SHA1.digest(str)).chomp! when :ssha - srand; salt = (rand * 1000).to_i.to_s + srand; salt = SecureRandom.random_bytes(16) attribute_value = '{SSHA}' + Base64.encode64(Digest::SHA1.digest(str + salt) + salt).chomp! else raise Net::LDAP::HashTypeUnsupportedError, "Unsupported password-hash type (#{type})" From c9d36cdf919e01996da5c61838c10c1bc59f3e81 Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Wed, 7 Jan 2015 08:51:11 +0900 Subject: [PATCH 5/9] Redefine Net::LDAP::LdapError to support backward compatibility --- lib/net/ldap/error.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/net/ldap/error.rb b/lib/net/ldap/error.rb index bd2c74ad..a5704b59 100644 --- a/lib/net/ldap/error.rb +++ b/lib/net/ldap/error.rb @@ -1,4 +1,10 @@ class Net::LDAP + class LdapError < StandardError + def message + "Deprecation warning: Net::LDAP::LdapError is no longer used. Use Net::LDAP::Error or rescue one of it's subclasses. \n" + super + end + end + class Error < StandardError; end class AlreadyOpenedError < Error; end From 120d8c8bf33b950ee4ada8059acb879aa33b6e4e Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Wed, 7 Jan 2015 09:10:34 +0900 Subject: [PATCH 6/9] Replace Net::LDAP::Error with proper subsclasses --- lib/net/ldap/connection.rb | 42 +++++++++++++++++++------------------- lib/net/ldap/error.rb | 1 + 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/lib/net/ldap/connection.rb b/lib/net/ldap/connection.rb index 929415c8..f23fa106 100644 --- a/lib/net/ldap/connection.rb +++ b/lib/net/ldap/connection.rb @@ -42,7 +42,7 @@ def close end def self.wrap_with_ssl(io, tls_options = {}) - raise Net::LDAP::Error, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL + raise Net::LDAP::NoOpenSSLError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL ctx = OpenSSL::SSL::SSLContext.new @@ -105,16 +105,16 @@ def setup_encryption(args) pdu = queued_read(message_id) if pdu.nil? || pdu.app_tag != Net::LDAP::PDU::ExtendedResponse - raise Net::LDAP::Error, "no start_tls result" + raise Net::LDAP::NoStartTLSResultError, "no start_tls result" end if pdu.result_code.zero? @conn = self.class.wrap_with_ssl(@conn, args[:tls_options]) else - raise Net::LDAP::Error, "start_tls failed: #{pdu.result_code}" + raise Net::LDAP::StartTlSError, "start_tls failed: #{pdu.result_code}" end else - raise Net::LDAP::Error, "unsupported encryption method #{args[:method]}" + raise Net::LDAP::EncMethodUnsupportedError, "unsupported encryption method #{args[:method]}" end end @@ -225,7 +225,7 @@ def bind(auth) elsif meth == :gss_spnego bind_gss_spnego(auth) else - raise Net::LDAP::Error, "Unsupported auth method (#{meth})" + raise Net::LDAP::AuthMethodUnsupportedError, "Unsupported auth method (#{meth})" end end end @@ -241,7 +241,7 @@ def bind_simple(auth) ["", ""] end - raise Net::LDAP::Error, "Invalid binding information" unless (user && psw) + raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw) message_id = next_msgid request = [ @@ -253,7 +253,7 @@ def bind_simple(auth) pdu = queued_read(message_id) if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult - raise Net::LDAP::Error, "no bind result" + raise Net::LDAP::NoBindResultError, "no bind result" end pdu @@ -283,7 +283,7 @@ def bind_simple(auth) def bind_sasl(auth) mech, cred, chall = auth[:mechanism], auth[:initial_credential], auth[:challenge_response] - raise Net::LDAP::Error, "Invalid binding information" unless (mech && cred && chall) + raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (mech && cred && chall) message_id = next_msgid @@ -298,16 +298,16 @@ def bind_sasl(auth) pdu = queued_read(message_id) if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult - raise Net::LDAP::Error, "no bind result" + raise Net::LDAP::NoBindResultError, "no bind result" end return pdu unless pdu.result_code == Net::LDAP::ResultCodeSaslBindInProgress - raise Net::LDAP::Error, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges) + raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges) cred = chall.call(pdu.result_server_sasl_creds) } - raise Net::LDAP::Error, "why are we here?" + raise Net::LDAP::SASLChallengeOverflowError, "why are we here?" end private :bind_sasl @@ -326,7 +326,7 @@ def bind_gss_spnego(auth) require 'ntlm' user, psw = [auth[:username] || auth[:dn], auth[:password]] - raise Net::LDAP::Error, "Invalid binding information" unless (user && psw) + raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw) nego = proc { |challenge| t2_msg = NTLM::Message.parse(challenge) @@ -412,9 +412,9 @@ def search(args = nil) sort = args.fetch(:sort_controls, false) # arg validation - raise Net::LDAP::Error, "search base is required" unless base - raise Net::LDAP::Error, "invalid search-size" unless size >= 0 - raise Net::LDAP::Error, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope) + raise Net::LDAP::NoSearchBaseError, "search base is required" unless base + raise Net::LDAP::SearchSizeInvalidError, "invalid search-size" unless size >= 0 + raise Net::LDAP::SearchScopeInvalidError, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope) raise Net::LDAP::Error, "invalid alias dereferencing value" unless Net::LDAP::DerefAliasesArray.include?(deref) # arg transforms @@ -527,7 +527,7 @@ def search(args = nil) end break else - raise Net::LDAP::Error, "invalid response-type in search: #{pdu.app_tag}" + raise Net::LDAP::ResponseTypeInvalidError, "invalid response-type in search: #{pdu.app_tag}" end end @@ -624,7 +624,7 @@ def modify(args) pdu = queued_read(message_id) if !pdu || pdu.app_tag != Net::LDAP::PDU::ModifyResponse - raise Net::LDAP::Error, "response missing or invalid" + raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid" end pdu @@ -638,7 +638,7 @@ def modify(args) # to the error message and the matched-DN returned by the server. #++ def add(args) - add_dn = args[:dn] or raise Net::LDAP::Error, "Unable to add empty DN" + add_dn = args[:dn] or raise Net::LDAP::EmptyDNError, "Unable to add empty DN" add_attrs = [] a = args[:attributes] and a.each { |k, v| add_attrs << [ k.to_s.to_ber, Array(v).map { |m| m.to_ber}.to_ber_set ].to_ber_sequence @@ -651,7 +651,7 @@ def add(args) pdu = queued_read(message_id) if !pdu || pdu.app_tag != Net::LDAP::PDU::AddResponse - raise Net::LDAP::Error, "response missing or invalid" + raise Net::LDAP::ResponseMissingError, "response missing or invalid" end pdu @@ -674,7 +674,7 @@ def rename(args) pdu = queued_read(message_id) if !pdu || pdu.app_tag != Net::LDAP::PDU::ModifyRDNResponse - raise Net::LDAP::Error.new "response missing or invalid" + raise Net::LDAP::ResponseMissingOrInvalidError.new "response missing or invalid" end pdu @@ -693,7 +693,7 @@ def delete(args) pdu = queued_read(message_id) if !pdu || pdu.app_tag != Net::LDAP::PDU::DeleteResponse - raise Net::LDAP::Error, "response missing or invalid" + raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid" end pdu diff --git a/lib/net/ldap/error.rb b/lib/net/ldap/error.rb index a5704b59..c9a25f90 100644 --- a/lib/net/ldap/error.rb +++ b/lib/net/ldap/error.rb @@ -12,6 +12,7 @@ class SocketError < Error; end class ConnectionRefusedError < Error; end class NoOpenSSLError < Error; end class NoStartTLSResultError < Error; end + class NoSearchBaseError < Error; end class StartTLSError < Error; end class EncryptionUnsupportedError < Error; end class EncMethodUnsupportedError < Error; end From 20d3a430747472f14b99a6487cfab026b1dcc493 Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Fri, 9 Jan 2015 23:30:34 +0900 Subject: [PATCH 7/9] Raise ArgumentError in arg validation of Net::LDAP::Connection#search --- lib/net/ldap/connection.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/net/ldap/connection.rb b/lib/net/ldap/connection.rb index f23fa106..23d9f1e4 100644 --- a/lib/net/ldap/connection.rb +++ b/lib/net/ldap/connection.rb @@ -412,10 +412,10 @@ def search(args = nil) sort = args.fetch(:sort_controls, false) # arg validation - raise Net::LDAP::NoSearchBaseError, "search base is required" unless base - raise Net::LDAP::SearchSizeInvalidError, "invalid search-size" unless size >= 0 - raise Net::LDAP::SearchScopeInvalidError, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope) - raise Net::LDAP::Error, "invalid alias dereferencing value" unless Net::LDAP::DerefAliasesArray.include?(deref) + raise ArgumentError, "search base is required" unless base + raise ArgumentError, "invalid search-size" unless size >= 0 + raise ArgumentError, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope) + raise ArgumentError, "invalid alias dereferencing value" unless Net::LDAP::DerefAliasesArray.include?(deref) # arg transforms filter = Net::LDAP::Filter.construct(filter) if filter.is_a?(String) From 94d2d6a2b79764ee4de3dfad05dcef8dde99a0d4 Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Sat, 10 Jan 2015 07:25:10 +0900 Subject: [PATCH 8/9] Remove the dangling srand --- lib/net/ldap/password.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/net/ldap/password.rb b/lib/net/ldap/password.rb index 729e17e3..28406f03 100644 --- a/lib/net/ldap/password.rb +++ b/lib/net/ldap/password.rb @@ -23,11 +23,11 @@ class << self def generate(type, str) case type when :md5 - attribute_value = '{MD5}' + Base64.encode64(Digest::MD5.digest(str)).chomp! + attribute_value = '{MD5}' + Base64.encode64(Digest::MD5.digest(str)).chomp! when :sha - attribute_value = '{SHA}' + Base64.encode64(Digest::SHA1.digest(str)).chomp! + attribute_value = '{SHA}' + Base64.encode64(Digest::SHA1.digest(str)).chomp! when :ssha - srand; salt = SecureRandom.random_bytes(16) + salt = SecureRandom.random_bytes(16) attribute_value = '{SSHA}' + Base64.encode64(Digest::SHA1.digest(str + salt) + salt).chomp! else raise Net::LDAP::HashTypeUnsupportedError, "Unsupported password-hash type (#{type})" From 5a2b2cac2e6e6bc64168bc4a39b0fe8a8cdbb630 Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Sat, 10 Jan 2015 07:26:42 +0900 Subject: [PATCH 9/9] Correct trailing white spaces --- lib/net/ldap/filter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/net/ldap/filter.rb b/lib/net/ldap/filter.rb index 355e0f29..0ab847b8 100644 --- a/lib/net/ldap/filter.rb +++ b/lib/net/ldap/filter.rb @@ -310,8 +310,8 @@ def parse_ber(ber) present?(ber.to_s) when 0xa9 # context-specific constructed 9, "extensible comparison" raise Net::LDAP::SearchFilterError, "Invalid extensible search filter, should be at least two elements" if ber.size < 2 - - # Reassembles the extensible filter parts + + # Reassembles the extensible filter parts # (["sn", "2.4.6.8.10", "Barbara Jones", '1']) type = value = dn = rule = nil ber.each do |element|