Class: Rex::Proto::LDAP::Client

Inherits:
Net::LDAP
  • Object
show all
Defined in:
lib/rex/proto/ldap/client.rb

Overview

This is a Rex Proto wrapper around the Net::LDAP client which is currently coming from the ‘net-ldap’ gem. The purpose of this wrapper is to provide ‘peerhost’ and ‘peerport’ methods to ensure the client interfaces are consistent between various session clients.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args) ⇒ Client

Returns a new instance of Client.



20
21
22
23
24
25
# File 'lib/rex/proto/ldap/client.rb', line 20

def initialize(args)
  @base_dn = args[:base]
  @last_interaction = nil
  @connection_use_mutex = Mutex.new
  super
end

Instance Attribute Details

#connection_use_mutexObject (readonly)

Mutex

Control access to the connection. One at a time.



18
19
20
# File 'lib/rex/proto/ldap/client.rb', line 18

def connection_use_mutex
  @connection_use_mutex
end

#last_interactionObject (readonly)

Time

The last time an interaction occurred on the connection (for keep-alive purposes)



15
16
17
# File 'lib/rex/proto/ldap/client.rb', line 15

def last_interaction
  @last_interaction
end

#socketRex::Socket (readonly)

Returns:

  • (Rex::Socket)


12
13
14
# File 'lib/rex/proto/ldap/client.rb', line 12

def socket
  @socket
end

Class Method Details

._open(connect_opts) ⇒ Object

github.com/ruby-ldap/ruby-net-ldap/issues/11 We want to keep the ldap connection open to use later but there’s no built in way within the ‘Net::LDAP` library to do that so we’re adding this function to do it instead

Parameters:

  • connect_opts (Hash)

    Options for the LDAP connection.



74
75
76
77
# File 'lib/rex/proto/ldap/client.rb', line 74

def self._open(connect_opts)
  client = new(connect_opts)
  client._open
end

Instance Method Details

#_openObject

Raises:

  • (Net::LDAP::AlreadyOpenedError)


80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/rex/proto/ldap/client.rb', line 80

def _open
  raise Net::LDAP::AlreadyOpenedError, 'Open already in progress' if @open_connection

  instrument 'open.net_ldap' do |payload|
    @open_connection = new_connection
    @socket = @open_connection.socket
    payload[:connection] = @open_connection
    payload[:bind] = @result = @open_connection.bind(@auth)
    register_interaction
    return self
  end
end

#base_dnString

Returns LDAP servers Base DN.

Returns:

  • (String)

    LDAP servers Base DN



37
38
39
# File 'lib/rex/proto/ldap/client.rb', line 37

def base_dn
  @base_dn ||= discover_base_dn
end

#discover_base_dnObject



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/rex/proto/ldap/client.rb', line 104

def discover_base_dn
  unless naming_contexts
    elog("#{peerinfo} Base DN cannot be determined, no naming contexts available")
    return
  end

  # NOTE: Find the first entry that starts with `DC=` as this will likely be the base DN.
  result = naming_contexts.select { |context| context =~ /^([Dd][Cc]=[A-Za-z0-9-]+,?)+$/ }
                          .reject { |context| context =~ /(Configuration)|(Schema)|(ForestDnsZones)/ }
  if result.blank?
    elog("#{peerinfo} A base DN matching the expected format could not be found!")
    return
  end
  base_dn = result[0]

  dlog("#{peerinfo} Discovered base DN: #{base_dn}")
  base_dn
end

#discover_schema_naming_contextObject



93
94
95
96
97
98
99
100
101
102
# File 'lib/rex/proto/ldap/client.rb', line 93

def discover_schema_naming_context
  result = search(base: '', attributes: [:schemanamingcontext], scope: Net::LDAP::SearchScope_BaseObject)
  if result.first && !result.first[:schemanamingcontext].empty?
    schema_dn = result.first[:schemanamingcontext].first
    ilog("#{peerinfo} Discovered Schema DN: #{schema_dn}")
    return schema_dn
  end
  wlog("#{peerinfo} Could not discover Schema DN")
  nil
end

#ldapwhoami(args = {}) ⇒ Object

Monkeypatch upstream library to support the extended Whoami request. Delete this after github.com/ruby-ldap/ruby-net-ldap/pull/425 is landed. This is not the only occurrence of a patch for this functionality.



126
127
128
129
130
131
# File 'lib/rex/proto/ldap/client.rb', line 126

def ldapwhoami(args = {})
  instrument "ldapwhoami.net_ldap", args do |payload|
    @result = use_connection(args, &:ldapwhoami)
    @result.success? ? @result.extended_response : nil
  end
end

#naming_contextsArray<String>

Returns LDAP servers naming contexts.

Returns:

  • (Array<String>)

    LDAP servers naming contexts



32
33
34
# File 'lib/rex/proto/ldap/client.rb', line 32

def naming_contexts
  @naming_contexts ||= search_root_dse[:namingcontexts]
end

#peerhostString

Returns The remote IP address that LDAP is running on.

Returns:

  • (String)

    The remote IP address that LDAP is running on



47
48
49
# File 'lib/rex/proto/ldap/client.rb', line 47

def peerhost
  host
end

#peerinfoString

Returns The remote peer information containing IP and port.

Returns:

  • (String)

    The remote peer information containing IP and port



57
58
59
# File 'lib/rex/proto/ldap/client.rb', line 57

def peerinfo
  "#{peerhost}:#{peerport}"
end

#peerportInteger

Returns The remote port that LDAP is running on.

Returns:

  • (Integer)

    The remote port that LDAP is running on



52
53
54
# File 'lib/rex/proto/ldap/client.rb', line 52

def peerport
  port
end

#register_interactionObject



27
28
29
# File 'lib/rex/proto/ldap/client.rb', line 27

def register_interaction
  @last_interaction = Process.clock_gettime(Process::CLOCK_MONOTONIC)
end

#schema_dnString?

Returns LDAP servers Schema DN, nil if one isn’t found.

Returns:

  • (String, nil)

    LDAP servers Schema DN, nil if one isn’t found



42
43
44
# File 'lib/rex/proto/ldap/client.rb', line 42

def schema_dn
  @schema_dn ||= discover_schema_naming_context
end

#use_connection(args) ⇒ Object



61
62
63
64
65
66
67
# File 'lib/rex/proto/ldap/client.rb', line 61

def use_connection(args)
  @connection_use_mutex.synchronize do
    return super(args)
  ensure
    register_interaction
  end
end