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.



86
87
88
89
# File 'lib/rex/proto/ldap/client.rb', line 86

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

Instance Method Details

#_openObject

Raises:

  • (Net::LDAP::AlreadyOpenedError)


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

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



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/rex/proto/ldap/client.rb', line 116

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



105
106
107
108
109
110
111
112
113
114
# File 'lib/rex/proto/ldap/client.rb', line 105

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 released. This is not the only occurrence of a patch for this functionality.



138
139
140
141
142
143
144
145
146
147
# File 'lib/rex/proto/ldap/client.rb', line 138

def ldapwhoami(args = {})
  instrument "ldapwhoami.net_ldap", args do |payload|
    @result = use_connection(args, &:ldapwhoami)
    if @result.success?
      @result.extended_response
    else
      raise Net::LDAP::Error, "#{peerinfo} LDAP Error: #{@result.error_message}"
    end
  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

#passwordObject



69
70
71
# File 'lib/rex/proto/ldap/client.rb', line 69

def password
  @auth[:password]
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

#realmObject



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

def realm
  @auth[:domain]
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



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

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

#usernameObject



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

def username
  @auth[:username]
end