ruby-net-ldap 0.0.2 → 0.0.3

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.
Files changed (4) hide show
  1. data/ChangeLog +4 -0
  2. data/lib/net/ber.rb +17 -1
  3. data/lib/net/ldap.rb +104 -10
  4. metadata +2 -2
data/ChangeLog CHANGED
@@ -1,5 +1,9 @@
1
1
  = Net::LDAP Changelog
2
2
 
3
+ == Net::LDAP 0.0.3: July 26, 2006
4
+ * Added simple TLS encryption.
5
+ Thanks to Garett Shulman for suggestions and for helping test.
6
+
3
7
  == Net::LDAP 0.0.2: July 12, 2006
4
8
  * Fixed malformation in distro tarball and gem.
5
9
  * Improved documentation.
@@ -1,4 +1,4 @@
1
- # $Id: ber.rb 93 2006-05-01 06:40:48Z blackhedd $
1
+ # $Id: ber.rb 142 2006-07-26 12:20:33Z blackhedd $
2
2
  #
3
3
  # NET::BER
4
4
  # Mixes ASN.1/BER convenience methods into several standard classes.
@@ -139,6 +139,22 @@ class StringIO
139
139
  include Net::BER::BERParser
140
140
  end
141
141
 
142
+ begin
143
+ require 'openssl'
144
+ class OpenSSL::SSL::SSLSocket
145
+ include Net::BER::BERParser
146
+ end
147
+ rescue LoadError
148
+ # Ignore LoadError.
149
+ # DON'T ignore NameError, which means the SSLSocket class
150
+ # is somehow unavailable on this implementation of Ruby's openssl.
151
+ # This may be WRONG, however, because we don't yet know how Ruby's
152
+ # openssl behaves on machines with no OpenSSL library. I suppose
153
+ # it's possible they do not fail to require 'openssl' but do not
154
+ # create the classes. So this code is provisional.
155
+ # Also, you might think that OpenSSL::SSL::SSLSocket inherits from
156
+ # IO so we'd pick it up above. But you'd be wrong.
157
+ end
142
158
 
143
159
  class String
144
160
  def read_ber syntax=nil
@@ -1,4 +1,4 @@
1
- # $Id: ldap.rb 141 2006-07-12 10:37:37Z blackhedd $
1
+ # $Id: ldap.rb 144 2006-07-26 21:35:38Z blackhedd $
2
2
  #
3
3
  # Net::LDAP for Ruby
4
4
  #
@@ -18,6 +18,13 @@
18
18
 
19
19
  require 'socket'
20
20
  require 'ostruct'
21
+
22
+ begin
23
+ require 'openssl'
24
+ $net_ldap_openssl_available = true
25
+ rescue LoadError
26
+ end
27
+
21
28
  require 'net/ber'
22
29
  require 'net/ldap/pdu'
23
30
  require 'net/ldap/filter'
@@ -256,7 +263,7 @@ module Net
256
263
 
257
264
  class LdapError < Exception; end
258
265
 
259
- VERSION = "0.0.2"
266
+ VERSION = "0.0.3"
260
267
 
261
268
 
262
269
  SearchScope_BaseObject = 0
@@ -348,7 +355,7 @@ module Net
348
355
 
349
356
 
350
357
  # Instantiate an object of type Net::LDAP to perform directory operations.
351
- # This constructor takes a Hash containing arguments. The following arguments
358
+ # This constructor takes a Hash containing arguments, all of which are either optional or may be specified later with other methods as described below. The following arguments
352
359
  # are supported:
353
360
  # * :host => the LDAP server's IP-address (default 127.0.0.1)
354
361
  # * :port => the LDAP server's TCP port (default 389)
@@ -356,6 +363,8 @@ module Net
356
363
  # {:method => :anonymous} and
357
364
  # {:method => :simple, :username => your_user_name, :password => your_password }
358
365
  # The password parameter may be a Proc that returns a String.
366
+ # * :base => a default treebase parameter for searches performed against the LDAP server. If you don't give this value, then each call to #search must specify a treebase parameter. If you do give this value, then it will be used in subsequent calls to #search that do not specify a treebase. If you give a treebase value in any particular call to #search, that value will override any treebase value you give here.
367
+ # * :encryption => specifies the encryption to be used in communicating with the LDAP server. The value is either a Hash containing additional parameters, or the Symbol :simple_tls, which is equivalent to specifying the Hash {:method => :simple_tls}. There is a fairly large range of potential values that may be given for this parameter. See #encryption for details.
359
368
  #
360
369
  # Instantiating a Net::LDAP object does <i>not</i> result in network traffic to
361
370
  # the LDAP server. It simply stores the connection and binding parameters in the
@@ -367,6 +376,7 @@ module Net
367
376
  @verbose = false # Make this configurable with a switch on the class.
368
377
  @auth = args[:auth] || DefaultAuth
369
378
  @base = args[:base] || DefaultTreebase
379
+ encryption args[:encryption] # may be nil
370
380
 
371
381
  if pr = @auth[:password] and pr.respond_to?(:call)
372
382
  @auth[:password] = pr.call
@@ -417,6 +427,51 @@ module Net
417
427
 
418
428
  alias_method :auth, :authenticate
419
429
 
430
+ # Convenience method to specify encryption characteristics for connections
431
+ # to LDAP servers. Called implicitly by #new and #open, but may also be called
432
+ # by user code if desired.
433
+ # The single argument is generally a Hash (but see below for convenience alternatives).
434
+ # This implementation is currently a stub, supporting only a few encryption
435
+ # alternatives. As additional capabilities are added, more configuration values
436
+ # will be added here.
437
+ #
438
+ # Currently, the only supported argument is {:method => :simple_tls}.
439
+ # (Equivalently, you may pass the symbol :simple_tls all by itself, without
440
+ # enclosing it in a Hash.)
441
+ #
442
+ # The :simple_tls encryption method encrypts <i>all</i> communications with the LDAP
443
+ # server.
444
+ # It completely establishes SSL/TLS encryption with the LDAP server
445
+ # before any LDAP-protocol data is exchanged.
446
+ # There is no plaintext negotiation and no special encryption-request controls
447
+ # are sent to the server.
448
+ # <i>The :simple_tls option is the simplest, easiest way to encrypt communications
449
+ # between Net::LDAP and LDAP servers.</i>
450
+ # It's intended for cases where you have an implicit level of trust in the authenticity
451
+ # of the LDAP server. No validation of the LDAP server's SSL certificate is
452
+ # performed. This means that :simple_tls will not produce errors if the LDAP
453
+ # server's encryption certificate is not signed by a well-known Certification
454
+ # Authority.
455
+ # If you get communications or protocol errors when using this option, check
456
+ # with your LDAP server administrator. Pay particular attention to the TCP port
457
+ # you are connecting to. It's impossible for an LDAP server to support plaintext
458
+ # LDAP communications and <i>simple TLS</i> connections on the same port.
459
+ # The standard TCP port for unencrypted LDAP connections is 389, but the standard
460
+ # port for simple-TLS encrypted connections is 636. Be sure you are using the
461
+ # correct port.
462
+ #
463
+ # <i>[Note: a future version of Net::LDAP will support the STARTTLS LDAP control,
464
+ # which will enable encrypted communications on the same TCP port used for
465
+ # unencrypted connections.]</i>
466
+ #
467
+ def encryption args
468
+ if args == :simple_tls
469
+ args = {:method => :simple_tls}
470
+ end
471
+ @encryption = args
472
+ end
473
+
474
+
420
475
  # #open takes the same parameters as #new. #open makes a network connection to the
421
476
  # LDAP server and then passes a newly-created Net::LDAP object to the caller-supplied block.
422
477
  # Within the block, you can call any of the instance methods of Net::LDAP to
@@ -484,7 +539,7 @@ module Net
484
539
  # if the bind was unsuccessful.
485
540
  def open
486
541
  raise LdapError.new( "open already in progress" ) if @open_connection
487
- @open_connection = Connection.new( :host => @host, :port => @port )
542
+ @open_connection = Connection.new( :host => @host, :port => @port, :encryption => @encryption )
488
543
  @open_connection.bind @auth
489
544
  yield self
490
545
  @open_connection.close
@@ -579,7 +634,7 @@ module Net
579
634
  }
580
635
  else
581
636
  @result = 0
582
- conn = Connection.new( :host => @host, :port => @port )
637
+ conn = Connection.new( :host => @host, :port => @port, :encryption => @encryption )
583
638
  if (@result = conn.bind( args[:auth] || @auth )) == 0
584
639
  @result = conn.search( args ) {|entry|
585
640
  result_set << entry if result_set
@@ -641,7 +696,7 @@ module Net
641
696
  if @open_connection
642
697
  @result = @open_connection.bind @auth
643
698
  else
644
- conn = Connection.new( :host => @host, :port => @port )
699
+ conn = Connection.new( :host => @host, :port => @port , :encryption => @encryption)
645
700
  @result = conn.bind @auth
646
701
  conn.close
647
702
  end
@@ -692,7 +747,7 @@ module Net
692
747
  @result = @open_connection.add( args )
693
748
  else
694
749
  @result = 0
695
- conn = Connection.new( :host => @host, :port => @port )
750
+ conn = Connection.new( :host => @host, :port => @port, :encryption => @encryption)
696
751
  if (@result = conn.bind( args[:auth] || @auth )) == 0
697
752
  @result = conn.add( args )
698
753
  end
@@ -776,7 +831,7 @@ module Net
776
831
  @result = @open_connection.modify( args )
777
832
  else
778
833
  @result = 0
779
- conn = Connection.new( :host => @host, :port => @port )
834
+ conn = Connection.new( :host => @host, :port => @port, :encryption => @encryption )
780
835
  if (@result = conn.bind( args[:auth] || @auth )) == 0
781
836
  @result = conn.modify( args )
782
837
  end
@@ -848,7 +903,7 @@ module Net
848
903
  @result = @open_connection.rename( args )
849
904
  else
850
905
  @result = 0
851
- conn = Connection.new( :host => @host, :port => @port )
906
+ conn = Connection.new( :host => @host, :port => @port, :encryption => @encryption )
852
907
  if (@result = conn.bind( args[:auth] || @auth )) == 0
853
908
  @result = conn.rename( args )
854
909
  end
@@ -878,7 +933,7 @@ module Net
878
933
  @result = @open_connection.delete( args )
879
934
  else
880
935
  @result = 0
881
- conn = Connection.new( :host => @host, :port => @port )
936
+ conn = Connection.new( :host => @host, :port => @port, :encryption => @encryption )
882
937
  if (@result = conn.bind( args[:auth] || @auth )) == 0
883
938
  @result = conn.delete( args )
884
939
  end
@@ -908,10 +963,49 @@ module Net
908
963
  raise LdapError.new( "no connection to server" )
909
964
  end
910
965
 
966
+ if server[:encryption]
967
+ setup_encryption server[:encryption]
968
+ end
969
+
911
970
  yield self if block_given?
912
971
  end
913
972
 
914
973
 
974
+ #--
975
+ # Helper method called only from new, and only after we have a successfully-opened
976
+ # @conn instance variable, which is a TCP connection.
977
+ # Depending on the received arguments, we establish SSL, potentially replacing
978
+ # the value of @conn accordingly.
979
+ # Don't generate any errors here if no encryption is requested.
980
+ # DO raise LdapError objects if encryption is requested and we have trouble setting
981
+ # it up. That includes if OpenSSL is not set up on the machine. (Question:
982
+ # how does the Ruby OpenSSL wrapper react in that case?)
983
+ # DO NOT filter exceptions raised by the OpenSSL library. Let them pass back
984
+ # to the user. That should make it easier for us to debug the problem reports.
985
+ # Presumably (hopefully?) that will also produce recognizable errors if someone
986
+ # tries to use this on a machine without OpenSSL.
987
+ #
988
+ # The simple_tls method is intended as the simplest, stupidest, easiest solution
989
+ # for people who want nothing more than encrypted comms with the LDAP server.
990
+ # It doesn't do any server-cert validation and requires nothing in the way
991
+ # of key files and root-cert files, etc etc.
992
+ # OBSERVE: WE REPLACE the value of @conn, which is presumed to be a connected
993
+ # TCPsocket object.
994
+ #
995
+ def setup_encryption args
996
+ case args[:method]
997
+ when :simple_tls
998
+ raise LdapError.new("openssl unavailable") unless $net_ldap_openssl_available
999
+ ctx = OpenSSL::SSL::SSLContext.new
1000
+ @conn = OpenSSL::SSL::SSLSocket.new(@conn, ctx)
1001
+ @conn.connect
1002
+ @conn.sync_close = true
1003
+ # additional branches requiring server validation and peer certs, etc. go here.
1004
+ else
1005
+ raise LdapError.new( "unsupported encryption method #{args[:method]}" )
1006
+ end
1007
+ end
1008
+
915
1009
  #--
916
1010
  # close
917
1011
  # This is provided as a convenience method to make
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: ruby-net-ldap
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.2
7
- date: 2006-07-12 00:00:00 -04:00
6
+ version: 0.0.3
7
+ date: 2006-07-27 00:00:00 -04:00
8
8
  summary: A pure Ruby LDAP client library.
9
9
  require_paths:
10
10
  - lib