net-ldap 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of net-ldap might be problematic. Click here for more details.

@@ -1,3 +1,9 @@
1
+ === Net::LDAP 0.16.0
2
+
3
+ * Sasl fix {#281}[https://github.com/ruby-ldap/ruby-net-ldap/pull/281]
4
+ * enable TLS hostname validation {#279}[https://github.com/ruby-ldap/ruby-net-ldap/pull/279]
5
+ * update rubocop to 0.42.0 {#278}[https://github.com/ruby-ldap/ruby-net-ldap/pull/278]
6
+
1
7
  === Net::LDAP 0.15.0
2
8
 
3
9
  * Respect connect_timeout when establishing SSL connections {#273}[https://github.com/ruby-ldap/ruby-net-ldap/pull/273]
@@ -52,12 +52,10 @@ This task will run the test suite and the
52
52
 
53
53
  rake rubotest
54
54
 
55
- To run the integration tests against an LDAP server:
56
-
57
- cd test/support/vm/openldap
58
- vagrant up
59
- cd ../../../..
60
- INTEGRATION=openldap bundle exec rake rubotest
55
+ CI takes too long? If your local box supports
56
+ {Vagrant}[https://www.vagrantup.com/], you can run most of the tests
57
+ in a VM on your local box. For more details and setup instructions, see
58
+ {test/support/vm/openldap/README.md}[https://github.com/ruby-ldap/ruby-net-ldap/tree/master/test/support/vm/openldap/README.md]
61
59
 
62
60
  == Release
63
61
 
@@ -327,11 +327,10 @@ class Net::BER::BerIdentifiedString < String
327
327
  # Check the encoding of the newly created String and set the encoding
328
328
  # to 'UTF-8' (NOTE: we do NOT change the bytes, but only set the
329
329
  # encoding to 'UTF-8').
330
+ return unless encoding == Encoding::BINARY
330
331
  current_encoding = encoding
331
- if current_encoding == Encoding::BINARY
332
- force_encoding('UTF-8')
333
- force_encoding(current_encoding) unless valid_encoding?
334
- end
332
+ force_encoding('UTF-8')
333
+ force_encoding(current_encoding) unless valid_encoding?
335
334
  end
336
335
  end
337
336
 
@@ -172,10 +172,10 @@ module Net::BER::BERParser
172
172
  yield id, content_length if block_given?
173
173
 
174
174
  if -1 == content_length
175
- raise Net::BER::BerError, "Indeterminite BER content length not implemented."
176
- else
177
- data = read(content_length)
175
+ raise Net::BER::BerError,
176
+ "Indeterminite BER content length not implemented."
178
177
  end
178
+ data = read(content_length)
179
179
 
180
180
  parse_ber_object(syntax, id, data)
181
181
  end
@@ -476,61 +476,73 @@ class Net::LDAP
476
476
  # specify a treebase. If you give a treebase value in any particular
477
477
  # call to #search, that value will override any treebase value you give
478
478
  # here.
479
+ # * :force_no_page => Set to true to prevent paged results even if your
480
+ # server says it supports them. This is a fix for MS Active Directory
481
+ # * :instrumentation_service => An object responsible for instrumenting
482
+ # operations, compatible with ActiveSupport::Notifications' public API.
479
483
  # * :encryption => specifies the encryption to be used in communicating
480
484
  # with the LDAP server. The value must be a Hash containing additional
481
485
  # parameters, which consists of two keys:
482
486
  # method: - :simple_tls or :start_tls
483
- # options: - Hash of options for that method
487
+ # tls_options: - Hash of options for that method
484
488
  # The :simple_tls encryption method encrypts <i>all</i> communications
485
489
  # with the LDAP server. It completely establishes SSL/TLS encryption with
486
490
  # the LDAP server before any LDAP-protocol data is exchanged. There is no
487
491
  # plaintext negotiation and no special encryption-request controls are
488
492
  # sent to the server. <i>The :simple_tls option is the simplest, easiest
489
493
  # way to encrypt communications between Net::LDAP and LDAP servers.</i>
490
- # It's intended for cases where you have an implicit level of trust in the
491
- # authenticity of the LDAP server. No validation of the LDAP server's SSL
492
- # certificate is performed. This means that :simple_tls will not produce
493
- # errors if the LDAP server's encryption certificate is not signed by a
494
- # well-known Certification Authority. If you get communications or
495
- # protocol errors when using this option, check with your LDAP server
496
- # administrator. Pay particular attention to the TCP port you are
497
- # connecting to. It's impossible for an LDAP server to support plaintext
498
- # LDAP communications and <i>simple TLS</i> connections on the same port.
499
- # The standard TCP port for unencrypted LDAP connections is 389, but the
500
- # standard port for simple-TLS encrypted connections is 636. Be sure you
501
- # are using the correct port.
502
- #
494
+ # If you get communications or protocol errors when using this option,
495
+ # check with your LDAP server administrator. Pay particular attention
496
+ # to the TCP port you are connecting to. It's impossible for an LDAP
497
+ # server to support plaintext LDAP communications and <i>simple TLS</i>
498
+ # connections on the same port. The standard TCP port for unencrypted
499
+ # LDAP connections is 389, but the standard port for simple-TLS
500
+ # encrypted connections is 636. Be sure you are using the correct port.
503
501
  # The :start_tls like the :simple_tls encryption method also encrypts all
504
502
  # communcations with the LDAP server. With the exception that it operates
505
503
  # over the standard TCP port.
506
504
  #
507
- # In order to verify certificates and enable other TLS options, the
508
- # :tls_options hash can be passed alongside :simple_tls or :start_tls.
509
- # This hash contains any options that can be passed to
510
- # OpenSSL::SSL::SSLContext#set_params(). The most common options passed
511
- # should be OpenSSL::SSL::SSLContext::DEFAULT_PARAMS, or the :ca_file option,
512
- # which contains a path to a Certificate Authority file (PEM-encoded).
513
- #
514
- # Example for a default setup without custom settings:
515
- # {
516
- # :method => :simple_tls,
517
- # :tls_options => OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
518
- # }
519
- #
520
- # Example for specifying a CA-File and only allowing TLSv1.1 connections:
505
+ # To validate the LDAP server's certificate (a security must if you're
506
+ # talking over the public internet), you need to set :tls_options
507
+ # something like this...
521
508
  #
522
- # {
523
- # :method => :start_tls,
524
- # :tls_options => { :ca_file => "/etc/cafile.pem", :ssl_version => "TLSv1_1" }
509
+ # Net::LDAP.new(
510
+ # # ... set host, bind dn, etc ...
511
+ # encryption: {
512
+ # method: :simple_tls,
513
+ # tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS,
525
514
  # }
526
- # * :force_no_page => Set to true to prevent paged results even if your
527
- # server says it supports them. This is a fix for MS Active Directory
528
- # * :instrumentation_service => An object responsible for instrumenting
529
- # operations, compatible with ActiveSupport::Notifications' public API.
515
+ # )
516
+ #
517
+ # The above will use the operating system-provided store of CA
518
+ # certificates to validate your LDAP server's cert.
519
+ # If cert validation fails, it'll happen during the #bind
520
+ # whenever you first try to open a connection to the server.
521
+ # Those methods will throw Net::LDAP::ConnectionError with
522
+ # a message about certificate verify failing. If your
523
+ # LDAP server's certificate is signed by DigiCert, Comodo, etc.,
524
+ # you're probably good. If you've got a self-signed cert but it's
525
+ # been added to the host's OS-maintained CA store (e.g. on Debian
526
+ # add foobar.crt to /usr/local/share/ca-certificates/ and run
527
+ # `update-ca-certificates`), then the cert should pass validation.
528
+ # To ignore the OS's CA store, put your CA in a PEM-encoded file and...
529
+ #
530
+ # encryption: {
531
+ # method: :simple_tls,
532
+ # tls_options: { ca_file: '/path/to/my-little-ca.pem',
533
+ # ssl_version: 'TLSv1_1' },
534
+ # }
535
+ #
536
+ # As you might guess, the above example also fails the connection
537
+ # if the client can't negotiate TLS v1.1.
538
+ # tls_options is ultimately passed to OpenSSL::SSL::SSLContext#set_params
539
+ # For more details, see
540
+ # http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html
530
541
  #
531
542
  # Instantiating a Net::LDAP object does <i>not</i> result in network
532
543
  # traffic to the LDAP server. It simply stores the connection and binding
533
- # parameters in the object.
544
+ # parameters in the object. That's why Net::LDAP.new doesn't throw
545
+ # cert validation errors itself; #bind does instead.
534
546
  def initialize(args = {})
535
547
  @host = args[:host] || DefaultHost
536
548
  @port = args[:port] || DefaultPort
@@ -1286,11 +1298,9 @@ class Net::LDAP
1286
1298
  else
1287
1299
  begin
1288
1300
  conn = new_connection
1289
- if (result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
1290
- yield conn
1291
- else
1292
- return result
1293
- end
1301
+ result = conn.bind(args[:auth] || @auth)
1302
+ return result unless result.result_code == Net::LDAP::ResultCodeSuccess
1303
+ yield conn
1294
1304
  ensure
1295
1305
  conn.close if conn
1296
1306
  end
@@ -4,6 +4,8 @@ module Net
4
4
  class LDAP
5
5
  class AuthAdapter
6
6
  class Sasl < Net::LDAP::AuthAdapter
7
+ MAX_SASL_CHALLENGES = 10
8
+
7
9
  #--
8
10
  # Required parameters: :mechanism, :initial_credential and
9
11
  # :challenge_response
@@ -47,7 +49,7 @@ module Net
47
49
  end
48
50
 
49
51
  return pdu unless pdu.result_code == Net::LDAP::ResultCodeSaslBindInProgress
50
- raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
52
+ raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MAX_SASL_CHALLENGES)
51
53
 
52
54
  cred = chall.call(pdu.result_server_sasl_creds)
53
55
  end
@@ -7,7 +7,6 @@ class Net::LDAP::Connection #:nodoc:
7
7
  DefaultConnectTimeout = 5
8
8
 
9
9
  LdapVersion = 3
10
- MaxSaslChallenges = 10
11
10
 
12
11
  # Initialize a connection to an LDAP server
13
12
  #
@@ -52,6 +51,15 @@ class Net::LDAP::Connection #:nodoc:
52
51
  hosts.each do |host, port|
53
52
  begin
54
53
  prepare_socket(server.merge(socket: @socket_class.new(host, port, socket_opts)), timeout)
54
+ if encryption
55
+ if encryption[:tls_options] &&
56
+ encryption[:tls_options][:verify_mode] &&
57
+ encryption[:tls_options][:verify_mode] == OpenSSL::SSL::VERIFY_NONE
58
+ warn "not verifying SSL hostname of LDAPS server '#{host}:#{port}'"
59
+ else
60
+ @conn.post_connection_check(host)
61
+ end
62
+ end
55
63
  return
56
64
  rescue Net::LDAP::Error, SocketError, SystemCallError,
57
65
  OpenSSL::SSL::SSLError => e
@@ -95,17 +103,13 @@ class Net::LDAP::Connection #:nodoc:
95
103
  conn.connect
96
104
  end
97
105
  rescue IO::WaitReadable
98
- if IO.select([conn], nil, nil, timeout)
99
- retry
100
- else
101
- raise Errno::ETIMEDOUT, "OpenSSL connection read timeout"
102
- end
106
+ raise Errno::ETIMEDOUT, "OpenSSL connection read timeout" unless
107
+ IO.select([conn], nil, nil, timeout)
108
+ retry
103
109
  rescue IO::WaitWritable
104
- if IO.select(nil, [conn], nil, timeout)
105
- retry
106
- else
107
- raise Errno::ETIMEDOUT, "OpenSSL connection write timeout"
108
- end
110
+ raise Errno::ETIMEDOUT, "OpenSSL connection write timeout" unless
111
+ IO.select(nil, [conn], nil, timeout)
112
+ retry
109
113
  end
110
114
 
111
115
  # Doesn't work:
@@ -163,11 +167,9 @@ class Net::LDAP::Connection #:nodoc:
163
167
  raise Net::LDAP::NoStartTLSResultError, "no start_tls result"
164
168
  end
165
169
 
166
- if pdu.result_code.zero?
167
- @conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout)
168
- else
169
- raise Net::LDAP::StartTLSError, "start_tls failed: #{pdu.result_code}"
170
- end
170
+ raise Net::LDAP::StartTLSError,
171
+ "start_tls failed: #{pdu.result_code}" unless pdu.result_code.zero?
172
+ @conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout)
171
173
  else
172
174
  raise Net::LDAP::EncMethodUnsupportedError, "unsupported encryption method #{args[:method]}"
173
175
  end
@@ -197,12 +199,10 @@ class Net::LDAP::Connection #:nodoc:
197
199
 
198
200
  # read messages until we have a match for the given message_id
199
201
  while pdu = read
200
- if pdu.message_id == message_id
201
- return pdu
202
- else
203
- message_queue[pdu.message_id].push pdu
204
- next
205
- end
202
+ return pdu if pdu.message_id == message_id
203
+
204
+ message_queue[pdu.message_id].push pdu
205
+ next
206
206
  end
207
207
 
208
208
  pdu
@@ -400,12 +400,11 @@ class Net::LDAP::Connection #:nodoc:
400
400
  # should collect this into a private helper to clarify the structure
401
401
  query_limit = 0
402
402
  if size > 0
403
- if paged
404
- query_limit = (((size - n_results) < 126) ? (size -
405
- n_results) : 0)
406
- else
407
- query_limit = size
408
- end
403
+ query_limit = if paged
404
+ (((size - n_results) < 126) ? (size - n_results) : 0)
405
+ else
406
+ size
407
+ end
409
408
  end
410
409
 
411
410
  request = [
@@ -169,11 +169,10 @@ class Net::LDAP::DN
169
169
  end
170
170
 
171
171
  # Last pair
172
- if [:value, :value_normal, :value_hexstring, :value_end].include? state
173
- yield key.string.strip, value.string.rstrip
174
- else
175
- raise "DN badly formed"
176
- end
172
+ raise "DN badly formed" unless
173
+ [:value, :value_normal, :value_hexstring, :value_end].include? state
174
+
175
+ yield key.string.strip, value.string.rstrip
177
176
  end
178
177
 
179
178
  ##
@@ -140,11 +140,10 @@ class Net::LDAP::Entry
140
140
  # arguments to the block: a Symbol giving the name of the attribute, and a
141
141
  # (possibly empty) \Array of data values.
142
142
  def each # :yields: attribute-name, data-values-array
143
- if block_given?
144
- attribute_names.each do|a|
145
- attr_name, values = a, self[a]
146
- yield attr_name, values
147
- end
143
+ return unless block_given?
144
+ attribute_names.each do|a|
145
+ attr_name, values = a, self[a]
146
+ yield attr_name, values
148
147
  end
149
148
  end
150
149
  alias_method :each_attribute, :each
@@ -1,5 +1,5 @@
1
1
  module Net
2
2
  class LDAP
3
- VERSION = "0.15.0"
3
+ VERSION = "0.16.0"
4
4
  end
5
5
  end
@@ -31,7 +31,7 @@ the most recent LDAP RFCs (4510-4519, plutions of 4520-4532).}
31
31
 
32
32
  s.add_development_dependency("flexmock", "~> 1.3")
33
33
  s.add_development_dependency("rake", "~> 10.0")
34
- s.add_development_dependency("rubocop", "~> 0.28.0")
34
+ s.add_development_dependency("rubocop", "~> 0.42.0")
35
35
  s.add_development_dependency("test-unit")
36
36
  s.add_development_dependency("byebug")
37
37
  end
@@ -0,0 +1,48 @@
1
+ #!/bin/bash
2
+
3
+ BASE_PATH=$( cd "`dirname $0`/../test/fixtures/ca" && pwd )
4
+ cd "${BASE_PATH}" || exit 4
5
+
6
+ USAGE=$( cat << EOS
7
+ Usage:
8
+ $0 --regenerate
9
+
10
+ Generates a new self-signed CA, for integration testing. This should only need
11
+ to be run if you are writing new TLS/SSL tests, and need to generate
12
+ additional fixtuer CAs.
13
+
14
+ This script uses the GnuTLS certtool CLI. If you are on macOS,
15
+ 'brew install gnutls', and it will be installed as 'gnutls-certtool'.
16
+ Apple unfortunately ships with an incompatible /usr/bin/certtool that does
17
+ different things.
18
+ EOS
19
+ )
20
+
21
+ if [ "x$1" != 'x--regenerate' ]; then
22
+ echo "${USAGE}"
23
+ exit 1
24
+ fi
25
+
26
+ TOOL=`type -p certtool`
27
+ if [ "$(uname)" = "Darwin" ]; then
28
+ TOOL=`type -p gnutls-certtool`
29
+ if [ ! -x "${TOOL}" ]; then
30
+ echo "Sorry, Darwin requires gnutls-certtool; try `brew install gnutls`"
31
+ exit 2
32
+ fi
33
+ fi
34
+
35
+ if [ ! -x "${TOOL}" ]; then
36
+ echo "Sorry, no certtool found!"
37
+ exit 3
38
+ fi
39
+ export TOOL
40
+
41
+
42
+ ${TOOL} --generate-privkey > ./cakey.pem
43
+ ${TOOL} --generate-self-signed \
44
+ --load-privkey ./cakey.pem \
45
+ --template ./ca.info \
46
+ --outfile ./cacert.pem
47
+
48
+ echo "cert and private key generated! Don't forget to check them in"
@@ -2,8 +2,8 @@
2
2
  set -e
3
3
  set -x
4
4
 
5
- BASE_PATH="$( cd `dirname $0`/../test/fixtures/openldap && pwd )"
6
- SEED_PATH="$( cd `dirname $0`/../test/fixtures && pwd )"
5
+ BASE_PATH=$( cd "`dirname $0`/../test/fixtures/openldap" && pwd )
6
+ SEED_PATH=$( cd "`dirname $0`/../test/fixtures" && pwd )
7
7
 
8
8
  dpkg -s slapd time ldap-utils gnutls-bin ssl-cert > /dev/null ||\
9
9
  DEBIAN_FRONTEND=noninteractive apt-get update -y --force-yes && \
@@ -48,47 +48,58 @@ chown -R openldap.openldap /var/lib/ldap
48
48
  rm -rf $TMPDIR
49
49
 
50
50
  # SSL
51
+ export CA_CERT="/usr/local/share/ca-certificates/rubyldap-ca.crt"
52
+ export CA_KEY="/etc/ssl/private/rubyldap-ca.key"
51
53
 
52
- sh -c "certtool --generate-privkey > /etc/ssl/private/cakey.pem"
54
+ # The self-signed fixture CA cert & key are generated by
55
+ # `script/generate-fiuxture-ca` and checked into version control.
56
+ # You shouldn't need to muck with these unless you're writing more
57
+ # TLS/SSL integration tests, and need special magic values in the cert.
53
58
 
54
- sh -c "cat > /etc/ssl/ca.info <<EOF
55
- cn = rubyldap
56
- ca
57
- cert_signing_key
58
- EOF"
59
+ cp "${SEED_PATH}/ca/cacert.pem" "${CA_CERT}"
60
+ cp "${SEED_PATH}/ca/cakey.pem" "${CA_KEY}"
59
61
 
60
- # Create the self-signed CA certificate:
61
- certtool --generate-self-signed \
62
- --load-privkey /etc/ssl/private/cakey.pem \
63
- --template /etc/ssl/ca.info \
64
- --outfile /etc/ssl/certs/cacert.pem
62
+ # actually add the fixture CA to the system store
63
+ update-ca-certificates
65
64
 
66
65
  # Make a private key for the server:
67
66
  certtool --generate-privkey \
68
- --bits 1024 \
69
- --outfile /etc/ssl/private/ldap01_slapd_key.pem
67
+ --bits 1024 \
68
+ --outfile /etc/ssl/private/ldap01_slapd_key.pem
70
69
 
71
70
  sh -c "cat > /etc/ssl/ldap01.info <<EOF
72
71
  organization = Example Company
73
72
  cn = ldap01.example.com
73
+ dns_name = ldap01.example.com
74
+ dns_name = ldap02.example.com
75
+ dns_name = localhost
74
76
  tls_www_server
75
77
  encryption_key
76
78
  signing_key
77
79
  expiration_days = 3650
78
80
  EOF"
79
81
 
82
+ # The integration server may be accessed by IP address, in which case
83
+ # we want some of the IPs included in the cert. We skip loopback (127.0.0.1)
84
+ # because that's the IP we use in the integration test for cert name mismatches.
85
+ ADDRS=$(ifconfig -a | grep 'inet addr:' | cut -f 2 -d : | cut -f 1 -d ' ')
86
+ for ip in $ADDRS; do
87
+ if [ "x$ip" = 'x127.0.0.1' ]; then continue; fi
88
+ echo "ip_address = $ip" >> /etc/ssl/ldap01.info
89
+ done
90
+
80
91
  # Create the server certificate
81
92
  certtool --generate-certificate \
82
93
  --load-privkey /etc/ssl/private/ldap01_slapd_key.pem \
83
- --load-ca-certificate /etc/ssl/certs/cacert.pem \
84
- --load-ca-privkey /etc/ssl/private/cakey.pem \
94
+ --load-ca-certificate "${CA_CERT}" \
95
+ --load-ca-privkey "${CA_KEY}" \
85
96
  --template /etc/ssl/ldap01.info \
86
97
  --outfile /etc/ssl/certs/ldap01_slapd_cert.pem
87
98
 
88
99
  ldapmodify -Y EXTERNAL -H ldapi:/// <<EOF | true
89
100
  dn: cn=config
90
101
  add: olcTLSCACertificateFile
91
- olcTLSCACertificateFile: /etc/ssl/certs/cacert.pem
102
+ olcTLSCACertificateFile: ${CA_CERT}
92
103
  -
93
104
  add: olcTLSCertificateFile
94
105
  olcTLSCertificateFile: /etc/ssl/certs/ldap01_slapd_cert.pem
@@ -110,6 +121,14 @@ chmod g+r /etc/ssl/private/ldap01_slapd_key.pem
110
121
  chmod o-r /etc/ssl/private/ldap01_slapd_key.pem
111
122
 
112
123
  # Drop packets on a secondary port used to specific timeout tests
113
- iptables -A OUTPUT -p tcp -j DROP --dport 8389
124
+ iptables -A INPUT -p tcp -j DROP --dport 8389
125
+
126
+ # Forward a port for Vagrant
127
+ iptables -t nat -A PREROUTING -p tcp --dport 9389 -j REDIRECT --to-port 389
128
+
129
+ # fix up /etc/hosts for cert validation
130
+ grep ldap01 /etc/hosts || echo "127.0.0.1 ldap01.example.com" >> /etc/hosts
131
+ grep ldap02 /etc/hosts || echo "127.0.0.1 ldap02.example.com" >> /etc/hosts
132
+ grep bogus /etc/hosts || echo "127.0.0.1 bogus.example.com" >> /etc/hosts
114
133
 
115
134
  service slapd restart